1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-18 09:13:33 +00:00

Merged with master and fixed conflicts

This commit is contained in:
gbubemismith
2023-05-08 12:16:25 -04:00
1131 changed files with 52396 additions and 51717 deletions

View File

@@ -1,4 +1,4 @@
import { OrganizationConnectionType } from "../admin-console/enums/organization-connection-type";
import { OrganizationConnectionType } from "../admin-console/enums";
import { CollectionRequest } from "../admin-console/models/request/collection.request";
import { OrganizationConnectionRequest } from "../admin-console/models/request/organization-connection.request";
import { OrganizationSponsorshipCreateRequest } from "../admin-console/models/request/organization/organization-sponsorship-create.request";
@@ -80,6 +80,7 @@ import { IdentityCaptchaResponse } from "../auth/models/response/identity-captch
import { IdentityTokenResponse } from "../auth/models/response/identity-token.response";
import { IdentityTwoFactorResponse } from "../auth/models/response/identity-two-factor.response";
import { KeyConnectorUserKeyResponse } from "../auth/models/response/key-connector-user-key.response";
import { MasterPasswordPolicyResponse } from "../auth/models/response/master-password-policy.response";
import { PreloginResponse } from "../auth/models/response/prelogin.response";
import { RegisterResponse } from "../auth/models/response/register.response";
import { SsoPreValidateResponse } from "../auth/models/response/sso-pre-validate.response";
@@ -187,7 +188,9 @@ export abstract class ApiService {
postAccountKeys: (request: KeysRequest) => Promise<any>;
postAccountVerifyEmail: () => Promise<any>;
postAccountVerifyEmailToken: (request: VerifyEmailRequest) => Promise<any>;
postAccountVerifyPassword: (request: SecretVerificationRequest) => Promise<any>;
postAccountVerifyPassword: (
request: SecretVerificationRequest
) => Promise<MasterPasswordPolicyResponse>;
postAccountRecoverDelete: (request: DeleteRecoverRequest) => Promise<any>;
postAccountRecoverDeleteToken: (request: VerifyDeleteRecoverRequest) => Promise<any>;
postAccountKdf: (request: KdfRequest) => Promise<any>;

View File

@@ -1,7 +1,13 @@
import { Observable } from "rxjs";
import { FeatureFlag } from "../../enums/feature-flag.enum";
import { ServerConfig } from "./server-config";
export abstract class ConfigServiceAbstraction {
serverConfig$: Observable<ServerConfig | null>;
fetchServerConfig: () => Promise<ServerConfig>;
getFeatureFlagBool: (key: FeatureFlag, defaultValue?: boolean) => Promise<boolean>;
getFeatureFlagString: (key: FeatureFlag, defaultValue?: string) => Promise<string>;
getFeatureFlagNumber: (key: FeatureFlag, defaultValue?: number) => Promise<number>;
}

View File

@@ -15,6 +15,7 @@ export class ServerConfig {
server?: ThirdPartyServerConfigData;
environment?: EnvironmentServerConfigData;
utcDate: Date;
featureStates: { [key: string]: string } = {};
constructor(serverConfigData: ServerConfigData) {
this.version = serverConfigData.version;
@@ -22,6 +23,7 @@ export class ServerConfig {
this.server = serverConfigData.server;
this.utcDate = new Date(serverConfigData.utcDate);
this.environment = serverConfigData.environment;
this.featureStates = serverConfigData.featureStates;
if (this.server?.name == null && this.server?.url == null) {
this.server = null;

View File

@@ -2,9 +2,7 @@ import { ProfileOrganizationResponse } from "../admin-console/models/response/pr
import { ProfileProviderOrganizationResponse } from "../admin-console/models/response/profile-provider-organization.response";
import { ProfileProviderResponse } from "../admin-console/models/response/profile-provider.response";
import { KdfConfig } from "../auth/models/domain/kdf-config";
import { HashPurpose } from "../enums/hashPurpose";
import { KdfType } from "../enums/kdfType";
import { KeySuffixOptions } from "../enums/keySuffixOptions";
import { HashPurpose, KdfType, KeySuffixOptions } from "../enums";
import { EncArrayBuffer } from "../models/domain/enc-array-buffer";
import { EncString } from "../models/domain/enc-string";
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";

View File

@@ -1,5 +1,6 @@
import { DecryptParameters } from "../models/domain/decrypt-parameters";
import { SymmetricCryptoKey } from "../models/domain/symmetric-crypto-key";
import { CsprngArray } from "../types/csprng";
export abstract class CryptoFunctionService {
pbkdf2: (
@@ -65,5 +66,5 @@ export abstract class CryptoFunctionService {
) => Promise<ArrayBuffer>;
rsaExtractPublicKey: (privateKey: ArrayBuffer) => Promise<ArrayBuffer>;
rsaGenerateKeyPair: (length: 1024 | 2048 | 4096) => Promise<[ArrayBuffer, ArrayBuffer]>;
randomBytes: (length: number) => Promise<ArrayBuffer>;
randomBytes: (length: number) => Promise<CsprngArray>;
}

View File

@@ -1,4 +1,4 @@
import { EventType } from "../../enums/eventType";
import { EventType } from "../../enums";
export abstract class EventCollectionService {
collect: (

View File

@@ -1,11 +0,0 @@
import { EventView } from "../models/view/event.view";
export type ExportFormat = "csv" | "json" | "encrypted_json";
export abstract class ExportService {
getExport: (format?: ExportFormat, organizationId?: string) => Promise<string>;
getPasswordProtectedExport: (password: string, organizationId?: string) => Promise<string>;
getOrganizationExport: (organizationId: string, format?: ExportFormat) => Promise<string>;
getEventExport: (events: EventView[]) => Promise<string>;
getFileName: (prefix?: string, extension?: string) => string;
}

View File

@@ -1,4 +1,4 @@
import { FileUploadType } from "../../enums/fileUploadType";
import { FileUploadType } from "../../enums";
import { EncArrayBuffer } from "../../models/domain/enc-array-buffer";
import { EncString } from "../../models/domain/enc-string";

View File

@@ -1,4 +1,4 @@
import { LogLevelType } from "../enums/logLevelType";
import { LogLevelType } from "../enums";
export abstract class LogService {
debug: (message: string) => void;

View File

@@ -1,6 +1,7 @@
import { ListResponse } from "../../models/response/list.response";
import {
OrganizationUserAcceptInitRequest,
OrganizationUserAcceptRequest,
OrganizationUserBulkConfirmRequest,
OrganizationUserConfirmRequest,
@@ -14,7 +15,7 @@ import {
OrganizationUserBulkPublicKeyResponse,
OrganizationUserBulkResponse,
OrganizationUserDetailsResponse,
OrganizationUserResetPasswordDetailsReponse,
OrganizationUserResetPasswordDetailsResponse,
OrganizationUserUserDetailsResponse,
} from "./responses";
@@ -64,7 +65,7 @@ export abstract class OrganizationUserService {
abstract getOrganizationUserResetPasswordDetails(
organizationId: string,
id: string
): Promise<OrganizationUserResetPasswordDetailsReponse>;
): Promise<OrganizationUserResetPasswordDetailsResponse>;
/**
* Create new organization user invite(s) for the specified organization
@@ -94,6 +95,20 @@ export abstract class OrganizationUserService {
ids: string[]
): Promise<ListResponse<OrganizationUserBulkResponse>>;
/**
* Accept an invitation to initialize and join an organization created via the Admin Portal **only**.
* This is only used once for the initial Owner, because it also creates the organization's encryption keys.
* This should not be used for organizations created via the Web client.
* @param organizationId - Identifier for the organization to accept
* @param id - Organization user identifier
* @param request - Request details for accepting the invitation
*/
abstract postOrganizationUserAcceptInit(
organizationId: string,
id: string,
request: OrganizationUserAcceptInitRequest
): Promise<void>;
/**
* Accept an organization user invitation
* @param organizationId - Identifier for the organization to accept

View File

@@ -1,3 +1,4 @@
export * from "./organization-user-accept-init.request";
export * from "./organization-user-accept.request";
export * from "./organization-user-bulk-confirm.request";
export * from "./organization-user-confirm.request";

View File

@@ -0,0 +1,8 @@
import { OrganizationKeysRequest } from "../../../admin-console/models/request/organization-keys.request";
export class OrganizationUserAcceptInitRequest {
token: string;
key: string;
keys: OrganizationKeysRequest;
collectionName: string;
}

View File

@@ -1,4 +1,4 @@
import { OrganizationUserType } from "../../../admin-console/enums/organization-user-type";
import { OrganizationUserType } from "../../../admin-console/enums";
import { PermissionsApi } from "../../../admin-console/models/api/permissions.api";
import { SelectionReadOnlyRequest } from "../../../admin-console/models/request/selection-read-only.request";

View File

@@ -1,4 +1,4 @@
import { OrganizationUserType } from "../../../admin-console/enums/organization-user-type";
import { OrganizationUserType } from "../../../admin-console/enums";
import { PermissionsApi } from "../../../admin-console/models/api/permissions.api";
import { SelectionReadOnlyRequest } from "../../../admin-console/models/request/selection-read-only.request";

View File

@@ -1,8 +1,7 @@
import { OrganizationUserStatusType } from "../../../admin-console/enums/organization-user-status-type";
import { OrganizationUserType } from "../../../admin-console/enums/organization-user-type";
import { OrganizationUserStatusType, OrganizationUserType } from "../../../admin-console/enums";
import { PermissionsApi } from "../../../admin-console/models/api/permissions.api";
import { SelectionReadOnlyResponse } from "../../../admin-console/models/response/selection-read-only.response";
import { KdfType } from "../../../enums/kdfType";
import { KdfType } from "../../../enums";
import { BaseResponse } from "../../../models/response/base.response";
export class OrganizationUserResponse extends BaseResponse {
@@ -64,7 +63,7 @@ export class OrganizationUserDetailsResponse extends OrganizationUserResponse {
}
}
export class OrganizationUserResetPasswordDetailsReponse extends BaseResponse {
export class OrganizationUserResetPasswordDetailsResponse extends BaseResponse {
kdf: KdfType;
kdfIterations: number;
kdfMemory?: number;

View File

@@ -1,5 +1,4 @@
import { ClientType } from "../enums/clientType";
import { DeviceType } from "../enums/deviceType";
import { ClientType, DeviceType } from "../enums";
interface ToastOptions {
timeout?: number;
@@ -28,15 +27,6 @@ export abstract class PlatformUtilsService {
text: string | string[],
options?: ToastOptions
) => void;
showDialog: (
body: string,
title?: string,
confirmText?: string,
cancelText?: string,
type?: string,
bodyIsHtml?: boolean,
target?: string
) => Promise<boolean>;
isDev: () => boolean;
isSelfHost: () => boolean;
copyToClipboard: (text: string, options?: any) => void | boolean;

View File

@@ -5,7 +5,7 @@ export abstract class SearchService {
indexedEntityId?: string = null;
clearIndex: () => void;
isSearchable: (query: string) => boolean;
indexCiphers: (indexedEntityGuid?: string, ciphersToIndex?: CipherView[]) => Promise<void>;
indexCiphers: (ciphersToIndex: CipherView[], indexedEntityGuid?: string) => void;
searchCiphers: (
query: string,
filter?: ((cipher: CipherView) => boolean) | ((cipher: CipherView) => boolean)[],

View File

@@ -4,8 +4,11 @@ import { AccountSettingsSettings } from "../models/domain/account";
export abstract class SettingsService {
settings$: Observable<AccountSettingsSettings>;
disableFavicon$: Observable<boolean>;
setEquivalentDomains: (equivalentDomains: string[][]) => Promise<any>;
getEquivalentDomains: (url: string) => string[];
getEquivalentDomains: (url: string) => Set<string>;
setDisableFavicon: (value: boolean) => Promise<any>;
getDisableFavicon: () => boolean;
clear: (userId?: string) => Promise<void>;
}

View File

@@ -8,10 +8,10 @@ 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 { EnvironmentUrls } from "../auth/models/domain/environment-urls";
import { ForceResetPasswordReason } from "../auth/models/domain/force-reset-password-reason";
import { KdfConfig } from "../auth/models/domain/kdf-config";
import { KdfType } from "../enums/kdfType";
import { ThemeType } from "../enums/themeType";
import { UriMatchType } from "../enums/uriMatchType";
import { BiometricKey } from "../auth/types/biometric-key";
import { KdfType, ThemeType, UriMatchType } from "../enums";
import { EventData } from "../models/data/event.data";
import { ServerConfigData } from "../models/data/server-config.data";
import { Account, AccountSettingsSettings } from "../models/domain/account";
@@ -79,7 +79,7 @@ export abstract class StateService<T extends Account = Account> {
setCryptoMasterKeyB64: (value: string, options?: StorageOptions) => Promise<void>;
getCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise<string>;
hasCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise<boolean>;
setCryptoMasterKeyBiometric: (value: string, options?: StorageOptions) => Promise<void>;
setCryptoMasterKeyBiometric: (value: BiometricKey, options?: StorageOptions) => Promise<void>;
getDecryptedCiphers: (options?: StorageOptions) => Promise<CipherView[]>;
setDecryptedCiphers: (value: CipherView[], options?: StorageOptions) => Promise<void>;
getDecryptedCollections: (options?: StorageOptions) => Promise<CollectionView[]>;
@@ -145,7 +145,13 @@ export abstract class StateService<T extends Account = Account> {
) => Promise<void>;
getDisableContextMenuItem: (options?: StorageOptions) => Promise<boolean>;
setDisableContextMenuItem: (value: boolean, options?: StorageOptions) => Promise<void>;
/**
* @deprecated Do not call this, use SettingsService
*/
getDisableFavicon: (options?: StorageOptions) => Promise<boolean>;
/**
* @deprecated Do not call this, use SettingsService
*/
setDisableFavicon: (value: boolean, options?: StorageOptions) => Promise<void>;
getDisableGa: (options?: StorageOptions) => Promise<boolean>;
setDisableGa: (value: boolean, options?: StorageOptions) => Promise<void>;
@@ -165,8 +171,6 @@ export abstract class StateService<T extends Account = Account> {
setEnableAlwaysOnTop: (value: boolean, options?: StorageOptions) => Promise<void>;
getEnableAutoFillOnPageLoad: (options?: StorageOptions) => Promise<boolean>;
setEnableAutoFillOnPageLoad: (value: boolean, options?: StorageOptions) => Promise<void>;
getEnableBiometric: (options?: StorageOptions) => Promise<boolean>;
setEnableBiometric: (value: boolean, options?: StorageOptions) => Promise<void>;
getEnableBrowserIntegration: (options?: StorageOptions) => Promise<boolean>;
setEnableBrowserIntegration: (value: boolean, options?: StorageOptions) => Promise<void>;
getEnableBrowserIntegrationFingerprint: (options?: StorageOptions) => Promise<boolean>;
@@ -263,8 +267,11 @@ export abstract class StateService<T extends Account = Account> {
setEventCollection: (value: EventData[], options?: StorageOptions) => Promise<void>;
getEverBeenUnlocked: (options?: StorageOptions) => Promise<boolean>;
setEverBeenUnlocked: (value: boolean, options?: StorageOptions) => Promise<void>;
getForcePasswordReset: (options?: StorageOptions) => Promise<boolean>;
setForcePasswordReset: (value: boolean, options?: StorageOptions) => Promise<void>;
getForcePasswordResetReason: (options?: StorageOptions) => Promise<ForceResetPasswordReason>;
setForcePasswordResetReason: (
value: ForceResetPasswordReason,
options?: StorageOptions
) => Promise<void>;
getInstalledVersion: (options?: StorageOptions) => Promise<string>;
setInstalledVersion: (value: string, options?: StorageOptions) => Promise<void>;
getIsAuthenticated: (options?: StorageOptions) => Promise<boolean>;
@@ -291,14 +298,14 @@ export abstract class StateService<T extends Account = Account> {
setMinimizeOnCopyToClipboard: (value: boolean, options?: StorageOptions) => Promise<void>;
getNeverDomains: (options?: StorageOptions) => Promise<{ [id: string]: any }>;
setNeverDomains: (value: { [id: string]: any }, options?: StorageOptions) => Promise<void>;
getNoAutoPromptBiometrics: (options?: StorageOptions) => Promise<boolean>;
setNoAutoPromptBiometrics: (value: boolean, options?: StorageOptions) => Promise<void>;
getNoAutoPromptBiometricsText: (options?: StorageOptions) => Promise<string>;
setNoAutoPromptBiometricsText: (value: string, options?: StorageOptions) => Promise<void>;
getOpenAtLogin: (options?: StorageOptions) => Promise<boolean>;
setOpenAtLogin: (value: boolean, options?: StorageOptions) => Promise<void>;
getOrganizationInvitation: (options?: StorageOptions) => Promise<any>;
setOrganizationInvitation: (value: any, options?: StorageOptions) => Promise<void>;
getEmergencyAccessInvitation: (options?: StorageOptions) => Promise<any>;
setEmergencyAccessInvitation: (value: any, options?: StorageOptions) => Promise<void>;
/**
* @deprecated Do not call this directly, use OrganizationService
*/
@@ -348,7 +355,7 @@ export abstract class StateService<T extends Account = Account> {
setTwoFactorToken: (value: string, options?: StorageOptions) => Promise<void>;
getUserId: (options?: StorageOptions) => Promise<string>;
getUsesKeyConnector: (options?: StorageOptions) => Promise<boolean>;
setUsesKeyConnector: (vaule: boolean, options?: StorageOptions) => Promise<void>;
setUsesKeyConnector: (value: boolean, options?: StorageOptions) => Promise<void>;
getVaultTimeout: (options?: StorageOptions) => Promise<number>;
setVaultTimeout: (value: number, options?: StorageOptions) => Promise<void>;
getVaultTimeoutAction: (options?: StorageOptions) => Promise<string>;

View File

@@ -1,6 +1,12 @@
import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum";
export abstract class VaultTimeoutSettingsService {
setVaultTimeoutOptions: (vaultTimeout: number, vaultTimeoutAction: string) => Promise<void>;
setVaultTimeoutOptions: (
vaultTimeout: number,
vaultTimeoutAction: VaultTimeoutAction
) => Promise<void>;
getVaultTimeout: (userId?: string) => Promise<number>;
getVaultTimeoutAction: (userId?: string) => Promise<VaultTimeoutAction>;
isPinLockSet: () => Promise<[boolean, boolean]>;
isBiometricLockSet: () => Promise<boolean>;
clear: (userId?: string) => Promise<void>;

View File

@@ -15,7 +15,7 @@ import { SeatRequest } from "../../../models/request/seat.request";
import { StorageRequest } from "../../../models/request/storage.request";
import { VerifyBankRequest } from "../../../models/request/verify-bank.request";
import { ListResponse } from "../../../models/response/list.response";
import { OrganizationApiKeyType } from "../../enums/organization-api-key-type";
import { OrganizationApiKeyType } from "../../enums";
import { OrganizationCreateRequest } from "../../models/request/organization-create.request";
import { OrganizationKeysRequest } from "../../models/request/organization-keys.request";
import { OrganizationUpdateRequest } from "../../models/request/organization-update.request";

View File

@@ -56,13 +56,22 @@ export function canAccessAdmin(i18nService: I18nService) {
);
}
export function isNotProviderUser(org: Organization): boolean {
return !org.isProviderUser;
/**
* Returns `true` if a user is a member of an organization (rather than only being a ProviderUser)
* @deprecated Use organizationService.memberOrganizations$ instead
*/
export function isMember(org: Organization): boolean {
return org.isMember;
}
export abstract class OrganizationService {
organizations$: Observable<Organization[]>;
/**
* Organizations that the user is a member of (excludes organizations that they only have access to via a provider)
*/
memberOrganizations$: Observable<Organization[]>;
get$: (id: string) => Observable<Organization | undefined>;
get: (id: string) => Organization;
getByIdentifier: (identifier: string) => Organization;

View File

@@ -1,5 +1,5 @@
import { ListResponse } from "../../../models/response/list.response";
import { PolicyType } from "../../enums/policy-type";
import { PolicyType } from "../../enums";
import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
import { PolicyRequest } from "../../models/request/policy.request";
import { PolicyResponse } from "../../models/response/policy.response";
@@ -7,6 +7,7 @@ import { PolicyResponse } from "../../models/response/policy.response";
export class PolicyApiServiceAbstraction {
getPolicy: (organizationId: string, type: PolicyType) => Promise<PolicyResponse>;
getPolicies: (organizationId: string) => Promise<ListResponse<PolicyResponse>>;
getPoliciesByToken: (
organizationId: string,
token: string,

View File

@@ -1,7 +1,7 @@
import { Observable } from "rxjs";
import { ListResponse } from "../../../models/response/list.response";
import { PolicyType } from "../../enums/policy-type";
import { PolicyType } from "../../enums";
import { PolicyData } from "../../models/data/policy.data";
import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
import { Policy } from "../../models/domain/policy";
@@ -10,6 +10,7 @@ import { PolicyResponse } from "../../models/response/policy.response";
export abstract class PolicyService {
policies$: Observable<Policy[]>;
get$: (policyType: PolicyType, policyFilter?: (policy: Policy) => boolean) => Observable<Policy>;
masterPasswordPolicyOptions$: (policies?: Policy[]) => Observable<MasterPasswordPolicyOptions>;
policyAppliesToActiveUser$: (
policyType: PolicyType,

View File

@@ -0,0 +1,8 @@
export * from "./organization-api-key-type.enum";
export * from "./organization-connection-type.enum";
export * from "./organization-user-status-type.enum";
export * from "./organization-user-type.enum";
export * from "./policy-type.enum";
export * from "./provider-user-status-type.enum";
export * from "./provider-user-type.enum";
export * from "./scim-provider-type.enum";

View File

@@ -1,5 +1,5 @@
import { BaseResponse } from "../../../models/response/base.response";
import { ScimProviderType } from "../../enums/scim-provider-type";
import { ScimProviderType } from "../../enums";
export class ScimConfigApi extends BaseResponse {
enabled: boolean;

View File

@@ -1,6 +1,5 @@
import { ProductType } from "../../../enums/productType";
import { OrganizationUserStatusType } from "../../enums/organization-user-status-type";
import { OrganizationUserType } from "../../enums/organization-user-type";
import { ProductType, ProviderType } from "../../../enums";
import { OrganizationUserStatusType, OrganizationUserType } from "../../enums";
import { PermissionsApi } from "../api/permissions.api";
import { ProfileOrganizationResponse } from "../response/profile-organization.response";
@@ -37,7 +36,9 @@ export class OrganizationData {
hasPublicAndPrivateKeys: boolean;
providerId: string;
providerName: string;
providerType?: ProviderType;
isProviderUser: boolean;
isMember: boolean;
familySponsorshipFriendlyName: string;
familySponsorshipAvailable: boolean;
planProductType: ProductType;
@@ -48,7 +49,13 @@ export class OrganizationData {
familySponsorshipToDelete?: boolean;
accessSecretsManager: boolean;
constructor(response: ProfileOrganizationResponse) {
constructor(
response: ProfileOrganizationResponse,
options: {
isMember: boolean;
isProviderUser: boolean;
}
) {
this.id = response.id;
this.name = response.name;
this.status = response.status;
@@ -81,6 +88,7 @@ export class OrganizationData {
this.hasPublicAndPrivateKeys = response.hasPublicAndPrivateKeys;
this.providerId = response.providerId;
this.providerName = response.providerName;
this.providerType = response.providerType;
this.familySponsorshipFriendlyName = response.familySponsorshipFriendlyName;
this.familySponsorshipAvailable = response.familySponsorshipAvailable;
this.planProductType = response.planProductType;
@@ -90,5 +98,8 @@ export class OrganizationData {
this.familySponsorshipValidUntil = response.familySponsorshipValidUntil;
this.familySponsorshipToDelete = response.familySponsorshipToDelete;
this.accessSecretsManager = response.accessSecretsManager;
this.isMember = options.isMember;
this.isProviderUser = options.isProviderUser;
}
}

View File

@@ -1,4 +1,4 @@
import { PolicyType } from "../../enums/policy-type";
import { PolicyType } from "../../enums";
import { PolicyResponse } from "../response/policy.response";
export class PolicyData {

View File

@@ -1,5 +1,4 @@
import { ProviderUserStatusType } from "../../enums/provider-user-status-type";
import { ProviderUserType } from "../../enums/provider-user-type";
import { ProviderUserStatusType, ProviderUserType } from "../../enums";
import { ProfileProviderResponse } from "../response/profile-provider.response";
export class ProviderData {

View File

@@ -1,3 +1,4 @@
import { MasterPasswordPolicyResponse } from "../../../auth/models/response/master-password-policy.response";
import Domain from "../../../models/domain/domain-base";
export class MasterPasswordPolicyOptions extends Domain {
@@ -7,4 +8,26 @@ export class MasterPasswordPolicyOptions extends Domain {
requireLower = false;
requireNumbers = false;
requireSpecial = false;
/**
* Flag to indicate if the policy should be enforced on login.
* If true, and the user's password does not meet the policy requirements,
* the user will be forced to update their password.
*/
enforceOnLogin = false;
static fromResponse(policy: MasterPasswordPolicyResponse): MasterPasswordPolicyOptions {
if (policy == null) {
return null;
}
const options = new MasterPasswordPolicyOptions();
options.minComplexity = policy.minComplexity;
options.minLength = policy.minLength;
options.requireUpper = policy.requireUpper;
options.requireLower = policy.requireLower;
options.requireNumbers = policy.requireNumbers;
options.requireSpecial = policy.requireSpecial;
options.enforceOnLogin = policy.enforceOnLogin;
return options;
}
}

View File

@@ -1,8 +1,7 @@
import { Jsonify } from "type-fest";
import { ProductType } from "../../../enums/productType";
import { OrganizationUserStatusType } from "../../enums/organization-user-status-type";
import { OrganizationUserType } from "../../enums/organization-user-type";
import { ProductType, ProviderType } from "../../../enums";
import { OrganizationUserStatusType, OrganizationUserType } from "../../enums";
import { PermissionsApi } from "../api/permissions.api";
import { OrganizationData } from "../data/organization.data";
@@ -10,7 +9,14 @@ export class Organization {
id: string;
name: string;
status: OrganizationUserStatusType;
/**
* The member's role in the organization.
* Avoid using this for permission checks - use the getters instead (e.g. isOwner, isAdmin, canManageX), because they
* properly handle permission inheritance and relationships.
*/
type: OrganizationUserType;
enabled: boolean;
usePolicies: boolean;
useGroups: boolean;
@@ -39,7 +45,15 @@ export class Organization {
hasPublicAndPrivateKeys: boolean;
providerId: string;
providerName: string;
providerType?: ProviderType;
/**
* Indicates that a user is a ProviderUser for the organization
*/
isProviderUser: boolean;
/**
* Indicates that a user is a member for the organization (may be `false` if they have access via a Provider only)
*/
isMember: boolean;
familySponsorshipFriendlyName: string;
familySponsorshipAvailable: boolean;
planProductType: ProductType;
@@ -87,7 +101,9 @@ export class Organization {
this.hasPublicAndPrivateKeys = obj.hasPublicAndPrivateKeys;
this.providerId = obj.providerId;
this.providerName = obj.providerName;
this.providerType = obj.providerType;
this.isProviderUser = obj.isProviderUser;
this.isMember = obj.isMember;
this.familySponsorshipFriendlyName = obj.familySponsorshipFriendlyName;
this.familySponsorshipAvailable = obj.familySponsorshipAvailable;
this.planProductType = obj.planProductType;
@@ -100,24 +116,29 @@ export class Organization {
}
get canAccess() {
if (this.type === OrganizationUserType.Owner) {
if (this.isOwner) {
return true;
}
return this.enabled && this.status === OrganizationUserStatusType.Confirmed;
}
/**
* Whether a user has Manager permissions or greater
*/
get isManager() {
return (
this.type === OrganizationUserType.Manager ||
this.type === OrganizationUserType.Owner ||
this.type === OrganizationUserType.Admin
);
return this.type === OrganizationUserType.Manager || this.isAdmin;
}
/**
* Whether a user has Admin permissions or greater
*/
get isAdmin() {
return this.type === OrganizationUserType.Owner || this.type === OrganizationUserType.Admin;
return this.type === OrganizationUserType.Admin || this.isOwner;
}
/**
* Whether a user has Owner permissions (including ProviderUsers)
*/
get isOwner() {
return this.type === OrganizationUserType.Owner || this.isProviderUser;
}
@@ -198,8 +219,26 @@ export class Organization {
return this.canManagePolicies;
}
get canManageBilling() {
return this.isOwner && (this.isProviderUser || !this.hasProvider);
get canViewSubscription() {
if (this.canEditSubscription) {
return true;
}
return this.hasProvider && this.providerType === ProviderType.Msp
? this.isProviderUser
: this.isOwner;
}
get canEditSubscription() {
return this.hasProvider ? this.isProviderUser : this.isOwner;
}
get canEditPaymentMethods() {
return this.canEditSubscription;
}
get canViewBillingHistory() {
return this.canEditSubscription;
}
get hasProvider() {

View File

@@ -1,5 +1,5 @@
import Domain from "../../../models/domain/domain-base";
import { PolicyType } from "../../enums/policy-type";
import { PolicyType } from "../../enums";
import { PolicyData } from "../data/policy.data";
export class Policy extends Domain {

View File

@@ -0,0 +1,14 @@
import { Collection } from "../domain/collection";
import { CollectionRequest } from "../request/collection.request";
export class CollectionWithIdRequest extends CollectionRequest {
id: string;
constructor(collection?: Collection) {
if (collection == null) {
return;
}
super(collection);
this.id = collection.id;
}
}

View File

@@ -1,5 +1,5 @@
import { SecretVerificationRequest } from "../../../auth/models/request/secret-verification.request";
import { OrganizationApiKeyType } from "../../enums/organization-api-key-type";
import { OrganizationApiKeyType } from "../../enums";
export class OrganizationApiKeyRequest extends SecretVerificationRequest {
type: OrganizationApiKeyType = OrganizationApiKeyType.Default;

View File

@@ -1,5 +1,5 @@
import { BillingSyncConfigRequest } from "../../../billing/models/request/billing-sync-config.request";
import { OrganizationConnectionType } from "../../enums/organization-connection-type";
import { OrganizationConnectionType } from "../../enums";
import { ScimConfigRequest } from "./scim-config.request";

View File

@@ -1,5 +1,4 @@
import { PaymentMethodType } from "../../../billing/enums/payment-method-type";
import { PlanType } from "../../../billing/enums/plan-type";
import { PaymentMethodType, PlanType } from "../../../billing/enums";
import { OrganizationKeysRequest } from "./organization-keys.request";

View File

@@ -1,4 +1,4 @@
import { PlanType } from "../../../billing/enums/plan-type";
import { PlanType } from "../../../billing/enums";
import { OrganizationKeysRequest } from "./organization-keys.request";

View File

@@ -1,4 +1,4 @@
import { PlanSponsorshipType } from "../../../../billing/enums/plan-sponsorship-type";
import { PlanSponsorshipType } from "../../../../billing/enums";
export class OrganizationSponsorshipCreateRequest {
sponsoredEmail: string;

View File

@@ -1,4 +1,4 @@
import { PlanSponsorshipType } from "../../../../billing/enums/plan-sponsorship-type";
import { PlanSponsorshipType } from "../../../../billing/enums";
export class OrganizationSponsorshipRedeemRequest {
planSponsorshipType: PlanSponsorshipType;

View File

@@ -1,4 +1,4 @@
import { PolicyType } from "../../enums/policy-type";
import { PolicyType } from "../../enums";
export class PolicyRequest {
type: PolicyType;

View File

@@ -1,4 +1,4 @@
import { ProviderUserType } from "../../../enums/provider-user-type";
import { ProviderUserType } from "../../../enums";
export class ProviderUserInviteRequest {
emails: string[] = [];

View File

@@ -1,4 +1,4 @@
import { ProviderUserType } from "../../../enums/provider-user-type";
import { ProviderUserType } from "../../../enums";
export class ProviderUserUpdateRequest {
type: ProviderUserType;

View File

@@ -1,4 +1,4 @@
import { ScimProviderType } from "../../enums/scim-provider-type";
import { ScimProviderType } from "../../enums";
export class ScimConfigRequest {
constructor(private enabled: boolean, private scimProvider: ScimProviderType = null) {}

View File

@@ -1,5 +1,5 @@
import { BaseResponse } from "../../../models/response/base.response";
import { OrganizationApiKeyType } from "../../enums/organization-api-key-type";
import { OrganizationApiKeyType } from "../../enums";
export class OrganizationApiKeyInformationResponse extends BaseResponse {
keyType: OrganizationApiKeyType;

View File

@@ -1,6 +1,6 @@
import { BillingSyncConfigApi } from "../../../billing/models/api/billing-sync-config.api";
import { BaseResponse } from "../../../models/response/base.response";
import { OrganizationConnectionType } from "../../enums/organization-connection-type";
import { OrganizationConnectionType } from "../../enums";
import { ScimConfigApi } from "../api/scim-config.api";
/**API response config types for OrganizationConnectionResponse */

View File

@@ -1,4 +1,4 @@
import { PlanType } from "../../../billing/enums/plan-type";
import { PlanType } from "../../../billing/enums";
import { PlanResponse } from "../../../billing/models/response/plan.response";
import { BaseResponse } from "../../../models/response/base.response";

View File

@@ -1,5 +1,5 @@
import { BaseResponse } from "../../../models/response/base.response";
import { PolicyType } from "../../enums/policy-type";
import { PolicyType } from "../../enums";
export class PolicyResponse extends BaseResponse {
id: string;

View File

@@ -1,7 +1,6 @@
import { ProductType } from "../../../enums/productType";
import { ProductType, ProviderType } from "../../../enums";
import { BaseResponse } from "../../../models/response/base.response";
import { OrganizationUserStatusType } from "../../enums/organization-user-status-type";
import { OrganizationUserType } from "../../enums/organization-user-type";
import { OrganizationUserStatusType, OrganizationUserType } from "../../enums";
import { PermissionsApi } from "../api/permissions.api";
export class ProfileOrganizationResponse extends BaseResponse {
@@ -38,6 +37,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
userId: string;
providerId: string;
providerName: string;
providerType?: ProviderType;
familySponsorshipFriendlyName: string;
familySponsorshipAvailable: boolean;
planProductType: ProductType;
@@ -83,6 +83,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
this.userId = this.getResponseProperty("UserId");
this.providerId = this.getResponseProperty("ProviderId");
this.providerName = this.getResponseProperty("ProviderName");
this.providerType = this.getResponseProperty("ProviderType");
this.familySponsorshipFriendlyName = this.getResponseProperty("FamilySponsorshipFriendlyName");
this.familySponsorshipAvailable = this.getResponseProperty("FamilySponsorshipAvailable");
this.planProductType = this.getResponseProperty("PlanProductType");

View File

@@ -1,6 +1,5 @@
import { BaseResponse } from "../../../models/response/base.response";
import { ProviderUserStatusType } from "../../enums/provider-user-status-type";
import { ProviderUserType } from "../../enums/provider-user-type";
import { ProviderUserStatusType, ProviderUserType } from "../../enums";
import { PermissionsApi } from "../api/permissions.api";
export class ProfileProviderResponse extends BaseResponse {

View File

@@ -1,6 +1,5 @@
import { BaseResponse } from "../../../../models/response/base.response";
import { ProviderUserStatusType } from "../../../enums/provider-user-status-type";
import { ProviderUserType } from "../../../enums/provider-user-type";
import { ProviderUserStatusType, ProviderUserType } from "../../../enums";
import { PermissionsApi } from "../../api/permissions.api";
export class ProviderUserResponse extends BaseResponse {

View File

@@ -3,6 +3,8 @@ import { View } from "../../../models/view/view";
import { Collection } from "../domain/collection";
import { CollectionAccessDetailsResponse } from "../response/collection.response";
export const NestingDelimiter = "/";
export class CollectionView implements View, ITreeNodeObject {
id: string = null;
organizationId: string = null;

View File

@@ -18,7 +18,7 @@ import { VerifyBankRequest } from "../../../models/request/verify-bank.request";
import { ListResponse } from "../../../models/response/list.response";
import { SyncService } from "../../../vault/abstractions/sync/sync.service.abstraction";
import { OrganizationApiServiceAbstraction } from "../../abstractions/organization/organization-api.service.abstraction";
import { OrganizationApiKeyType } from "../../enums/organization-api-key-type";
import { OrganizationApiKeyType } from "../../enums";
import { OrganizationCreateRequest } from "../../models/request/organization-create.request";
import { OrganizationKeysRequest } from "../../models/request/organization-keys.request";
import { OrganizationUpdateRequest } from "../../models/request/organization-update.request";

View File

@@ -1,7 +1,10 @@
import { BehaviorSubject, concatMap, map, Observable } from "rxjs";
import { StateService } from "../../../abstractions/state.service";
import { InternalOrganizationService as InternalOrganizationServiceAbstraction } from "../../abstractions/organization/organization.service.abstraction";
import {
InternalOrganizationService as InternalOrganizationServiceAbstraction,
isMember,
} from "../../abstractions/organization/organization.service.abstraction";
import { OrganizationData } from "../../models/data/organization.data";
import { Organization } from "../../models/domain/organization";
@@ -9,6 +12,7 @@ export class OrganizationService implements InternalOrganizationServiceAbstracti
protected _organizations = new BehaviorSubject<Organization[]>([]);
organizations$ = this._organizations.asObservable();
memberOrganizations$ = this.organizations$.pipe(map((orgs) => orgs.filter(isMember)));
constructor(private stateService: StateService) {
this.stateService.activeAccountUnlocked$

View File

@@ -6,7 +6,7 @@ import { Utils } from "../../../misc/utils";
import { ListResponse } from "../../../models/response/list.response";
import { PolicyApiServiceAbstraction } from "../../abstractions/policy/policy-api.service.abstraction";
import { InternalPolicyService } from "../../abstractions/policy/policy.service.abstraction";
import { PolicyType } from "../../enums/policy-type";
import { PolicyType } from "../../enums";
import { PolicyData } from "../../models/data/policy.data";
import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
import { PolicyRequest } from "../../models/request/policy.request";

View File

@@ -1,13 +1,11 @@
import { of, concatMap, BehaviorSubject, Observable, map } from "rxjs";
import { BehaviorSubject, concatMap, map, Observable, of } from "rxjs";
import { StateService } from "../../../abstractions/state.service";
import { Utils } from "../../../misc/utils";
import { ListResponse } from "../../../models/response/list.response";
import { OrganizationService } from "../../abstractions/organization/organization.service.abstraction";
import { InternalPolicyService as InternalPolicyServiceAbstraction } from "../../abstractions/policy/policy.service.abstraction";
import { OrganizationUserStatusType } from "../../enums/organization-user-status-type";
import { OrganizationUserType } from "../../enums/organization-user-type";
import { PolicyType } from "../../enums/policy-type";
import { OrganizationUserStatusType, OrganizationUserType, PolicyType } from "../../enums";
import { PolicyData } from "../../models/data/policy.data";
import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options";
import { Organization } from "../../models/domain/organization";
@@ -44,6 +42,28 @@ export class PolicyService implements InternalPolicyServiceAbstraction {
.subscribe();
}
/**
* Returns the first policy found that applies to the active user
* @param policyType Policy type to search for
* @param policyFilter Additional filter to apply to the policy
*/
get$(policyType: PolicyType, policyFilter?: (policy: Policy) => boolean): Observable<Policy> {
return this.policies$.pipe(
concatMap(async (policies) => {
const userId = await this.stateService.getUserId();
const appliesToCurrentUser = await this.checkPoliciesThatApplyToUser(
policies,
policyType,
policyFilter,
userId
);
if (appliesToCurrentUser) {
return policies.find((policy) => policy.type === policyType && policy.enabled);
}
})
);
}
/**
* @deprecated Do not call this, use the policies$ observable collection
*/
@@ -117,6 +137,10 @@ export class PolicyService implements InternalPolicyServiceAbstraction {
if (currentPolicy.data.requireSpecial) {
enforcedOptions.requireSpecial = true;
}
if (currentPolicy.data.enforceOnLogin) {
enforcedOptions.enforceOnLogin = true;
}
});
return enforcedOptions;
@@ -240,7 +264,7 @@ export class PolicyService implements InternalPolicyServiceAbstraction {
await this.stateService.setEncryptedPolicies(null, { userId: userId });
}
private isExcemptFromPolicies(organization: Organization, policyType: PolicyType) {
private isExemptFromPolicies(organization: Organization, policyType: PolicyType) {
if (policyType === PolicyType.MaximumVaultTimeout) {
return organization.type === OrganizationUserType.Owner;
}
@@ -271,7 +295,7 @@ export class PolicyService implements InternalPolicyServiceAbstraction {
o.status >= OrganizationUserStatusType.Accepted &&
o.usePolicies &&
policySet.has(o.id) &&
!this.isExcemptFromPolicies(o, policyType)
!this.isExemptFromPolicies(o, policyType)
);
}
}

View File

@@ -18,6 +18,7 @@ export abstract class AuthService {
email: string;
accessCode: string;
authRequestId: string;
ssoEmail2FaSessionToken: string;
logIn: (
credentials:
@@ -37,11 +38,11 @@ export abstract class AuthService {
authingWithPassword: () => boolean;
authingWithPasswordless: () => boolean;
getAuthStatus: (userId?: string) => Promise<AuthenticationStatus>;
authResponsePushNotifiction: (notification: AuthRequestPushNotification) => Promise<any>;
authResponsePushNotification: (notification: AuthRequestPushNotification) => Promise<any>;
passwordlessLogin: (
id: string,
key: string,
requestApproved: boolean
) => Promise<AuthRequestResponse>;
getPushNotifcationObs$: () => Observable<any>;
getPushNotificationObs$: () => Observable<any>;
}

View File

@@ -7,20 +7,24 @@ import { LogService } from "../../abstractions/log.service";
import { MessagingService } from "../../abstractions/messaging.service";
import { PlatformUtilsService } from "../../abstractions/platformUtils.service";
import { StateService } from "../../abstractions/state.service";
import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction";
import { Utils } from "../../misc/utils";
import { Account, AccountProfile, AccountTokens } from "../../models/domain/account";
import { EncString } from "../../models/domain/enc-string";
import { PasswordGenerationService } from "../../tools/generator/password";
import { AuthService } from "../abstractions/auth.service";
import { TokenService } from "../abstractions/token.service";
import { TwoFactorService } from "../abstractions/two-factor.service";
import { TwoFactorProviderType } from "../enums/two-factor-provider-type";
import { AuthResult } from "../models/domain/auth-result";
import { ForceResetPasswordReason } from "../models/domain/force-reset-password-reason";
import { PasswordLogInCredentials } from "../models/domain/log-in-credentials";
import { PasswordTokenRequest } from "../models/request/identity-token/password-token.request";
import { TokenTwoFactorRequest } from "../models/request/identity-token/token-two-factor.request";
import { IdentityCaptchaResponse } from "../models/response/identity-captcha.response";
import { IdentityTokenResponse } from "../models/response/identity-token.response";
import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response";
import { MasterPasswordPolicyResponse } from "../models/response/master-password-policy.response";
import { PasswordLogInStrategy } from "./password-login.strategy";
@@ -50,7 +54,9 @@ const twoFactorProviderType = TwoFactorProviderType.Authenticator;
const twoFactorToken = "TWO_FACTOR_TOKEN";
const twoFactorRemember = true;
export function identityTokenResponseFactory() {
export function identityTokenResponseFactory(
masterPasswordPolicyResponse: MasterPasswordPolicyResponse = null
) {
return new IdentityTokenResponse({
ForcePasswordReset: false,
Kdf: kdf,
@@ -63,6 +69,7 @@ export function identityTokenResponseFactory() {
refresh_token: refreshToken,
scope: "api offline_access",
token_type: "Bearer",
MasterPasswordPolicy: masterPasswordPolicyResponse,
});
}
@@ -77,6 +84,8 @@ describe("LogInStrategy", () => {
let stateService: MockProxy<StateService>;
let twoFactorService: MockProxy<TwoFactorService>;
let authService: MockProxy<AuthService>;
let policyService: MockProxy<PolicyService>;
let passwordGenerationService: MockProxy<PasswordGenerationService>;
let passwordLogInStrategy: PasswordLogInStrategy;
let credentials: PasswordLogInCredentials;
@@ -92,6 +101,8 @@ describe("LogInStrategy", () => {
stateService = mock<StateService>();
twoFactorService = mock<TwoFactorService>();
authService = mock<AuthService>();
policyService = mock<PolicyService>();
passwordGenerationService = mock<PasswordGenerationService>();
appIdService.getAppId.mockResolvedValue(deviceId);
tokenService.decodeToken.calledWith(accessToken).mockResolvedValue(decodedToken);
@@ -107,6 +118,8 @@ describe("LogInStrategy", () => {
logService,
stateService,
twoFactorService,
passwordGenerationService,
policyService,
authService
);
credentials = new PasswordLogInCredentials(email, masterPassword);
@@ -155,7 +168,7 @@ describe("LogInStrategy", () => {
const result = await passwordLogInStrategy.logIn(credentials);
expect(result).toEqual({
forcePasswordReset: true,
forcePasswordReset: ForceResetPasswordReason.AdminForcePasswordReset,
resetMasterPassword: true,
twoFactorProviders: null,
captchaSiteKey: "",
@@ -210,6 +223,9 @@ describe("LogInStrategy", () => {
TwoFactorProviders2: { 0: null },
error: "invalid_grant",
error_description: "Two factor required.",
// only sent for emailed 2FA
email: undefined,
ssoEmail2faSessionToken: undefined,
});
apiService.postIdentityToken.mockResolvedValue(tokenResponse);
@@ -225,6 +241,39 @@ describe("LogInStrategy", () => {
expect(result).toEqual(expected);
});
it("rejects login if 2FA via email is required + maps required information", async () => {
// Sample response where Email 2FA required
const userEmail = "kyle@bitwarden.com";
const ssoEmail2FaSessionToken =
"BwSsoEmail2FaSessionToken_CfDJ8AMrVzKqBFpKqzzsahUx8ubIi9AhHm6aLHDLpCUYc3QV3qC14iuSVkNg57Q7-kGQUn1z87bGY1WP58jFMNJ6ndaurIgQWNfPNN4DG-dBhvzarOAZ0RKY5oKT5futWm6_k9NMMGd8PcGGHg5Pq1_koOIwRtiXO3IpD-bemB7m8oEvbj__JTQP3Mcz-UediFlCbYBKU3wyIiBL_tF8hW5D4RAUa5ZzXIuauJiiCdDS7QOzBcqcusVAPGFfKjfIdAwFfKSOYd5KmYrhK7Y7ymjweP_igPYKB5aMfcVaYr5ux-fdffeJTGqtJorwNjLUYNv7KA";
const tokenResponse = new IdentityTwoFactorResponse({
TwoFactorProviders: ["1"],
TwoFactorProviders2: { "1": { Email: "k***@bitwarden.com" } },
error: "invalid_grant",
error_description: "Two factor required.",
// only sent for emailed 2FA
email: userEmail,
ssoEmail2faSessionToken: ssoEmail2FaSessionToken,
});
apiService.postIdentityToken.mockResolvedValue(tokenResponse);
const result = await passwordLogInStrategy.logIn(credentials);
expect(stateService.addAccount).not.toHaveBeenCalled();
expect(messagingService.send).not.toHaveBeenCalled();
const expected = new AuthResult();
expected.twoFactorProviders = new Map<TwoFactorProviderType, { [key: string]: string }>();
expected.twoFactorProviders.set(1, { Email: "k***@bitwarden.com" });
expected.email = userEmail;
expected.ssoEmail2FaSessionToken = ssoEmail2FaSessionToken;
expect(result).toEqual(expected);
});
it("sends stored 2FA token to server", async () => {
tokenService.getTwoFactorToken.mockResolvedValue(twoFactorToken);
apiService.postIdentityToken.mockResolvedValue(identityTokenResponseFactory());

View File

@@ -11,11 +11,12 @@ import { TokenService } from "../abstractions/token.service";
import { TwoFactorService } from "../abstractions/two-factor.service";
import { TwoFactorProviderType } from "../enums/two-factor-provider-type";
import { AuthResult } from "../models/domain/auth-result";
import { ForceResetPasswordReason } from "../models/domain/force-reset-password-reason";
import {
UserApiLogInCredentials,
PasswordlessLogInCredentials,
PasswordLogInCredentials,
SsoLogInCredentials,
PasswordlessLogInCredentials,
UserApiLogInCredentials,
} from "../models/domain/log-in-credentials";
import { DeviceRequest } from "../models/request/identity-token/device.request";
import { PasswordTokenRequest } from "../models/request/identity-token/password-token.request";
@@ -26,6 +27,8 @@ import { IdentityCaptchaResponse } from "../models/response/identity-captcha.res
import { IdentityTokenResponse } from "../models/response/identity-token.response";
import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response";
type IdentityResponse = IdentityTokenResponse | IdentityTwoFactorResponse | IdentityCaptchaResponse;
export abstract class LogInStrategy {
protected abstract tokenRequest: UserApiTokenRequest | PasswordTokenRequest | SsoTokenRequest;
protected captchaBypassToken: string = null;
@@ -58,20 +61,21 @@ export abstract class LogInStrategy {
captchaResponse: string = null
): Promise<AuthResult> {
this.tokenRequest.setTwoFactor(twoFactor);
return this.startLogIn();
const [authResult] = await this.startLogIn();
return authResult;
}
protected async startLogIn(): Promise<AuthResult> {
protected async startLogIn(): Promise<[AuthResult, IdentityResponse]> {
this.twoFactorService.clearSelectedProvider();
const response = await this.apiService.postIdentityToken(this.tokenRequest);
if (response instanceof IdentityTwoFactorResponse) {
return this.processTwoFactorResponse(response);
return [await this.processTwoFactorResponse(response), response];
} else if (response instanceof IdentityCaptchaResponse) {
return this.processCaptchaResponse(response);
return [await this.processCaptchaResponse(response), response];
} else if (response instanceof IdentityTokenResponse) {
return this.processTokenResponse(response);
return [await this.processTokenResponse(response), response];
}
throw new Error("Invalid response object.");
@@ -126,7 +130,10 @@ export abstract class LogInStrategy {
protected async processTokenResponse(response: IdentityTokenResponse): Promise<AuthResult> {
const result = new AuthResult();
result.resetMasterPassword = response.resetMasterPassword;
result.forcePasswordReset = response.forcePasswordReset;
if (response.forcePasswordReset) {
result.forcePasswordReset = ForceResetPasswordReason.AdminForcePasswordReset;
}
await this.saveAccountInformation(response);
@@ -153,8 +160,11 @@ export abstract class LogInStrategy {
private async processTwoFactorResponse(response: IdentityTwoFactorResponse): Promise<AuthResult> {
const result = new AuthResult();
result.twoFactorProviders = response.twoFactorProviders2;
this.twoFactorService.setProviders(response);
this.captchaBypassToken = response.captchaToken ?? null;
result.ssoEmail2FaSessionToken = response.ssoEmail2faSessionToken;
result.email = response.email;
return result;
}

View File

@@ -7,13 +7,19 @@ import { LogService } from "../../abstractions/log.service";
import { MessagingService } from "../../abstractions/messaging.service";
import { PlatformUtilsService } from "../../abstractions/platformUtils.service";
import { StateService } from "../../abstractions/state.service";
import { HashPurpose } from "../../enums/hashPurpose";
import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction";
import { HashPurpose } from "../../enums";
import { Utils } from "../../misc/utils";
import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key";
import { PasswordGenerationService } from "../../tools/generator/password";
import { AuthService } from "../abstractions/auth.service";
import { TokenService } from "../abstractions/token.service";
import { TwoFactorService } from "../abstractions/two-factor.service";
import { TwoFactorProviderType } from "../enums/two-factor-provider-type";
import { ForceResetPasswordReason } from "../models/domain/force-reset-password-reason";
import { PasswordLogInCredentials } from "../models/domain/log-in-credentials";
import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response";
import { MasterPasswordPolicyResponse } from "../models/response/master-password-policy.response";
import { identityTokenResponseFactory } from "./login.strategy.spec";
import { PasswordLogInStrategy } from "./password-login.strategy";
@@ -28,6 +34,10 @@ const preloginKey = new SymmetricCryptoKey(
)
);
const deviceId = Utils.newGuid();
const masterPasswordPolicy = new MasterPasswordPolicyResponse({
EnforceOnLogin: true,
MinLength: 8,
});
describe("PasswordLogInStrategy", () => {
let cryptoService: MockProxy<CryptoService>;
@@ -40,6 +50,8 @@ describe("PasswordLogInStrategy", () => {
let stateService: MockProxy<StateService>;
let twoFactorService: MockProxy<TwoFactorService>;
let authService: MockProxy<AuthService>;
let policyService: MockProxy<PolicyService>;
let passwordGenerationService: MockProxy<PasswordGenerationService>;
let passwordLogInStrategy: PasswordLogInStrategy;
let credentials: PasswordLogInCredentials;
@@ -55,6 +67,8 @@ describe("PasswordLogInStrategy", () => {
stateService = mock<StateService>();
twoFactorService = mock<TwoFactorService>();
authService = mock<AuthService>();
policyService = mock<PolicyService>();
passwordGenerationService = mock<PasswordGenerationService>();
appIdService.getAppId.mockResolvedValue(deviceId);
tokenService.decodeToken.mockResolvedValue({});
@@ -68,6 +82,8 @@ describe("PasswordLogInStrategy", () => {
.calledWith(masterPassword, expect.anything(), HashPurpose.LocalAuthorization)
.mockResolvedValue(localHashedPassword);
policyService.evaluateMasterPassword.mockReturnValue(true);
passwordLogInStrategy = new PasswordLogInStrategy(
cryptoService,
apiService,
@@ -78,11 +94,15 @@ describe("PasswordLogInStrategy", () => {
logService,
stateService,
twoFactorService,
passwordGenerationService,
policyService,
authService
);
credentials = new PasswordLogInCredentials(email, masterPassword);
apiService.postIdentityToken.mockResolvedValue(identityTokenResponseFactory());
apiService.postIdentityToken.mockResolvedValue(
identityTokenResponseFactory(masterPasswordPolicy)
);
});
it("sends master password credentials to the server", async () => {
@@ -110,4 +130,75 @@ describe("PasswordLogInStrategy", () => {
expect(cryptoService.setKey).toHaveBeenCalledWith(preloginKey);
expect(cryptoService.setKeyHash).toHaveBeenCalledWith(localHashedPassword);
});
it("does not force the user to update their master password when there are no requirements", async () => {
apiService.postIdentityToken.mockResolvedValueOnce(identityTokenResponseFactory(null));
const result = await passwordLogInStrategy.logIn(credentials);
expect(policyService.evaluateMasterPassword).not.toHaveBeenCalled();
expect(result.forcePasswordReset).toEqual(ForceResetPasswordReason.None);
});
it("does not force the user to update their master password when it meets requirements", async () => {
passwordGenerationService.passwordStrength.mockReturnValue({ score: 5 } as any);
policyService.evaluateMasterPassword.mockReturnValue(true);
const result = await passwordLogInStrategy.logIn(credentials);
expect(policyService.evaluateMasterPassword).toHaveBeenCalled();
expect(result.forcePasswordReset).toEqual(ForceResetPasswordReason.None);
});
it("forces the user to update their master password on successful login when it does not meet master password policy requirements", async () => {
passwordGenerationService.passwordStrength.mockReturnValue({ score: 0 } as any);
policyService.evaluateMasterPassword.mockReturnValue(false);
const result = await passwordLogInStrategy.logIn(credentials);
expect(policyService.evaluateMasterPassword).toHaveBeenCalled();
expect(stateService.setForcePasswordResetReason).toHaveBeenCalledWith(
ForceResetPasswordReason.WeakMasterPassword
);
expect(result.forcePasswordReset).toEqual(ForceResetPasswordReason.WeakMasterPassword);
});
it("forces the user to update their master password on successful 2FA login when it does not meet master password policy requirements", async () => {
passwordGenerationService.passwordStrength.mockReturnValue({ score: 0 } as any);
policyService.evaluateMasterPassword.mockReturnValue(false);
const token2FAResponse = new IdentityTwoFactorResponse({
TwoFactorProviders: ["0"],
TwoFactorProviders2: { 0: null },
error: "invalid_grant",
error_description: "Two factor required.",
MasterPasswordPolicy: masterPasswordPolicy,
});
// First login request fails requiring 2FA
apiService.postIdentityToken.mockResolvedValueOnce(token2FAResponse);
const firstResult = await passwordLogInStrategy.logIn(credentials);
// Second login request succeeds
apiService.postIdentityToken.mockResolvedValueOnce(
identityTokenResponseFactory(masterPasswordPolicy)
);
const secondResult = await passwordLogInStrategy.logInTwoFactor(
{
provider: TwoFactorProviderType.Authenticator,
token: "123456",
remember: false,
},
""
);
// First login attempt should not save the force password reset options
expect(firstResult.forcePasswordReset).toEqual(ForceResetPasswordReason.None);
// Second login attempt should save the force password reset options and return in result
expect(stateService.setForcePasswordResetReason).toHaveBeenCalledWith(
ForceResetPasswordReason.WeakMasterPassword
);
expect(secondResult.forcePasswordReset).toEqual(ForceResetPasswordReason.WeakMasterPassword);
});
});

View File

@@ -5,15 +5,22 @@ import { LogService } from "../../abstractions/log.service";
import { MessagingService } from "../../abstractions/messaging.service";
import { PlatformUtilsService } from "../../abstractions/platformUtils.service";
import { StateService } from "../../abstractions/state.service";
import { HashPurpose } from "../../enums/hashPurpose";
import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction";
import { MasterPasswordPolicyOptions } from "../../admin-console/models/domain/master-password-policy-options";
import { HashPurpose } from "../../enums";
import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key";
import { PasswordGenerationServiceAbstraction } from "../../tools/generator/password";
import { AuthService } from "../abstractions/auth.service";
import { TokenService } from "../abstractions/token.service";
import { TwoFactorService } from "../abstractions/two-factor.service";
import { AuthResult } from "../models/domain/auth-result";
import { ForceResetPasswordReason } from "../models/domain/force-reset-password-reason";
import { PasswordLogInCredentials } from "../models/domain/log-in-credentials";
import { PasswordTokenRequest } from "../models/request/identity-token/password-token.request";
import { TokenTwoFactorRequest } from "../models/request/identity-token/token-two-factor.request";
import { IdentityCaptchaResponse } from "../models/response/identity-captcha.response";
import { IdentityTokenResponse } from "../models/response/identity-token.response";
import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response";
import { LogInStrategy } from "./login.strategy";
@@ -31,6 +38,12 @@ export class PasswordLogInStrategy extends LogInStrategy {
private localHashedPassword: string;
private key: SymmetricCryptoKey;
/**
* Options to track if the user needs to update their password due to a password that does not meet an organization's
* master password policy.
*/
private forcePasswordResetReason: ForceResetPasswordReason = ForceResetPasswordReason.None;
constructor(
cryptoService: CryptoService,
apiService: ApiService,
@@ -39,8 +52,10 @@ export class PasswordLogInStrategy extends LogInStrategy {
platformUtilsService: PlatformUtilsService,
messagingService: MessagingService,
logService: LogService,
stateService: StateService,
protected stateService: StateService,
twoFactorService: TwoFactorService,
private passwordGenerationService: PasswordGenerationServiceAbstraction,
private policyService: PolicyService,
private authService: AuthService
) {
super(
@@ -66,7 +81,19 @@ export class PasswordLogInStrategy extends LogInStrategy {
captchaResponse: string
): Promise<AuthResult> {
this.tokenRequest.captchaResponse = captchaResponse ?? this.captchaBypassToken;
return super.logInTwoFactor(twoFactor);
const result = await super.logInTwoFactor(twoFactor);
// 2FA was successful, save the force update password options with the state service if defined
if (
!result.requiresTwoFactor &&
!result.requiresCaptcha &&
this.forcePasswordResetReason != ForceResetPasswordReason.None
) {
await this.stateService.setForcePasswordResetReason(this.forcePasswordResetReason);
result.forcePasswordReset = this.forcePasswordResetReason;
}
return result;
}
async logIn(credentials: PasswordLogInCredentials) {
@@ -90,6 +117,52 @@ export class PasswordLogInStrategy extends LogInStrategy {
await this.buildDeviceRequest()
);
return this.startLogIn();
const [authResult, identityResponse] = await this.startLogIn();
const masterPasswordPolicyOptions =
this.getMasterPasswordPolicyOptionsFromResponse(identityResponse);
// The identity result can contain master password policies for the user's organizations
if (masterPasswordPolicyOptions?.enforceOnLogin) {
// If there is a policy active, evaluate the supplied password before its no longer in memory
const meetsRequirements = this.evaluateMasterPassword(
credentials,
masterPasswordPolicyOptions
);
if (!meetsRequirements) {
if (authResult.requiresCaptcha || authResult.requiresTwoFactor) {
// Save the flag to this strategy for later use as the master password is about to pass out of scope
this.forcePasswordResetReason = ForceResetPasswordReason.WeakMasterPassword;
} else {
// Authentication was successful, save the force update password options with the state service
await this.stateService.setForcePasswordResetReason(
ForceResetPasswordReason.WeakMasterPassword
);
authResult.forcePasswordReset = ForceResetPasswordReason.WeakMasterPassword;
}
}
}
return authResult;
}
private getMasterPasswordPolicyOptionsFromResponse(
response: IdentityTokenResponse | IdentityTwoFactorResponse | IdentityCaptchaResponse
): MasterPasswordPolicyOptions {
if (response == null || response instanceof IdentityCaptchaResponse) {
return null;
}
return MasterPasswordPolicyOptions.fromResponse(response.masterPasswordPolicy);
}
private evaluateMasterPassword(
{ masterPassword, email }: PasswordLogInCredentials,
options: MasterPasswordPolicyOptions
): boolean {
const passwordStrength = this.passwordGenerationService.passwordStrength(
masterPassword,
email
)?.score;
return this.policyService.evaluateMasterPassword(passwordStrength, masterPassword, options);
}
}

View File

@@ -81,6 +81,7 @@ export class PasswordlessLogInStrategy extends LogInStrategy {
);
this.tokenRequest.setPasswordlessAccessCode(credentials.authRequestId);
return this.startLogIn();
const [authResult] = await this.startLogIn();
return authResult;
}
}

View File

@@ -18,6 +18,12 @@ export class SsoLogInStrategy extends LogInStrategy {
tokenRequest: SsoTokenRequest;
orgId: string;
// A session token server side to serve as an authentication factor for the user
// in order to send email OTPs to the user's configured 2FA email address
// as we don't have a master password hash or other verifiable secret when using SSO.
ssoEmail2FaSessionToken?: string;
email?: string; // email not preserved through SSO process so get from server
constructor(
cryptoService: CryptoService,
apiService: ApiService,
@@ -65,6 +71,11 @@ export class SsoLogInStrategy extends LogInStrategy {
await this.buildDeviceRequest()
);
return this.startLogIn();
const [ssoAuthResult] = await this.startLogIn();
this.email = ssoAuthResult.email;
this.ssoEmail2FaSessionToken = ssoAuthResult.ssoEmail2FaSessionToken;
return ssoAuthResult;
}
}

View File

@@ -59,7 +59,8 @@ export class UserApiLogInStrategy extends LogInStrategy {
await this.buildDeviceRequest()
);
return this.startLogIn();
const [authResult] = await this.startLogIn();
return authResult;
}
protected async saveAccountInformation(tokenResponse: IdentityTokenResponse) {

View File

@@ -1,11 +1,15 @@
import { Utils } from "../../../misc/utils";
import { TwoFactorProviderType } from "../../enums/two-factor-provider-type";
import { ForceResetPasswordReason } from "./force-reset-password-reason";
export class AuthResult {
captchaSiteKey = "";
resetMasterPassword = false;
forcePasswordReset = false;
forcePasswordReset: ForceResetPasswordReason = ForceResetPasswordReason.None;
twoFactorProviders: Map<TwoFactorProviderType, { [key: string]: string }> = null;
ssoEmail2FaSessionToken?: string;
email: string;
get requiresCaptcha() {
return !Utils.isNullOrWhitespace(this.captchaSiteKey);

View File

@@ -0,0 +1,17 @@
export enum ForceResetPasswordReason {
/**
* A password reset should not be forced.
*/
None,
/**
* Occurs when an organization admin forces a user to reset their password.
*/
AdminForcePasswordReset,
/**
* Occurs when a user logs in / unlocks their vault with a master password that does not meet an organization's
* master password policy that is enforced on login/unlock.
*/
WeakMasterPassword,
}

View File

@@ -1,5 +1,5 @@
import { PlatformUtilsService } from "../../../../abstractions/platformUtils.service";
import { DeviceType } from "../../../../enums/deviceType";
import { DeviceType } from "../../../../enums";
export class DeviceRequest {
type: DeviceType;

View File

@@ -1,4 +1,4 @@
import { ClientType } from "../../../../enums/clientType";
import { ClientType } from "../../../../enums";
import { Utils } from "../../../../misc/utils";
import { CaptchaProtectedRequest } from "../captcha-protected.request";

View File

@@ -1,4 +1,4 @@
import { KdfType } from "../../../enums/kdfType";
import { KdfType } from "../../../enums";
import { KeysRequest } from "../../../models/request/keys.request";
import { KdfConfig } from "../domain/kdf-config";

View File

@@ -1,4 +1,4 @@
import { KdfType } from "../../../enums/kdfType";
import { KdfType } from "../../../enums";
import { KeysRequest } from "../../../models/request/keys.request";
export class SetPasswordRequest {

View File

@@ -4,4 +4,5 @@ export class TwoFactorEmailRequest extends SecretVerificationRequest {
email: string;
deviceIdentifier: string;
authRequestId: string;
ssoEmail2FaSessionToken?: string;
}

View File

@@ -1,4 +1,4 @@
import { DeviceType } from "../../../enums/deviceType";
import { DeviceType } from "../../../enums";
import { BaseResponse } from "../../../models/response/base.response";
const RequestTimeOut = 60000 * 15; //15 Minutes

View File

@@ -1,4 +1,4 @@
import { DeviceType } from "../../../enums/deviceType";
import { DeviceType } from "../../../enums";
import { BaseResponse } from "../../../models/response/base.response";
export class DeviceResponse extends BaseResponse {

View File

@@ -1,4 +1,4 @@
import { KdfType } from "../../../enums/kdfType";
import { KdfType } from "../../../enums";
import { BaseResponse } from "../../../models/response/base.response";
import { CipherResponse } from "../../../vault/models/response/cipher.response";
import { EmergencyAccessStatusType } from "../../enums/emergency-access-status-type";

View File

@@ -1,6 +1,8 @@
import { KdfType } from "../../../enums/kdfType";
import { KdfType } from "../../../enums";
import { BaseResponse } from "../../../models/response/base.response";
import { MasterPasswordPolicyResponse } from "./master-password-policy.response";
export class IdentityTokenResponse extends BaseResponse {
accessToken: string;
expiresIn: number;
@@ -16,6 +18,7 @@ export class IdentityTokenResponse extends BaseResponse {
kdfMemory?: number;
kdfParallelism?: number;
forcePasswordReset: boolean;
masterPasswordPolicy: MasterPasswordPolicyResponse;
apiUseKeyConnector: boolean;
keyConnectorUrl: string;
@@ -37,5 +40,8 @@ export class IdentityTokenResponse extends BaseResponse {
this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset");
this.apiUseKeyConnector = this.getResponseProperty("ApiUseKeyConnector");
this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl");
this.masterPasswordPolicy = new MasterPasswordPolicyResponse(
this.getResponseProperty("MasterPasswordPolicy")
);
}
}

View File

@@ -1,10 +1,15 @@
import { BaseResponse } from "../../../models/response/base.response";
import { TwoFactorProviderType } from "../../enums/two-factor-provider-type";
import { MasterPasswordPolicyResponse } from "./master-password-policy.response";
export class IdentityTwoFactorResponse extends BaseResponse {
twoFactorProviders: TwoFactorProviderType[];
twoFactorProviders2 = new Map<TwoFactorProviderType, { [key: string]: string }>();
captchaToken: string;
ssoEmail2faSessionToken: string;
email?: string;
masterPasswordPolicy?: MasterPasswordPolicyResponse;
constructor(response: any) {
super(response);
@@ -19,5 +24,11 @@ export class IdentityTwoFactorResponse extends BaseResponse {
}
}
}
this.masterPasswordPolicy = new MasterPasswordPolicyResponse(
this.getResponseProperty("MasterPasswordPolicy")
);
this.ssoEmail2faSessionToken = this.getResponseProperty("SsoEmail2faSessionToken");
this.email = this.getResponseProperty("Email");
}
}

View File

@@ -0,0 +1,29 @@
import { BaseResponse } from "../../../models/response/base.response";
export class MasterPasswordPolicyResponse extends BaseResponse {
minComplexity: number;
minLength: number;
requireUpper: boolean;
requireLower: boolean;
requireNumbers: boolean;
requireSpecial: boolean;
/**
* Flag to indicate if the policy should be enforced on login.
* If true, and the user's password does not meet the policy requirements,
* the user will be forced to update their password.
*/
enforceOnLogin: boolean;
constructor(response: any) {
super(response);
this.minComplexity = this.getResponseProperty("MinComplexity");
this.minLength = this.getResponseProperty("MinLength");
this.requireUpper = this.getResponseProperty("RequireUpper");
this.requireLower = this.getResponseProperty("RequireLower");
this.requireNumbers = this.getResponseProperty("RequireNumbers");
this.requireSpecial = this.getResponseProperty("RequireSpecial");
this.enforceOnLogin = this.getResponseProperty("EnforceOnLogin");
}
}

View File

@@ -1,4 +1,4 @@
import { KdfType } from "../../../enums/kdfType";
import { KdfType } from "../../../enums";
import { BaseResponse } from "../../../models/response/base.response";
export class PreloginResponse extends BaseResponse {

View File

@@ -10,13 +10,14 @@ import { LogService } from "../../abstractions/log.service";
import { MessagingService } from "../../abstractions/messaging.service";
import { PlatformUtilsService } from "../../abstractions/platformUtils.service";
import { StateService } from "../../abstractions/state.service";
import { KdfType } from "../../enums/kdfType";
import { KeySuffixOptions } from "../../enums/keySuffixOptions";
import { PolicyService } from "../../admin-console/abstractions/policy/policy.service.abstraction";
import { KdfType, KeySuffixOptions } from "../../enums";
import { Utils } from "../../misc/utils";
import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key";
import { PreloginRequest } from "../../models/request/prelogin.request";
import { ErrorResponse } from "../../models/response/error.response";
import { AuthRequestPushNotification } from "../../models/response/notification.response";
import { PasswordGenerationServiceAbstraction } from "../../tools/generator/password";
import { AuthService as AuthServiceAbstraction } from "../abstractions/auth.service";
import { KeyConnectorService } from "../abstractions/key-connector.service";
import { TokenService } from "../abstractions/token.service";
@@ -30,10 +31,10 @@ import { UserApiLogInStrategy } from "../login-strategies/user-api-login.strateg
import { AuthResult } from "../models/domain/auth-result";
import { KdfConfig } from "../models/domain/kdf-config";
import {
UserApiLogInCredentials,
PasswordlessLogInCredentials,
PasswordLogInCredentials,
SsoLogInCredentials,
PasswordlessLogInCredentials,
UserApiLogInCredentials,
} from "../models/domain/log-in-credentials";
import { TokenTwoFactorRequest } from "../models/request/identity-token/token-two-factor.request";
import { PasswordlessAuthRequest } from "../models/request/passwordless-auth.request";
@@ -45,7 +46,8 @@ export class AuthService implements AuthServiceAbstraction {
get email(): string {
if (
this.logInStrategy instanceof PasswordLogInStrategy ||
this.logInStrategy instanceof PasswordlessLogInStrategy
this.logInStrategy instanceof PasswordlessLogInStrategy ||
this.logInStrategy instanceof SsoLogInStrategy
) {
return this.logInStrategy.email;
}
@@ -71,6 +73,12 @@ export class AuthService implements AuthServiceAbstraction {
: null;
}
get ssoEmail2FaSessionToken(): string {
return this.logInStrategy instanceof SsoLogInStrategy
? this.logInStrategy.ssoEmail2FaSessionToken
: null;
}
private logInStrategy:
| UserApiLogInStrategy
| PasswordLogInStrategy
@@ -93,7 +101,9 @@ export class AuthService implements AuthServiceAbstraction {
protected stateService: StateService,
protected twoFactorService: TwoFactorService,
protected i18nService: I18nService,
protected encryptService: EncryptService
protected encryptService: EncryptService,
protected passwordGenerationService: PasswordGenerationServiceAbstraction,
protected policyService: PolicyService
) {}
async logIn(
@@ -123,6 +133,8 @@ export class AuthService implements AuthServiceAbstraction {
this.logService,
this.stateService,
this.twoFactorService,
this.passwordGenerationService,
this.policyService,
this
);
break;
@@ -272,11 +284,11 @@ export class AuthService implements AuthServiceAbstraction {
return this.cryptoService.makeKey(masterPassword, email, kdf, kdfConfig);
}
async authResponsePushNotifiction(notification: AuthRequestPushNotification): Promise<any> {
async authResponsePushNotification(notification: AuthRequestPushNotification): Promise<any> {
this.pushNotificationSubject.next(notification.id);
}
getPushNotifcationObs$(): Observable<any> {
getPushNotificationObs$(): Observable<any> {
return this.pushNotificationSubject.asObservable();
}

View File

@@ -4,7 +4,7 @@ import { CryptoFunctionService } from "../../abstractions/cryptoFunction.service
import { LogService } from "../../abstractions/log.service";
import { StateService } from "../../abstractions/state.service";
import { OrganizationService } from "../../admin-console/abstractions/organization/organization.service.abstraction";
import { OrganizationUserType } from "../../admin-console/enums/organization-user-type";
import { OrganizationUserType } from "../../admin-console/enums";
import { Utils } from "../../misc/utils";
import { SymmetricCryptoKey } from "../../models/domain/symmetric-crypto-key";
import { KeysRequest } from "../../models/request/keys.request";

View File

@@ -0,0 +1,6 @@
import { CsprngString } from "../../types/csprng";
export type BiometricKey = {
key: string;
clientEncKeyHalf: CsprngString;
};

View File

@@ -0,0 +1,4 @@
export * from "./payment-method-type.enum";
export * from "./plan-sponsorship-type.enum";
export * from "./plan-type.enum";
export * from "./transaction-type.enum";

View File

@@ -1,4 +1,4 @@
import { PaymentMethodType } from "../../enums/payment-method-type";
import { PaymentMethodType } from "../../enums";
import { OrganizationTaxInfoUpdateRequest } from "./organization-tax-info-update.request";

View File

@@ -1,6 +1,5 @@
import { BaseResponse } from "../../../models/response/base.response";
import { PaymentMethodType } from "../../enums/payment-method-type";
import { TransactionType } from "../../enums/transaction-type";
import { PaymentMethodType, TransactionType } from "../../enums";
export class BillingResponse extends BaseResponse {
balance: number;

View File

@@ -1,6 +1,6 @@
import { ProductType } from "../../../enums/productType";
import { ProductType } from "../../../enums";
import { BaseResponse } from "../../../models/response/base.response";
import { PlanType } from "../../enums/plan-type";
import { PlanType } from "../../enums";
export class PlanResponse extends BaseResponse {
type: PlanType;

View File

@@ -75,7 +75,7 @@ export class BillingSubscriptionItemResponse extends BaseResponse {
export class BillingSubscriptionUpcomingInvoiceResponse extends BaseResponse {
date: string;
amount: number;
amount?: number;
constructor(response: any) {
super(response);

Some files were not shown because too many files have changed in this diff Show More