UNPKG

vibez-core

Version:

Utilities, types and common dependencies.

345 lines 24.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const DomainError_1 = require("../common/DomainError"); const IdentityManagement_1 = require("./IdentityManagement"); const Security_1 = require("./Security"); const util_1 = require("../util"); const business_1 = require("../business"); const common_1 = require("../common"); const firestore_1 = require("@google-cloud/firestore"); class AccountManager extends common_1.DataProvider { constructor(context, dataSource) { super(dataSource); this.context = context; } getDefaultRoleForAccount(userKind) { return __awaiter(this, void 0, void 0, function* () { let result = this.context.performTask("Read", AccountManager.COMPONENT, (c) => __awaiter(this, void 0, void 0, function* () { let rolesCollection = this.dataSource.collection("Roles"); let roleDocumentsQuery = rolesCollection.where("name", "==", `DEFAULT_${userKind}`); let { docs, empty } = yield roleDocumentsQuery.get(); if (empty) { return Promise.reject(new DomainError_1.DomainError("NOT_FOUND", `There is no default role defined to account of type ${userKind}`, AccountManager.COMPONENT, "Please validate with the platform administrator", undefined, "MEDIUM", `When retrieving default role for account of type ${userKind}, there were no matching records`)); } let [snapshot] = [...docs]; let role = Object.assign({ id: snapshot.id }, snapshot.data()); return role; })); return result; }); } validateActivation(actionCode, continueUrl) { return __awaiter(this, void 0, void 0, function* () { let { operation, data } = yield firebase.auth().checkActionCode(actionCode); if (operation !== "VERIFY_EMAIL") { return Promise.reject(new DomainError_1.DomainError("INVALID_ACTION", "Activation Action is not a valid one", AccountManager.COMPONENT, "Please validate the submitted information and try again", undefined, "LOW", "When validating activation for activation intent, the submitted action was invalid)")); } let { email, fromEmail } = data; let result = yield this.context.performTask("Write", AccountManager.COMPONENT, (context) => __awaiter(this, void 0, void 0, function* () { let activatingUser; let usersCollection = this.dataSource.collection("Users"); let { empty, docs } = yield usersCollection.get(); if (empty) { return Promise.reject(new DomainError_1.DomainError("NOT_FOUND", `There were no matches for the submitted credentials`, AccountManager.COMPONENT, "Please validate the submitted information and try again", undefined, "LOW", "When validating account's activation there were not records matching credentials for the linked email")); } let match = docs.map((snapshot) => (Object.assign({}, snapshot.data(), { id: snapshot.id }))).find(({ credentials }) => credentials.some(c => c.issuer == "Local" && c.id == email)); if (!match) { return Promise.reject(new DomainError_1.DomainError("NOT_FOUND", `There were no matches for the submitted credentials`, AccountManager.COMPONENT, "Please validate the submitted information and try again", undefined, "LOW", "When validating account's activation there were not records matching credentials for the linked email")); } activatingUser = match; let credential = activatingUser.credentials.find(c => c.issuer == "Local" && c.id == email); let credentialIndex = activatingUser.credentials.indexOf(credential); activatingUser.credentials[credentialIndex].validatedOn = new Date(Date.now()); activatingUser.validatedOn = new Date(Date.now()); let action = { actor: context.getActorInformation(), date: new Date(Date.now()), description: "Activate Account" }; activatingUser.actions.push(action); yield usersCollection.doc(activatingUser.id).update(activatingUser); return true; })); return true; }); } authenticateAccount(credentialID, password, token) { return __awaiter(this, void 0, void 0, function* () { let result = yield this.context.performTask("Read", AccountManager.COMPONENT, (c) => __awaiter(this, void 0, void 0, function* () { let authenticatedUser; let userCollection = this.dataSource.collection("Users"); // let userDocumentsQuery = userCollection.where( // "credentials.id", // "==", // credentialID // ); // if (password) { // userDocumentsQuery = userDocumentsQuery // .where("credential.issuer", "==", "Local") // .where("credentials.password", "==", password); // } // if (token) { // userDocumentsQuery = userDocumentsQuery.where( // "credentials.token", // "==", // token // ); // } let { docs, empty } = yield userCollection.get(); if (empty) { return Promise.reject(new DomainError_1.DomainError("NOT_FOUND", `There were no matches for the submitted credentials`, AccountManager.COMPONENT, "Please validate the submitted information or if there is a pending transaction on your account/credentials and try again", undefined, "LOW", "When authenticating the account for the submitted credentials there were not records matching either the credential ID, password or token and in a valid state (Successfully Validated)")); } let match = docs.map((snapshot) => (Object.assign({}, snapshot.data(), { id: snapshot.id }))).find(({ credentials }) => { return credentials.some(c => c.id == credentialID && password ? c.issuer == "Local" && c.password == password : token ? c.token == token : false); }); if (!match) { return Promise.reject(new DomainError_1.DomainError("NOT_FOUND", `There were no matches for the submitted credentials`, AccountManager.COMPONENT, "Please validate the submitted information or if there is a pending transaction on your account/credentials and try again", undefined, "LOW", "When authenticating the account for the submitted credentials there were not records matching either the credential ID, password or token and in a valid state (Successfully Validated)")); } //let [{ id, data }] = [...docs]; authenticatedUser = match; if (!authenticatedUser.validatedOn) { return Promise.reject(new DomainError_1.DomainError("INVALID_ACCOUNT", `Account hasn't been validated`, AccountManager.COMPONENT, "Please complete the registration process by checking your e-mails for an e-mail from [@vibez.io] with instructions.", undefined, "LOW", "When authenticating the account for the submitted credentials the account hasn't been validated")); } let { issuedOn, expiration, validatedOn } = authenticatedUser.credentials.find(c => c.id == credentialID && (password ? c.password == password : false || token ? c.token == token : false)); if (!validatedOn) { return Promise.reject(new DomainError_1.DomainError("INVALID_CREDENTIAL", `Credentials haven't been validated`, AccountManager.COMPONENT, "Please complete the registration process by checking your e-mails for an e-mail from [@vibez.io] with instructions.", undefined, "LOW", "When authenticating the account's credential haven't been validated")); } let expiringAt = issuedOn; expiringAt.setSeconds(expiringAt.getSeconds() + expiration); if (expiringAt < new Date(Date.now())) { return Promise.reject(new DomainError_1.DomainError("INVALID_CREDENTIAL", `Expired credentials`, AccountManager.COMPONENT, "Please renew your credentials to continue", undefined, "LOW", "When authenticating the account for the submitted credentials the retrieved credentials were expired")); } return authenticatedUser; })); return result; }); } createAccount(identity, credential, userKind) { return __awaiter(this, void 0, void 0, function* () { if (!credential.password) { return Promise.reject(new DomainError_1.DomainError("INVALID_CREDENTIAL", `Password required`, AccountManager.COMPONENT, "Please validate your information and try again", undefined, "LOW", "When signing up the submitted account's information. There provided credentials had missing required information")); } credential.issuer = "Local"; credential.issuedOn = new Date(Date.now()); credential.expiration = Security_1.DEFAULT_EXPIRATION_SECONDS; let identityManager = new IdentityManagement_1.IdentityManager(this.context, this.dataSource); let contactProvider = new business_1.ContactProvider(this.context, this.dataSource); let fetchedIdentity = yield identityManager.getIdentity(identity); let contact = yield contactProvider.getContact(fetchedIdentity.contact); let role = yield this.getDefaultRoleForAccount(userKind); let result = yield this.context.performTask("Write", AccountManager.COMPONENT, (c) => __awaiter(this, void 0, void 0, function* () { let action = { actor: c.getActorInformation(), date: new Date(Date.now()), description: `Register account for identity with id [${identity.id}]` }; let account = { id: "", identity, credentials: [credential], kind: userKind, role, actions: [action], collection: "Users" }; let usersCollection = this.dataSource.collection("Users"); let userDocument = yield usersCollection.add(account); let { id } = yield userDocument.get(); account.id = id; let user = yield firebase .auth() .createUserWithEmailAndPassword(contact.email, credential.password); yield user.sendEmailVerification(); return account; })); return result; }); } activateAccount(user, userKind) { return __awaiter(this, void 0, void 0, function* () { let result = yield this.context.performTask("Write", AccountManager.COMPONENT, (c) => __awaiter(this, void 0, void 0, function* () { let action = { actor: c.getActorInformation(), date: new Date(Date.now()), description: `Account with id [${user.id}] has been activated successfully` }; let updatedAccount; let userCollection = this.dataSource.collection("Users"); let userDocument = userCollection.doc(user.id); let snapshot = yield userDocument.get(); if (!snapshot.exists) { return Promise.reject(new DomainError_1.DomainError("NOT_FOUND", "User cannot be activated", AccountManager.COMPONENT, "Please validate the submitted information", undefined, "LOW", "When activating account, the submitted account information has no matches")); } updatedAccount = Object.assign({ id: snapshot.id }, snapshot.data()); if (userKind != updatedAccount.kind) { return Promise.reject(new DomainError_1.DomainError("INVALID_ACCOUNT", "User cannot be activated", AccountManager.COMPONENT, "Please validate the submitted information", undefined, "LOW", "When activating account, the submitted account information doesn't match available records")); } updatedAccount.actions.push(action); updatedAccount.validatedOn = new Date(Date.now()); let { actions, validatedOn } = updatedAccount; yield userDocument.update({ actions, validatedOn }); return updatedAccount; })); return result; }); } assignAccountRole(user, role, userKind) { return __awaiter(this, void 0, void 0, function* () { let result = yield this.context.performTask("Write", AccountManager.COMPONENT, (c) => __awaiter(this, void 0, void 0, function* () { let action = { actor: c.getActorInformation(), date: new Date(Date.now()), description: `The role of the account with id [${user.id}] has been changed to [${role.id}]` }; let updatedAccount; let rolesCollection = this.dataSource.collection("Roles"); let roleDocument = rolesCollection.doc(role.id); { let { id, exists, data } = yield roleDocument.get(); if (!exists) { return Promise.reject(new DomainError_1.DomainError("NOT_FOUND", "New role couldn't be assigned", AccountManager.COMPONENT, "Please validate the submitted information", undefined, "LOW", "When assigning account's role, the submitted role information has no matches")); } } let userCollection = this.dataSource.collection("Users"); let userDocument = userCollection.doc(user.id); { let snapshot = yield userDocument.get(); updatedAccount = Object.assign({ id: snapshot.id }, snapshot.data()); if (!snapshot.exists) { return Promise.reject(new DomainError_1.DomainError("NOT_FOUND", "New role couldn't be assigned", AccountManager.COMPONENT, "Please validate the submitted information", undefined, "LOW", "When assigning account's role, the submitted account information has no matches")); } let { kind, actions } = updatedAccount; if (userKind != kind) { return Promise.reject(new DomainError_1.DomainError("INVALID_ACCOUNT", "User cannot be activated", AccountManager.COMPONENT, "Please validate the submitted information", undefined, "LOW", "When assigning account's role, the submitted account information doesn't match available records")); } actions.push(action); updatedAccount.role = role; yield userDocument.update(updatedAccount); } return updatedAccount; })); return result; }); } updateCredential(user, credential, issuer, data, userKind) { return __awaiter(this, void 0, void 0, function* () { let result = yield this.context.performTask("Write", AccountManager.COMPONENT, (c) => __awaiter(this, void 0, void 0, function* () { let action = { actor: c.getActorInformation(), date: new Date(Date.now()), description: `The credentials of the account with id [${user.id}] and provider [${issuer}] has been successfully updated` }; let updatedAccount; let userCollection = this.dataSource.collection("Users"); let userDocument = userCollection.doc(user.id); { let snapshot = yield userDocument.get(); if (!snapshot.exists) { return Promise.reject(new DomainError_1.DomainError("NOT_FOUND", "Cannot update credentials", AccountManager.COMPONENT, "Please validate the submitted information", undefined, "LOW", "When updating account's credentials, the submitted account information has no matches")); } updatedAccount = Object.assign({ id: snapshot.id }, snapshot.data()); if (userKind != updatedAccount.kind) { return Promise.reject(new DomainError_1.DomainError("INVALID_ACCOUNT", "User cannot be activated", AccountManager.COMPONENT, "Please validate the submitted information", undefined, "LOW", "When activating account, the submitted account information doesn't match available records")); } } if (!updatedAccount.credentials) { return Promise.reject(new DomainError_1.DomainError("UNASSIGNED_CREDENTIALS", `Account has no credentials registered`, AccountManager.COMPONENT, "Please validate the account information", undefined, "LOW", "When fetching account credentials for updating credentials, there were no credentials assigned to the corresponding account")); } let updatedCredentialIndex = updatedAccount.credentials.findIndex(c => c.id == credential.id && c.issuer == issuer); if (updatedCredentialIndex < 0) { return Promise.reject(new DomainError_1.DomainError("CREDENTIAL_NOT_FOUND", `Account has no credentials matching the submitted criteria`, AccountManager.COMPONENT, "Please validate the submitted information", undefined, "LOW", "When fetching account credentials for updating credentials, there were no credentials matching the submitted criteria")); } updatedAccount.credentials[updatedCredentialIndex].validatedOn = new Date(Date.now()); updatedAccount.credentials[updatedCredentialIndex].issuedOn = new Date(Date.now()); updatedAccount.credentials[updatedCredentialIndex].token = data.token; updatedAccount.credentials[updatedCredentialIndex].password = data.password; updatedAccount.actions.push(action); let { credentials, actions } = updatedAccount; yield userDocument.update({ credentials, actions }); return updatedAccount; })); return result; }); } resetCredential(identity, request) { return __awaiter(this, void 0, void 0, function* () { let result = yield this.context.performTask("Write", AccountManager.COMPONENT, (c) => __awaiter(this, void 0, void 0, function* () { let usersCollection = this.dataSource.collection("Users"); let userDocumentsQuery = usersCollection.where("identity.id", "==", identity.id); let { empty, docs } = yield userDocumentsQuery.get(); if (empty) { return Promise.reject(new DomainError_1.DomainError("ACCOUNT_NOT_FOUND", `The account for the submitted identity wasn't found`, AccountManager.COMPONENT, "Please validate the identity information", undefined, "LOW", "When fetching account information for recovering credentials, there were no accounts matching the submitted criteria")); } let [snapshot] = [...docs]; if (!snapshot.exists) { return Promise.reject(new DomainError_1.DomainError("ACCOUNT_NOT_FOUND", `The account for the submitted identity wasn't found`, AccountManager.COMPONENT, "Please validate the identity information", undefined, "LOW", "When fetching account information for recovering credentials, there were no accounts matching the submitted criteria")); } let updatedAccount; updatedAccount = Object.assign({ id: snapshot.id }, snapshot.data()); if (!updatedAccount.credentials) { return Promise.reject(new DomainError_1.DomainError("UNASSIGNED_CREDENTIALS", `Account has no credentials registered`, AccountManager.COMPONENT, "Please validate the account information", undefined, "LOW", "When fetching account credentials for updating credentials, there were no credentials assigned to the corresponding account")); } let updatedCredentialIndex = updatedAccount.credentials.findIndex(c => c.issuer == "Local"); if (updatedCredentialIndex < 0) { return Promise.reject(new DomainError_1.DomainError("CREDENTIAL_NOT_FOUND", `Account has no credentials matching the submitted criteria`, AccountManager.COMPONENT, "Please validate the submitted information", undefined, "LOW", "When fetching account credentials for updating credentials, there were no credentials matching the submitted criteria")); } let yesterday = new Date(Date.now()); yesterday.setDate(yesterday.getDate() - 1); if (!request) { let resetRequest = { date: new Date(Date.now()), ipAddress: c.clientAddress, id: util_1.randomString(12) }; let action = { actor: c.getActorInformation(), date: new Date(Date.now()), description: `Scheduled request [${resetRequest.id}] to reset password of credentials with id [${updatedAccount.credentials[updatedCredentialIndex].id}]` }; updatedAccount.resetRequest = resetRequest; updatedAccount.actions.push(action); return updatedAccount; } else if (request && updatedAccount.resetRequest && updatedAccount.resetRequest.id == request.id && updatedAccount.resetRequest.date > yesterday) { let action = { actor: c.getActorInformation(), date: new Date(Date.now()), description: `Reset password of credentials with id [${updatedAccount.credentials[updatedCredentialIndex].id}` }; updatedAccount.credentials[updatedCredentialIndex].issuedOn = new Date(Date.now()); updatedAccount.credentials[updatedCredentialIndex].password = this.generateRandomPassword(); updatedAccount.actions.push(action); let { credentials, actions, resetRequest } = updatedAccount; yield usersCollection.doc(snapshot.id).update({ credentials, actions, resetRequest: firestore_1.FieldValue.delete() }); return updatedAccount; } else { return Promise.reject(new DomainError_1.DomainError("INVALID_OPERATION", `The account's credentials reset request is invalid`, AccountManager.COMPONENT, "Please validate the submitted information", undefined, "LOW", "When proceeding to reset credentials for the account marching the criteria, the submitted information couldn't be validated")); } })); return result; }); } generateRandomPassword() { let passwordLength = Security_1.MIN_PASSWORD_LENGTH; return util_1.randomString(passwordLength); } } AccountManager.COMPONENT = "ACCOUNT_MANAGER"; exports.AccountManager = AccountManager; //# sourceMappingURL=AccountManagement.js.map