Skip to content
Snippets Groups Projects
viamapi-iframe.js 77.9 KiB
Newer Older
Sasha Ilieva's avatar
Sasha Ilieva committed
import "../lib/textencoder.polyfill";
import { parseSMIME, prepareVCardParts } from "../utilities/emailUtilities";
import {
  stringToUtf8ByteArray,
  utf8ByteArrayToString,
  stringToUtf8Base64,
  utf8Base64ToString,
  base64ToByteArray,
  byteArrayToBase64,
  hexStringToUtf8ByteArray
Alexey Lunin's avatar
Alexey Lunin committed
} from "../utilities/stringUtilities";
import { extractMessageID } from "../helpers/mailparser";
Alexey Lunin's avatar
Alexey Lunin committed
const Penpal = require("penpal").default;
import {
  createDeviceHash,
  destroyIdentityFromLocalStorage,
  encodeResponse,
Alexey Lunin's avatar
Alexey Lunin committed
  listIdentitiesFromLocalStorage,
  makeid
} from "../utilities/appUtility";
import { LOGIN_MODES } from "../constants/authentication";
Damyan Mitev's avatar
Damyan Mitev committed
  ImageData,
  createOneTimePassportCertificate,
  createPassportCertificate,
  decryptMessage,
  encryptMessage,
  parseCertificate,
  signEmail,
  verifySMIME
Alexey Lunin's avatar
Alexey Lunin committed
} from "../utilities/signingUtilities";
import { signPdf } from "../utilities/pdfUtilities";
import CryptoData from "../CryptoData";
import Identity from "../Identity";
import {
  STATUS_DEVICE_REVOKED,
Sasha Ilieva's avatar
Sasha Ilieva committed
  STATUS_USER_NOT_ACTIVATED,
  STATUS_USER_BLOCKED
} from "../constants/statuses";
import generateQrCode from "../utilities/generateQrCode";
Alexey Lunin's avatar
Alexey Lunin committed

const penpalMethods = require("../../temp/penpal-methods").default;
const WopiAPI = require("./wopiapi-iframe");
const CollaboraAPI = require("./collaboraapi-iframe");
const ViamAPI = require("../../temp/viamapi");
Zdravko Iliev's avatar
Zdravko Iliev committed
const identityColors = ["#994392", "#cb0767", "#e51d31", "#ec671b", "#fab610"];

