Newer
Older
import {
stringToUtf8ByteArray,
utf8ByteArrayToString,
stringToUtf8Base64,
utf8Base64ToString,
base64ToByteArray,
byteArrayToBase64
const QRCode = require("qrcode");
const Penpal = require("penpal").default;
Markin Igor
committed
import {
createDeviceHash,
destroyIdentityFromLocalStorage,
encodeResponse,
listIdentitiesFromLocalStorage,
makeid
} from "../utilities/appUtility";
import { LOGIN_MODES } from "../constants/authentication";
import {
createOneTimePassportCertificate,
createPassportCertificate,
decryptMessage,
encryptMessage,
signEmail
} from "../utilities/signingUtilities";
import { signPdf } from "../utilities/pdfUtilities";
import CryptoData from "../CryptoData";
import Identity from "../Identity";
import { STATUS_DEVICE_REVOKED } from "../constants/statuses";
const penpalMethods = require("../../temp/penpal-methods").default;
const WopiAPI = require("./wopiapi-iframe");
const CollaboraAPI = require("./collaboraapi-iframe");
const ViamAPI = require("../../temp/viamapi");
let identityColors = ["#994392", "#cb0767", "#e51d31", "#ec671b", "#fab610"];
}
function setKeyForUUID(uuid, key) {
let storedIdentityForUuid = localStorage.getItem("keyperuuid/" + uuid);
if (
storedIdentityForUuid !== key &&
storedIdentityForUuid != null &&
storedIdentityForUuid !== ""
) {
destroyIdentityFromLocalStorage(storedIdentityForUuid);
}
function getColorForIdentity(key) {
let storedColor = localStorage.getItem("colors/" + key);
}
function setIdentityInLocalStorage(identityToStore, extendKey = true) {
const serializedIdentity = JSON.stringify(identityToStore);
const key = identityToStore.authentication.publicKey;
if (pinCode == null || pinCode === "") {
pinCode = getPincode(key);
return encryptMessage(serializedIdentity, pinCode, "identity").then(
encryptedIdentity => {
if (extendKey === true) {
success = extendPinCodeTtl(key, pinCode);
}
if (success === true) {
localStorage.setItem(key, encryptedIdentity);
let serializedIdentitiesList = localStorage.getItem("identities");
let identities = JSON.parse(serializedIdentitiesList);
identities[key] = true;
localStorage.setItem("identities", JSON.stringify(identities));
} else {
console.log("Can not extend pincode ttl");
}
}
function getProfileData(identity) {
return new Penpal.Promise(executeResultUpper => {
executeRestfulFunction(
"private",
viamApi,
viamApi.identityGetIdentityProfileData,
null
).then(executeResult => {
if (executeResult.code === "200") {
listItem.identityColor = getColorForIdentity(
identity.authentication.publicKey
);
localStorage.setItem(
"profiles/" + identity.authentication.publicKey,
JSON.stringify(listItem)
);
executeResultUpper(listItem);
async function getIdentityFromLocalStorage(key, pinCode, extendTtl = true) {
const encryptedIdentity = localStorage.getItem(key);
if (!encryptedIdentity) {
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;
}
}
return identity;
}
function extendPinCodeTtl(key, pinCode) {
let now = new Date();
let nowMillis = now.getTime();
let ttl = window.sessionStorage.getItem("pincodettls/" + key);
if (ttl == null || ttl === "" || nowMillis >= parseInt(ttl)) {
window.sessionStorage.setItem("pincodettls/" + key, ttl);
}
} else {
let now = new Date();
let ttl = now.getTime() + 4 * 60 * 60 * 1000;
window.sessionStorage.setItem("pincodettls/" + key, ttl);
window.sessionStorage.setItem("pincodes/" + key, pinCode);
}
return true;
}
window.extendPinCodeTtl = extendPinCodeTtl;
window.sessionStorage.removeItem("pincodettls/" + key);
let now = new Date();
let nowMillis = now.getTime();
let ttl = window.sessionStorage.getItem("pincodettls/" + key);
} else {
return window.sessionStorage.getItem("pincodes/" + key);
}
}
}
function createEvent(actionId, type, payloads) {
return {
actionID: actionId,
type: type,
stamp: new Date().getTime(),
payloads: payloads
};
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
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);
}
};
window.wopiAPI = new WopiAPI();
window.collaboraApi = new CollaboraAPI();
window.viamApi = new ViamAPI();
window.viamAnonymousApi = new ViamAPI();
window.currentlyAuthenticatedIdentity = null;
window.currentlyLoadedIdentity = null;
window.lastTimeGetProfile = 0;
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);

Alexey Lunin
committed
async function executeRestfulFunction(type, that, fn, config, ...args) {
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 = {
const identity = currentlyAuthenticatedIdentity || currentlyLoadedIdentity;

Alexey Lunin
committed
const { code, status } = response.data;
const deviceRevoked =
type === "private" && code === "401" && status === STATUS_DEVICE_REVOKED;

Alexey Lunin
committed
if (deviceRevoked) {
destroyIdentity();

Alexey Lunin
committed
const event = createEvent("", "DeviceRevoked");
iframeParent.onEvent(event);

Alexey Lunin
committed
return response.data;
}
const badSession =
type === "private" &&
identity &&
code === "400" &&
status === "Bad session";
if (!badSession) return response.data;
const loginResponse = await viamApi.identityLogin(
null,
"previousaddeddevice"
);
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 => {
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);
}
window.viamAnonymousApi.setIdentity(
window.currentlyLoadedIdentity.authentication.publicKey
);
const { publicKey, x509Certificate } = loadedIdentity.authentication;
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) {
return { data: "", code: "400", status: "Identity not authenticated" };
const passportIdentity = window.currentlyAuthenticatedIdentity;
let passport = passportIdentity.getPassport(passportUUID);
if (passport === undefined || passport === null) {
createPassportCertificate(passportUUID).then(function(keys) {
cryptoData.setPublicKey(keys["publicKeyPEM"]);
cryptoData.setPrivateKey(keys["privateKeyPEM"]);
//download("passportCertificateBeforeSigning.crt", "text/plain", certificate)
//cryptoData.setx509Certificate(keys["certificate"])
executeRestfulFunction(
"private",
viamApi,
viamApi.signSignCertificate,
null,
btoa(certificate),
passportUUID
).then(executeResult => {
if (executeResult.code === "200") {
//download("passportCertificateAfterSigning.crt", "text/plain", signedCertificate)
let keyUUID = executeResult.data["CertificateUUID"];
let encodedChain = executeResult.data["Chain"];
//download("rootCertificate.crt", "text/plain", atob(encodedChain[0]))
cryptoData.setx509Certificate(signedCertificate);
cryptoData.setKeyUUID(keyUUID);
cryptoData.setChain(chain);
passportIdentity.setPassport(passportUUID, cryptoData);
getProfileData(passportIdentity).then(executeResult1 => {
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);
let copyOfCryptoData = JSON.parse(JSON.stringify(passport));
if (internal === false) {
copyOfCryptoData["privateKey"] = "";
certificateResult({
data: copyOfCryptoData,
code: "200",
status: "Certificate got"
});
}
});
}
const connection = Penpal.connectToParent({
// Methods child is exposing to parent
methods: {
initialize: (apiUrl, wopiUrl, collaboraUrl) => {
Alexey Lunin
committed
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;
console.warn(
`Collabora host URL not specified. Fall back to ${collaboraUrl}`
); // eslint-disable-line no-console
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 =
collaboraUrl.charAt(collaboraUrl.length - 1) === "/"
? collaboraUrl
: collaboraUrl + "/";
...penpalMethods,
createIdentity(pinCode) {
return new Penpal.Promise(result => {
let newIdentity = new Identity();
let cryptoData = new CryptoData();
cryptoData.setPublicKey(keys["publicKeyPEM"]);
cryptoData.setPrivateKey(keys["privateKeyPEM"]);
cryptoData.setx509Certificate(keys["certificatePEM"]);
newIdentity.setAuthentication(cryptoData);
newIdentity.setPinCode(pinCode);
window.currentlyLoadedIdentity = newIdentity;
window.loadedIdentities[
newIdentity.authentication.publicKey
] = newIdentity;
extendPinCodeTtl(newIdentity.authentication.publicKey, pinCode);
window.viamAnonymousApi.setIdentity(
newIdentity.authentication.publicKey
);
result({
data: newIdentity,
code: "200",
status: "Identity created"
});
},
listIdentities() {
return new Penpal.Promise(result => {
result({ data: identities, code: "200", status: "Identities listed" });
});
},
loadIdentity(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 {
const identity = await getIdentityFromLocalStorage(
key,
oldPinCode,
false
);
if (identity) {
identity.pinCode = newPinCode;
await setIdentityInLocalStorage(identity);
return encodeResponse("200", null, "Successfully changed pincode");
} else {
return encodeResponse("400", null, "Identity not found");
}
} catch (e) {
return encodeResponse("400", e.message, "Change pincode error");
},
getIdentityProfile(identityKey) {
return new Penpal.Promise(result => {
const serializedProfile = localStorage.getItem(
"profiles/" + identityKey
);
if (serializedProfile === null || serializedProfile === "") {
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 => {
viamApi.setIdentity(identity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identityConfirmIdentificator,
null,
confirmationCodeArg
).then(executeResult => {
result(executeResult);
});
});
},
identityGetIdentificatorByRegisterToken(identity, tokenArg) {
return new Penpal.Promise(result => {
viamApi.setIdentity(identity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identityGetIdentificatorByRegisterToken,
null,
tokenArg
).then(executeResult => {
result(executeResult);
});
});
},
submitIdentificator(identity, identificatorArg, registerToken) {
return new Penpal.Promise(result => {
viamApi.setIdentity(identity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identitySubmitIdentificator,
null,
identificatorArg,
registerToken
).then(executeResult => {
result(executeResult);
});
});
},
submitRegisterClaims(
identity,
givennameArg,
familynameArg,
emailArg,
phonenumberArg
) {
return new Penpal.Promise(result => {
viamApi.setIdentity(identity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identitySubmitRegisterClaims,
null,
givennameArg,
familynameArg,
emailArg,
phonenumberArg
).then(executeResult => {
result(executeResult);
});
});
},
agreeOnRegistration(registerIdentity) {
viamApi.setIdentity(registerIdentity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identityAgreeOnRegistration,
null
).then(executeResult => {
if (executeResult.code === "200") {
sequence = sequence.then(() => {
.catch(e => {
result({
data: "",
code: "400",
status: "Can not store identity: " + e
});
});
});
});
},
resendConfirmationCode(identity, identificatorArg) {
return new Penpal.Promise(result => {
viamApi.setIdentity(identity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identityResendConfirmationCode,
null,
identificatorArg
).then(executeResult => {
result(executeResult);
});
});
},
login: async (loginIdentity, mode, requestCode, requestActionID) => {
if (!window.loadedIdentities[loginIdentity.authentication.publicKey]) {
return {
data: "",
code: "400",
status: "Identity not loaded"
};
}
const deviceHash = await createDeviceHash(
loginIdentity.authentication.publicKey
);
window.viamApi.setSessionData("", "");
window.viamApi.setIdentity(loginIdentity.authentication.publicKey);
const identityLoginResponse = await executeRestfulFunction(
"public",
window.viamApi,
window.viamApi.identityLogin,
null,
mode,
requestCode,
requestActionID
);
const { code, data } = identityLoginResponse;
const responseToClient = Object.assign({}, identityLoginResponse);
if (code === "200") {
if (
mode === LOGIN_MODES.SMS ||
mode === LOGIN_MODES.PREVIOUSLY_ADDED_DEVICE
) {
handleIdentityLogin(loginIdentity, data.Uuid, data.Session);
if (mode === LOGIN_MODES.SMS) {
const dataUrl = await QRCode.toDataURL(
`${data.ActionID},${data.QrCode}`
);
Object.assign(responseToClient.data, { image: dataUrl });
}
}
return responseToClient;
},
identityAddNewDevice() {
return new Penpal.Promise(result => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
});
if (window.loadedIdentities[authenticationPublicKey] === null) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
});
let success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
});
executeRestfulFunction(
"private",
viamApi,
viamApi.identityAddNewDevice,
null
).then(executeResult => {
let actionID = executeResult.data["ActionID"];
let QrCode = executeResult.data["QrCode"];
} else {
result(executeResult);
}
});
});
},
identityDestroyKeysForDevice(authenticationPublicKeyArg) {
return new Penpal.Promise(result => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
});
if (window.loadedIdentities[authenticationPublicKey] === null) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
});
let success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
});
executeRestfulFunction(
"private",
viamApi,
viamApi.identityDestroyKeysForDevice,
null,
btoa(authenticationPublicKeyArg)
).then(executeResult => {
result(executeResult);
});
});
},
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey]
) {
return {
data: "",
code: "400",
status: "Identity not loaded"
};
}
// 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
destroyAuthentication();
"private",
window.viamApi,
window.viamApi.identityLogout,
}
},
identityRestoreAccess(restoreAccessIdentity, identificator) {
return new Penpal.Promise(result => {
viamApi.setSessionData("", "");
viamApi.setIdentity(restoreAccessIdentity.authentication.publicKey);
executeRestfulFunction(
"public",
viamApi,
viamApi.identityRestoreAccess,
null,
identificator
).then(executeResult => {
result(executeResult);
getCurrentlyLoggedInUUID() {
return new Penpal.Promise(result => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
return { data: "", code: "400", status: "Identity not loaded" };
if (window.loadedIdentities[authenticationPublicKey] === null) {
return { data: "", code: "400", status: "Identity not loaded" };
let success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
});
if (localStorage.getItem("uuid") === null) {
result({ data: "", code: "400", status: "Not logged in UUID" });
result({
data: localStorage.getItem("uuid"),
code: "200",
status: "UUID loaded"
});
});
},
getCertificateByPassport(passportUUID) {
return new Penpal.Promise(result => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
return { data: "", code: "400", status: "Identity not loaded" };
if (window.loadedIdentities[authenticationPublicKey] === null) {
return { data: "", code: "400", status: "Identity not loaded" };
let success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
});
getCertificateForPassport(passportUUID, false).then(
certificateResult => {
result(certificateResult);
}
);
});
},
getOneTimeCertificateByPassport(passportUUID, emailArg) {
return new Penpal.Promise(result => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
return { data: "", code: "400", status: "Identity not loaded" };
if (window.loadedIdentities[authenticationPublicKey] === null) {
return { data: "", code: "400", status: "Identity not loaded" };
let success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
});
getCertificateForPassport(passportUUID, true).then(
certificateResult => {
if (certificateResult.code === "200") {
let passportPrivateKey = certificateResult.data["privateKey"];
let passportChain = certificateResult.data["chain"];
createOneTimePassportCertificate(
makeid() + "-" + passportUUID,
emailArg,
passportPrivateKey,
passportCertificate
).then(function(keys) {
let publicKeyOneTime = keys["publicKeyPEM"];
let privateKeyOneTime = keys["privateKeyPEM"];
let certificateOneTime = keys["certificatePEM"];
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"
});
}
signEmail: async (passportUUID, emailArg, emailMessage) => {
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;
const keys = await createOneTimePassportCertificate(
makeid() + "-" + passportUUID,
emailArg,
passportPrivateKey,
passportCertificate
);
const {
privateKeyPEM: privateKeyOneTime,
certificatePEM: certificateOneTime
} = keys;
passportChain.push(passportCertificate);
response = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.passportGetEmailWithHeaderByPassport,
null,
passportUUID,
emailMessage
);
if (response.code !== "200") {
return encodeResponse("400", "", response.status);
}
const signedEmail = await signEmail(
response.data,
certificateOneTime,
passportChain,
privateKeyOneTime
);
response = await executeRestfulFunction(
"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");
signDocument: 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
const keys = await createOneTimePassportCertificate(
makeid() + "-" + passportUUID,
null,
passportPrivateKey,
passportCertificate
);
const {
privateKeyPEM: privateKeyOneTime,
certificatePEM: certificateOneTime
} = keys;
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 downloadResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentGetDocumentByUUID,
null,
documentUUID,
pdfContentType
);
if (downloadResponse.code !== "200") {
return encodeResponse("400", "", downloadResponse.status);
}
const pdfRaw = base64ToByteArray(downloadResponse.data);
signedPdf = await signPdf(
pdfRaw,
certificateOneTime,
passportChain,
privateKeyOneTime
);
} catch (err) {
console.error(err);
return encodeResponse("500", "", err.message);
}
const signedPdfB64 = byteArrayToBase64(signedPdf);
const uploadResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentPutDocumentByUUID,
null,
documentUUID,
pdfContentType,
signedPdfB64
);
if (uploadResponse.code !== "200") {
return encodeResponse("400", "", uploadResponse.status);
}
const signResponse = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentSignDocumentByUUID,
null,
passportUUID,
documentUUID,
pdfContentType
);
if (signResponse.code !== "200") {
return encodeResponse("400", "", signResponse.status);
}
return encodeResponse("200", "", "Document signed");
documentCreateDocument: async (passportUUID, path, contentType, title) => {
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,
contentType,
title
}
};
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");
},
documentPutDocument: async (
passportUUID,
resourceid,
contentType,
) => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
resourceid = encodeURI(resourceid);
contentType = encodeURI(contentType);
const config = {
headers: {
passportuuid: passportUUID,
}
};
const response = await executeRestfulFunction(
"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 => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (authenticationPublicKey === null) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
if (window.loadedIdentities[authenticationPublicKey] === null) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
let success = extendPinCodeTtl(authenticationPublicKey);
if (success === false) {
result({
data: "",
code: "400",
status: "Identity not authenticated"
});
executeRestfulFunction(
"private",
viamApi,
viamApi.identityHasSession,
null
).then(executeResult => {
result(executeResult);
});
});
},
marketingSignUpIdentificator(identificator, reference) {
return new Penpal.Promise(result => {
executeRestfulFunction(
"public",
viamApi,
viamApi.marketingSignUpIdentificator,
null,
identificator,
reference
).then(executeResult => {
result(executeResult);
});
});
},
marketingGetIdentificatorProfile(identificator, pincode) {
return new Penpal.Promise(result => {
executeRestfulFunction(
"public",
viamApi,
viamApi.marketingGetIdentificatorProfile,
null,
identificator,
pincode
).then(executeResult => {
result(executeResult);
});
});
},
marketingExecuteEventForIdentificator(identificator, pincode, event) {
executeRestfulFunction(
"public",
viamApi,
viamApi.marketingExecuteEventForIdentificator,
null,
identificator,
pincode,
event
).then(executeResult => {
result(executeResult);
});
});
},
getCurrentlyAuthenticatedIdentity() {
const {
publicKey,
x509Certificate
} = window.currentlyAuthenticatedIdentity.authentication;
return encodeResponse(
"200",
{
authentication: {
publicKey,
x509Certificate
}
},
"Currently authenticated identity"
);
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));
});
return collaboraApi.discovery().then(apps => apps);
getPassportsNewProtocol: async (resourceID, contentType) => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
const response = await wopiAPI.getPassportsNewProtocol(
resourceID,
contentType
);
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
const response = await wopiAPI.getPassports(fileId);
wopiCreateDocument: async (passportUUID, path, contentType, title) => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
const config = {
headers: {
path,
passportuuid: passportUUID,
contentType,
title
}
};
const executeResult = await executeRestfulFunction(
"private",
window.viamApi,
window.viamApi.documentCreateDocument,
config
);
if (executeResult.code !== "200") return executeResult;
const resourceID = executeResult.data;
const passports = await wopiAPI.getPassports(resourceID, contentType);
return passports;
},
wopiPutFile: async (path, accessToken, file) => {
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
if (
!authenticationPublicKey ||
!window.loadedIdentities[authenticationPublicKey] ||
!extendPinCodeTtl(authenticationPublicKey)
) {
return encodeResponse("400", "", "Identity not authenticated");
}
const response = await wopiAPI.putDocument(path, accessToken, file);
connection.promise.then(parent => {
if (!navigator.cookieEnabled) {
console.warn("Cookie disabled. Can't start library.");
return;
}
if (event.key === "authenticatedIdentity" && event.newValue === null) {
const publicKey =
window.currentlyAuthenticatedIdentity.authentication.publicKey;
window.currentlyLoadedIdentity = null;
window.currentlyAuthenticatedIdentity = null;
const event = createEvent("LogoutFromAnotherTab", "Logout", [publicKey]);
parent.onEvent(event);
}
});
const identities = localStorage.getItem("identities");
console.log("Library loaded at: " + new Date().toISOString());
if (identities === "" || identities === null) {
localStorage.setItem("identities", JSON.stringify({}));
if (
localStorage.getItem("uuid") === null ||
localStorage.getItem("token") === null ||
localStorage.getItem("authenticatedIdentity") === null
) {
const event = createEvent("", "NotAuthenticated");
parent.onEvent(event);
localStorage.removeItem("uuid");
localStorage.removeItem("token");
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
const pinCode = getPincode(authenticationPublicKey);
loadIdentityInternal(authenticationPublicKey, "00000000").then(result => {
if (result.code !== "200") {
const event = createEvent(
"CanNotGetPincodeForAuthenticatedIdentity",
"IdentityNotLoaded",
loadIdentityInternal(authenticationPublicKey, pinCode).then(result => {
if (result.code !== "200") {
const event = createEvent(
"CanNotLoadIdentity",
"ErrorDuringLoadingIdentity",
let anynomousDeviceKeyEventsProcessing = false;
let maxDeviceKeyAnonymousEventTime = 0;
let eventsDeviceEventsProcessing = false;
let maxDeviceKeyEventTime = 0;
let eventsEntityEventsProcessing = false;
let maxEntityEventTime = 0;
let identityLoadedEvent = false;
let identityAuthenticatedEvent = false;
let previousLocalStorageUUID;
let previousLocalStorageToken;
let previousLocalStorageIdentity;
if (window.currentlyAuthenticatedIdentity) {
const { authentication } = window.currentlyAuthenticatedIdentity;
const pinCode = getPincode(authentication.publicKey);
if (pinCode) {
const identity = await getIdentityFromLocalStorage(
authentication.publicKey,
pinCode,
false
);
window.currentlyLoadedIdentity = identity;
if (!identityAuthenticatedEvent && identity) {
const event = createEvent("IdentityAuthenticated", "Authenticated", [
identity.authentication.publicKey
]);
parent.onEvent(event);
identityAuthenticatedEvent = true;
}
const authenticationPublicKey = localStorage.getItem(
"authenticatedIdentity"
);
const result = await loadIdentityInternal(
authenticationPublicKey,
"00000000"
);
const event = createEvent(
"CanNotGetPincodeForAuthenticatedIdentity",
"IdentityNotLoaded",
[authenticationPublicKey]
);
parent.onEvent(event);
clearPinCodeTtl(authenticationPublicKey);
window.currentlyAuthenticatedIdentity = null;
}
const pinCode = getPincode(
window.currentlyLoadedIdentity.authentication.publicKey
);
if (!pinCode) {
if (!identityLoadedEvent) {
const result = await loadIdentityInternal(
window.currentlyLoadedIdentity.authentication.publicKey,
"00000000"
);

Alexey Lunin
committed
if (window.currentlyLoadedIdentity && result.code !== "200") {
const event = createEvent(
"CanNotLoadPincodeForLoadedIdentity",
"IdentityNotLoaded",
[window.currentlyLoadedIdentity.authentication.publicKey]
);
parent.onEvent(event);
identityLoadedEvent = true;
}
if (window.currentlyAuthenticatedIdentity) {
const now = new Date().getTime();
if (now - window.lastTimeGetProfile > 30000) {
getProfileData(window.currentlyAuthenticatedIdentity);
const currentLocalStorageUUID = localStorage.getItem("uuid");
const currentLocalStorageToken = localStorage.getItem("token");
const currentLocalStorageIdentity = localStorage.getItem(
"authenticatedIdentity"
);
(!currentLocalStorageUUID && previousLocalStorageUUID) ||
(!currentLocalStorageToken && previousLocalStorageToken) ||
(!currentLocalStorageIdentity && previousLocalStorageIdentity)
) {
previousLocalStorageUUID = null;
previousLocalStorageToken = null;
previousLocalStorageIdentity = null;
destroyAuthentication();
const event = createEvent("", "LogoutExternal");
parent.onEvent(event);
} else {
previousLocalStorageUUID = currentLocalStorageUUID;
previousLocalStorageToken = currentLocalStorageToken;
previousLocalStorageIdentity = currentLocalStorageIdentity;
}
const getNewEventsWithoutSession = async () => {
anynomousDeviceKeyEventsProcessing = true;
try {
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) {
await setIdentityInLocalStorage(window.currentlyLoadedIdentity);
parent.onEvent(event);
break;
const actionID = event["actionID"];
const QrCode = event["payloads"][1];
const eventCopy = JSON.parse(JSON.stringify(event));
parent.onEvent(eventCopy);
});
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;
}
parent.onEvent(event);
changedMaxDeviceKeyAnonymousEventTime = true;
maxDeviceKeyAnonymousEventTime = Math.max(
maxDeviceKeyAnonymousEventTime,
event.stamp
);
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 {
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));
eventCopy["payloads"].push(url);
parent.onEvent(eventCopy);
});
} else {
parent.onEvent(event);
maxDeviceKeyEventTime = Math.max(maxDeviceKeyEventTime, event.stamp);
}
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 {
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));
eventCopy["payloads"].push(url);
parent.onEvent(eventCopy);
});
parent.onEvent(event);
changedMaxEntityEventTime = true;
maxEntityEventTime = Math.max(maxEntityEventTime, event.stamp);
}
if (changedMaxEntityEventTime) {
await executeRestfulFunction(
"private",
viamApi,
viamApi.eventUpdateLastViewed,
null,
"entity",
maxEntityEventTime.toString()
);
} catch (e) {
console.warn(e);
}
eventsEntityEventsProcessing = false;
if (
window.currentlyLoadedIdentity &&
!anynomousDeviceKeyEventsProcessing &&
!window.currentlyAuthenticatedIdentity
) {
getNewEventsWithoutSession();
}
if (window.currentlyAuthenticatedIdentity) {
// These functions has to be executed at the same time.
!eventsDeviceEventsProcessing && getNewDeviceEvents();
!eventsEntityEventsProcessing && getNewEntityEvents();