Skip to content
Snippets Groups Projects
viamapi-iframe.js 70.8 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
Alexey Lunin's avatar
Alexey Lunin committed
} from "../utilities/stringUtilities";
import { extractMessageID } from "../helpers/mailparser";
Alexey Lunin's avatar
Alexey Lunin committed
const QRCode = require("qrcode");
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;
  const { code, status } = response.data;
Sasha Ilieva's avatar
Sasha Ilieva committed
  console.log({ response });
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;

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
                  });
                });
            });
          } else {
            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();
Markin Igor's avatar
Markin Igor committed

      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 => {
          result(executeResult);
        });
      });
    },
    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 => {
          result(executeResult);
        });
      });
    },
    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) {
Alexey Lunin's avatar
Alexey Lunin committed
          const dataUrl = await QRCode.toDataURL(
            `${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(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"];
Sasha Ilieva's avatar
Sasha Ilieva committed
            QRCode.toDataURL(actionID + "," + QrCode, function(err, url) {
Markin Igor's avatar
Markin Igor committed
              executeResult.data["image"] = url;
              result(executeResult);
Alexey Lunin's avatar
Alexey Lunin committed
            });
          } 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 };

        destroyAuthentication();
        return executeRestfulFunction(
          "private",
          window.viamApi,
          window.viamApi.identityLogout,
        );
      } catch (e) {
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" };
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
        if (localStorage.getItem("uuid") === null) {
Sasha Ilieva's avatar
Sasha Ilieva committed
          result({ data: "", code: "400", status: "Not logged in UUID" });
Alexey Lunin's avatar
Alexey Lunin committed
        result({
          data: localStorage.getItem("uuid"),
          code: "200",
          status: "UUID loaded"
        });
      });
    },
    getCertificateByPassport(passportUUID) {
      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" };
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
        getCertificateForPassport(passportUUID, false).then(
          certificateResult => {
            result(certificateResult);
          }
        );
      });
    },
    getOneTimeCertificateByPassport(passportUUID, emailArg) {
      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" };
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
        getCertificateForPassport(passportUUID, true).then(
          certificateResult => {
            if (certificateResult.code === "200") {
Zdravko Iliev's avatar
Zdravko Iliev committed
              const passportCertificate =
Alexey Lunin's avatar
Alexey Lunin committed
                certificateResult.data["x509Certificate"];
Zdravko Iliev's avatar
Zdravko Iliev committed
              const passportPrivateKey = certificateResult.data["privateKey"];
              const passportChain = certificateResult.data["chain"];
Alexey Lunin's avatar
Alexey Lunin committed

              createOneTimePassportCertificate(
                makeid() + "-" + passportUUID,
                emailArg,
                passportPrivateKey,
                passportCertificate
Sasha Ilieva's avatar
Sasha Ilieva committed
              ).then(function(keys) {
Zdravko Iliev's avatar
Zdravko Iliev committed
                const publicKeyOneTime = keys["publicKeyPEM"];
                const privateKeyOneTime = keys["privateKeyPEM"];
                const certificateOneTime = keys["certificatePEM"];
Alexey Lunin's avatar
Alexey Lunin committed
                passportChain.push(passportCertificate);

Zdravko Iliev's avatar
Zdravko Iliev committed
                const oneTimeCryptoData = new CryptoData();
Alexey Lunin's avatar
Alexey Lunin committed
                oneTimeCryptoData.setx509Certificate(certificateOneTime);
                oneTimeCryptoData.setPrivateKey(privateKeyOneTime);
                oneTimeCryptoData.setPublicKey(publicKeyOneTime);
                oneTimeCryptoData.setChain(passportChain);

                result({
                  data: oneTimeCryptoData,
                  code: "200",
                  status: "One time certificate generated"
                });
                // Prints PEM formatted signed certificate
                // -----BEGIN CERTIFICATE-----MIID....7Hyg==-----END CERTIFICATE-----
              });
            } else {
              result({
                data: "",
                code: "400",
                status: "Can not generate one time certificate"
              });
            }
Alexey Lunin's avatar
Alexey Lunin committed
        );
    verifySMIME: async smimeString => {
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );

      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

Damyan Mitev's avatar
Damyan Mitev committed
      //TODO cache (for some time) the root certificate
      // either as PEM or as certificate object (preferred)
      const rootCaResponse = await executeRestfulFunction(
        "private",
        window.viamApi,
        window.viamApi.signRetrieveRootCertificate,
        null
      );

      if (rootCaResponse.code !== "200") {
        return encodeResponse("400", "", rootCaResponse.status);
      }

      const rootCaPem = rootCaResponse.data;
Damyan Mitev's avatar
Damyan Mitev committed
      const verificationResult = await verifySMIME(smimeString, rootCaPem);
      return encodeResponse(
        "200",
        verificationResult.verified,
        verificationResult.message
      );
    validateDocument: async (documentUUID, contentType) => {
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );

      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

      const validateDocumentResponse = await executeRestfulFunction(
        "private",
        window.viamApi,
        window.viamApi.documentValidateDocumentByUUID,
        null,
        documentUUID,

      if (validateDocumentResponse.code !== "200") {
        return encodeResponse("400", "", validateDocumentResponse.status);
      }

      const signatures = validateDocumentResponse.data;
      if (signatures) {
        for (const signature of signatures) {
          const certificateChain = signature.certificateChainPEM.map(
            certificatePEM => {
              const certificate = parseCertificate(certificatePEM);
              const certificateData = new CertificateData(certificate);
              return certificateData;
            }
          );
          signature.certificateChain = certificateChain;
        }
    signEmail: async (passportUUID, emailArg, emailMessage) => {
Alexey Lunin's avatar
Alexey Lunin committed
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );
      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }
      let response = await getCertificateForPassport(passportUUID, true);
      if (response.code !== "200") {
        return encodeResponse("400", "", response.status);
      }
      const {
        x509Certificate: passportCertificate,
        privateKey: passportPrivateKey,
        chain: passportChain
      } = response.data;
Alexey Lunin's avatar
Alexey Lunin committed
      const keys = await createOneTimePassportCertificate(
        makeid() + "-" + passportUUID,
        emailArg,
        passportPrivateKey,
        passportCertificate
      );
Alexey Lunin's avatar
Alexey Lunin committed
      const {
        privateKeyPEM: privateKeyOneTime,
        certificatePEM: certificateOneTime
      } = keys;
      passportChain.push(passportCertificate);

      response = await executeRestfulFunction(
Alexey Lunin's avatar
Alexey Lunin committed
        "private",
        window.viamApi,
        window.viamApi.passportGetEmailWithHeaderByPassport,
        null,
        passportUUID,
        emailMessage
      );

      if (response.code !== "200") {
        return encodeResponse("400", "", response.status);
      }

Alexey Lunin's avatar
Alexey Lunin committed
      const signedEmail = await signEmail(
        response.data,
        certificateOneTime,
        passportChain,
        privateKeyOneTime
      );

      response = await executeRestfulFunction(
Alexey Lunin's avatar
Alexey Lunin committed
        "private",
        window.viamApi,
        window.viamApi.signResignEmail,
        null,
        passportUUID,
        signedEmail
      );

      if (response.code !== "200") {
        return encodeResponse("400", "", response.status);
      }

      return encodeResponse("200", response.data, "Email signed");
Damyan Mitev's avatar
Damyan Mitev committed
    signDocument: async (passportUUID, documentUUID, documentContentType) => {
Alexey Lunin's avatar
Alexey Lunin committed
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );
Damyan Mitev's avatar
Damyan Mitev committed

      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

      const certResponse = await getCertificateForPassport(passportUUID, true);
Damyan Mitev's avatar
Damyan Mitev committed

      if (certResponse.code !== "200") {
        return encodeResponse("400", "", certResponse.status);
Damyan Mitev's avatar
Damyan Mitev committed
      }

      const {
        x509Certificate: passportCertificate,
        privateKey: passportPrivateKey,
        chain: passportChain
      } = certResponse.data;
Damyan Mitev's avatar
Damyan Mitev committed

Alexey Lunin's avatar
Alexey Lunin committed
      const keys = await createOneTimePassportCertificate(
        makeid() + "-" + passportUUID,
        null,
        passportPrivateKey,
        passportCertificate
      );
Damyan Mitev's avatar
Damyan Mitev committed

Alexey Lunin's avatar
Alexey Lunin committed
      const {
        privateKeyPEM: privateKeyOneTime,
        certificatePEM: certificateOneTime
      } = keys;
Damyan Mitev's avatar
Damyan Mitev committed

Gospodin Bodurov's avatar
Gospodin Bodurov committed
      passportChain.reverse();

Damyan Mitev's avatar
Damyan Mitev committed
      passportChain.push(passportCertificate);

Gospodin Bodurov's avatar
Gospodin Bodurov committed
      passportChain.reverse();

Alexey Lunin's avatar
Alexey Lunin committed
      const pdfContentType = "application/pdf";
Damyan Mitev's avatar
Damyan Mitev committed

      if (documentContentType !== pdfContentType) {
        const convResponse = await executeRestfulFunction(
Alexey Lunin's avatar
Alexey Lunin committed
          "private",
          window.viamApi,
          window.viamApi.documentConvertDocumentByUUID,
          null,
          documentUUID,
          documentContentType,
          pdfContentType
        );
Damyan Mitev's avatar
Damyan Mitev committed
        if (convResponse.code !== "200") {
          return encodeResponse("400", "", convResponse.status);
        }
      }

      const downloadResponse = await executeRestfulFunction(
Alexey Lunin's avatar
Alexey Lunin committed
        "private",
        window.viamApi,
        window.viamApi.documentGetDocumentByUUID,
        null,
        documentUUID,
        pdfContentType
      );
Damyan Mitev's avatar
Damyan Mitev committed

      if (downloadResponse.code !== "200") {
        return encodeResponse("400", "", downloadResponse.status);
      }

      const pdfRaw = base64ToByteArray(downloadResponse.data);
Damyan Mitev's avatar
Damyan Mitev committed

      let signedPdf;
      try {
Alexey Lunin's avatar
Alexey Lunin committed
        signedPdf = await signPdf(
          pdfRaw,
          certificateOneTime,
          passportChain,
          privateKeyOneTime
        );
      } catch (err) {
        console.error(err);
        return encodeResponse("500", "", err.message);
      }
Damyan Mitev's avatar
Damyan Mitev committed

      const signedPdfB64 = byteArrayToBase64(signedPdf);

      const uploadResponse = await executeRestfulFunction(
Alexey Lunin's avatar
Alexey Lunin committed
        "private",
        window.viamApi,
        window.viamApi.documentPutDocumentByUUID,
        null,
        documentUUID,
        pdfContentType,
        signedPdfB64
      );
      if (uploadResponse.code !== "200") {
        return encodeResponse("400", "", uploadResponse.status);
      }

      const signResponse = await executeRestfulFunction(
Alexey Lunin's avatar
Alexey Lunin committed
        "private",
        window.viamApi,
        window.viamApi.documentSignDocumentByUUID,
        null,
        passportUUID,
        documentUUID,
        pdfContentType
      );
      if (signResponse.code !== "200") {
        return encodeResponse("400", "", signResponse.status);
      }

      return encodeResponse("200", "", "Document signed");
Damyan Mitev's avatar
Damyan Mitev committed
    },
    signDocumentJava: async (
      passportUUID,
      documentUUID,
      documentContentType
    ) => {
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );

      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

      const certResponse = await getCertificateForPassport(passportUUID, true);

      if (certResponse.code !== "200") {
        return encodeResponse("400", "", certResponse.status);
      }

      const {
        x509Certificate: passportCertificate,
        privateKey: passportPrivateKey,
        chain: passportChain
      } = certResponse.data;

      const keys = await createOneTimePassportCertificate(
        makeid() + "-" + passportUUID,
        passportPrivateKey,
        passportCertificate
      );

      const {
        privateKeyPEM: privateKeyOneTime,
        certificatePEM: certificateOneTime
      } = keys;

Gospodin Bodurov's avatar
Gospodin Bodurov committed
      passportChain.reverse();

      passportChain.push(passportCertificate);
      passportChain.push(certificateOneTime);

Gospodin Bodurov's avatar
Gospodin Bodurov committed
      passportChain.reverse();

      const pdfContentType = "application/pdf";

      if (documentContentType !== pdfContentType) {
        const convResponse = await executeRestfulFunction(
          "private",
          window.viamApi,
          window.viamApi.documentConvertDocumentByUUID,
          null,
          documentUUID,
          documentContentType,
          pdfContentType
        );
        if (convResponse.code !== "200") {
          return encodeResponse("400", "", convResponse.status);
        }
      }

      const signResponse = await executeRestfulFunction(
        "private",
        window.viamApi,
        window.viamApi.documentSignDocumentJavaService,
        null,
        privateKeyOneTime,
        passportChain,
        passportUUID,
        documentUUID,
        pdfContentType
      );
      if (signResponse.code !== "200") {
        return encodeResponse("400", "", signResponse.status);
      }

      return encodeResponse("200", "", "Document signed");
    },
    // passportUUID - String - passport to sign the vCard
    // senderEmail - the email address of the sender
    // attribs - additional attributes, to be added to the vCard. Recognized attribs are:
    //   - emailService: "GMail", "Outlook 365", ...
    // text, html - String - the text and html part of the email, both optional
    // parts - array of objects, representing the MIME structure in format:
Damyan Mitev's avatar
Damyan Mitev committed
    // {
    //   headers: {
    //     "Content-Type": "image/jpeg",
    //     "Content-Disposition": "inline" or "attachment" with additional attributes,
    //     ... //other headers from MIME
Damyan Mitev's avatar
Damyan Mitev committed
    //   },
    //   body: String if it is a text part (Content-Type = "text/...") or Uint8Array otherwise; filled for leaf MIME nodes
    //   parts: array of instances of the same object; filled for container MIME nodes (Content-Type = "multipart/...")
Damyan Mitev's avatar
Damyan Mitev committed
    // }
    signVCard: async (
      passportUUID,
      senderEmail,
      attribs,
      textBody,
      htmlBody,
      parts
    ) => {
Damyan Mitev's avatar
Damyan Mitev committed
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );

      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

      const messageUUID = makeid();

      const vCardAttribs = {
        ...attribs,
        passportUUID,
        messageUUID
      let vCardImageData;
      let vCardImageClaimValue;

      const vCardImageClaimName = "vCardImage";
      const defaultTagName = "notag";

      const vCardClaimResponse = await executeRestfulFunction(
Damyan Mitev's avatar
Damyan Mitev committed
        "private",
        window.viamApi,
        window.viamApi.entityGetClaim,
Damyan Mitev's avatar
Damyan Mitev committed
        null,
        vCardImageClaimName,
        defaultTagName,
Damyan Mitev's avatar
Damyan Mitev committed
        passportUUID
      );
      // if (vCardClaimResponse.code !== "200") {
      //   return encodeResponse("400", "", vCardClaimResponse.status);
      // }
      if (vCardClaimResponse.code === "200") {
        vCardImageClaimValue = vCardClaimResponse.data;
      }
      let qrCodeImageData;
      let qrCodeCoordinates = { fromL: -1, fromR: -1, toL: -1, toR: -1 };
      if (
        vCardImageClaimValue &&
        "state" in vCardImageClaimValue &&
        vCardImageClaimValue.state === "disabled"
      ) {
        vCardImageData = new ImageData({
          contentType: "image/png",
          contentBase64:
            "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=" //1x1px transparent pixel
        });
      } else {
        const vCardImageResponse = await executeRestfulFunction(
          "private",
          window.viamApi,
          window.viamApi.passportGetVCardImage,
          null,
          passportUUID
        );
Gospodin Bodurov's avatar
Gospodin Bodurov committed

        console.log(vCardImageResponse);

        if (vCardImageResponse.code !== "200") {
          return encodeResponse("400", "", vCardImageResponse.status);
        }
        vCardImageData = new ImageData(vCardImageResponse.data.Image);
        if (vCardImageData.contentType !== "image/png") {
          return encodeResponse(
            "400",
            "",
            "Content type of vCard mmust be 'image/png'"
          );
        qrCodeCoordinates = vCardImageResponse.data.QRCodeCoordinates;
        const qrCodeBase64Content = await generateQrCode(
          "https://" + location.host + "/check/" + messageUUID
        qrCodeImageData = new ImageData({
          contentType: "image/png",
          content: qrCodeBase64Content
        });
Damyan Mitev's avatar
Damyan Mitev committed
      if (typeof parts === "undefined" || parts === null) {
Damyan Mitev's avatar
Damyan Mitev committed
        parts = [];
      }

      if (htmlBody) {
        if (typeof htmlBody !== "string") {
          throw new Error("htmlBody is not string"); // should not happen
        }
Damyan Mitev's avatar
Damyan Mitev committed
        const htmlPart = {
          headers: {
            "Content-Type": "text/html"
          },
          body: htmlBody
Damyan Mitev's avatar
Damyan Mitev committed
        };
        parts.unshift(htmlPart);
      } else {
        console.log("Html body is not passed to signVCard, its value is ", {
          html: htmlBody
        });
      if (textBody) {
        if (typeof textBody !== "string") {
          throw new Error("textBody is not string"); // should not happen
        }
Damyan Mitev's avatar
Damyan Mitev committed
        const textPart = {
          headers: {
            "Content-Type": "text/plain"
          },
          body: textBody
Damyan Mitev's avatar
Damyan Mitev committed
        };
        parts.unshift(textPart);
      } else {
        console.log("Text body is not passed to signVCard, its value is ", {
          text: textBody
        });
      const count = await prepareVCardParts(parts);
      if (count.textParts === 0) {
        return encodeResponse("400", "", "No text parts passed to signVCard");
      }
      if (count.htmlParts === 0) {
        return encodeResponse("400", "", "No html parts passed to signVCard");
      }
Damyan Mitev's avatar
Damyan Mitev committed
      const certResponse = await getCertificateForPassport(passportUUID, true);

      if (certResponse.code !== "200") {
        return encodeResponse("400", "", certResponse.status);
      }

      const {
        x509Certificate: passportCertificate,
        privateKey: passportPrivateKey,
        chain: passportChain
      } = certResponse.data;

      const keys = await createOneTimePassportCertificate(
        makeid() + "-" + passportUUID,
        senderEmail,
Damyan Mitev's avatar
Damyan Mitev committed
        passportPrivateKey,
        passportCertificate
      );

      const {
        privateKeyPEM: privateKeyOneTime,
        certificatePEM: certificateOneTime
      } = keys;

      passportChain.reverse();

      passportChain.push(passportCertificate);
      passportChain.push(certificateOneTime);

      passportChain.reverse();

      console.log(qrCodeImageData);
      console.log(qrCodeCoordinates);
Gospodin Bodurov's avatar
Gospodin Bodurov committed

Damyan Mitev's avatar
Damyan Mitev committed
      const signVCardResponse = await executeRestfulFunction(
        "private",
        window.viamApi,
        window.viamApi.signSignVCardForChain,
        null,
        vCardImageData,
        privateKeyOneTime,
        passportChain,
        vCardAttribs,
        qrCodeImageData,
Damyan Mitev's avatar
Damyan Mitev committed
      );
      if (signVCardResponse.code !== "200") {
        return encodeResponse("400", "", signVCardResponse.status);
      }

Damyan Mitev's avatar
Damyan Mitev committed
      const signedVCardImageData = new ImageData(signVCardResponse.data);
      return encodeResponse(
        "200",
        {
          image: signedVCardImageData,
          messageUUID: messageUUID
        },
        "vCard signed"
      );
Damyan Mitev's avatar
Damyan Mitev committed
    },
    // mime - String - the MIME of the email message
    // vCardAttribs - optional attributes for the verification procedure in format
Damyan Mitev's avatar
Damyan Mitev committed
    // {
    //   passportUUID: passportUUID,
    //   messageUUID: messageUUID
    // };
Damyan Mitev's avatar
Damyan Mitev committed
    validateVMime: async (vMime, vCardAttribs = null) => {
Damyan Mitev's avatar
Damyan Mitev committed
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );

      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

      const validateVMimeResponse = await executeRestfulFunction(
Damyan Mitev's avatar
Damyan Mitev committed
        "private",
        window.viamApi,
        window.viamApi.signValidateVMime,
Damyan Mitev's avatar
Damyan Mitev committed
        null,
Damyan Mitev's avatar
Damyan Mitev committed
        vMime,
Damyan Mitev's avatar
Damyan Mitev committed
      );
      if (validateVMimeResponse.code !== "200") {
        return encodeResponse("400", "", validateVMimeResponse.status);
Damyan Mitev's avatar
Damyan Mitev committed
      const validationResult = validateVMimeResponse.data;
      const { signatures } = validationResult;
      if (signatures) {
        for (const signature of signatures) {
          const certificateChain = signature.certificateChainPEM.map(
            certificatePEM => {
              const certificate = parseCertificate(certificatePEM);
              const certificateData = new CertificateData(certificate);
              return certificateData;
            }
          );
          signature.certificateChain = certificateChain;
      return encodeResponse(
        "200",
        validationResult,
        "Validation result retrieved"
      );
Damyan Mitev's avatar
Damyan Mitev committed
    },
    generateQrCode,
    documentCreateDocument: async (passportUUID, path, contentType, title) => {
Alexey Lunin's avatar
Alexey Lunin committed
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );
      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

      path = encodeURI(path);
      contentType = encodeURI(contentType);
      title = encodeURI(title);

      const config = {
        headers: {
          path,
          passportuuid: passportUUID,
Alexey Lunin's avatar
Alexey Lunin committed
      const response = await executeRestfulFunction(
        "private",
        window.viamApi,
        window.viamApi.documentCreateDocument,
        config
      );
      if (response.code !== "200") {
        return encodeResponse("400", "", response.status);
      }

      return encodeResponse("200", response.data, "Document created");
    },
Alexey Lunin's avatar
Alexey Lunin committed
    documentPutDocument: async (
      passportUUID,
      resourceid,
      contentType,
Alexey Lunin's avatar
Alexey Lunin committed
      file,
      upload
Alexey Lunin's avatar
Alexey Lunin committed
    ) => {
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );
      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

      resourceid = encodeURI(resourceid);
      contentType = encodeURI(contentType);

Alexey Lunin's avatar
Alexey Lunin committed
          "Content-Type": "multipart/form-data",
          contentType,
          upload

      const response = await executeRestfulFunction(
Alexey Lunin's avatar
Alexey Lunin committed
        "private",
        window.viamApi,
        window.viamApi.documentPutDocument,
        config,
        file
      );
      if (response.code !== "200") {
        return encodeResponse("400", "", response.status);
      }

      return encodeResponse("200", response.data, "Document stored");
    },
    hasSession() {
      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.identityHasSession,
          null
        ).then(executeResult => {
          result(executeResult);
        });
      });
    },
    marketingSignUpIdentificator(identificator, reference) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity("marketingapppublickey");
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "public",
          viamApi,
          viamApi.marketingSignUpIdentificator,
          null,
          identificator,
          reference
        ).then(executeResult => {
Markin Igor's avatar
Markin Igor committed
          viamApi.setIdentity("");
Markin Igor's avatar
Markin Igor committed
          viamApi.setSessionData("", "");
          result(executeResult);
        });
      });
    },
    marketingGetIdentificatorProfile(identificator, pincode) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity("marketingapppublickey");
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "public",
          viamApi,
          viamApi.marketingGetIdentificatorProfile,
          null,
          identificator,
          pincode
        ).then(executeResult => {
Markin Igor's avatar
Markin Igor committed
          viamApi.setIdentity("");
Markin Igor's avatar
Markin Igor committed
          viamApi.setSessionData("", "");
          result(executeResult);
        });
      });
    },
    marketingExecuteEventForIdentificator(identificator, pincode, event) {
      return new Penpal.Promise(result => {
Markin Igor's avatar
Markin Igor committed
        viamApi.setIdentity("marketingapppublickey");
Alexey Lunin's avatar
Alexey Lunin committed
        executeRestfulFunction(
          "public",
          viamApi,
          viamApi.marketingExecuteEventForIdentificator,
          null,
          identificator,
          pincode,
          event
        ).then(executeResult => {
Markin Igor's avatar
Markin Igor committed
          viamApi.setIdentity("");
Markin Igor's avatar
Markin Igor committed
          viamApi.setSessionData("", "");
          result(executeResult);
        });
      });
    },
    getCurrentlyAuthenticatedIdentity() {
Alexey Lunin's avatar
Alexey Lunin committed
      const {
        publicKey,
        x509Certificate
      } = window.currentlyAuthenticatedIdentity.authentication;

      return encodeResponse(
        "200",
        {
          authentication: {
            publicKey,
            x509Certificate
          }
        },
        "Currently authenticated identity"
      );
    extractMessageID(mime) {
      return new Penpal.Promise(result => {
        result(extractMessageID(mime));
      });
    },
    stringToUtf8ByteArray(str) {
      return new Penpal.Promise(result => {
        result(stringToUtf8ByteArray(str));
      });
    },
    utf8ByteArrayToString(ba) {
      return new Penpal.Promise(result => {
        result(utf8ByteArrayToString(ba));
      });
    },
    stringToUtf8Base64(str) {
      return new Penpal.Promise(result => {
        result(stringToUtf8Base64(str));
      });
    },
    utf8Base64ToString(strBase64) {
      return new Penpal.Promise(result => {
        result(utf8Base64ToString(strBase64));
      });
    },
    base64ToByteArray(strBase64) {
      return new Penpal.Promise(result => {
        result(base64ToByteArray(strBase64));
      });
    },
    byteArrayToBase64(ba) {
      return new Penpal.Promise(result => {
        result(byteArrayToBase64(ba));
      });
Alexey Lunin's avatar
Alexey Lunin committed

    // Collabora APIs
    collaboraDiscovery() {
      return collaboraApi.discovery().then(apps => apps);
Alexey Lunin's avatar
Alexey Lunin committed

Alexey Lunin's avatar
Alexey Lunin committed
    // WOPI
Gospodin Bodurov's avatar
Gospodin Bodurov committed
    getPassportsNewProtocol: async (resourceID, contentType) => {
Alexey Lunin's avatar
Alexey Lunin committed
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );
Alexey Lunin's avatar
Alexey Lunin committed

Alexey Lunin's avatar
Alexey Lunin committed
      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }
Alexey Lunin's avatar
Alexey Lunin committed

Alexey Lunin's avatar
Alexey Lunin committed
      const response = await wopiAPI.getPassportsNewProtocol(
        resourceID,
        contentType
      );
Alexey Lunin's avatar
Alexey Lunin committed
      return response.data;
Alexey Lunin's avatar
Alexey Lunin committed
    getPassports: async fileId => {
Alexey Lunin's avatar
Alexey Lunin committed
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );
Alexey Lunin's avatar
Alexey Lunin committed

Alexey Lunin's avatar
Alexey Lunin committed
      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }
Alexey Lunin's avatar
Alexey Lunin committed

      const response = await wopiAPI.getPassports(fileId);
Alexey Lunin's avatar
Alexey Lunin committed
      return response.data;
    wopiCreateDocument: async (passportUUID, path, title) => {
Alexey Lunin's avatar
Alexey Lunin committed
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );
      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

      const config = {
        headers: {
          path,
          passportuuid: passportUUID,
          title
        }
      };
      const createDocumentResult = await executeRestfulFunction(
Alexey Lunin's avatar
Alexey Lunin committed
        "private",
        window.viamApi,
        window.viamApi.documentCreateDocument,
        config
      );
      if (createDocumentResult.code !== "200") {
        return createDocumentResult;

      const resourceID = createDocumentResult.data;

      const accessTokenResponse = await wopiAPI.getAccessToken(
        passportUUID,
        resourceID
      );

      if (accessTokenResponse.data.code !== "200") {
        return accessTokenResponse.data;
      }

      const accessToken = accessTokenResponse.data.data;

      const result = {
        resourceID,
        accessToken
      };
      return encodeResponse("200", result, "ok");
    wopiGetAccessToken: async (passportUUID, resourceID, contentType) => {
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );

      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

      const response = await wopiAPI.getAccessToken(
        passportUUID,
        resourceID,
        contentType
      );
      return response.data;
    },

    wopiPutFile: async (resourceID, accessToken, file) => {
Alexey Lunin's avatar
Alexey Lunin committed
      const authenticationPublicKey = localStorage.getItem(
        "authenticatedIdentity"
      );
Alexey Lunin's avatar
Alexey Lunin committed

Alexey Lunin's avatar
Alexey Lunin committed
      if (
        !authenticationPublicKey ||
        !window.loadedIdentities[authenticationPublicKey] ||
        !extendPinCodeTtl(authenticationPublicKey)
      ) {
        return encodeResponse("400", "", "Identity not authenticated");
      }

      const response = await wopiAPI.putDocument(resourceID, accessToken, file);
Alexey Lunin's avatar
Alexey Lunin committed
      return response.data;
Markin Igor's avatar
Markin Igor committed
});
connection.promise.then(parent => {
  iframeParent = parent;

  if (!navigator.cookieEnabled) {
    console.warn("Cookie disabled. Can't start library.");
    return;
  }
Alexey Lunin's avatar
Alexey Lunin committed
  window.addEventListener("storage", event => {
    if (event.key === "authenticatedIdentity" && event.newValue === null) {
Alexey Lunin's avatar
Alexey Lunin committed
      const publicKey =
        window.currentlyAuthenticatedIdentity.authentication.publicKey;
      window.currentlyLoadedIdentity = null;
      window.currentlyAuthenticatedIdentity = null;
Markin Igor's avatar
Markin Igor committed
      const event = createEvent("LogoutFromAnotherTab", "Logout", [publicKey]);
      parent.onEvent(event);
Markin Igor's avatar
Markin Igor committed
  const identities = localStorage.getItem("identities");
Markin Igor's avatar
Markin Igor committed
  console.log("Library loaded at: " + new Date().toISOString());

  if (identities === "" || identities === null) {
Markin Igor's avatar
Markin Igor committed
    localStorage.setItem("identities", JSON.stringify({}));
Markin Igor's avatar
Markin Igor committed
  if (
    localStorage.getItem("uuid") === null ||
    localStorage.getItem("token") === null ||
    localStorage.getItem("authenticatedIdentity") === null
  ) {
    const event = createEvent("", "NotAuthenticated");
    parent.onEvent(event);
Markin Igor's avatar
Markin Igor committed
    localStorage.removeItem("uuid");
    localStorage.removeItem("token");
Markin Igor's avatar
Markin Igor committed
    localStorage.removeItem("authenticatedIdentity");
  } else {
Alexey Lunin's avatar
Alexey Lunin committed
    const authenticationPublicKey = localStorage.getItem(
      "authenticatedIdentity"
    );
    const pinCode = getPincode(authenticationPublicKey);
Markin Igor's avatar
Markin Igor committed
    if (pinCode === "" || pinCode === null) {
      loadIdentityInternal(authenticationPublicKey, "00000000").then(result => {
Markin Igor's avatar
Markin Igor committed
        if (result.code !== "200") {
          const event = createEvent(
            "CanNotGetPincodeForAuthenticatedIdentity",
            "IdentityNotLoaded",
            [authenticationPublicKey]
Markin Igor's avatar
Markin Igor committed
          );
          parent.onEvent(event);
      loadIdentityInternal(authenticationPublicKey, pinCode).then(result => {
Markin Igor's avatar
Markin Igor committed
        if (result.code !== "200") {
          const event = createEvent(
            "CanNotLoadIdentity",
            "ErrorDuringLoadingIdentity",
            [authenticationPublicKey]
Markin Igor's avatar
Markin Igor committed
          );
          parent.onEvent(event);
  let anynomousDeviceKeyEventsProcessing = false;
  let maxDeviceKeyAnonymousEventTime = 0;
  let eventsDeviceEventsProcessing = false;
  let maxDeviceKeyEventTime = 0;
  let eventsEntityEventsProcessing = false;
  let maxEntityEventTime = 0;
  let identityLoadedEvent = false;
  let identityAuthenticatedEvent = false;
Alexey Lunin's avatar
Alexey Lunin committed
  let previousLocalStorageUUID;
  let previousLocalStorageToken;
  let previousLocalStorageIdentity;

Sasha Ilieva's avatar
Sasha Ilieva committed
  setInterval(async function() {
Markin Igor's avatar
Markin Igor committed
    if (window.currentlyAuthenticatedIdentity) {
      const { authentication } = window.currentlyAuthenticatedIdentity;
      const pinCode = getPincode(authentication.publicKey);
      if (pinCode) {
Alexey Lunin's avatar
Alexey Lunin committed
        const identity = await getIdentityFromLocalStorage(
          authentication.publicKey,
          pinCode,
          false
        );
Markin Igor's avatar
Markin Igor committed
        window.currentlyLoadedIdentity = identity;

        if (!identityAuthenticatedEvent && identity) {
Alexey Lunin's avatar
Alexey Lunin committed
          const event = createEvent("IdentityAuthenticated", "Authenticated", [
            identity.authentication.publicKey
          ]);
Markin Igor's avatar
Markin Igor committed
          parent.onEvent(event);
          identityAuthenticatedEvent = true;
        }
      } else {
Alexey Lunin's avatar
Alexey Lunin committed
        const authenticationPublicKey = localStorage.getItem(
          "authenticatedIdentity"
        );
Markin Igor's avatar
Markin Igor committed
        if (authenticationPublicKey) {
Alexey Lunin's avatar
Alexey Lunin committed
          const result = await loadIdentityInternal(
            authenticationPublicKey,
            "00000000"
          );
Markin Igor's avatar
Markin Igor committed
          if (result.code !== "200") {
Alexey Lunin's avatar
Alexey Lunin committed
            const event = createEvent(
              "CanNotGetPincodeForAuthenticatedIdentity",
              "IdentityNotLoaded",
              [authenticationPublicKey]
            );
Markin Igor's avatar
Markin Igor committed
            parent.onEvent(event);
            clearPinCodeTtl(authenticationPublicKey);
            window.currentlyAuthenticatedIdentity = null;
          }
Markin Igor's avatar
Markin Igor committed
        identityAuthenticatedEvent = false;
Markin Igor's avatar
Markin Igor committed
        window.currentlyLoadedIdentity = null;
Markin Igor's avatar
Markin Igor committed
    if (window.currentlyLoadedIdentity) {
Alexey Lunin's avatar
Alexey Lunin committed
      const pinCode = getPincode(
        window.currentlyLoadedIdentity.authentication.publicKey
      );
Markin Igor's avatar
Markin Igor committed
      if (!pinCode) {
        if (!identityLoadedEvent) {
Alexey Lunin's avatar
Alexey Lunin committed
          const result = await loadIdentityInternal(
            window.currentlyLoadedIdentity.authentication.publicKey,
            "00000000"
          );
          if (window.currentlyLoadedIdentity && result.code !== "200") {
Alexey Lunin's avatar
Alexey Lunin committed
            const event = createEvent(
              "CanNotLoadPincodeForLoadedIdentity",
              "IdentityNotLoaded",
              [window.currentlyLoadedIdentity.authentication.publicKey]
            );
Markin Igor's avatar
Markin Igor committed
            parent.onEvent(event);
            identityLoadedEvent = true;
          }
Markin Igor's avatar
Markin Igor committed
        identityLoadedEvent = false;
Markin Igor's avatar
Markin Igor committed
    if (window.currentlyAuthenticatedIdentity) {
      const now = new Date().getTime();
      if (now - window.lastTimeGetProfile > 30000) {
        getProfileData(window.currentlyAuthenticatedIdentity);
Markin Igor's avatar
Markin Igor committed
        window.lastTimeGetProfile = now;
Alexey Lunin's avatar
Alexey Lunin committed

    const currentLocalStorageUUID = localStorage.getItem("uuid");
    const currentLocalStorageToken = localStorage.getItem("token");
Alexey Lunin's avatar
Alexey Lunin committed
    const currentLocalStorageIdentity = localStorage.getItem(
      "authenticatedIdentity"
    );
Alexey Lunin's avatar
Alexey Lunin committed
    if (
Alexey Lunin's avatar
Alexey Lunin committed
      (!currentLocalStorageUUID && previousLocalStorageUUID) ||
      (!currentLocalStorageToken && previousLocalStorageToken) ||
      (!currentLocalStorageIdentity && previousLocalStorageIdentity)
Alexey Lunin's avatar
Alexey Lunin committed
    ) {
      previousLocalStorageUUID = null;
      previousLocalStorageToken = null;
      previousLocalStorageIdentity = null;

      destroyAuthentication();

      const event = createEvent("", "LogoutExternal");
      parent.onEvent(event);
    } else {
      previousLocalStorageUUID = currentLocalStorageUUID;
      previousLocalStorageToken = currentLocalStorageToken;
      previousLocalStorageIdentity = currentLocalStorageIdentity;
    }
Markin Igor's avatar
Markin Igor committed
  }, 50);
  const getNewEventsWithoutSession = async () => {
    anynomousDeviceKeyEventsProcessing = true;
    try {
Alexey Lunin's avatar
Alexey Lunin committed
      const executeResult = await executeRestfulFunction(
        "public",
        viamAnonymousApi,
        viamAnonymousApi.eventGetNewEventsWithoutSession,
        null,
        "devicekey"
      );
      if (executeResult.code === "200") {
        const eventsLen = executeResult.data.length;
        let changedMaxDeviceKeyAnonymousEventTime = false;
        for (let i = 0; i < eventsLen; i++) {
          const event = executeResult.data[i];
          switch (event.type) {
Alexey Lunin's avatar
Alexey Lunin committed
            case "DeviceConfirmed": {
              await setIdentityInLocalStorage(window.currentlyLoadedIdentity);
              parent.onEvent(event);
              break;
Alexey Lunin's avatar
Alexey Lunin committed
            case "QRCodeUpdated": {
              const actionID = event["actionID"];
              const QrCode = event["payloads"][1];
              const eventCopy = JSON.parse(JSON.stringify(event));
Sasha Ilieva's avatar
Sasha Ilieva committed
              QRCode.toDataURL(actionID + "," + QrCode, function(err, url) {
Markin Igor's avatar
Markin Igor committed
                eventCopy["payloads"].push(url);
Alexey Lunin's avatar
Alexey Lunin committed
            case "KeyDeleted": {
              const authenticationPublicKey = localStorage.getItem(
                "authenticatedIdentity"
              );
              clearPinCodeTtl(authenticationPublicKey);
              localStorage.removeItem("uuid");
              localStorage.removeItem("token");
              localStorage.removeItem("authenticatedIdentity");
              delete window.loadedIdentities[authenticationPublicKey];
              window.currentlyLoadedIdentity = null;
              window.currentlyAuthenticatedIdentity = null;
              window.lastTimeGetProfile = 0;

              destroyIdentityFromLocalStorage(authenticationPublicKey);
              break;
            }

Alexey Lunin's avatar
Alexey Lunin committed
            default: {
          changedMaxDeviceKeyAnonymousEventTime = true;
Alexey Lunin's avatar
Alexey Lunin committed
          maxDeviceKeyAnonymousEventTime = Math.max(
            maxDeviceKeyAnonymousEventTime,
            event.stamp
          );
Alexey Lunin's avatar
Alexey Lunin committed
        if (changedMaxDeviceKeyAnonymousEventTime) {
          await executeRestfulFunction(
            "public",
            viamAnonymousApi,
            viamAnonymousApi.eventUpdateLastViewedWithoutSession,
            null,
            "devicekey",
            maxDeviceKeyAnonymousEventTime.toString()
          );
        }
      }
    } catch (e) {
      console.warn(e);
    }
    anynomousDeviceKeyEventsProcessing = false;
  };

  const getNewDeviceEvents = async () => {
    eventsDeviceEventsProcessing = true;
    try {
Alexey Lunin's avatar
Alexey Lunin committed
      const executeResult = await executeRestfulFunction(
        "private",
        viamApi,
        viamApi.eventGetNewEvents,
        null,
        "devicekey"
      );
      if (executeResult.code === "200") {
        const eventsLen = executeResult.data.length;
        const changedMaxDeviceKeyEventTime = false;
        for (let i = 0; i < eventsLen; i++) {
          const event = executeResult.data[i];
          if (event.type === "QRCodeUpdated") {
            const actionID = event["actionID"];
            const QrCode = event["payloads"][1];

            const eventCopy = JSON.parse(JSON.stringify(event));

Sasha Ilieva's avatar
Sasha Ilieva committed
            QRCode.toDataURL(actionID + "," + QrCode, function(err, url) {
              eventCopy["payloads"].push(url);
              parent.onEvent(eventCopy);
            });
          } else {
            parent.onEvent(event);
          maxDeviceKeyEventTime = Math.max(maxDeviceKeyEventTime, event.stamp);
        }
Alexey Lunin's avatar
Alexey Lunin committed
        if (changedMaxDeviceKeyEventTime) {
          await executeRestfulFunction(
            "private",
            viamApi,
            viamApi.eventUpdateLastViewed,
            null,
            "devicekey",
            maxDeviceKeyEventTime.toString()
          );
    } catch (e) {
      console.warn(e);
    eventsDeviceEventsProcessing = false;
  };
  const getNewEntityEvents = async () => {
    eventsEntityEventsProcessing = true;
    try {
Alexey Lunin's avatar
Alexey Lunin committed
      const executeResult = await executeRestfulFunction(
        "private",
        viamApi,
        viamApi.eventGetNewEvents,
        null,
        "entity"
      );
      if (executeResult.code === "200") {
        const eventsLen = executeResult.data.length;
        let changedMaxEntityEventTime = false;
        for (let i = 0; i < eventsLen; i++) {
          const event = executeResult.data[i];
          if (event.type === "QRCodeUpdated") {
            const actionID = event["actionID"];
            const QrCode = event["payloads"][1];
            const eventCopy = JSON.parse(JSON.stringify(event));
Sasha Ilieva's avatar
Sasha Ilieva committed
            QRCode.toDataURL(actionID + "," + QrCode, function(err, url) {
              eventCopy["payloads"].push(url);
              parent.onEvent(eventCopy);
            });

          parent.onEvent(event);
          changedMaxEntityEventTime = true;
          maxEntityEventTime = Math.max(maxEntityEventTime, event.stamp);
        }
Alexey Lunin's avatar
Alexey Lunin committed
        if (changedMaxEntityEventTime) {
          await executeRestfulFunction(
            "private",
            viamApi,
            viamApi.eventUpdateLastViewed,
            null,
            "entity",
            maxEntityEventTime.toString()
          );
    } catch (e) {
      console.warn(e);
    }
    eventsEntityEventsProcessing = false;
Alexey Lunin's avatar
Alexey Lunin committed
  };

  setInterval(() => {
Alexey Lunin's avatar
Alexey Lunin committed
    if (
      window.currentlyLoadedIdentity &&
      !anynomousDeviceKeyEventsProcessing &&
      !window.currentlyAuthenticatedIdentity
    ) {
      getNewEventsWithoutSession();
    }

    if (window.currentlyAuthenticatedIdentity) {
      // These functions has to be executed at the same time.
      !eventsDeviceEventsProcessing && getNewDeviceEvents();
      !eventsEntityEventsProcessing && getNewEntityEvents();