function getNextColor() {
Alexey Lunin's avatar
Alexey Lunin committed
  let colorIndex = localStorage.getItem("colorIndex");
Markin Igor's avatar
Markin Igor committed
  if (colorIndex == null || colorIndex === "") {
Alexey Lunin's avatar
Alexey Lunin committed
    colorIndex = 0;
Zdravko Iliev's avatar
Zdravko Iliev committed
  const color = identityColors[colorIndex];
Markin Igor's avatar
Markin Igor committed
  colorIndex = colorIndex % identityColors.length;
Markin Igor's avatar
Markin Igor committed
  localStorage.setItem("colorIndex", colorIndex);
Alexey Lunin's avatar
Alexey Lunin committed
  return color;
}

function setKeyForUUID(uuid, key) {
Zdravko Iliev's avatar
Zdravko Iliev committed
  const storedIdentityForUuid = localStorage.getItem("keyperuuid/" + uuid);
Alexey Lunin's avatar
Alexey Lunin committed
  if (
    storedIdentityForUuid !== key &&
    storedIdentityForUuid != null &&
    storedIdentityForUuid !== ""
  ) {
    destroyIdentityFromLocalStorage(storedIdentityForUuid);
Alexey Lunin's avatar
Alexey Lunin committed
  localStorage.setItem("keyperuuid/" + uuid, key);
}

function getColorForIdentity(key) {
Alexey Lunin's avatar
Alexey Lunin committed
  let storedColor = localStorage.getItem("colors/" + key);
Alexey Lunin's avatar
Alexey Lunin committed
  if (storedColor == null || storedColor === "") {
Markin Igor's avatar
Markin Igor committed
    storedColor = getNextColor();
Alexey Lunin's avatar
Alexey Lunin committed
    localStorage.setItem("colors/" + key, storedColor);
Alexey Lunin's avatar
Alexey Lunin committed
  return storedColor;
}

function setIdentityInLocalStorage(identityToStore, extendKey = true) {
Alexey Lunin's avatar
Alexey Lunin committed
  let pinCode = identityToStore.pinCode;
  const serializedIdentity = JSON.stringify(identityToStore);
  const key = identityToStore.authentication.publicKey;

Alexey Lunin's avatar
Alexey Lunin committed
  if (pinCode == null || pinCode === "") {
    pinCode = getPincode(key);
Alexey Lunin's avatar
Alexey Lunin committed
  if (pinCode == null || pinCode === "") {
Alexey Lunin's avatar
Alexey Lunin committed
  return encryptMessage(serializedIdentity, pinCode, "identity").then(
    encryptedIdentity => {
Alexey Lunin's avatar
Alexey Lunin committed
      let success = true;
Alexey Lunin's avatar
Alexey Lunin committed
      if (extendKey === true) {
        success = extendPinCodeTtl(key, pinCode);
      }
      if (success === true) {
        localStorage.setItem(key, encryptedIdentity);
Zdravko Iliev's avatar
Zdravko Iliev committed
        const serializedIdentitiesList = localStorage.getItem("identities");
        const identities = JSON.parse(serializedIdentitiesList);
Alexey Lunin's avatar
Alexey Lunin committed
        identities[key] = true;
Alexey Lunin's avatar
Alexey Lunin committed
        localStorage.setItem("identities", JSON.stringify(identities));
      } else {
        console.log("Can not extend pincode ttl");
      }
Alexey Lunin's avatar
Alexey Lunin committed
  );
}

function getProfileData(identity) {
  return new Penpal.Promise(executeResultUpper => {
Alexey Lunin's avatar
Alexey Lunin committed
    executeRestfulFunction(
      "private",
      viamApi,
      viamApi.identityGetIdentityProfileData,
      null
    ).then(executeResult => {
      if (executeResult.code === "200") {
Zdravko Iliev's avatar
Zdravko Iliev committed
        const listItem = {};
Alexey Lunin's avatar
Alexey Lunin committed
        listItem.identityColor = getColorForIdentity(
          identity.authentication.publicKey
        );
Markin Igor's avatar
Markin Igor committed
        listItem.initials = executeResult.data.initials;
Alexey Lunin's avatar
Alexey Lunin committed
        if (listItem.initials === null || listItem.initials === "") {
          listItem.initials = "JD";
        }
Alexey Lunin's avatar
Alexey Lunin committed
        localStorage.setItem(
          "profiles/" + identity.authentication.publicKey,
          JSON.stringify(listItem)
        );
        executeResultUpper(listItem);
      } else {
Alexey Lunin's avatar
Alexey Lunin committed
        executeResultUpper({});
async function getIdentityFromLocalStorage(key, pinCode, extendTtl = true) {
  const encryptedIdentity = localStorage.getItem(key);
Markin Igor's avatar
Markin Igor committed
    console.log("No such identity for public key");
  const serializedIdentity = await decryptMessage(encryptedIdentity, pinCode);

  const identity = new Identity(serializedIdentity);
  if (extendTtl) {
    const success = extendPinCodeTtl(key, pinCode);
    if (!success) {
      console.log("Can not extend pincode ttl");
      return null;
    }
  }
function extendPinCodeTtl(key, pinCode) {
Alexey Lunin's avatar
Alexey Lunin committed
  if (pinCode == null || pinCode === "") {
Zdravko Iliev's avatar
Zdravko Iliev committed
    const now = new Date();
    const nowMillis = now.getTime();
    const ttl = window.sessionStorage.getItem("pincodettls/" + key);
    if (ttl == null || ttl === "" || nowMillis >= parseInt(ttl)) {
Markin Igor's avatar
Markin Igor committed
      clearPinCodeTtl(key);
Alexey Lunin's avatar
Alexey Lunin committed
      return false;
Zdravko Iliev's avatar
Zdravko Iliev committed
      const ttl = now.getTime() + 4 * 60 * 60 * 1000;
      window.sessionStorage.setItem("pincodettls/" + key, ttl);
    }
  } else {
Zdravko Iliev's avatar
Zdravko Iliev committed
    const now = new Date();
    const ttl = now.getTime() + 4 * 60 * 60 * 1000;
    window.sessionStorage.setItem("pincodettls/" + key, ttl);
    window.sessionStorage.setItem("pincodes/" + key, pinCode);
  }

  return true;
}

window.extendPinCodeTtl = extendPinCodeTtl;

function clearPinCodeTtl(key) {
Markin Igor's avatar
Markin Igor committed
  window.sessionStorage.removeItem("pincodettls/" + key);
Alexey Lunin's avatar
Alexey Lunin committed
  window.sessionStorage.removeItem("pincodes/" + key);
}

function getPincode(key) {
Zdravko Iliev's avatar
Zdravko Iliev committed
  const now = new Date();
  const nowMillis = now.getTime();
  const ttl = window.sessionStorage.getItem("pincodettls/" + key);
Markin Igor's avatar
Markin Igor committed
  if (ttl == null || ttl === "") {
Alexey Lunin's avatar
Alexey Lunin committed
    return null;
  } else {
Alexey Lunin's avatar
Alexey Lunin committed
    if (nowMillis >= parseInt(ttl)) {
Markin Igor's avatar
Markin Igor committed
      clearPinCodeTtl(key);
Alexey Lunin's avatar
Alexey Lunin committed
      return null;
    } else {
      return window.sessionStorage.getItem("pincodes/" + key);
    }
  }
}

function createEvent(actionId, type, payloads) {
  return {
Alexey Lunin's avatar
Alexey Lunin committed
    actionID: actionId,
Zdravko Iliev's avatar
Zdravko Iliev committed
    type,
Alexey Lunin's avatar
Alexey Lunin committed
    stamp: new Date().getTime(),
Zdravko Iliev's avatar
Zdravko Iliev committed
    payloads
Alexey Lunin's avatar
Alexey Lunin committed
  };
const destroyAuthentication = () => {
  const authenticationPublicKey = localStorage.getItem("authenticatedIdentity");

  window.viamApi.setIdentity("");
  window.viamApi.setSessionData("", "");

  clearPinCodeTtl(authenticationPublicKey);

  localStorage.removeItem("uuid");
  localStorage.removeItem("token");
  localStorage.removeItem("authenticatedIdentity");

  window.currentlyAuthenticatedIdentity = null;
  window.lastTimeGetProfile = 0;
};

const destroyIdentity = () => {
  destroyAuthentication();

  if (window.currentlyLoadedIdentity) {
    const { publicKey } = window.currentlyLoadedIdentity.authentication;

    delete window.loadedIdentities[publicKey];
    window.currentlyLoadedIdentity = null;
    destroyIdentityFromLocalStorage(publicKey);
  }
};

Markin Igor's avatar
Markin Igor committed
window.loadedIdentities = {};
window.wopiAPI = new WopiAPI();
window.collaboraApi = new CollaboraAPI();
window.viamApi = new ViamAPI();
window.viamAnonymousApi = new ViamAPI();
Markin Igor's avatar
Markin Igor committed
window.currentlyAuthenticatedIdentity = null;
window.currentlyLoadedIdentity = null;
window.lastTimeGetProfile = 0;
let iframeParent = null;

Markin Igor's avatar
Markin Igor committed
const handleIdentityLogin = (identity, uuid, token) => {
  const { loadedIdentities, viamApi } = window;
  const { publicKey } = identity.authentication;

  viamApi.setSessionData(uuid, token);
  localStorage.setItem("uuid", uuid);
  localStorage.setItem("token", token);
  localStorage.setItem("authenticatedIdentity", publicKey);
  window.currentlyAuthenticatedIdentity = loadedIdentities[publicKey];
  window.lastTimeGetProfile = 0;
  setKeyForUUID(uuid, publicKey);
async function executeRestfulFunction(type, that, fn, config, ...args) {
Alexey Lunin's avatar
Alexey Lunin committed
  const {
    currentlyAuthenticatedIdentity,
    viamApi,
    currentlyLoadedIdentity
  } = window;
  let response;
  try {
    response = await fn.apply(that, [config, ...args]);
  } catch (error) {
    if (error.response) {
      //Resposnse with status code != 2xx still has valid response
      response = error.response;
    } else {
      //Connection error or similar
      const data = {
Alexey Lunin's avatar
Alexey Lunin committed
        data: "",
        code: "999",
        status: error.message
  const identity = currentlyAuthenticatedIdentity || currentlyLoadedIdentity;
Sasha Ilieva's avatar
Sasha Ilieva committed

Alexey Lunin's avatar
Alexey Lunin committed
  const deviceRevoked =
    type === "private" && code === "401" && status === STATUS_DEVICE_REVOKED;
    const event = createEvent("", "DeviceRevoked");
    iframeParent.onEvent(event);
  const userNotActivated =
    type === "private" &&
    code === "400" &&
Sasha Ilieva's avatar
Sasha Ilieva committed
    (status === STATUS_USER_NOT_ACTIVATED || status === STATUS_USER_BLOCKED);

  if (userNotActivated) {
    destroyIdentity();

Sasha Ilieva's avatar
Sasha Ilieva committed
    const event = createEvent("", "UserBlocked");
    iframeParent.onEvent(event);

    return response.data;
  }

Alexey Lunin's avatar
Alexey Lunin committed
  const badSession =
    type === "private" &&
    identity &&
    code === "400" &&
    status === "Bad session";
Sasha Ilieva's avatar
Sasha Ilieva committed
  if (!badSession) {
    return response.data;
  }
Alexey Lunin's avatar
Alexey Lunin committed
  const loginResponse = await viamApi.identityLogin(
    null,
    "previousaddeddevice"
  );
Sasha Ilieva's avatar
Sasha Ilieva committed
  if (loginResponse.data.code !== "200") {
    return loginResponse.data;
  }

  const uuid = loginResponse.data.data["Uuid"];
  const token = loginResponse.data.data["Session"];
  handleIdentityLogin(identity, uuid, token);
  try {
    response = await fn.apply(that, [config, ...args]);
  } catch (error) {
    response = error.response;
  }
  return response.data;
}

window.executeRestfulFunction = executeRestfulFunction;

const TRANSPARENT_PIXEL = new ImageData({
  contentType: "image/png",
  contentBase64:
    "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" //1x1px transparent pixel
});

function loadIdentityInternal(identityKey, pinCode) {
  return new Penpal.Promise(result => {
Alexey Lunin's avatar
Alexey Lunin committed
    getIdentityFromLocalStorage(identityKey, pinCode)
      .then(async loadedIdentity => {
        if (loadedIdentity == null) {
          result({
            data: "",
            code: "400",
            status:
              "Please restore or authorize your account via another device."
          });
        }
        localStorage.removeItem("attempt");

        window.loadedIdentities[identityKey] = loadedIdentity;
        window.currentlyLoadedIdentity = loadedIdentity;

        if (identityKey === localStorage.getItem("authenticatedIdentity")) {
          window.currentlyAuthenticatedIdentity = loadedIdentity;
          const uuid = localStorage.getItem("uuid");
          const token = localStorage.getItem("token");
          const deviceHash = await createDeviceHash(identityKey);
          window.viamApi.setIdentity(identityKey);
          window.viamApi.setDeviceHash(deviceHash);
          window.viamApi.setSessionData(uuid, token);
        }
Alexey Lunin's avatar
Alexey Lunin committed
        window.viamAnonymousApi.setIdentity(
          window.currentlyLoadedIdentity.authentication.publicKey
        );
Alexey Lunin's avatar
Alexey Lunin committed
        const { publicKey, x509Certificate } = loadedIdentity.authentication;
Alexey Lunin's avatar
Alexey Lunin committed
        result({
          data: {
            authentication: {
              publicKey,
              x509Certificate
            }
          },
          code: "200",
          status: "Identity loaded"
        });
      })
      .catch(e => {
        result({
          data: "",
          code: "400",
          status: "" + e
        });
  });
}

function getCertificateForPassport(passportUUID, internal) {
  return new Penpal.Promise(certificateResult => {
    if (window.currentlyAuthenticatedIdentity === null) {
Sasha Ilieva's avatar
Sasha Ilieva committed
      return { data: "", code: "400", status: "Identity not authenticated" };
    const passportIdentity = window.currentlyAuthenticatedIdentity;
Zdravko Iliev's avatar
Zdravko Iliev committed
    const passport = passportIdentity.getPassport(passportUUID);
Alexey Lunin's avatar
Alexey Lunin committed
    if (passport === undefined || passport === null) {
Sasha Ilieva's avatar
Sasha Ilieva committed
      createPassportCertificate(passportUUID).then(function(keys) {
Zdravko Iliev's avatar
Zdravko Iliev committed
        const cryptoData = new CryptoData();
Markin Igor's avatar
Markin Igor committed
        cryptoData.setPublicKey(keys["publicKeyPEM"]);
        cryptoData.setPrivateKey(keys["privateKeyPEM"]);
Zdravko Iliev's avatar
Zdravko Iliev committed
        const certificate = keys["certificatePEM"];
        //download("passportCertificateBeforeSigning.crt", "text/plain", certificate)
        //cryptoData.setx509Certificate(keys["certificate"])
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "private",
          viamApi,
          viamApi.signSignCertificate,
          null,
          btoa(certificate),
          passportUUID
        ).then(executeResult => {
          if (executeResult.code === "200") {
Zdravko Iliev's avatar
Zdravko Iliev committed
            const signedCertificate = atob(
Alexey Lunin's avatar
Alexey Lunin committed
              executeResult.data["SignedCertificate"]
            );
            //download("passportCertificateAfterSigning.crt", "text/plain", signedCertificate)
Zdravko Iliev's avatar
Zdravko Iliev committed
            const keyUUID = executeResult.data["CertificateUUID"];
            const encodedChain = executeResult.data["Chain"];
            //download("rootCertificate.crt", "text/plain", atob(encodedChain[0]))

Zdravko Iliev's avatar
Zdravko Iliev committed
            const chain = [];
Alexey Lunin's avatar
Alexey Lunin committed
            for (let i = 0; i < encodedChain.length; i++) {
Alexey Lunin's avatar
Alexey Lunin committed
              chain.push(atob(encodedChain[i]));
Markin Igor's avatar
Markin Igor committed
            cryptoData.setx509Certificate(signedCertificate);
            cryptoData.setKeyUUID(keyUUID);
            cryptoData.setChain(chain);
Markin Igor's avatar
Markin Igor committed
            passportIdentity.setPassport(passportUUID, cryptoData);

            getProfileData(passportIdentity).then(executeResult1 => {
Alexey Lunin's avatar
Alexey Lunin committed
              setIdentityInLocalStorage(passportIdentity)
                .then(() => {
                  window.currentlyAuthenticatedIdentity = passportIdentity;
                  window.lastTimeGetProfile = 0;
                  window.currentlyLoadedIdentity = passportIdentity;
                  const copyOfCryptoData = JSON.parse(
                    JSON.stringify(cryptoData)
                  );

                  if (internal === false) {
                    copyOfCryptoData["privateKey"] = "";
                  }

                  certificateResult({
                    data: copyOfCryptoData,
                    code: "200",
                    status: "Certificate got"
                  });
                })
                .catch(e => {
                  certificateResult({
                    data: "",
                    code: "400",
                    status: "Can not store certificate " + e
                  });
            certificateResult(executeResult);
Zdravko Iliev's avatar
Zdravko Iliev committed
      const copyOfCryptoData = JSON.parse(JSON.stringify(passport));
Alexey Lunin's avatar
Alexey Lunin committed
      if (internal === false) {
        copyOfCryptoData["privateKey"] = "";
Alexey Lunin's avatar
Alexey Lunin committed
      certificateResult({
        data: copyOfCryptoData,
        code: "200",
        status: "Certificate got"
      });
    }
  });
}

const connection = Penpal.connectToParent({
  // Methods child is exposing to parent
  methods: {
    initialize: (apiUrl, wopiUrl, collaboraUrl) => {
      if (!apiUrl) {
        apiUrl = `${window.location.origin}/api/`;
        console.warn(`API host URL not specified. Fall back to ${apiUrl}`); // eslint-disable-line no-console
      }

      if (!wopiUrl) {
        wopiUrl = `${window.location.origin}/wopi/`;
        console.warn(`WOPI host URL not specified. Fall back to ${wopiUrl}`); // eslint-disable-line no-console
      }

      if (!collaboraUrl) {
        collaboraUrl = window.location.origin;
Alexey Lunin's avatar
Alexey Lunin committed
        console.warn(
          `Collabora host URL not specified. Fall back to ${collaboraUrl}`
        ); // eslint-disable-line no-console
Alexey Lunin's avatar
Alexey Lunin committed
      window.API_HOST =
        apiUrl.charAt(apiUrl.length - 1) === "/" ? apiUrl : apiUrl + "/";
      window.WOPI_URL =
        wopiUrl.charAt(wopiUrl.length - 1) === "/" ? wopiUrl : wopiUrl + "/";
      window.COLLABORA_URL =
Sasha Ilieva's avatar
Sasha Ilieva committed
        collaboraUrl.charAt(collaboraUrl.length - 1) === "/"
          ? collaboraUrl
          : collaboraUrl + "/";
    createIdentity(pinCode) {
      return new Penpal.Promise(result => {
Sasha Ilieva's avatar
Sasha Ilieva committed
        createPassportCertificate(makeid()).then(function(keys) {
Zdravko Iliev's avatar
Zdravko Iliev committed
          const newIdentity = new Identity();
          const cryptoData = new CryptoData();
Markin Igor's avatar
Markin Igor committed
          cryptoData.setPublicKey(keys["publicKeyPEM"]);
          cryptoData.setPrivateKey(keys["privateKeyPEM"]);
          cryptoData.setx509Certificate(keys["certificatePEM"]);
          newIdentity.setAuthentication(cryptoData);
          newIdentity.setPinCode(pinCode);
          window.currentlyLoadedIdentity = newIdentity;
          const { publicKey, x509Certificate } = newIdentity.authentication;
          window.loadedIdentities[publicKey] = newIdentity;
Markin Igor's avatar
Markin Igor committed
          extendPinCodeTtl(newIdentity.authentication.publicKey, pinCode);
Alexey Lunin's avatar
Alexey Lunin committed
          window.viamAnonymousApi.setIdentity(
            newIdentity.authentication.publicKey
          );
Alexey Lunin's avatar
Alexey Lunin committed
          result({
            data: {
              authentication: {
                publicKey,
                x509Certificate
              }
            },
Alexey Lunin's avatar
Alexey Lunin committed
            code: "200",
            status: "Identity created"
          });
Alexey Lunin's avatar
Alexey Lunin committed
      });
    },
    listIdentities() {
      return new Penpal.Promise(result => {
Zdravko Iliev's avatar
Zdravko Iliev committed
        const identities = listIdentitiesFromLocalStorage();
Sasha Ilieva's avatar
Sasha Ilieva committed
        result({ data: identities, code: "200", status: "Identities listed" });
      });
    },
    loadIdentity(identityKey, pinCode) {
Alexey Lunin's avatar
Alexey Lunin committed
      return loadIdentityInternal(identityKey, pinCode);
    checkIdentityPinCode: async (key, pinCode) => {
      try {
        const identity = await getIdentityFromLocalStorage(key, pinCode, false);

        if (identity) {
          return encodeResponse("200", null, "Pincode check successful");
        } else {
          return encodeResponse("400", null, "Pincode check failed");
        }
      } catch (e) {
        return encodeResponse("400", e, "Pincode check error");
      }
    },
    changeIdentityPinCode: async (key, oldPinCode, newPinCode) => {
      try {
Alexey Lunin's avatar
Alexey Lunin committed
        const identity = await getIdentityFromLocalStorage(
          key,
          oldPinCode,
          false
        );

        if (identity) {
          identity.pinCode = newPinCode;
          await setIdentityInLocalStorage(identity);
          window.currentlyAuthenticatedIdentity = identity;
          window.currentlyLoadedIdentity = identity;
igorwork's avatar
igorwork committed
          return encodeResponse("200", null, "Successfully changed pincode");
igorwork's avatar
igorwork committed
          return encodeResponse("400", null, "Identity not found");
igorwork's avatar
igorwork committed
        return encodeResponse("400", e.message, "Change pincode error");
    },
    getIdentityProfile(identityKey) {
      return new Penpal.Promise(result => {
Alexey Lunin's avatar
Alexey Lunin committed
        const serializedProfile = localStorage.getItem(
          "profiles/" + identityKey
        );
        if (serializedProfile === null || serializedProfile === "") {
Sasha Ilieva's avatar
Sasha Ilieva committed
          result({ data: "", code: "400", status: "Profile is empty" });
        } else {
Alexey Lunin's avatar
Alexey Lunin committed
          result({
            data: JSON.parse(serializedProfile),
            code: "200",
            status: "Identities cleared"
          });
    clearIdentities: async () => {
      destroyAuthentication();
      const identitiesTemp = listIdentitiesFromLocalStorage();

      for (const i in identitiesTemp) {
        destroyIdentityFromLocalStorage(i);
      }
      return encodeResponse("200", "", "Identities cleared");
    confirmIdentificator(identity, confirmationCodeArg) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(identity.authentication.publicKey);
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "public",
          viamApi,
          viamApi.identityConfirmIdentificator,
          null,
          confirmationCodeArg
        ).then(executeResult => {
          result(executeResult);
        });
      });
    },
    identityGetIdentificatorByRegisterToken(identity, tokenArg) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(identity.authentication.publicKey);
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "public",
          viamApi,
          viamApi.identityGetIdentificatorByRegisterToken,
          null,
          tokenArg
        ).then(executeResult => {
    identityCreateHollowIdentity: async (identity) => {
      viamApi.setIdentity(identity.authentication.publicKey);
      return executeRestfulFunction(
        "public",
        viamApi,
        viamApi.identityCreateHollowIdentity,
        null,
      )
    },
    submitIdentificator(identity, identificatorArg, registerToken) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(identity.authentication.publicKey);
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "public",
          viamApi,
          viamApi.identitySubmitIdentificator,
          null,
          identificatorArg,
          registerToken
        ).then(executeResult => {
Alexey Lunin's avatar
Alexey Lunin committed
    submitRegisterClaims(
      identity,
      givennameArg,
      familynameArg,
      emailArg,
      phonenumberArg
    ) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(identity.authentication.publicKey);
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "public",
          viamApi,
          viamApi.identitySubmitRegisterClaims,
          null,
          givennameArg,
          familynameArg,
          emailArg,
          phonenumberArg
        ).then(executeResult => {
    finalizeEmployeeRegistration: async (
      identity,
      identifier
    ) => {
      viamApi.setIdentity(identity.authentication.publicKey);
      return executeRestfulFunction(
        "public",
        viamApi,
        viamApi.identityFinalizeEmployeeRegistration,
        null,
        identifier
      );
    },
    agreeOnRegistration(registerIdentity) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(registerIdentity.authentication.publicKey);
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "public",
          viamApi,
          viamApi.identityAgreeOnRegistration,
          null
        ).then(executeResult => {
Markin Igor's avatar
Markin Igor committed
          let sequence = Promise.resolve();
          if (executeResult.code === "200") {
            sequence = sequence.then(() => {
Alexey Lunin's avatar
Alexey Lunin committed
              setIdentityInLocalStorage(registerIdentity);
            });
Alexey Lunin's avatar
Alexey Lunin committed
          sequence
            .then(() => {
              result(executeResult);
Alexey Lunin's avatar
Alexey Lunin committed
            .catch(e => {
              result({
                data: "",
                code: "400",
                status: "Can not store identity: " + e
              });
            });
        });
      });
    },
    resendConfirmationCode(identity, identificatorArg) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(identity.authentication.publicKey);
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "public",
          viamApi,
          viamApi.identityResendConfirmationCode,
          null,
          identificatorArg
        ).then(executeResult => {
          result(executeResult);
        });
      });
    },
Markin Igor's avatar
Markin Igor committed
    login: async (loginIdentity, mode, requestCode, requestActionID) => {
      if (!window.loadedIdentities[loginIdentity.authentication.publicKey]) {
        return {
          data: "",
          code: "400",
          status: "Identity not loaded"
        };
      }
Alexey Lunin's avatar
Alexey Lunin committed
      const deviceHash = await createDeviceHash(
        loginIdentity.authentication.publicKey
      );
      window.viamApi.setSessionData("", "");
Markin Igor's avatar
Markin Igor committed
      window.viamApi.setDeviceHash(deviceHash);
Markin Igor's avatar
Markin Igor committed
      window.viamApi.setIdentity(loginIdentity.authentication.publicKey);

Alexey Lunin's avatar
Alexey Lunin committed
      const identityLoginResponse = await executeRestfulFunction(
        "public",
        window.viamApi,
        window.viamApi.identityLogin,
        null,
        mode,
        requestCode,
        requestActionID
      );
Markin Igor's avatar
Markin Igor committed

      const { code, data } = identityLoginResponse;
      const responseToClient = Object.assign({}, identityLoginResponse);

      if (code === "200") {
Alexey Lunin's avatar
Alexey Lunin committed
        if (
          mode === LOGIN_MODES.SMS ||
          mode === LOGIN_MODES.PREVIOUSLY_ADDED_DEVICE
        ) {
Markin Igor's avatar
Markin Igor committed
          handleIdentityLogin(loginIdentity, data.Uuid, data.Session);
Markin Igor's avatar
Markin Igor committed
          await getProfileData(loginIdentity);

          if (mode === LOGIN_MODES.SMS) {
Markin Igor's avatar
Markin Igor committed
            await setIdentityInLocalStorage(loginIdentity);
Markin Igor's avatar
Markin Igor committed
        } else if (mode === LOGIN_MODES.NEW_DEVICE) {
          const dataUrl = await generateQrCode(
Alexey Lunin's avatar
Alexey Lunin committed
            `${data.ActionID},${data.QrCode}`
          );
Markin Igor's avatar
Markin Igor committed
          Object.assign(responseToClient.data, { image: dataUrl });
        }
      }

      return responseToClient;
    },
    identityAddNewDevice() {
      return new Penpal.Promise(result => {
Alexey Lunin's avatar
Alexey Lunin committed
        const authenticationPublicKey = localStorage.getItem(
          "authenticatedIdentity"
        );
        if (authenticationPublicKey === null) {
Alexey Lunin's avatar
Alexey Lunin committed
          result({
            data: "",
            code: "400",
            status: "Identity not authenticated"
          });
        if (window.loadedIdentities[authenticationPublicKey] === null) {
Alexey Lunin's avatar
Alexey Lunin committed
          result({
            data: "",
            code: "400",
            status: "Identity not authenticated"
          });
Zdravko Iliev's avatar
Zdravko Iliev committed
        const success = extendPinCodeTtl(authenticationPublicKey);
Alexey Lunin's avatar
Alexey Lunin committed
        if (success === false) {
          result({
            data: "",
            code: "400",
            status: "Identity not authenticated"
          });
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "private",
          viamApi,
          viamApi.identityAddNewDevice,
          null
        ).then(async executeResult => {
Markin Igor's avatar
Markin Igor committed
          if (executeResult.code === "200") {
Zdravko Iliev's avatar
Zdravko Iliev committed
            const actionID = executeResult.data["ActionID"];
            const QrCode = executeResult.data["QrCode"];
            const dataUrl = await generateQrCode(actionID + "," + QrCode);
            executeResult.data["image"] = dataUrl;
            result(executeResult);
          } else {
            result(executeResult);
          }
        });
      });
    },
    identityDestroyKeysForDevice(authenticationPublicKeyArg) {
      return new Penpal.Promise(result => {
Alexey Lunin's avatar
Alexey Lunin committed
        const authenticationPublicKey = localStorage.getItem(
          "authenticatedIdentity"
        );
        if (authenticationPublicKey === null) {
Alexey Lunin's avatar
Alexey Lunin committed
          result({
            data: "",
            code: "400",
            status: "Identity not authenticated"
          });
        if (window.loadedIdentities[authenticationPublicKey] === null) {
Alexey Lunin's avatar
Alexey Lunin committed
          result({
            data: "",
            code: "400",
            status: "Identity not authenticated"
          });
Zdravko Iliev's avatar
Zdravko Iliev committed
        const success = extendPinCodeTtl(authenticationPublicKey);
Alexey Lunin's avatar
Alexey Lunin committed
        if (success === false) {
          result({
            data: "",
            code: "400",
            status: "Identity not authenticated"
          });
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "private",
          viamApi,
          viamApi.identityDestroyKeysForDevice,
          null,
          btoa(authenticationPublicKeyArg)
        ).then(executeResult => {
          result(executeResult);
        });
      });
    },
Markin Igor's avatar
Markin Igor committed
    logout: async () => {
Alexey Lunin's avatar
Alexey Lunin committed
        const authenticationPublicKey = localStorage.getItem(
          "authenticatedIdentity"
        );
        if (
          !authenticationPublicKey ||
          !window.loadedIdentities[authenticationPublicKey]
        ) {
          return {
            data: "",
            code: "400",
            status: "Identity not loaded"
          };
        }

igorwork's avatar
igorwork committed
        // Clone headers to be able destroy authentication first.
        // We need it because clients should be able reload page right after logout invocation and not wait until request completed
Alexey Lunin's avatar
Alexey Lunin committed
        const headers = { ...window.viamApi.getConfig().headers };
        return executeRestfulFunction(
          "private",
          window.viamApi,
          window.viamApi.identityLogout,
Markin Igor's avatar
Markin Igor committed
        return {
          data: "",
          code: "400",
          status: e.message
Markin Igor's avatar
Markin Igor committed
        };
      }
    },
    identityRestoreAccess(restoreAccessIdentity, identificator) {
      return new Penpal.Promise(result => {
        viamApi.setSessionData("", "");
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity(restoreAccessIdentity.authentication.publicKey);
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "public",
          viamApi,
          viamApi.identityRestoreAccess,
          null,
          identificator
        ).then(executeResult => {
          result(executeResult);
    getCurrentlyLoggedInUUID() {
      return new Penpal.Promise(result => {
Alexey Lunin's avatar
Alexey Lunin committed
        const authenticationPublicKey = localStorage.getItem(
          "authenticatedIdentity"
        );
        if (authenticationPublicKey === null) {
Sasha Ilieva's avatar
Sasha Ilieva committed
          return { data: "", code: "400", status: "Identity not loaded" };
        if (window.loadedIdentities[authenticationPublicKey] === null) {
Sasha Ilieva's avatar
Sasha Ilieva committed
          return { data: "", code: "400", status: "Identity not loaded" };