[PS-616] Feature/require auth for enroll password reset (#1698)
* Make enrollment a modal component * Add verification to org user module * Move enroll to component * Update warning for modal * update jslib * Use ModalRef to close the modal * Use bit-button
This commit is contained in:
parent
0e62e2ec2b
commit
ae3788d42b
|
@ -0,0 +1,59 @@
|
||||||
|
<div
|
||||||
|
class="modal fade"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="enrollMasterPasswordResetTitle"
|
||||||
|
>
|
||||||
|
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||||
|
<form
|
||||||
|
class="modal-content"
|
||||||
|
#form
|
||||||
|
(ngSubmit)="submit()"
|
||||||
|
[appApiAction]="formPromise"
|
||||||
|
ngNativeValidate
|
||||||
|
>
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="modal-title" id="enrollMasterPasswordResetTitle">
|
||||||
|
{{ (isEnrolled ? "withdrawPasswordReset" : "enrollPasswordReset") | i18n }}
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="close"
|
||||||
|
data-dismiss="modal"
|
||||||
|
appA11yTitle="{{ 'close' | i18n }}"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<app-callout type="warning" *ngIf="!isEnrolled">
|
||||||
|
{{ "resetPasswordEnrollmentWarning" | i18n }}
|
||||||
|
</app-callout>
|
||||||
|
<app-user-verification [(ngModel)]="verification" name="secret"> </app-user-verification>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button bit-button buttonType="primary" type="submit" [disabled]="form.loading">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
*ngIf="form.loading"
|
||||||
|
></i>
|
||||||
|
<span>
|
||||||
|
{{ "submit" | i18n }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
bit-button
|
||||||
|
buttonType="secondary"
|
||||||
|
type="button"
|
||||||
|
data-dismiss="modal"
|
||||||
|
appA11yTitle="{{ 'close' | i18n }}"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -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<any>;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {}
|
|
@ -1,17 +1,17 @@
|
||||||
import { Component, Input } from "@angular/core";
|
import { Component, Input } from "@angular/core";
|
||||||
|
|
||||||
|
import { ModalService } from "jslib-angular/services/modal.service";
|
||||||
import { ApiService } from "jslib-common/abstractions/api.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 { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { LogService } from "jslib-common/abstractions/log.service";
|
import { LogService } from "jslib-common/abstractions/log.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
import { PolicyService } from "jslib-common/abstractions/policy.service";
|
||||||
import { SyncService } from "jslib-common/abstractions/sync.service";
|
import { SyncService } from "jslib-common/abstractions/sync.service";
|
||||||
import { PolicyType } from "jslib-common/enums/policyType";
|
import { PolicyType } from "jslib-common/enums/policyType";
|
||||||
import { Utils } from "jslib-common/misc/utils";
|
|
||||||
import { Organization } from "jslib-common/models/domain/organization";
|
import { Organization } from "jslib-common/models/domain/organization";
|
||||||
import { Policy } from "jslib-common/models/domain/policy";
|
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({
|
@Component({
|
||||||
selector: "app-organization-options",
|
selector: "app-organization-options",
|
||||||
|
@ -29,8 +29,8 @@ export class OrganizationOptionsComponent {
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private syncService: SyncService,
|
private syncService: SyncService,
|
||||||
private cryptoService: CryptoService,
|
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
|
private modalService: ModalService,
|
||||||
private logService: LogService
|
private logService: LogService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@ -113,70 +113,11 @@ export class OrganizationOptionsComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleResetPasswordEnrollment(org: Organization) {
|
async toggleResetPasswordEnrollment(org: Organization) {
|
||||||
// Set variables
|
this.modalService.open(EnrollMasterPasswordReset, {
|
||||||
let keyString: string = null;
|
allowMultipleModals: true,
|
||||||
let toastStringRef = "withdrawPasswordResetSuccess";
|
data: {
|
||||||
|
organization: org,
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
import { LooseComponentsModule } from "./modules/loose-components.module";
|
import { LooseComponentsModule } from "./modules/loose-components.module";
|
||||||
import { OrganizationManageModule } from "./modules/organizations/manage/organization-manage.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 { PipesModule } from "./modules/pipes/pipes.module";
|
||||||
import { SharedModule } from "./modules/shared.module";
|
import { SharedModule } from "./modules/shared.module";
|
||||||
import { VaultFilterModule } from "./modules/vault-filter/vault-filter.module";
|
import { VaultFilterModule } from "./modules/vault-filter/vault-filter.module";
|
||||||
|
@ -15,6 +16,7 @@ import { OrganizationBadgeModule } from "./modules/vault/modules/organization-ba
|
||||||
OrganizationBadgeModule,
|
OrganizationBadgeModule,
|
||||||
PipesModule,
|
PipesModule,
|
||||||
OrganizationManageModule,
|
OrganizationManageModule,
|
||||||
|
OrganizationUserModule,
|
||||||
],
|
],
|
||||||
exports: [LooseComponentsModule, VaultFilterModule, OrganizationBadgeModule, PipesModule],
|
exports: [LooseComponentsModule, VaultFilterModule, OrganizationBadgeModule, PipesModule],
|
||||||
bootstrap: [],
|
bootstrap: [],
|
||||||
|
|
|
@ -4163,7 +4163,7 @@
|
||||||
"message": "Password reset success!"
|
"message": "Password reset success!"
|
||||||
},
|
},
|
||||||
"resetPasswordEnrollmentWarning": {
|
"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": {
|
"resetPasswordPolicy": {
|
||||||
"message": "Master Password Reset"
|
"message": "Master Password Reset"
|
||||||
|
|
Loading…
Reference in New Issue