diff --git a/src/app/modules/organizations/users/enroll-master-password-reset.component.html b/src/app/modules/organizations/users/enroll-master-password-reset.component.html new file mode 100644 index 0000000000..e1e389caa5 --- /dev/null +++ b/src/app/modules/organizations/users/enroll-master-password-reset.component.html @@ -0,0 +1,59 @@ + diff --git a/src/app/modules/organizations/users/enroll-master-password-reset.component.ts b/src/app/modules/organizations/users/enroll-master-password-reset.component.ts new file mode 100644 index 0000000000..b996493d85 --- /dev/null +++ b/src/app/modules/organizations/users/enroll-master-password-reset.component.ts @@ -0,0 +1,97 @@ +import { Component } from "@angular/core"; + +import { ModalRef } from "jslib-angular/components/modal/modal.ref"; +import { ModalConfig } from "jslib-angular/services/modal.service"; +import { ApiService } from "jslib-common/abstractions/api.service"; +import { CryptoService } from "jslib-common/abstractions/crypto.service"; +import { I18nService } from "jslib-common/abstractions/i18n.service"; +import { LogService } from "jslib-common/abstractions/log.service"; +import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; +import { SyncService } from "jslib-common/abstractions/sync.service"; +import { UserVerificationService } from "jslib-common/abstractions/userVerification.service"; +import { Utils } from "jslib-common/misc/utils"; +import { Organization } from "jslib-common/models/domain/organization"; +import { OrganizationUserResetPasswordEnrollmentRequest } from "jslib-common/models/request/organizationUserResetPasswordEnrollmentRequest"; +import { Verification } from "jslib-common/types/verification"; + +@Component({ + selector: "app-enroll-master-password-reset", + templateUrl: "enroll-master-password-reset.component.html", +}) +export class EnrollMasterPasswordReset { + organization: Organization; + + verification: Verification; + formPromise: Promise; + + constructor( + private userVerificationService: UserVerificationService, + private apiService: ApiService, + private platformUtilsService: PlatformUtilsService, + private i18nService: I18nService, + private cryptoService: CryptoService, + private syncService: SyncService, + private logService: LogService, + private modalRef: ModalRef, + config: ModalConfig + ) { + this.organization = config.data.organization; + } + + async submit() { + let toastStringRef = "withdrawPasswordResetSuccess"; + + this.formPromise = this.userVerificationService + .buildRequest(this.verification, OrganizationUserResetPasswordEnrollmentRequest) + .then(async (request) => { + // Set variables + let keyString: string = null; + + // Enrolling + if (!this.organization.resetPasswordEnrolled) { + // Retrieve Public Key + const orgKeys = await this.apiService.getOrganizationKeys(this.organization.id); + if (orgKeys == null) { + throw new Error(this.i18nService.t("resetPasswordOrgKeysError")); + } + + const publicKey = Utils.fromB64ToArray(orgKeys.publicKey); + + // RSA Encrypt user's encKey.key with organization public key + const encKey = await this.cryptoService.getEncKey(); + const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer); + keyString = encryptedKey.encryptedString; + toastStringRef = "enrollPasswordResetSuccess"; + + // Create request and execute enrollment + request.resetPasswordKey = keyString; + await this.apiService.putOrganizationUserResetPasswordEnrollment( + this.organization.id, + this.organization.userId, + request + ); + } else { + // Withdrawal + request.resetPasswordKey = keyString; + await this.apiService.putOrganizationUserResetPasswordEnrollment( + this.organization.id, + this.organization.userId, + request + ); + } + + await this.syncService.fullSync(true); + }); + try { + await this.formPromise; + this.platformUtilsService.showToast("success", null, this.i18nService.t(toastStringRef)); + this.modalRef.close(); + } catch (e) { + this.logService.error(e); + } + } + + get isEnrolled(): boolean { + return this.organization.resetPasswordEnrolled; + } +} diff --git a/src/app/modules/organizations/users/organization-user.module.ts b/src/app/modules/organizations/users/organization-user.module.ts new file mode 100644 index 0000000000..aed0ac6629 --- /dev/null +++ b/src/app/modules/organizations/users/organization-user.module.ts @@ -0,0 +1,14 @@ +import { ScrollingModule } from "@angular/cdk/scrolling"; +import { NgModule } from "@angular/core"; + +import { LooseComponentsModule } from "../../loose-components.module"; +import { SharedModule } from "../../shared.module"; + +import { EnrollMasterPasswordReset } from "./enroll-master-password-reset.component"; + +@NgModule({ + imports: [SharedModule, ScrollingModule, LooseComponentsModule], + declarations: [EnrollMasterPasswordReset], + exports: [EnrollMasterPasswordReset], +}) +export class OrganizationUserModule {} diff --git a/src/app/modules/vault-filter/components/organization-options.component.ts b/src/app/modules/vault-filter/components/organization-options.component.ts index 3e0256110c..fb256acd58 100644 --- a/src/app/modules/vault-filter/components/organization-options.component.ts +++ b/src/app/modules/vault-filter/components/organization-options.component.ts @@ -1,17 +1,17 @@ import { Component, Input } from "@angular/core"; +import { ModalService } from "jslib-angular/services/modal.service"; import { ApiService } from "jslib-common/abstractions/api.service"; -import { CryptoService } from "jslib-common/abstractions/crypto.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { LogService } from "jslib-common/abstractions/log.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { PolicyService } from "jslib-common/abstractions/policy.service"; import { SyncService } from "jslib-common/abstractions/sync.service"; import { PolicyType } from "jslib-common/enums/policyType"; -import { Utils } from "jslib-common/misc/utils"; import { Organization } from "jslib-common/models/domain/organization"; import { Policy } from "jslib-common/models/domain/policy"; -import { OrganizationUserResetPasswordEnrollmentRequest } from "jslib-common/models/request/organizationUserResetPasswordEnrollmentRequest"; + +import { EnrollMasterPasswordReset } from "../../organizations/users/enroll-master-password-reset.component"; @Component({ selector: "app-organization-options", @@ -29,8 +29,8 @@ export class OrganizationOptionsComponent { private i18nService: I18nService, private apiService: ApiService, private syncService: SyncService, - private cryptoService: CryptoService, private policyService: PolicyService, + private modalService: ModalService, private logService: LogService ) {} @@ -113,70 +113,11 @@ export class OrganizationOptionsComponent { } async toggleResetPasswordEnrollment(org: Organization) { - // Set variables - let keyString: string = null; - let toastStringRef = "withdrawPasswordResetSuccess"; - - // Enrolling - if (!org.resetPasswordEnrolled) { - // Alert user about enrollment - const confirmed = await this.platformUtilsService.showDialog( - this.i18nService.t("resetPasswordEnrollmentWarning"), - null, - this.i18nService.t("yes"), - this.i18nService.t("no"), - "warning" - ); - if (!confirmed) { - return; - } - - // Retrieve Public Key - this.actionPromise = this.apiService - .getOrganizationKeys(org.id) - .then(async (response) => { - if (response == null) { - throw new Error(this.i18nService.t("resetPasswordOrgKeysError")); - } - - const publicKey = Utils.fromB64ToArray(response.publicKey); - - // RSA Encrypt user's encKey.key with organization public key - const encKey = await this.cryptoService.getEncKey(); - const encryptedKey = await this.cryptoService.rsaEncrypt(encKey.key, publicKey.buffer); - keyString = encryptedKey.encryptedString; - toastStringRef = "enrollPasswordResetSuccess"; - - // Create request and execute enrollment - const request = new OrganizationUserResetPasswordEnrollmentRequest(); - request.resetPasswordKey = keyString; - return this.apiService.putOrganizationUserResetPasswordEnrollment( - org.id, - org.userId, - request - ); - }) - .then(() => { - return this.syncService.fullSync(true); - }); - } else { - // Withdrawal - const request = new OrganizationUserResetPasswordEnrollmentRequest(); - request.resetPasswordKey = keyString; - this.actionPromise = this.apiService - .putOrganizationUserResetPasswordEnrollment(org.id, org.userId, request) - .then(() => { - return this.syncService.fullSync(true); - }); - } - - try { - await this.actionPromise; - this.platformUtilsService.showToast("success", null, this.i18nService.t(toastStringRef)); - await this.load(); - } catch (e) { - this.platformUtilsService.showToast("error", this.i18nService.t("errorOccurred"), e.message); - this.logService.error(e); - } + this.modalService.open(EnrollMasterPasswordReset, { + allowMultipleModals: true, + data: { + organization: org, + }, + }); } } diff --git a/src/app/oss.module.ts b/src/app/oss.module.ts index 88311f9ded..cd4d3d9d05 100644 --- a/src/app/oss.module.ts +++ b/src/app/oss.module.ts @@ -2,6 +2,7 @@ import { NgModule } from "@angular/core"; import { LooseComponentsModule } from "./modules/loose-components.module"; import { OrganizationManageModule } from "./modules/organizations/manage/organization-manage.module"; +import { OrganizationUserModule } from "./modules/organizations/users/organization-user.module"; import { PipesModule } from "./modules/pipes/pipes.module"; import { SharedModule } from "./modules/shared.module"; import { VaultFilterModule } from "./modules/vault-filter/vault-filter.module"; @@ -15,6 +16,7 @@ import { OrganizationBadgeModule } from "./modules/vault/modules/organization-ba OrganizationBadgeModule, PipesModule, OrganizationManageModule, + OrganizationUserModule, ], exports: [LooseComponentsModule, VaultFilterModule, OrganizationBadgeModule, PipesModule], bootstrap: [], diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 1b62eec2be..6cd0bbd02d 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -4163,7 +4163,7 @@ "message": "Password reset success!" }, "resetPasswordEnrollmentWarning": { - "message": "Enrollment will allow organization administrators to change your master password. Are you sure you want to enroll?" + "message": "Enrollment will allow organization administrators to change your master password" }, "resetPasswordPolicy": { "message": "Master Password Reset"