diff --git a/src/app/accounts/accept-emergency.component.ts b/src/app/accounts/accept-emergency.component.ts index d2d24e09850..5caba800937 100644 --- a/src/app/accounts/accept-emergency.component.ts +++ b/src/app/accounts/accept-emergency.component.ts @@ -1,7 +1,4 @@ -import { - Component, - OnInit, -} from '@angular/core'; +import { Component } from '@angular/core'; import { ActivatedRoute, Router, @@ -17,77 +14,47 @@ import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { StateService } from 'jslib-common/abstractions/state.service'; import { UserService } from 'jslib-common/abstractions/user.service'; import { EmergencyAccessAcceptRequest } from 'jslib-common/models/request/emergencyAccessAcceptRequest'; +import { BaseAcceptComponent } from '../common/base.accept.component'; @Component({ selector: 'app-accept-emergency', templateUrl: 'accept-emergency.component.html', }) -export class AcceptEmergencyComponent implements OnInit { - loading = true; - authed = false; +export class AcceptEmergencyComponent extends BaseAcceptComponent { + name: string; - email: string; - actionPromise: Promise; - constructor(private router: Router, private toasterService: ToasterService, - private i18nService: I18nService, private route: ActivatedRoute, - private apiService: ApiService, private userService: UserService, - private stateService: StateService) { } + protected requiredParameters: string[] = ['id', 'name', 'email', 'token']; + protected failedShortMessage = 'emergencyInviteAcceptFailedShort'; + protected failedMessage = 'emergencyInviteAcceptFailed'; - ngOnInit() { - let fired = false; - this.route.queryParams.subscribe(async qParams => { - if (fired) { - return; - } - fired = true; - await this.stateService.remove('emergencyInvitation'); - let error = qParams.id == null || qParams.name == null || qParams.email == null || qParams.token == null; - let errorMessage: string = null; - if (!error) { - this.authed = await this.userService.isAuthenticated(); - if (this.authed) { - const request = new EmergencyAccessAcceptRequest(); - request.token = qParams.token; - try { - this.actionPromise = this.apiService.postEmergencyAccessAccept(qParams.id, request); - await this.actionPromise; - const toast: Toast = { - type: 'success', - title: this.i18nService.t('inviteAccepted'), - body: this.i18nService.t('emergencyInviteAcceptedDesc'), - timeout: 10000, - }; - this.toasterService.popAsync(toast); - this.router.navigate(['/vault']); - } catch (e) { - error = true; - errorMessage = e.message; - } - } else { - await this.stateService.save('emergencyInvitation', qParams); - this.email = qParams.email; - this.name = qParams.name; - if (this.name != null) { - // Fix URL encoding of space issue with Angular - this.name = this.name.replace(/\+/g, ' '); - } - } - } + constructor(router: Router, toasterService: ToasterService, + i18nService: I18nService, route: ActivatedRoute, + private apiService: ApiService, userService: UserService, + stateService: StateService) { + super(router, toasterService, i18nService, route, userService, stateService); + } - if (error) { - const toast: Toast = { - type: 'error', - title: null, - body: errorMessage != null ? this.i18nService.t('emergencyInviteAcceptFailedShort', errorMessage) : - this.i18nService.t('emergencyInviteAcceptFailed'), - timeout: 10000, - }; - this.toasterService.popAsync(toast); - this.router.navigate(['/']); - } + async authedHandler(qParams: any): Promise { + const request = new EmergencyAccessAcceptRequest(); + request.token = qParams.token; + this.actionPromise = this.apiService.postEmergencyAccessAccept(qParams.id, request); + await this.actionPromise; + const toast: Toast = { + type: 'success', + title: this.i18nService.t('inviteAccepted'), + body: this.i18nService.t('emergencyInviteAcceptedDesc'), + timeout: 10000, + }; + this.toasterService.popAsync(toast); + this.router.navigate(['/vault']); + } - this.loading = false; - }); + async unauthedHandler(qParams: any): Promise { + this.name = qParams.name; + if (this.name != null) { + // Fix URL encoding of space issue with Angular + this.name = this.name.replace(/\+/g, ' '); + } } } diff --git a/src/app/accounts/accept-organization.component.ts b/src/app/accounts/accept-organization.component.ts index 6bd386fe042..40708a4eb14 100644 --- a/src/app/accounts/accept-organization.component.ts +++ b/src/app/accounts/accept-organization.component.ts @@ -1,8 +1,4 @@ -import { - Component, - OnInit, -} from '@angular/core'; - +import { Component } from '@angular/core'; import { ActivatedRoute, Router, @@ -25,108 +21,75 @@ import { OrganizationUserResetPasswordEnrollmentRequest } from 'jslib-common/mod import { Utils } from 'jslib-common/misc/utils'; import { Policy } from 'jslib-common/models/domain/policy'; +import { BaseAcceptComponent } from '../common/base.accept.component'; @Component({ selector: 'app-accept-organization', templateUrl: 'accept-organization.component.html', }) -export class AcceptOrganizationComponent implements OnInit { - loading = true; - authed = false; +export class AcceptOrganizationComponent extends BaseAcceptComponent { orgName: string; - email: string; - actionPromise: Promise; - constructor(private router: Router, private toasterService: ToasterService, - private i18nService: I18nService, private route: ActivatedRoute, - private apiService: ApiService, private userService: UserService, - private stateService: StateService, private cryptoService: CryptoService, - private policyService: PolicyService) { } + protected requiredParameters: string[] = ['organizationId', 'organizationUserId', 'token']; - ngOnInit() { - let fired = false; - this.route.queryParams.subscribe(async qParams => { - if (fired) { - return; - } - fired = true; - await this.stateService.remove('orgInvitation'); - let error = qParams.organizationId == null || qParams.organizationUserId == null || qParams.token == null; - let errorMessage: string = null; - if (!error) { - this.authed = await this.userService.isAuthenticated(); - if (this.authed) { - const request = new OrganizationUserAcceptRequest(); - request.token = qParams.token; - try { - if (await this.performResetPasswordAutoEnroll(qParams)) { - this.actionPromise = this.apiService.postOrganizationUserAccept(qParams.organizationId, - qParams.organizationUserId, request).then(() => { - // Retrieve Public Key - return this.apiService.getOrganizationKeys(qParams.organizationId); - }).then(async response => { - if (response == null) { - throw new Error(this.i18nService.t('resetPasswordOrgKeysError')); - } + constructor(router: Router, toasterService: ToasterService, + i18nService: I18nService, route: ActivatedRoute, + private apiService: ApiService, userService: UserService, + stateService: StateService, private cryptoService: CryptoService, + private policyService: PolicyService) { + super(router, toasterService, i18nService, route, userService, stateService); + } - 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); - - // Create request and execute enrollment - const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest(); - resetRequest.resetPasswordKey = encryptedKey.encryptedString; - - // Get User Id - const userId = await this.userService.getUserId(); - - return this.apiService.putOrganizationUserResetPasswordEnrollment(qParams.organizationId, userId, resetRequest); - }); - } else { - this.actionPromise = this.apiService.postOrganizationUserAccept(qParams.organizationId, - qParams.organizationUserId, request); - } - - await this.actionPromise; - const toast: Toast = { - type: 'success', - title: this.i18nService.t('inviteAccepted'), - body: this.i18nService.t('inviteAcceptedDesc'), - timeout: 10000, - }; - this.toasterService.popAsync(toast); - this.router.navigate(['/vault']); - } catch (e) { - error = true; - errorMessage = e.message; + async authedHandler(qParams: any): Promise { + const request = new OrganizationUserAcceptRequest(); + request.token = qParams.token; + if (await this.performResetPasswordAutoEnroll(qParams)) { + this.actionPromise = this.apiService.postOrganizationUserAccept(qParams.organizationId, + qParams.organizationUserId, request).then(() => { + // Retrieve Public Key + return this.apiService.getOrganizationKeys(qParams.organizationId); + }).then(async response => { + if (response == null) { + throw new Error(this.i18nService.t('resetPasswordOrgKeysError')); } - } else { - await this.stateService.save('orgInvitation', qParams); - this.email = qParams.email; - this.orgName = qParams.organizationName; - if (this.orgName != null) { - // Fix URL encoding of space issue with Angular - this.orgName = this.orgName.replace(/\+/g, ' '); - } - } - } - if (error) { - const toast: Toast = { - type: 'error', - title: null, - body: errorMessage != null ? this.i18nService.t('inviteAcceptFailedShort', errorMessage) : - this.i18nService.t('inviteAcceptFailed'), - timeout: 10000, - }; - this.toasterService.popAsync(toast); - this.router.navigate(['/']); - } + const publicKey = Utils.fromB64ToArray(response.publicKey); - this.loading = false; - }); + // 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); + + // Create request and execute enrollment + const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest(); + resetRequest.resetPasswordKey = encryptedKey.encryptedString; + + // Get User Id + const userId = await this.userService.getUserId(); + + return this.apiService.putOrganizationUserResetPasswordEnrollment(qParams.organizationId, userId, resetRequest); + }); + } else { + this.actionPromise = this.apiService.postOrganizationUserAccept(qParams.organizationId, + qParams.organizationUserId, request); + } + + await this.actionPromise; + const toast: Toast = { + type: 'success', + title: this.i18nService.t('inviteAccepted'), + body: this.i18nService.t('inviteAcceptedDesc'), + timeout: 10000, + }; + this.toasterService.popAsync(toast); + this.router.navigate(['/vault']); + } + + async unauthedHandler(qParams: any): Promise { + this.orgName = qParams.organizationName; + if (this.orgName != null) { + // Fix URL encoding of space issue with Angular + this.orgName = this.orgName.replace(/\+/g, ' '); + } } private async performResetPasswordAutoEnroll(qParams: any): Promise { diff --git a/src/app/accounts/login.component.ts b/src/app/accounts/login.component.ts index d705f097c78..da59edf28b4 100644 --- a/src/app/accounts/login.component.ts +++ b/src/app/accounts/login.component.ts @@ -76,20 +76,12 @@ export class LoginComponent extends BaseLoginComponent { } async goAfterLogIn() { - const orgInvite = await this.stateService.get('orgInvitation'); - const emergencyInvite = await this.stateService.get('emergencyInvitation'); - if (orgInvite != null) { - this.router.navigate(['accept-organization'], { queryParams: orgInvite }); - } else if (emergencyInvite != null) { - this.router.navigate(['accept-emergency'], { queryParams: emergencyInvite }); + const loginRedirect = await this.stateService.get('loginRedirect'); + if (loginRedirect != null) { + this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams }); + await this.stateService.remove('loginRedirect'); } else { - const loginRedirect = await this.stateService.get('loginRedirect'); - if (loginRedirect != null) { - this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams }); - await this.stateService.remove('loginRedirect'); - } else { - this.router.navigate([this.successRoute]); - } + this.router.navigate([this.successRoute]); } } } diff --git a/src/app/accounts/two-factor.component.ts b/src/app/accounts/two-factor.component.ts index 270ad631cb7..10e8ef261a2 100644 --- a/src/app/accounts/two-factor.component.ts +++ b/src/app/accounts/two-factor.component.ts @@ -60,24 +60,16 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { } async goAfterLogIn() { - const orgInvite = await this.stateService.get('orgInvitation'); - const emergencyInvite = await this.stateService.get('emergencyInvitation'); - if (orgInvite != null) { - this.router.navigate(['accept-organization'], { queryParams: orgInvite }); - } else if (emergencyInvite != null) { - this.router.navigate(['accept-emergency'], { queryParams: emergencyInvite }); + const loginRedirect = await this.stateService.get('loginRedirect'); + if (loginRedirect != null) { + this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams }); + await this.stateService.remove('loginRedirect'); } else { - const loginRedirect = await this.stateService.get('loginRedirect'); - if (loginRedirect != null) { - this.router.navigate([loginRedirect.route], { queryParams: loginRedirect.qParams }); - await this.stateService.remove('loginRedirect'); - } else { - this.router.navigate([this.successRoute], { - queryParams: { - identifier: this.identifier, - }, - }); - } + this.router.navigate([this.successRoute], { + queryParams: { + identifier: this.identifier, + }, + }); } } } diff --git a/src/app/common/base.accept.component.ts b/src/app/common/base.accept.component.ts new file mode 100644 index 00000000000..5d9273a16aa --- /dev/null +++ b/src/app/common/base.accept.component.ts @@ -0,0 +1,90 @@ +import { + Directive, + OnInit, +} from '@angular/core'; +import { + ActivatedRoute, + Router, +} from '@angular/router'; + +import { + Toast, + ToasterService, +} from 'angular2-toaster'; + +import { I18nService } from 'jslib-common/abstractions/i18n.service'; +import { StateService } from 'jslib-common/abstractions/state.service'; +import { UserService } from 'jslib-common/abstractions/user.service'; + +@Directive() +export abstract class BaseAcceptComponent implements OnInit { + loading = true; + authed = false; + email: string; + actionPromise: Promise; + + protected requiredParameters: string[] = []; + protected failedShortMessage = 'inviteAcceptFailedShort'; + protected failedMessage = 'inviteAcceptFailed'; + + constructor(protected router: Router, protected toasterService: ToasterService, + protected i18nService: I18nService, protected route: ActivatedRoute, + protected userService: UserService, private stateService: StateService) { } + + abstract authedHandler(qParams: any): Promise; + abstract unauthedHandler(qParams: any): Promise; + + ngOnInit() { + let fired = false; + this.route.queryParams.subscribe(async qParams => { + if (fired) { + return; + } + fired = true; + await this.stateService.remove('loginRedirect'); + + let error = this.requiredParameters.some(e => qParams?.[e] == null || qParams[e] === ''); + let errorMessage: string = null; + if (!error) { + this.authed = await this.userService.isAuthenticated(); + + if (this.authed) { + try { + await this.authedHandler(qParams); + } catch (e) { + error = true; + errorMessage = e.message; + } + } else { + await this.stateService.save('loginRedirect', { + route: this.getRedirectRoute(), + qParams: qParams, + }); + + this.email = qParams.email; + await this.unauthedHandler(qParams); + } + } + + if (error) { + const toast: Toast = { + type: 'error', + title: null, + body: errorMessage != null ? this.i18nService.t(this.failedShortMessage, errorMessage) : + this.i18nService.t(this.failedMessage), + timeout: 10000, + }; + this.toasterService.popAsync(toast); + this.router.navigate(['/']); + } + + this.loading = false; + }); + } + + getRedirectRoute() { + const urlTree = this.router.parseUrl(this.router.url); + urlTree.queryParams = {}; + return urlTree.toString(); + } +}