diff --git a/libs/common/src/auth/enums/user-decryption-option.enum.ts b/libs/common/src/auth/enums/user-decryption-option.enum.ts new file mode 100644 index 0000000000..b72e01d6a2 --- /dev/null +++ b/libs/common/src/auth/enums/user-decryption-option.enum.ts @@ -0,0 +1,5 @@ +export enum UserDecryptionOption { + MASTER_PASSWORD = "masterPasswordOption", + TRUSTED_DEVICE = "trustedDeviceOption", + KEY_CONNECTOR = "keyConnectorOption", +} diff --git a/libs/common/src/auth/login-strategies/login.strategy.ts b/libs/common/src/auth/login-strategies/login.strategy.ts index 2bcbee88e0..e1e5ab9530 100644 --- a/libs/common/src/auth/login-strategies/login.strategy.ts +++ b/libs/common/src/auth/login-strategies/login.strategy.ts @@ -101,6 +101,9 @@ export abstract class LogInStrategy { protected async saveAccountInformation(tokenResponse: IdentityTokenResponse) { const accountInformation = await this.tokenService.decodeToken(tokenResponse.accessToken); + + // TODO: add AccountDecryptionOptions to the account + await this.stateService.addAccount( new Account({ profile: { diff --git a/libs/common/src/auth/models/domain/user-decryption-options/key-connector-user-decryption-option.model.ts b/libs/common/src/auth/models/domain/user-decryption-options/key-connector-user-decryption-option.model.ts new file mode 100644 index 0000000000..e24ebcfd3f --- /dev/null +++ b/libs/common/src/auth/models/domain/user-decryption-options/key-connector-user-decryption-option.model.ts @@ -0,0 +1,11 @@ +import { Jsonify } from "type-fest"; + +import { UserDecryptionOptionBase } from "./user-decryption-options-base.model"; + +export class KeyConnectorUserDecryptionOption extends UserDecryptionOptionBase { + keyConnectorUrl: string; + constructor(data: Jsonify) { + super(data.enabled); + this.keyConnectorUrl = data.keyConnectorUrl; + } +} diff --git a/libs/common/src/auth/models/domain/user-decryption-options/master-password-user-decryption-option.model.ts b/libs/common/src/auth/models/domain/user-decryption-options/master-password-user-decryption-option.model.ts new file mode 100644 index 0000000000..b36e192334 --- /dev/null +++ b/libs/common/src/auth/models/domain/user-decryption-options/master-password-user-decryption-option.model.ts @@ -0,0 +1,7 @@ +import { UserDecryptionOptionBase } from "./user-decryption-options-base.model"; + +export class MasterPasswordUserDecryptionOption extends UserDecryptionOptionBase { + constructor(enabled: boolean) { + super(enabled); + } +} diff --git a/libs/common/src/auth/models/domain/user-decryption-options/trusted-device-user-decryption-option.model.ts b/libs/common/src/auth/models/domain/user-decryption-options/trusted-device-user-decryption-option.model.ts new file mode 100644 index 0000000000..7f6e0707c3 --- /dev/null +++ b/libs/common/src/auth/models/domain/user-decryption-options/trusted-device-user-decryption-option.model.ts @@ -0,0 +1,11 @@ +import { Jsonify } from "type-fest"; + +import { UserDecryptionOptionBase } from "./user-decryption-options-base.model"; + +export class TrustedDeviceUserDecryptionOption extends UserDecryptionOptionBase { + hasAdminApproval: boolean; + constructor(data: Jsonify) { + super(data.enabled); + this.hasAdminApproval = data.hasAdminApproval; + } +} diff --git a/libs/common/src/auth/models/domain/user-decryption-options/user-decryption-options-base.model.ts b/libs/common/src/auth/models/domain/user-decryption-options/user-decryption-options-base.model.ts new file mode 100644 index 0000000000..6804a1d8fa --- /dev/null +++ b/libs/common/src/auth/models/domain/user-decryption-options/user-decryption-options-base.model.ts @@ -0,0 +1,6 @@ +export class UserDecryptionOptionBase { + enabled: boolean; + constructor(enabled: boolean) { + this.enabled = enabled; + } +} diff --git a/libs/common/src/auth/models/response/identity-token.response.ts b/libs/common/src/auth/models/response/identity-token.response.ts index 76f34617f8..6c649c6863 100644 --- a/libs/common/src/auth/models/response/identity-token.response.ts +++ b/libs/common/src/auth/models/response/identity-token.response.ts @@ -1,7 +1,13 @@ import { KdfType } from "../../../enums"; import { BaseResponse } from "../../../models/response/base.response"; +import { UserDecryptionOption } from "../../enums/user-decryption-option.enum"; +import { UserDecryptionOptionResponseType } from "../../types/user-decryption-option-response"; +import { KeyConnectorDecryptionOptionResponse } from "./key-connector-decryption-option.response"; +import { MasterPasswordDecryptionOptionResponse } from "./master-password-decryption-option.response"; import { MasterPasswordPolicyResponse } from "./master-password-policy.response"; +import { TrustedDeviceDecryptionOptionResponse } from "./trusted-device-decryption-option.response"; +import { UserDecryptionOptionResponse } from "./user-decryption-option.response"; export class IdentityTokenResponse extends BaseResponse { accessToken: string; @@ -22,6 +28,8 @@ export class IdentityTokenResponse extends BaseResponse { apiUseKeyConnector: boolean; keyConnectorUrl: string; + userDecryptionOptions: Array; + constructor(response: any) { super(response); this.accessToken = response.access_token; @@ -43,5 +51,27 @@ export class IdentityTokenResponse extends BaseResponse { this.masterPasswordPolicy = new MasterPasswordPolicyResponse( this.getResponseProperty("MasterPasswordPolicy") ); + + const serverUserDecryptionOptions = this.getResponseProperty("UserDecryptionOptions"); + + if (serverUserDecryptionOptions) { + this.userDecryptionOptions = serverUserDecryptionOptions.map( + (serverUserDecryptionOption: any) => { + const response = new UserDecryptionOptionResponse(serverUserDecryptionOption); + + switch (response.object) { + case UserDecryptionOption.MASTER_PASSWORD: { + return new MasterPasswordDecryptionOptionResponse(serverUserDecryptionOption); + } + case UserDecryptionOption.TRUSTED_DEVICE: { + return new TrustedDeviceDecryptionOptionResponse(serverUserDecryptionOption); + } + case UserDecryptionOption.KEY_CONNECTOR: { + return new KeyConnectorDecryptionOptionResponse(serverUserDecryptionOption); + } + } + } + ); + } } } diff --git a/libs/common/src/auth/models/response/key-connector-decryption-option.response.ts b/libs/common/src/auth/models/response/key-connector-decryption-option.response.ts new file mode 100644 index 0000000000..f5c157ce25 --- /dev/null +++ b/libs/common/src/auth/models/response/key-connector-decryption-option.response.ts @@ -0,0 +1,10 @@ +import { UserDecryptionOptionResponse } from "./user-decryption-option.response"; + +export class KeyConnectorDecryptionOptionResponse extends UserDecryptionOptionResponse { + keyConnectorUrl: string; + + constructor(response: any) { + super(response); + this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl"); + } +} diff --git a/libs/common/src/auth/models/response/master-password-decryption-option.response.ts b/libs/common/src/auth/models/response/master-password-decryption-option.response.ts new file mode 100644 index 0000000000..31493810f6 --- /dev/null +++ b/libs/common/src/auth/models/response/master-password-decryption-option.response.ts @@ -0,0 +1,7 @@ +import { UserDecryptionOptionResponse } from "./user-decryption-option.response"; + +export class MasterPasswordDecryptionOptionResponse extends UserDecryptionOptionResponse { + constructor(response: any) { + super(response); + } +} diff --git a/libs/common/src/auth/models/response/trusted-device-decryption-option.response.ts b/libs/common/src/auth/models/response/trusted-device-decryption-option.response.ts new file mode 100644 index 0000000000..cde2acd631 --- /dev/null +++ b/libs/common/src/auth/models/response/trusted-device-decryption-option.response.ts @@ -0,0 +1,10 @@ +import { UserDecryptionOptionResponse } from "./user-decryption-option.response"; + +export class TrustedDeviceDecryptionOptionResponse extends UserDecryptionOptionResponse { + hasAdminApproval: boolean; + + constructor(response: any) { + super(response); + this.hasAdminApproval = this.getResponseProperty("HasAdminApproval"); + } +} diff --git a/libs/common/src/auth/models/response/user-decryption-option.response.ts b/libs/common/src/auth/models/response/user-decryption-option.response.ts new file mode 100644 index 0000000000..77e445c61a --- /dev/null +++ b/libs/common/src/auth/models/response/user-decryption-option.response.ts @@ -0,0 +1,11 @@ +import { BaseResponse } from "../../../models/response/base.response"; + +export class UserDecryptionOptionResponse extends BaseResponse { + object: string; + + constructor(response: any) { + super(response); + + this.object = this.getResponseProperty("Object"); + } +} diff --git a/libs/common/src/auth/types/user-decryption-option-response.ts b/libs/common/src/auth/types/user-decryption-option-response.ts new file mode 100644 index 0000000000..623928aa23 --- /dev/null +++ b/libs/common/src/auth/types/user-decryption-option-response.ts @@ -0,0 +1,8 @@ +import { KeyConnectorDecryptionOptionResponse } from "../models/response/key-connector-decryption-option.response"; +import { MasterPasswordDecryptionOptionResponse } from "../models/response/master-password-decryption-option.response"; +import { TrustedDeviceDecryptionOptionResponse } from "../models/response/trusted-device-decryption-option.response"; + +export type UserDecryptionOptionResponseType = + | MasterPasswordDecryptionOptionResponse + | TrustedDeviceDecryptionOptionResponse + | KeyConnectorDecryptionOptionResponse; diff --git a/libs/common/src/models/domain/account.ts b/libs/common/src/models/domain/account.ts index db98a17b42..838742bb47 100644 --- a/libs/common/src/models/domain/account.ts +++ b/libs/common/src/models/domain/account.ts @@ -8,8 +8,15 @@ import { ProviderData } from "../../admin-console/models/data/provider.data"; import { Policy } from "../../admin-console/models/domain/policy"; import { CollectionView } from "../../admin-console/models/view/collection.view"; import { AuthenticationStatus } from "../../auth/enums/authentication-status"; +import { UserDecryptionOption } from "../../auth/enums/user-decryption-option.enum"; import { EnvironmentUrls } from "../../auth/models/domain/environment-urls"; import { ForceResetPasswordReason } from "../../auth/models/domain/force-reset-password-reason"; +import { KeyConnectorUserDecryptionOption } from "../../auth/models/domain/user-decryption-options/key-connector-user-decryption-option.model"; +import { MasterPasswordUserDecryptionOption } from "../../auth/models/domain/user-decryption-options/master-password-user-decryption-option.model"; +import { TrustedDeviceUserDecryptionOption } from "../../auth/models/domain/user-decryption-options/trusted-device-user-decryption-option.model"; +import { KeyConnectorDecryptionOptionResponse } from "../../auth/models/response/key-connector-decryption-option.response"; +import { TrustedDeviceDecryptionOptionResponse } from "../../auth/models/response/trusted-device-decryption-option.response"; +import { UserDecryptionOptionResponseType } from "../../auth/types/user-decryption-option-response"; import { KdfType, UriMatchType } from "../../enums"; import { Utils } from "../../misc/utils"; import { GeneratedPasswordHistory } from "../../tools/generator/password"; @@ -269,12 +276,71 @@ export class AccountTokens { } } +export class AccountDecryptionOptions { + [UserDecryptionOption.MASTER_PASSWORD]?: MasterPasswordUserDecryptionOption; + [UserDecryptionOption.TRUSTED_DEVICE]?: TrustedDeviceUserDecryptionOption; + [UserDecryptionOption.KEY_CONNECTOR]?: KeyConnectorUserDecryptionOption; + + constructor(init?: Partial) { + if (init) { + Object.assign(this, init); + } + } + + static fromIdTokenResponse( + // serverUserDecryptionOptions: Array<{ Object: string; [key: string]: any }> + userDecryptionOptionResponse: Array + ) { + const accountDecryptionOptions = new AccountDecryptionOptions(); + + // Convert UserDecryptionOptions array to dictionary + for (const optionResponse of userDecryptionOptionResponse) { + const type = optionResponse.object as UserDecryptionOption; + + switch (type) { + case UserDecryptionOption.MASTER_PASSWORD: + accountDecryptionOptions[UserDecryptionOption.MASTER_PASSWORD] = + new MasterPasswordUserDecryptionOption(true); + break; + case UserDecryptionOption.TRUSTED_DEVICE: + accountDecryptionOptions[UserDecryptionOption.TRUSTED_DEVICE] = + new TrustedDeviceUserDecryptionOption({ + enabled: true, + hasAdminApproval: (optionResponse as TrustedDeviceDecryptionOptionResponse) + .hasAdminApproval, + }); + break; + case UserDecryptionOption.KEY_CONNECTOR: + accountDecryptionOptions[UserDecryptionOption.KEY_CONNECTOR] = + new KeyConnectorUserDecryptionOption({ + enabled: true, + keyConnectorUrl: (optionResponse as KeyConnectorDecryptionOptionResponse) + .keyConnectorUrl, + }); + break; + default: + continue; + } + } + return accountDecryptionOptions; + } + + static fromJSON(obj: Jsonify): AccountDecryptionOptions { + if (obj == null) { + return null; + } + + return Object.assign(new AccountDecryptionOptions(), obj); + } +} + export class Account { data?: AccountData = new AccountData(); keys?: AccountKeys = new AccountKeys(); profile?: AccountProfile = new AccountProfile(); settings?: AccountSettings = new AccountSettings(); tokens?: AccountTokens = new AccountTokens(); + decryptionOptions?: AccountDecryptionOptions = new AccountDecryptionOptions(); constructor(init: Partial) { Object.assign(this, { @@ -298,6 +364,10 @@ export class Account { ...new AccountTokens(), ...init?.tokens, }, + decryptionOptions: { + ...new AccountDecryptionOptions(), + ...init?.decryptionOptions, + }, }); } @@ -311,6 +381,7 @@ export class Account { profile: AccountProfile.fromJSON(json?.profile), settings: AccountSettings.fromJSON(json?.settings), tokens: AccountTokens.fromJSON(json?.tokens), + decryptionOptions: AccountDecryptionOptions.fromJSON(json?.decryptionOptions), }); } }