mirror of
https://github.com/bitwarden/browser
synced 2026-03-01 02:51:24 +00:00
Merge branch 'main' of https://github.com/bitwarden/clients into innovation/user-achievements/event-stream-prototype
This commit is contained in:
@@ -38,7 +38,6 @@ import {
|
||||
ProviderUserUserDetailsResponse,
|
||||
} from "../admin-console/models/response/provider/provider-user.response";
|
||||
import { SelectionReadOnlyResponse } from "../admin-console/models/response/selection-read-only.response";
|
||||
import { AuthRequest } from "../auth/models/request/auth.request";
|
||||
import { DeviceVerificationRequest } from "../auth/models/request/device-verification.request";
|
||||
import { DisableTwoFactorAuthenticatorRequest } from "../auth/models/request/disable-two-factor-authenticator.request";
|
||||
import { EmailTokenRequest } from "../auth/models/request/email-token.request";
|
||||
@@ -47,19 +46,12 @@ import { PasswordTokenRequest } from "../auth/models/request/identity-token/pass
|
||||
import { SsoTokenRequest } from "../auth/models/request/identity-token/sso-token.request";
|
||||
import { UserApiTokenRequest } from "../auth/models/request/identity-token/user-api-token.request";
|
||||
import { WebAuthnLoginTokenRequest } from "../auth/models/request/identity-token/webauthn-login-token.request";
|
||||
import { KeyConnectorUserKeyRequest } from "../auth/models/request/key-connector-user-key.request";
|
||||
import { PasswordHintRequest } from "../auth/models/request/password-hint.request";
|
||||
import { PasswordRequest } from "../auth/models/request/password.request";
|
||||
import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-auth.request";
|
||||
import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request";
|
||||
import { SetKeyConnectorKeyRequest } from "../auth/models/request/set-key-connector-key.request";
|
||||
import { SetPasswordRequest } from "../auth/models/request/set-password.request";
|
||||
import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request";
|
||||
import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request";
|
||||
import { TwoFactorRecoveryRequest } from "../auth/models/request/two-factor-recovery.request";
|
||||
import { UpdateProfileRequest } from "../auth/models/request/update-profile.request";
|
||||
import { UpdateTdeOffboardingPasswordRequest } from "../auth/models/request/update-tde-offboarding-password.request";
|
||||
import { UpdateTempPasswordRequest } from "../auth/models/request/update-temp-password.request";
|
||||
import { UpdateTwoFactorAuthenticatorRequest } from "../auth/models/request/update-two-factor-authenticator.request";
|
||||
import { UpdateTwoFactorDuoRequest } from "../auth/models/request/update-two-factor-duo.request";
|
||||
import { UpdateTwoFactorEmailRequest } from "../auth/models/request/update-two-factor-email.request";
|
||||
@@ -96,6 +88,8 @@ import { PaymentResponse } from "../billing/models/response/payment.response";
|
||||
import { PlanResponse } from "../billing/models/response/plan.response";
|
||||
import { SubscriptionResponse } from "../billing/models/response/subscription.response";
|
||||
import { TaxInfoResponse } from "../billing/models/response/tax-info.response";
|
||||
import { KeyConnectorUserKeyRequest } from "../key-management/key-connector/models/key-connector-user-key.request";
|
||||
import { SetKeyConnectorKeyRequest } from "../key-management/key-connector/models/set-key-connector-key.request";
|
||||
import { DeleteRecoverRequest } from "../models/request/delete-recover.request";
|
||||
import { EventRequest } from "../models/request/event.request";
|
||||
import { KdfRequest } from "../models/request/kdf.request";
|
||||
@@ -169,8 +163,6 @@ export abstract class ApiService {
|
||||
postPrelogin: (request: PreloginRequest) => Promise<PreloginResponse>;
|
||||
postEmailToken: (request: EmailTokenRequest) => Promise<any>;
|
||||
postEmail: (request: EmailRequest) => Promise<any>;
|
||||
postPassword: (request: PasswordRequest) => Promise<any>;
|
||||
setPassword: (request: SetPasswordRequest) => Promise<any>;
|
||||
postSetKeyConnectorKey: (request: SetKeyConnectorKeyRequest) => Promise<any>;
|
||||
postSecurityStamp: (request: SecretVerificationRequest) => Promise<any>;
|
||||
getAccountRevisionDate: () => Promise<number>;
|
||||
@@ -189,13 +181,8 @@ export abstract class ApiService {
|
||||
postAccountKdf: (request: KdfRequest) => Promise<any>;
|
||||
postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||
putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise<any>;
|
||||
putUpdateTdeOffboardingPassword: (request: UpdateTdeOffboardingPasswordRequest) => Promise<any>;
|
||||
postConvertToKeyConnector: () => Promise<void>;
|
||||
//passwordless
|
||||
postAuthRequest: (request: AuthRequest) => Promise<AuthRequestResponse>;
|
||||
postAdminAuthRequest: (request: AuthRequest) => Promise<AuthRequestResponse>;
|
||||
getAuthResponse: (id: string, accessCode: string) => Promise<AuthRequestResponse>;
|
||||
getAuthRequest: (id: string) => Promise<AuthRequestResponse>;
|
||||
putAuthRequest: (id: string, request: PasswordlessAuthRequest) => Promise<AuthRequestResponse>;
|
||||
getAuthRequests: () => Promise<ListResponse<AuthRequestResponse>>;
|
||||
@@ -356,7 +343,6 @@ export abstract class ApiService {
|
||||
organizationId: string,
|
||||
request: TwoFactorProviderRequest,
|
||||
) => Promise<TwoFactorProviderResponse>;
|
||||
postTwoFactorRecover: (request: TwoFactorRecoveryRequest) => Promise<any>;
|
||||
postTwoFactorEmailSetup: (request: TwoFactorEmailRequest) => Promise<any>;
|
||||
postTwoFactorEmail: (request: TwoFactorEmailRequest) => Promise<any>;
|
||||
getDeviceVerificationSettings: () => Promise<DeviceVerificationResponse>;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { PasswordRequest } from "../models/request/password.request";
|
||||
import { SetPasswordRequest } from "../models/request/set-password.request";
|
||||
import { UpdateTdeOffboardingPasswordRequest } from "../models/request/update-tde-offboarding-password.request";
|
||||
import { UpdateTempPasswordRequest } from "../models/request/update-temp-password.request";
|
||||
|
||||
export abstract class MasterPasswordApiService {
|
||||
/**
|
||||
* POSTs a SetPasswordRequest to "/accounts/set-password"
|
||||
*/
|
||||
abstract setPassword: (request: SetPasswordRequest) => Promise<any>;
|
||||
|
||||
/**
|
||||
* POSTs a PasswordRequest to "/accounts/password"
|
||||
*/
|
||||
abstract postPassword: (request: PasswordRequest) => Promise<any>;
|
||||
|
||||
/**
|
||||
* PUTs an UpdateTempPasswordRequest to "/accounts/update-temp-password"
|
||||
*/
|
||||
abstract putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise<any>;
|
||||
|
||||
/**
|
||||
* PUTs an UpdateTdeOffboardingPasswordRequest to "/accounts/update-tde-offboarding-password"
|
||||
*/
|
||||
abstract putUpdateTdeOffboardingPassword: (
|
||||
request: UpdateTdeOffboardingPasswordRequest,
|
||||
) => Promise<any>;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { SecretVerificationRequest } from "./secret-verification.request";
|
||||
|
||||
export class TwoFactorRecoveryRequest extends SecretVerificationRequest {
|
||||
recoveryCode: string;
|
||||
email: string;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request";
|
||||
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
|
||||
import { View } from "@bitwarden/common/models/view/view";
|
||||
|
||||
export class LoginViaAuthRequestView implements View {
|
||||
authRequest: AuthRequest | undefined = undefined;
|
||||
authRequestResponse: AuthRequestResponse | undefined = undefined;
|
||||
fingerprintPhrase: string | undefined = undefined;
|
||||
privateKey: string | undefined = undefined;
|
||||
publicKey: string | undefined = undefined;
|
||||
|
||||
static fromJSON(obj: Partial<Jsonify<LoginViaAuthRequestView>>): LoginViaAuthRequestView {
|
||||
return Object.assign(new LoginViaAuthRequestView(), obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
|
||||
import { MasterPasswordApiService as MasterPasswordApiServiceAbstraction } from "../../abstractions/master-password-api.service.abstraction";
|
||||
import { PasswordRequest } from "../../models/request/password.request";
|
||||
import { SetPasswordRequest } from "../../models/request/set-password.request";
|
||||
import { UpdateTdeOffboardingPasswordRequest } from "../../models/request/update-tde-offboarding-password.request";
|
||||
import { UpdateTempPasswordRequest } from "../../models/request/update-temp-password.request";
|
||||
|
||||
export class MasterPasswordApiService implements MasterPasswordApiServiceAbstraction {
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private logService: LogService,
|
||||
) {}
|
||||
|
||||
async setPassword(request: SetPasswordRequest): Promise<any> {
|
||||
try {
|
||||
const response = await this.apiService.send(
|
||||
"POST",
|
||||
"/accounts/set-password",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (e: unknown) {
|
||||
this.logService.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async postPassword(request: PasswordRequest): Promise<any> {
|
||||
try {
|
||||
const response = await this.apiService.send(
|
||||
"POST",
|
||||
"/accounts/password",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (e: unknown) {
|
||||
this.logService.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async putUpdateTempPassword(request: UpdateTempPasswordRequest): Promise<any> {
|
||||
try {
|
||||
const response = await this.apiService.send(
|
||||
"PUT",
|
||||
"/accounts/update-temp-password",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (e: unknown) {
|
||||
this.logService.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async putUpdateTdeOffboardingPassword(
|
||||
request: UpdateTdeOffboardingPasswordRequest,
|
||||
): Promise<any> {
|
||||
try {
|
||||
const response = await this.apiService.send(
|
||||
"PUT",
|
||||
"/accounts/update-tde-offboarding-password",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (e: unknown) {
|
||||
this.logService.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { KdfType } from "@bitwarden/key-management";
|
||||
|
||||
import { PasswordRequest } from "../../models/request/password.request";
|
||||
import { SetPasswordRequest } from "../../models/request/set-password.request";
|
||||
import { UpdateTdeOffboardingPasswordRequest } from "../../models/request/update-tde-offboarding-password.request";
|
||||
import { UpdateTempPasswordRequest } from "../../models/request/update-temp-password.request";
|
||||
|
||||
import { MasterPasswordApiService } from "./master-password-api.service.implementation";
|
||||
|
||||
describe("MasterPasswordApiService", () => {
|
||||
let apiService: MockProxy<ApiService>;
|
||||
let logService: MockProxy<LogService>;
|
||||
|
||||
let sut: MasterPasswordApiService;
|
||||
|
||||
beforeEach(() => {
|
||||
apiService = mock<ApiService>();
|
||||
logService = mock<LogService>();
|
||||
|
||||
sut = new MasterPasswordApiService(apiService, logService);
|
||||
});
|
||||
|
||||
it("should instantiate", () => {
|
||||
expect(sut).not.toBeFalsy();
|
||||
});
|
||||
|
||||
describe("setPassword", () => {
|
||||
it("should call apiService.send with the correct parameters", async () => {
|
||||
// Arrange
|
||||
const request = new SetPasswordRequest(
|
||||
"masterPasswordHash",
|
||||
"key",
|
||||
"masterPasswordHint",
|
||||
"orgIdentifier",
|
||||
{
|
||||
publicKey: "publicKey",
|
||||
encryptedPrivateKey: "encryptedPrivateKey",
|
||||
},
|
||||
KdfType.PBKDF2_SHA256,
|
||||
600_000,
|
||||
);
|
||||
|
||||
// Act
|
||||
await sut.setPassword(request);
|
||||
|
||||
// Assert
|
||||
expect(apiService.send).toHaveBeenCalledWith(
|
||||
"POST",
|
||||
"/accounts/set-password",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("postPassword", () => {
|
||||
it("should call apiService.send with the correct parameters", async () => {
|
||||
// Arrange
|
||||
const request = {
|
||||
newMasterPasswordHash: "newMasterPasswordHash",
|
||||
masterPasswordHint: "masterPasswordHint",
|
||||
key: "key",
|
||||
masterPasswordHash: "masterPasswordHash",
|
||||
} as PasswordRequest;
|
||||
|
||||
// Act
|
||||
await sut.postPassword(request);
|
||||
|
||||
// Assert
|
||||
expect(apiService.send).toHaveBeenCalledWith(
|
||||
"POST",
|
||||
"/accounts/password",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("putUpdateTempPassword", () => {
|
||||
it("should call apiService.send with the correct parameters", async () => {
|
||||
// Arrange
|
||||
const request = {
|
||||
masterPasswordHint: "masterPasswordHint",
|
||||
newMasterPasswordHash: "newMasterPasswordHash",
|
||||
key: "key",
|
||||
} as UpdateTempPasswordRequest;
|
||||
|
||||
// Act
|
||||
await sut.putUpdateTempPassword(request);
|
||||
|
||||
// Assert
|
||||
expect(apiService.send).toHaveBeenCalledWith(
|
||||
"PUT",
|
||||
"/accounts/update-temp-password",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("putUpdateTdeOffboardingPassword", () => {
|
||||
it("should call apiService.send with the correct parameters", async () => {
|
||||
// Arrange
|
||||
const request = {
|
||||
masterPasswordHint: "masterPasswordHint",
|
||||
newMasterPasswordHash: "newMasterPasswordHash",
|
||||
key: "key",
|
||||
} as UpdateTdeOffboardingPasswordRequest;
|
||||
|
||||
// Act
|
||||
await sut.putUpdateTdeOffboardingPassword(request);
|
||||
|
||||
// Assert
|
||||
expect(apiService.send).toHaveBeenCalledWith(
|
||||
"PUT",
|
||||
"/accounts/update-tde-offboarding-password",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,13 +16,13 @@ import {
|
||||
} from "@bitwarden/key-management";
|
||||
|
||||
import { FakeAccountService, mockAccountServiceWith } from "../../../../spec";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../../key-management/master-password/abstractions/master-password.service.abstraction";
|
||||
import { VaultTimeoutSettingsService } from "../../../key-management/vault-timeout";
|
||||
import { I18nService } from "../../../platform/abstractions/i18n.service";
|
||||
import { HashPurpose } from "../../../platform/enums";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { MasterKey } from "../../../types/key";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../abstractions/master-password.service.abstraction";
|
||||
import { UserVerificationApiServiceAbstraction } from "../../abstractions/user-verification/user-verification-api.service.abstraction";
|
||||
import { VerificationType } from "../../enums/verification-type";
|
||||
import { MasterPasswordPolicyResponse } from "../../models/response/master-password-policy.response";
|
||||
|
||||
@@ -13,11 +13,11 @@ import {
|
||||
// FIXME: remove `src` and fix import
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { PinServiceAbstraction } from "../../../../../auth/src/common/abstractions/pin.service.abstraction";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../../key-management/master-password/abstractions/master-password.service.abstraction";
|
||||
import { I18nService } from "../../../platform/abstractions/i18n.service";
|
||||
import { HashPurpose } from "../../../platform/enums";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { AccountService } from "../../abstractions/account.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../abstractions/master-password.service.abstraction";
|
||||
import { UserVerificationApiServiceAbstraction } from "../../abstractions/user-verification/user-verification-api.service.abstraction";
|
||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "../../abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { VerificationType } from "../../enums/verification-type";
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
|
||||
|
||||
import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request";
|
||||
import { ProviderOrganizationOrganizationDetailsResponse } from "../../admin-console/models/response/provider/provider-organization.response";
|
||||
import { SubscriptionCancellationRequest } from "../../billing/models/request/subscription-cancellation.request";
|
||||
@@ -50,6 +52,8 @@ export abstract class BillingApiServiceAbstraction {
|
||||
|
||||
getProviderSubscription: (providerId: string) => Promise<ProviderSubscriptionResponse>;
|
||||
|
||||
getProviderTaxInformation: (providerId: string) => Promise<TaxInfoResponse>;
|
||||
|
||||
updateOrganizationPaymentMethod: (
|
||||
organizationId: string,
|
||||
request: UpdatePaymentMethodRequest,
|
||||
@@ -66,6 +70,11 @@ export abstract class BillingApiServiceAbstraction {
|
||||
request: UpdateClientOrganizationRequest,
|
||||
) => Promise<any>;
|
||||
|
||||
updateProviderPaymentMethod: (
|
||||
providerId: string,
|
||||
request: UpdatePaymentMethodRequest,
|
||||
) => Promise<void>;
|
||||
|
||||
updateProviderTaxInformation: (
|
||||
providerId: string,
|
||||
request: ExpandedTaxInfoUpdateRequest,
|
||||
@@ -76,6 +85,11 @@ export abstract class BillingApiServiceAbstraction {
|
||||
request: VerifyBankAccountRequest,
|
||||
) => Promise<void>;
|
||||
|
||||
verifyProviderBankAccount: (
|
||||
providerId: string,
|
||||
request: VerifyBankAccountRequest,
|
||||
) => Promise<void>;
|
||||
|
||||
restartSubscription: (
|
||||
organizationId: string,
|
||||
request: OrganizationCreateRequest,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response";
|
||||
|
||||
import { ProviderType } from "../../../admin-console/enums";
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
import { PlanType, ProductTierType } from "../../enums";
|
||||
@@ -16,6 +18,7 @@ export class ProviderSubscriptionResponse extends BaseResponse {
|
||||
cancelAt?: string;
|
||||
suspension?: SubscriptionSuspensionResponse;
|
||||
providerType: ProviderType;
|
||||
paymentSource?: PaymentSourceResponse;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
@@ -38,6 +41,10 @@ export class ProviderSubscriptionResponse extends BaseResponse {
|
||||
this.suspension = new SubscriptionSuspensionResponse(suspension);
|
||||
}
|
||||
this.providerType = this.getResponseProperty("providerType");
|
||||
const paymentSource = this.getResponseProperty("paymentSource");
|
||||
if (paymentSource != null) {
|
||||
this.paymentSource = new PaymentSourceResponse(paymentSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
|
||||
import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response";
|
||||
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { OrganizationCreateRequest } from "../../admin-console/models/request/organization-create.request";
|
||||
import { ProviderOrganizationOrganizationDetailsResponse } from "../../admin-console/models/response/provider/provider-organization.response";
|
||||
@@ -143,6 +145,17 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
||||
return new ProviderSubscriptionResponse(response);
|
||||
}
|
||||
|
||||
async getProviderTaxInformation(providerId: string): Promise<TaxInfoResponse> {
|
||||
const response = await this.apiService.send(
|
||||
"GET",
|
||||
"/providers/" + providerId + "/billing/tax-information",
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
);
|
||||
return new TaxInfoResponse(response);
|
||||
}
|
||||
|
||||
async updateOrganizationPaymentMethod(
|
||||
organizationId: string,
|
||||
request: UpdatePaymentMethodRequest,
|
||||
@@ -183,6 +196,19 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
||||
);
|
||||
}
|
||||
|
||||
async updateProviderPaymentMethod(
|
||||
providerId: string,
|
||||
request: UpdatePaymentMethodRequest,
|
||||
): Promise<void> {
|
||||
return await this.apiService.send(
|
||||
"PUT",
|
||||
"/providers/" + providerId + "/billing/payment-method",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
async updateProviderTaxInformation(providerId: string, request: ExpandedTaxInfoUpdateRequest) {
|
||||
return await this.apiService.send(
|
||||
"PUT",
|
||||
@@ -206,6 +232,19 @@ export class BillingApiService implements BillingApiServiceAbstraction {
|
||||
);
|
||||
}
|
||||
|
||||
async verifyProviderBankAccount(
|
||||
providerId: string,
|
||||
request: VerifyBankAccountRequest,
|
||||
): Promise<void> {
|
||||
return await this.apiService.send(
|
||||
"POST",
|
||||
"/providers/" + providerId + "/billing/payment-method/verify-bank-account",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
async restartSubscription(
|
||||
organizationId: string,
|
||||
request: OrganizationCreateRequest,
|
||||
|
||||
@@ -26,23 +26,30 @@ export enum FeatureFlag {
|
||||
CriticalApps = "pm-14466-risk-insights-critical-application",
|
||||
EnableRiskInsightsNotifications = "enable-risk-insights-notifications",
|
||||
DesktopSendUIRefresh = "desktop-send-ui-refresh",
|
||||
ExportAttachments = "export-attachments",
|
||||
|
||||
/* Vault */
|
||||
PM8851_BrowserOnboardingNudge = "pm-8851-browser-onboarding-nudge",
|
||||
PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form",
|
||||
NewDeviceVerificationTemporaryDismiss = "new-device-temporary-dismiss",
|
||||
NewDeviceVerificationPermanentDismiss = "new-device-permanent-dismiss",
|
||||
VaultBulkManagementAction = "vault-bulk-management-action",
|
||||
SecurityTasks = "security-tasks",
|
||||
|
||||
/* Auth */
|
||||
PM9112_DeviceApprovalPersistence = "pm-9112-device-approval-persistence",
|
||||
|
||||
PM4154_BulkEncryptionService = "PM-4154-bulk-encryption-service",
|
||||
VaultBulkManagementAction = "vault-bulk-management-action",
|
||||
UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh",
|
||||
CipherKeyEncryption = "cipher-key-encryption",
|
||||
TrialPaymentOptional = "PM-8163-trial-payment",
|
||||
SecurityTasks = "security-tasks",
|
||||
NewDeviceVerificationTemporaryDismiss = "new-device-temporary-dismiss",
|
||||
NewDeviceVerificationPermanentDismiss = "new-device-permanent-dismiss",
|
||||
MacOsNativeCredentialSync = "macos-native-credential-sync",
|
||||
PM9111ExtensionPersistAddEditForm = "pm-9111-extension-persist-add-edit-form",
|
||||
PrivateKeyRegeneration = "pm-12241-private-key-regeneration",
|
||||
ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs",
|
||||
AccountDeprovisioningBanner = "pm-17120-account-deprovisioning-admin-console-banner",
|
||||
PM15179_AddExistingOrgsFromProviderPortal = "pm-15179-add-existing-orgs-from-provider-portal",
|
||||
RecoveryCodeLogin = "pm-17128-recovery-code-login",
|
||||
PM12276_BreadcrumbEventLogs = "pm-12276-breadcrumbing-for-business-features",
|
||||
PM18794_ProviderPaymentMethod = "pm-18794-provider-payment-method",
|
||||
}
|
||||
|
||||
export type AllowedFeatureFlagTypes = boolean | number | string;
|
||||
@@ -79,23 +86,30 @@ export const DefaultFeatureFlagValue = {
|
||||
[FeatureFlag.CriticalApps]: FALSE,
|
||||
[FeatureFlag.EnableRiskInsightsNotifications]: FALSE,
|
||||
[FeatureFlag.DesktopSendUIRefresh]: FALSE,
|
||||
[FeatureFlag.ExportAttachments]: FALSE,
|
||||
|
||||
/* Vault */
|
||||
[FeatureFlag.PM8851_BrowserOnboardingNudge]: FALSE,
|
||||
[FeatureFlag.PM9111ExtensionPersistAddEditForm]: FALSE,
|
||||
[FeatureFlag.NewDeviceVerificationTemporaryDismiss]: FALSE,
|
||||
[FeatureFlag.NewDeviceVerificationPermanentDismiss]: FALSE,
|
||||
[FeatureFlag.VaultBulkManagementAction]: FALSE,
|
||||
[FeatureFlag.SecurityTasks]: FALSE,
|
||||
|
||||
/* Auth */
|
||||
[FeatureFlag.PM9112_DeviceApprovalPersistence]: FALSE,
|
||||
|
||||
[FeatureFlag.PM4154_BulkEncryptionService]: FALSE,
|
||||
[FeatureFlag.VaultBulkManagementAction]: FALSE,
|
||||
[FeatureFlag.UnauthenticatedExtensionUIRefresh]: FALSE,
|
||||
[FeatureFlag.CipherKeyEncryption]: FALSE,
|
||||
[FeatureFlag.TrialPaymentOptional]: FALSE,
|
||||
[FeatureFlag.SecurityTasks]: FALSE,
|
||||
[FeatureFlag.NewDeviceVerificationTemporaryDismiss]: FALSE,
|
||||
[FeatureFlag.NewDeviceVerificationPermanentDismiss]: FALSE,
|
||||
[FeatureFlag.MacOsNativeCredentialSync]: FALSE,
|
||||
[FeatureFlag.PM9111ExtensionPersistAddEditForm]: FALSE,
|
||||
[FeatureFlag.PrivateKeyRegeneration]: FALSE,
|
||||
[FeatureFlag.ResellerManagedOrgAlert]: FALSE,
|
||||
[FeatureFlag.AccountDeprovisioningBanner]: FALSE,
|
||||
[FeatureFlag.PM15179_AddExistingOrgsFromProviderPortal]: FALSE,
|
||||
[FeatureFlag.RecoveryCodeLogin]: FALSE,
|
||||
[FeatureFlag.PM12276_BreadcrumbEventLogs]: FALSE,
|
||||
[FeatureFlag.PM18794_ProviderPaymentMethod]: FALSE,
|
||||
} satisfies Record<FeatureFlag, AllowedFeatureFlagTypes>;
|
||||
|
||||
export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue;
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
// @ts-strict-ignore
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { EncString } from "../../platform/models/domain/enc-string";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { DeviceKey, UserKey } from "../../types/key";
|
||||
|
||||
import { DeviceResponse } from "./devices/responses/device.response";
|
||||
import { DeviceResponse } from "../../../auth/abstractions/devices/responses/device.response";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { DeviceKey, UserKey } from "../../../types/key";
|
||||
|
||||
export abstract class DeviceTrustServiceAbstraction {
|
||||
/**
|
||||
@@ -5,30 +5,30 @@ import { firstValueFrom, map, Observable, Subject } from "rxjs";
|
||||
import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service";
|
||||
import { AppIdService } from "../../platform/abstractions/app-id.service";
|
||||
import { ConfigService } from "../../platform/abstractions/config/config.service";
|
||||
import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service";
|
||||
import { I18nService } from "../../platform/abstractions/i18n.service";
|
||||
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
|
||||
import { AbstractStorageService } from "../../platform/abstractions/storage.service";
|
||||
import { StorageLocation } from "../../platform/enums";
|
||||
import { EncString } from "../../platform/models/domain/enc-string";
|
||||
import { StorageOptions } from "../../platform/models/domain/storage-options";
|
||||
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
|
||||
import { DEVICE_TRUST_DISK_LOCAL, StateProvider, UserKeyDefinition } from "../../platform/state";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { UserKey, DeviceKey } from "../../types/key";
|
||||
import { DeviceTrustServiceAbstraction } from "../abstractions/device-trust.service.abstraction";
|
||||
import { DeviceResponse } from "../abstractions/devices/responses/device.response";
|
||||
import { DevicesApiServiceAbstraction } from "../abstractions/devices-api.service.abstraction";
|
||||
import { SecretVerificationRequest } from "../models/request/secret-verification.request";
|
||||
import { DeviceResponse } from "../../../auth/abstractions/devices/responses/device.response";
|
||||
import { DevicesApiServiceAbstraction } from "../../../auth/abstractions/devices-api.service.abstraction";
|
||||
import { SecretVerificationRequest } from "../../../auth/models/request/secret-verification.request";
|
||||
import {
|
||||
DeviceKeysUpdateRequest,
|
||||
UpdateDevicesTrustRequest,
|
||||
} from "../models/request/update-devices-trust.request";
|
||||
} from "../../../auth/models/request/update-devices-trust.request";
|
||||
import { AppIdService } from "../../../platform/abstractions/app-id.service";
|
||||
import { ConfigService } from "../../../platform/abstractions/config/config.service";
|
||||
import { CryptoFunctionService } from "../../../platform/abstractions/crypto-function.service";
|
||||
import { I18nService } from "../../../platform/abstractions/i18n.service";
|
||||
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
|
||||
import { AbstractStorageService } from "../../../platform/abstractions/storage.service";
|
||||
import { StorageLocation } from "../../../platform/enums";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { StorageOptions } from "../../../platform/models/domain/storage-options";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { DEVICE_TRUST_DISK_LOCAL, StateProvider, UserKeyDefinition } from "../../../platform/state";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { UserKey, DeviceKey } from "../../../types/key";
|
||||
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
|
||||
import { DeviceTrustServiceAbstraction } from "../abstractions/device-trust.service.abstraction";
|
||||
|
||||
/** Uses disk storage so that the device key can persist after log out and tab removal. */
|
||||
export const DEVICE_KEY = new UserKeyDefinition<DeviceKey | null>(
|
||||
@@ -3,38 +3,38 @@
|
||||
import { matches, mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject, firstValueFrom, of } from "rxjs";
|
||||
|
||||
import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import {
|
||||
UserDecryptionOptionsServiceAbstraction,
|
||||
UserDecryptionOptions,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
// FIXME: remove `src` and fix import
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { UserDecryptionOptions } from "../../../../auth/src/common/models/domain/user-decryption-options";
|
||||
import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service";
|
||||
import { FakeActiveUserState } from "../../../spec/fake-state";
|
||||
import { FakeStateProvider } from "../../../spec/fake-state-provider";
|
||||
import { DeviceType } from "../../enums";
|
||||
import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service";
|
||||
import { AppIdService } from "../../platform/abstractions/app-id.service";
|
||||
import { ConfigService } from "../../platform/abstractions/config/config.service";
|
||||
import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service";
|
||||
import { I18nService } from "../../platform/abstractions/i18n.service";
|
||||
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
|
||||
import { AbstractStorageService } from "../../platform/abstractions/storage.service";
|
||||
import { StorageLocation } from "../../platform/enums";
|
||||
import { EncryptionType } from "../../platform/enums/encryption-type.enum";
|
||||
import { Utils } from "../../platform/misc/utils";
|
||||
import { EncString } from "../../platform/models/domain/enc-string";
|
||||
import { StorageOptions } from "../../platform/models/domain/storage-options";
|
||||
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
|
||||
import { CsprngArray } from "../../types/csprng";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { DeviceKey, UserKey } from "../../types/key";
|
||||
import { DeviceResponse } from "../abstractions/devices/responses/device.response";
|
||||
import { DevicesApiServiceAbstraction } from "../abstractions/devices-api.service.abstraction";
|
||||
import { UpdateDevicesTrustRequest } from "../models/request/update-devices-trust.request";
|
||||
import { ProtectedDeviceResponse } from "../models/response/protected-device.response";
|
||||
import { FakeAccountService, mockAccountServiceWith } from "../../../../spec/fake-account-service";
|
||||
import { FakeActiveUserState } from "../../../../spec/fake-state";
|
||||
import { FakeStateProvider } from "../../../../spec/fake-state-provider";
|
||||
import { DeviceResponse } from "../../../auth/abstractions/devices/responses/device.response";
|
||||
import { DevicesApiServiceAbstraction } from "../../../auth/abstractions/devices-api.service.abstraction";
|
||||
import { UpdateDevicesTrustRequest } from "../../../auth/models/request/update-devices-trust.request";
|
||||
import { ProtectedDeviceResponse } from "../../../auth/models/response/protected-device.response";
|
||||
import { DeviceType } from "../../../enums";
|
||||
import { AppIdService } from "../../../platform/abstractions/app-id.service";
|
||||
import { ConfigService } from "../../../platform/abstractions/config/config.service";
|
||||
import { CryptoFunctionService } from "../../../platform/abstractions/crypto-function.service";
|
||||
import { I18nService } from "../../../platform/abstractions/i18n.service";
|
||||
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
|
||||
import { AbstractStorageService } from "../../../platform/abstractions/storage.service";
|
||||
import { StorageLocation } from "../../../platform/enums";
|
||||
import { EncryptionType } from "../../../platform/enums/encryption-type.enum";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { StorageOptions } from "../../../platform/models/domain/storage-options";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { CsprngArray } from "../../../types/csprng";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { DeviceKey, UserKey } from "../../../types/key";
|
||||
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
|
||||
|
||||
import {
|
||||
SHOULD_TRUST_DEVICE,
|
||||
@@ -1,8 +1,8 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Organization } from "../../admin-console/models/domain/organization";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { IdentityTokenResponse } from "../models/response/identity-token.response";
|
||||
import { Organization } from "../../../admin-console/models/domain/organization";
|
||||
import { IdentityTokenResponse } from "../../../auth/models/response/identity-token.response";
|
||||
import { UserId } from "../../../types/guid";
|
||||
|
||||
export abstract class KeyConnectorService {
|
||||
setMasterKeyFromUrl: (url: string, userId: UserId) => Promise<void>;
|
||||
@@ -4,27 +4,27 @@ import { of } from "rxjs";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from "../../../spec";
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { OrganizationData } from "../../admin-console/models/data/organization.data";
|
||||
import { Organization } from "../../admin-console/models/domain/organization";
|
||||
import { ProfileOrganizationResponse } from "../../admin-console/models/response/profile-organization.response";
|
||||
import { LogService } from "../../platform/abstractions/log.service";
|
||||
import { Utils } from "../../platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
|
||||
import { KeyGenerationService } from "../../platform/services/key-generation.service";
|
||||
import { OrganizationId, UserId } from "../../types/guid";
|
||||
import { MasterKey } from "../../types/key";
|
||||
import { KeyConnectorUserKeyRequest } from "../models/request/key-connector-user-key.request";
|
||||
import { KeyConnectorUserKeyResponse } from "../models/response/key-connector-user-key.response";
|
||||
import { FakeAccountService, FakeStateProvider, mockAccountServiceWith } from "../../../../spec";
|
||||
import { ApiService } from "../../../abstractions/api.service";
|
||||
import { OrganizationData } from "../../../admin-console/models/data/organization.data";
|
||||
import { Organization } from "../../../admin-console/models/domain/organization";
|
||||
import { ProfileOrganizationResponse } from "../../../admin-console/models/response/profile-organization.response";
|
||||
import { KeyConnectorUserKeyResponse } from "../../../auth/models/response/key-connector-user-key.response";
|
||||
import { TokenService } from "../../../auth/services/token.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import { KeyGenerationService } from "../../../platform/services/key-generation.service";
|
||||
import { OrganizationId, UserId } from "../../../types/guid";
|
||||
import { MasterKey } from "../../../types/key";
|
||||
import { FakeMasterPasswordService } from "../../master-password/services/fake-master-password.service";
|
||||
import { KeyConnectorUserKeyRequest } from "../models/key-connector-user-key.request";
|
||||
|
||||
import {
|
||||
USES_KEY_CONNECTOR,
|
||||
CONVERT_ACCOUNT_TO_KEY_CONNECTOR,
|
||||
KeyConnectorService,
|
||||
} from "./key-connector.service";
|
||||
import { FakeMasterPasswordService } from "./master-password/fake-master-password.service";
|
||||
import { TokenService } from "./token.service";
|
||||
|
||||
describe("KeyConnectorService", () => {
|
||||
let keyConnectorService: KeyConnectorService;
|
||||
@@ -3,8 +3,6 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { LogoutReason } from "@bitwarden/auth/common";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import {
|
||||
Argon2KdfConfig,
|
||||
KdfConfig,
|
||||
@@ -13,28 +11,30 @@ import {
|
||||
KdfType,
|
||||
} from "@bitwarden/key-management";
|
||||
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { OrganizationUserType } from "../../admin-console/enums";
|
||||
import { Organization } from "../../admin-console/models/domain/organization";
|
||||
import { KeysRequest } from "../../models/request/keys.request";
|
||||
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../platform/abstractions/log.service";
|
||||
import { Utils } from "../../platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
|
||||
import { ApiService } from "../../../abstractions/api.service";
|
||||
import { OrganizationService } from "../../../admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationUserType } from "../../../admin-console/enums";
|
||||
import { Organization } from "../../../admin-console/models/domain/organization";
|
||||
import { AccountService } from "../../../auth/abstractions/account.service";
|
||||
import { TokenService } from "../../../auth/abstractions/token.service";
|
||||
import { IdentityTokenResponse } from "../../../auth/models/response/identity-token.response";
|
||||
import { KeysRequest } from "../../../models/request/keys.request";
|
||||
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
|
||||
import {
|
||||
ActiveUserState,
|
||||
KEY_CONNECTOR_DISK,
|
||||
StateProvider,
|
||||
UserKeyDefinition,
|
||||
} from "../../platform/state";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { MasterKey } from "../../types/key";
|
||||
} from "../../../platform/state";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { MasterKey } from "../../../types/key";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../master-password/abstractions/master-password.service.abstraction";
|
||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "../abstractions/key-connector.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../abstractions/master-password.service.abstraction";
|
||||
import { TokenService } from "../abstractions/token.service";
|
||||
import { KeyConnectorUserKeyRequest } from "../models/request/key-connector-user-key.request";
|
||||
import { SetKeyConnectorKeyRequest } from "../models/request/set-key-connector-key.request";
|
||||
import { IdentityTokenResponse } from "../models/response/identity-token.response";
|
||||
import { KeyConnectorUserKeyRequest } from "../models/key-connector-user-key.request";
|
||||
import { SetKeyConnectorKeyRequest } from "../models/set-key-connector-key.request";
|
||||
|
||||
export const USES_KEY_CONNECTOR = new UserKeyDefinition<boolean | null>(
|
||||
KEY_CONNECTOR_DISK,
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { EncString } from "../../platform/models/domain/enc-string";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { MasterKey, UserKey } from "../../types/key";
|
||||
import { ForceSetPasswordReason } from "../models/domain/force-set-password-reason";
|
||||
import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { MasterKey, UserKey } from "../../../types/key";
|
||||
|
||||
export abstract class MasterPasswordServiceAbstraction {
|
||||
/**
|
||||
@@ -3,11 +3,11 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { ReplaySubject, Observable } from "rxjs";
|
||||
|
||||
import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason";
|
||||
import { EncString } from "../../../platform/models/domain/enc-string";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { MasterKey, UserKey } from "../../../types/key";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../abstractions/master-password.service.abstraction";
|
||||
import { ForceSetPasswordReason } from "../../models/domain/force-set-password-reason";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../abstractions/master-password.service.abstraction";
|
||||
|
||||
export class FakeMasterPasswordService implements InternalMasterPasswordServiceAbstraction {
|
||||
mock = mock<InternalMasterPasswordServiceAbstraction>();
|
||||
@@ -2,7 +2,7 @@
|
||||
// @ts-strict-ignore
|
||||
import { firstValueFrom, map, Observable } from "rxjs";
|
||||
|
||||
import { EncryptService } from "../../../key-management/crypto/abstractions/encrypt.service";
|
||||
import { ForceSetPasswordReason } from "../../../auth/models/domain/force-set-password-reason";
|
||||
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { StateService } from "../../../platform/abstractions/state.service";
|
||||
@@ -17,8 +17,8 @@ import {
|
||||
} from "../../../platform/state";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { MasterKey, UserKey } from "../../../types/key";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../abstractions/master-password.service.abstraction";
|
||||
import { ForceSetPasswordReason } from "../../models/domain/force-set-password-reason";
|
||||
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../abstractions/master-password.service.abstraction";
|
||||
|
||||
/** Memory since master key shouldn't be available on lock */
|
||||
const MASTER_KEY = new UserKeyDefinition<MasterKey>(MASTER_PASSWORD_MEMORY, "masterKey", {
|
||||
@@ -12,7 +12,6 @@ import { SearchService } from "../../../abstractions/search.service";
|
||||
import { AccountInfo } from "../../../auth/abstractions/account.service";
|
||||
import { AuthService } from "../../../auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "../../../auth/enums/authentication-status";
|
||||
import { FakeMasterPasswordService } from "../../../auth/services/master-password/fake-master-password.service";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { MessagingService } from "../../../platform/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
|
||||
@@ -23,6 +22,7 @@ import { StateEventRunnerService } from "../../../platform/state";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { CipherService } from "../../../vault/abstractions/cipher.service";
|
||||
import { FolderService } from "../../../vault/abstractions/folder/folder.service.abstraction";
|
||||
import { FakeMasterPasswordService } from "../../master-password/services/fake-master-password.service";
|
||||
import { VaultTimeoutAction } from "../enums/vault-timeout-action.enum";
|
||||
import { VaultTimeout, VaultTimeoutStringType } from "../types/vault-timeout.type";
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import { BiometricsService } from "@bitwarden/key-management";
|
||||
import { SearchService } from "../../../abstractions/search.service";
|
||||
import { AccountService } from "../../../auth/abstractions/account.service";
|
||||
import { AuthService } from "../../../auth/abstractions/auth.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../../auth/abstractions/master-password.service.abstraction";
|
||||
import { AuthenticationStatus } from "../../../auth/enums/authentication-status";
|
||||
import { LogService } from "../../../platform/abstractions/log.service";
|
||||
import { MessagingService } from "../../../platform/abstractions/messaging.service";
|
||||
@@ -20,6 +19,7 @@ import { StateEventRunnerService } from "../../../platform/state";
|
||||
import { UserId } from "../../../types/guid";
|
||||
import { CipherService } from "../../../vault/abstractions/cipher.service";
|
||||
import { FolderService } from "../../../vault/abstractions/folder/folder.service.abstraction";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../master-password/abstractions/master-password.service.abstraction";
|
||||
import { VaultTimeoutSettingsService } from "../abstractions/vault-timeout-settings.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "../abstractions/vault-timeout.service";
|
||||
import { VaultTimeoutAction } from "../enums/vault-timeout-action.enum";
|
||||
|
||||
@@ -131,6 +131,10 @@ export const THEMING_DISK = new StateDefinition("theming", "disk", { web: "disk-
|
||||
export const TRANSLATION_DISK = new StateDefinition("translation", "disk", { web: "disk-local" });
|
||||
export const ANIMATION_DISK = new StateDefinition("animation", "disk");
|
||||
export const TASK_SCHEDULER_DISK = new StateDefinition("taskScheduler", "disk");
|
||||
export const EXTENSION_INITIAL_INSTALL_DISK = new StateDefinition(
|
||||
"extensionInitialInstall",
|
||||
"disk",
|
||||
);
|
||||
|
||||
// Design System
|
||||
|
||||
|
||||
@@ -27,13 +27,13 @@ import { PolicyResponse } from "../../admin-console/models/response/policy.respo
|
||||
import { AccountService } from "../../auth/abstractions/account.service";
|
||||
import { AuthService } from "../../auth/abstractions/auth.service";
|
||||
import { AvatarService } from "../../auth/abstractions/avatar.service";
|
||||
import { KeyConnectorService } from "../../auth/abstractions/key-connector.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../auth/abstractions/master-password.service.abstraction";
|
||||
import { TokenService } from "../../auth/abstractions/token.service";
|
||||
import { AuthenticationStatus } from "../../auth/enums/authentication-status";
|
||||
import { ForceSetPasswordReason } from "../../auth/models/domain/force-set-password-reason";
|
||||
import { DomainSettingsService } from "../../autofill/services/domain-settings.service";
|
||||
import { BillingAccountProfileStateService } from "../../billing/abstractions";
|
||||
import { KeyConnectorService } from "../../key-management/key-connector/abstractions/key-connector.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "../../key-management/master-password/abstractions/master-password.service.abstraction";
|
||||
import { DomainsResponse } from "../../models/response/domains.response";
|
||||
import { ProfileResponse } from "../../models/response/profile.response";
|
||||
import { SendData } from "../../tools/send/models/data/send.data";
|
||||
|
||||
@@ -12,6 +12,7 @@ import { LogoutReason } from "@bitwarden/auth/common";
|
||||
|
||||
import { ApiService as ApiServiceAbstraction } from "../abstractions/api.service";
|
||||
import { OrganizationConnectionType } from "../admin-console/enums";
|
||||
import { CollectionBulkDeleteRequest } from "../admin-console/models/request/collection-bulk-delete.request";
|
||||
import { OrganizationSponsorshipCreateRequest } from "../admin-console/models/request/organization/organization-sponsorship-create.request";
|
||||
import { OrganizationSponsorshipRedeemRequest } from "../admin-console/models/request/organization/organization-sponsorship-redeem.request";
|
||||
import { OrganizationConnectionRequest } from "../admin-console/models/request/organization-connection.request";
|
||||
@@ -43,7 +44,6 @@ import {
|
||||
} from "../admin-console/models/response/provider/provider-user.response";
|
||||
import { SelectionReadOnlyResponse } from "../admin-console/models/response/selection-read-only.response";
|
||||
import { TokenService } from "../auth/abstractions/token.service";
|
||||
import { AuthRequest } from "../auth/models/request/auth.request";
|
||||
import { DeviceVerificationRequest } from "../auth/models/request/device-verification.request";
|
||||
import { DisableTwoFactorAuthenticatorRequest } from "../auth/models/request/disable-two-factor-authenticator.request";
|
||||
import { EmailTokenRequest } from "../auth/models/request/email-token.request";
|
||||
@@ -54,19 +54,12 @@ import { SsoTokenRequest } from "../auth/models/request/identity-token/sso-token
|
||||
import { TokenTwoFactorRequest } from "../auth/models/request/identity-token/token-two-factor.request";
|
||||
import { UserApiTokenRequest } from "../auth/models/request/identity-token/user-api-token.request";
|
||||
import { WebAuthnLoginTokenRequest } from "../auth/models/request/identity-token/webauthn-login-token.request";
|
||||
import { KeyConnectorUserKeyRequest } from "../auth/models/request/key-connector-user-key.request";
|
||||
import { PasswordHintRequest } from "../auth/models/request/password-hint.request";
|
||||
import { PasswordRequest } from "../auth/models/request/password.request";
|
||||
import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-auth.request";
|
||||
import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request";
|
||||
import { SetKeyConnectorKeyRequest } from "../auth/models/request/set-key-connector-key.request";
|
||||
import { SetPasswordRequest } from "../auth/models/request/set-password.request";
|
||||
import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request";
|
||||
import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request";
|
||||
import { TwoFactorRecoveryRequest } from "../auth/models/request/two-factor-recovery.request";
|
||||
import { UpdateProfileRequest } from "../auth/models/request/update-profile.request";
|
||||
import { UpdateTdeOffboardingPasswordRequest } from "../auth/models/request/update-tde-offboarding-password.request";
|
||||
import { UpdateTempPasswordRequest } from "../auth/models/request/update-temp-password.request";
|
||||
import { UpdateTwoFactorAuthenticatorRequest } from "../auth/models/request/update-two-factor-authenticator.request";
|
||||
import { UpdateTwoFactorDuoRequest } from "../auth/models/request/update-two-factor-duo.request";
|
||||
import { UpdateTwoFactorEmailRequest } from "../auth/models/request/update-two-factor-email.request";
|
||||
@@ -104,9 +97,10 @@ import { PlanResponse } from "../billing/models/response/plan.response";
|
||||
import { SubscriptionResponse } from "../billing/models/response/subscription.response";
|
||||
import { TaxInfoResponse } from "../billing/models/response/tax-info.response";
|
||||
import { DeviceType } from "../enums";
|
||||
import { KeyConnectorUserKeyRequest } from "../key-management/key-connector/models/key-connector-user-key.request";
|
||||
import { SetKeyConnectorKeyRequest } from "../key-management/key-connector/models/set-key-connector-key.request";
|
||||
import { VaultTimeoutSettingsService } from "../key-management/vault-timeout";
|
||||
import { VaultTimeoutAction } from "../key-management/vault-timeout/enums/vault-timeout-action.enum";
|
||||
import { CollectionBulkDeleteRequest } from "../models/request/collection-bulk-delete.request";
|
||||
import { DeleteRecoverRequest } from "../models/request/delete-recover.request";
|
||||
import { EventRequest } from "../models/request/event.request";
|
||||
import { KdfRequest } from "../models/request/kdf.request";
|
||||
@@ -279,22 +273,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
}
|
||||
|
||||
// TODO: PM-3519: Create and move to AuthRequest Api service
|
||||
// TODO: PM-9724: Remove legacy auth request methods when we remove legacy LoginViaAuthRequestV1Components
|
||||
async postAuthRequest(request: AuthRequest): Promise<AuthRequestResponse> {
|
||||
const r = await this.send("POST", "/auth-requests/", request, false, true);
|
||||
return new AuthRequestResponse(r);
|
||||
}
|
||||
async postAdminAuthRequest(request: AuthRequest): Promise<AuthRequestResponse> {
|
||||
const r = await this.send("POST", "/auth-requests/admin-request", request, true, true);
|
||||
return new AuthRequestResponse(r);
|
||||
}
|
||||
|
||||
async getAuthResponse(id: string, accessCode: string): Promise<AuthRequestResponse> {
|
||||
const path = `/auth-requests/${id}/response?code=${accessCode}`;
|
||||
const r = await this.send("GET", path, null, false, true);
|
||||
return new AuthRequestResponse(r);
|
||||
}
|
||||
|
||||
async getAuthRequest(id: string): Promise<AuthRequestResponse> {
|
||||
const path = `/auth-requests/${id}`;
|
||||
const r = await this.send("GET", path, null, true, true);
|
||||
@@ -374,14 +352,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return this.send("POST", "/accounts/email", request, true, false);
|
||||
}
|
||||
|
||||
postPassword(request: PasswordRequest): Promise<any> {
|
||||
return this.send("POST", "/accounts/password", request, true, false);
|
||||
}
|
||||
|
||||
setPassword(request: SetPasswordRequest): Promise<any> {
|
||||
return this.send("POST", "/accounts/set-password", request, true, false);
|
||||
}
|
||||
|
||||
postSetKeyConnectorKey(request: SetKeyConnectorKeyRequest): Promise<any> {
|
||||
return this.send("POST", "/accounts/set-key-connector-key", request, true, false);
|
||||
}
|
||||
@@ -479,14 +449,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return new ApiKeyResponse(r);
|
||||
}
|
||||
|
||||
putUpdateTempPassword(request: UpdateTempPasswordRequest): Promise<any> {
|
||||
return this.send("PUT", "/accounts/update-temp-password", request, true, false);
|
||||
}
|
||||
|
||||
putUpdateTdeOffboardingPassword(request: UpdateTdeOffboardingPasswordRequest): Promise<void> {
|
||||
return this.send("PUT", "/accounts/update-tde-offboarding-password", request, true, false);
|
||||
}
|
||||
|
||||
postConvertToKeyConnector(): Promise<void> {
|
||||
return this.send("POST", "/accounts/convert-to-key-connector", null, true, false);
|
||||
}
|
||||
@@ -1101,10 +1063,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return new TwoFactorProviderResponse(r);
|
||||
}
|
||||
|
||||
postTwoFactorRecover(request: TwoFactorRecoveryRequest): Promise<any> {
|
||||
return this.send("POST", "/two-factor/recover", request, false, false);
|
||||
}
|
||||
|
||||
postTwoFactorEmailSetup(request: TwoFactorEmailRequest): Promise<any> {
|
||||
return this.send("POST", "/two-factor/send-email", request, true, false);
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ describe("buildCipherIcon", () => {
|
||||
|
||||
expect(iconDetails).toEqual({
|
||||
icon: "bwi-globe",
|
||||
image: undefined,
|
||||
image: null,
|
||||
fallbackImage: "",
|
||||
imageEnabled: false,
|
||||
});
|
||||
@@ -102,7 +102,7 @@ describe("buildCipherIcon", () => {
|
||||
|
||||
expect(iconDetails).toEqual({
|
||||
icon: "bwi-globe",
|
||||
image: undefined,
|
||||
image: null,
|
||||
fallbackImage: "",
|
||||
imageEnabled: true,
|
||||
});
|
||||
|
||||
@@ -2,9 +2,23 @@ import { Utils } from "../../platform/misc/utils";
|
||||
import { CipherType } from "../enums/cipher-type";
|
||||
import { CipherView } from "../models/view/cipher.view";
|
||||
|
||||
export function buildCipherIcon(iconsServerUrl: string, cipher: CipherView, showFavicon: boolean) {
|
||||
let icon;
|
||||
let image;
|
||||
export interface CipherIconDetails {
|
||||
imageEnabled: boolean;
|
||||
image: string | null;
|
||||
/**
|
||||
* @deprecated Fallback to `icon` instead which will default to "bwi-globe" if no other icon is applicable.
|
||||
*/
|
||||
fallbackImage: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export function buildCipherIcon(
|
||||
iconsServerUrl: string | null,
|
||||
cipher: CipherView,
|
||||
showFavicon: boolean,
|
||||
): CipherIconDetails {
|
||||
let icon: string = "bwi-globe";
|
||||
let image: string | null = null;
|
||||
let fallbackImage = "";
|
||||
const cardIcons: Record<string, string> = {
|
||||
Visa: "card-visa",
|
||||
@@ -18,6 +32,10 @@ export function buildCipherIcon(iconsServerUrl: string, cipher: CipherView, show
|
||||
RuPay: "card-ru-pay",
|
||||
};
|
||||
|
||||
if (iconsServerUrl == null) {
|
||||
showFavicon = false;
|
||||
}
|
||||
|
||||
switch (cipher.type) {
|
||||
case CipherType.Login:
|
||||
icon = "bwi-globe";
|
||||
@@ -53,9 +71,7 @@ export function buildCipherIcon(iconsServerUrl: string, cipher: CipherView, show
|
||||
try {
|
||||
image = `${iconsServerUrl}/${Utils.getHostname(hostnameUri)}/icon.png`;
|
||||
fallbackImage = "images/bwi-globe.png";
|
||||
// FIXME: Remove when updating file. Eslint update
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Ignore error since the fallback icon will be shown if image is null.
|
||||
}
|
||||
}
|
||||
|
||||
21
libs/common/src/vault/models/api/cipher-permissions.api.ts
Normal file
21
libs/common/src/vault/models/api/cipher-permissions.api.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
|
||||
export class CipherPermissionsApi extends BaseResponse {
|
||||
delete: boolean = false;
|
||||
restore: boolean = false;
|
||||
|
||||
constructor(data: any = null) {
|
||||
super(data);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
this.delete = this.getResponseProperty("Delete");
|
||||
this.restore = this.getResponseProperty("Restore");
|
||||
}
|
||||
|
||||
static fromJSON(obj: Jsonify<CipherPermissionsApi>) {
|
||||
return Object.assign(new CipherPermissionsApi(), obj);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { Jsonify } from "type-fest";
|
||||
|
||||
import { CipherRepromptType } from "../../enums/cipher-reprompt-type";
|
||||
import { CipherType } from "../../enums/cipher-type";
|
||||
import { CipherPermissionsApi } from "../api/cipher-permissions.api";
|
||||
import { CipherResponse } from "../response/cipher.response";
|
||||
|
||||
import { AttachmentData } from "./attachment.data";
|
||||
@@ -21,6 +22,7 @@ export class CipherData {
|
||||
folderId: string;
|
||||
edit: boolean;
|
||||
viewPassword: boolean;
|
||||
permissions: CipherPermissionsApi;
|
||||
organizationUseTotp: boolean;
|
||||
favorite: boolean;
|
||||
revisionDate: string;
|
||||
@@ -51,6 +53,7 @@ export class CipherData {
|
||||
this.folderId = response.folderId;
|
||||
this.edit = response.edit;
|
||||
this.viewPassword = response.viewPassword;
|
||||
this.permissions = response.permissions;
|
||||
this.organizationUseTotp = response.organizationUseTotp;
|
||||
this.favorite = response.favorite;
|
||||
this.revisionDate = response.revisionDate;
|
||||
@@ -95,6 +98,8 @@ export class CipherData {
|
||||
}
|
||||
|
||||
static fromJSON(obj: Jsonify<CipherData>) {
|
||||
return Object.assign(new CipherData(), obj);
|
||||
const result = Object.assign(new CipherData(), obj);
|
||||
result.permissions = CipherPermissionsApi.fromJSON(obj.permissions);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import { SecureNote } from "../../models/domain/secure-note";
|
||||
import { CardView } from "../../models/view/card.view";
|
||||
import { IdentityView } from "../../models/view/identity.view";
|
||||
import { LoginView } from "../../models/view/login.view";
|
||||
import { CipherPermissionsApi } from "../api/cipher-permissions.api";
|
||||
|
||||
describe("Cipher DTO", () => {
|
||||
it("Convert from empty CipherData", () => {
|
||||
@@ -54,6 +55,7 @@ describe("Cipher DTO", () => {
|
||||
fields: null,
|
||||
passwordHistory: null,
|
||||
key: null,
|
||||
permissions: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -75,6 +77,7 @@ describe("Cipher DTO", () => {
|
||||
notes: "EncryptedString",
|
||||
creationDate: "2022-01-01T12:00:00.000Z",
|
||||
deletedDate: null,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
reprompt: CipherRepromptType.None,
|
||||
key: "EncryptedString",
|
||||
login: {
|
||||
@@ -149,6 +152,7 @@ describe("Cipher DTO", () => {
|
||||
localData: null,
|
||||
creationDate: new Date("2022-01-01T12:00:00.000Z"),
|
||||
deletedDate: null,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
reprompt: 0,
|
||||
key: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||
login: {
|
||||
@@ -228,6 +232,7 @@ describe("Cipher DTO", () => {
|
||||
cipher.deletedDate = null;
|
||||
cipher.reprompt = CipherRepromptType.None;
|
||||
cipher.key = mockEnc("EncKey");
|
||||
cipher.permissions = new CipherPermissionsApi();
|
||||
|
||||
const loginView = new LoginView();
|
||||
loginView.username = "username";
|
||||
@@ -270,6 +275,7 @@ describe("Cipher DTO", () => {
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
localData: undefined,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -297,6 +303,7 @@ describe("Cipher DTO", () => {
|
||||
secureNote: {
|
||||
type: SecureNoteType.Generic,
|
||||
},
|
||||
permissions: new CipherPermissionsApi(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -326,6 +333,7 @@ describe("Cipher DTO", () => {
|
||||
fields: null,
|
||||
passwordHistory: null,
|
||||
key: { encryptedString: "EncKey", encryptionType: 0 },
|
||||
permissions: new CipherPermissionsApi(),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -353,6 +361,7 @@ describe("Cipher DTO", () => {
|
||||
cipher.secureNote = new SecureNote();
|
||||
cipher.secureNote.type = SecureNoteType.Generic;
|
||||
cipher.key = mockEnc("EncKey");
|
||||
cipher.permissions = new CipherPermissionsApi();
|
||||
|
||||
const keyService = mock<KeyService>();
|
||||
const encryptService = mock<EncryptService>();
|
||||
@@ -387,6 +396,7 @@ describe("Cipher DTO", () => {
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
localData: undefined,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -409,6 +419,7 @@ describe("Cipher DTO", () => {
|
||||
notes: "EncryptedString",
|
||||
creationDate: "2022-01-01T12:00:00.000Z",
|
||||
deletedDate: null,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
reprompt: CipherRepromptType.None,
|
||||
card: {
|
||||
cardholderName: "EncryptedString",
|
||||
@@ -455,6 +466,7 @@ describe("Cipher DTO", () => {
|
||||
fields: null,
|
||||
passwordHistory: null,
|
||||
key: { encryptedString: "EncKey", encryptionType: 0 },
|
||||
permissions: new CipherPermissionsApi(),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -480,6 +492,7 @@ describe("Cipher DTO", () => {
|
||||
cipher.deletedDate = null;
|
||||
cipher.reprompt = CipherRepromptType.None;
|
||||
cipher.key = mockEnc("EncKey");
|
||||
cipher.permissions = new CipherPermissionsApi();
|
||||
|
||||
const cardView = new CardView();
|
||||
cardView.cardholderName = "cardholderName";
|
||||
@@ -522,6 +535,7 @@ describe("Cipher DTO", () => {
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
localData: undefined,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -544,6 +558,7 @@ describe("Cipher DTO", () => {
|
||||
notes: "EncryptedString",
|
||||
creationDate: "2022-01-01T12:00:00.000Z",
|
||||
deletedDate: null,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
reprompt: CipherRepromptType.None,
|
||||
key: "EncKey",
|
||||
identity: {
|
||||
@@ -614,6 +629,7 @@ describe("Cipher DTO", () => {
|
||||
fields: null,
|
||||
passwordHistory: null,
|
||||
key: { encryptedString: "EncKey", encryptionType: 0 },
|
||||
permissions: new CipherPermissionsApi(),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -639,6 +655,7 @@ describe("Cipher DTO", () => {
|
||||
cipher.deletedDate = null;
|
||||
cipher.reprompt = CipherRepromptType.None;
|
||||
cipher.key = mockEnc("EncKey");
|
||||
cipher.permissions = new CipherPermissionsApi();
|
||||
|
||||
const identityView = new IdentityView();
|
||||
identityView.firstName = "firstName";
|
||||
@@ -681,6 +698,7 @@ describe("Cipher DTO", () => {
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
localData: undefined,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-cr
|
||||
import { InitializerKey } from "../../../platform/services/cryptography/initializer-key";
|
||||
import { CipherRepromptType } from "../../enums/cipher-reprompt-type";
|
||||
import { CipherType } from "../../enums/cipher-type";
|
||||
import { CipherPermissionsApi } from "../api/cipher-permissions.api";
|
||||
import { CipherData } from "../data/cipher.data";
|
||||
import { LocalData } from "../data/local.data";
|
||||
import { AttachmentView } from "../view/attachment.view";
|
||||
@@ -39,6 +40,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
organizationUseTotp: boolean;
|
||||
edit: boolean;
|
||||
viewPassword: boolean;
|
||||
permissions: CipherPermissionsApi;
|
||||
revisionDate: Date;
|
||||
localData: LocalData;
|
||||
login: Login;
|
||||
@@ -84,6 +86,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
} else {
|
||||
this.viewPassword = true; // Default for already synced Ciphers without viewPassword
|
||||
}
|
||||
this.permissions = obj.permissions;
|
||||
this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null;
|
||||
this.collectionIds = obj.collectionIds;
|
||||
this.localData = localData;
|
||||
@@ -244,6 +247,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
|
||||
c.deletedDate = this.deletedDate != null ? this.deletedDate.toISOString() : null;
|
||||
c.reprompt = this.reprompt;
|
||||
c.key = this.key?.encryptedString;
|
||||
c.permissions = this.permissions;
|
||||
|
||||
this.buildDataModel(this, c, {
|
||||
name: null,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
import { CipherRepromptType } from "../../enums/cipher-reprompt-type";
|
||||
import { CardApi } from "../api/card.api";
|
||||
import { CipherPermissionsApi } from "../api/cipher-permissions.api";
|
||||
import { FieldApi } from "../api/field.api";
|
||||
import { IdentityApi } from "../api/identity.api";
|
||||
import { LoginApi } from "../api/login.api";
|
||||
@@ -28,6 +29,7 @@ export class CipherResponse extends BaseResponse {
|
||||
favorite: boolean;
|
||||
edit: boolean;
|
||||
viewPassword: boolean;
|
||||
permissions: CipherPermissionsApi;
|
||||
organizationUseTotp: boolean;
|
||||
revisionDate: string;
|
||||
attachments: AttachmentResponse[];
|
||||
@@ -53,6 +55,7 @@ export class CipherResponse extends BaseResponse {
|
||||
} else {
|
||||
this.viewPassword = this.getResponseProperty("ViewPassword");
|
||||
}
|
||||
this.permissions = new CipherPermissionsApi(this.getResponseProperty("Permissions"));
|
||||
this.organizationUseTotp = this.getResponseProperty("OrganizationUseTotp");
|
||||
this.revisionDate = this.getResponseProperty("RevisionDate");
|
||||
this.collectionIds = this.getResponseProperty("CollectionIds");
|
||||
|
||||
@@ -6,6 +6,7 @@ import { InitializerKey } from "../../../platform/services/cryptography/initiali
|
||||
import { DeepJsonify } from "../../../types/deep-jsonify";
|
||||
import { CipherType, LinkedIdType } from "../../enums";
|
||||
import { CipherRepromptType } from "../../enums/cipher-reprompt-type";
|
||||
import { CipherPermissionsApi } from "../api/cipher-permissions.api";
|
||||
import { LocalData } from "../data/local.data";
|
||||
import { Cipher } from "../domain/cipher";
|
||||
|
||||
@@ -29,6 +30,7 @@ export class CipherView implements View, InitializerMetadata {
|
||||
type: CipherType = null;
|
||||
favorite = false;
|
||||
organizationUseTotp = false;
|
||||
permissions: CipherPermissionsApi = new CipherPermissionsApi();
|
||||
edit = false;
|
||||
viewPassword = true;
|
||||
localData: LocalData;
|
||||
@@ -63,6 +65,7 @@ export class CipherView implements View, InitializerMetadata {
|
||||
this.organizationUseTotp = c.organizationUseTotp;
|
||||
this.edit = c.edit;
|
||||
this.viewPassword = c.viewPassword;
|
||||
this.permissions = c.permissions;
|
||||
this.type = c.type;
|
||||
this.localData = c.localData;
|
||||
this.collectionIds = c.collectionIds;
|
||||
|
||||
@@ -8,6 +8,8 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { CollectionId, UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { FakeAccountService, mockAccountServiceWith } from "../../../spec";
|
||||
import { ConfigService } from "../../platform/abstractions/config/config.service";
|
||||
import { CipherPermissionsApi } from "../models/api/cipher-permissions.api";
|
||||
import { CipherView } from "../models/view/cipher.view";
|
||||
|
||||
import {
|
||||
@@ -20,6 +22,7 @@ describe("CipherAuthorizationService", () => {
|
||||
|
||||
const mockCollectionService = mock<CollectionService>();
|
||||
const mockOrganizationService = mock<OrganizationService>();
|
||||
const mockConfigService = mock<ConfigService>();
|
||||
const mockUserId = Utils.newGuid() as UserId;
|
||||
let mockAccountService: FakeAccountService;
|
||||
|
||||
@@ -28,10 +31,12 @@ describe("CipherAuthorizationService", () => {
|
||||
organizationId: string | null,
|
||||
collectionIds: string[],
|
||||
edit: boolean = true,
|
||||
permissions: CipherPermissionsApi = new CipherPermissionsApi(),
|
||||
) => ({
|
||||
organizationId,
|
||||
collectionIds,
|
||||
edit,
|
||||
permissions,
|
||||
});
|
||||
|
||||
const createMockCollection = (id: string, manage: boolean) => ({
|
||||
@@ -63,7 +68,78 @@ describe("CipherAuthorizationService", () => {
|
||||
mockCollectionService,
|
||||
mockOrganizationService,
|
||||
mockAccountService,
|
||||
mockConfigService,
|
||||
);
|
||||
|
||||
mockConfigService.getFeatureFlag$.mockReturnValue(of(false));
|
||||
});
|
||||
|
||||
describe("canRestoreCipher$", () => {
|
||||
it("should return true if isAdminConsoleAction and cipher is unassigned", (done) => {
|
||||
const cipher = createMockCipher("org1", []) as CipherView;
|
||||
const organization = createMockOrganization({ canEditUnassignedCiphers: true });
|
||||
mockOrganizationService.organizations$.mockReturnValue(
|
||||
of([organization]) as Observable<Organization[]>,
|
||||
);
|
||||
|
||||
cipherAuthorizationService.canRestoreCipher$(cipher, true).subscribe((result) => {
|
||||
expect(result).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return true if isAdminConsleAction and user can edit all ciphers in the org", (done) => {
|
||||
const cipher = createMockCipher("org1", ["col1"]) as CipherView;
|
||||
const organization = createMockOrganization({ canEditAllCiphers: true });
|
||||
mockOrganizationService.organizations$.mockReturnValue(
|
||||
of([organization]) as Observable<Organization[]>,
|
||||
);
|
||||
|
||||
cipherAuthorizationService.canRestoreCipher$(cipher, true).subscribe((result) => {
|
||||
expect(result).toBe(true);
|
||||
expect(mockOrganizationService.organizations$).toHaveBeenCalledWith(mockUserId);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return false if isAdminConsoleAction is true but user does not have permission to edit unassigned ciphers", (done) => {
|
||||
const cipher = createMockCipher("org1", []) as CipherView;
|
||||
const organization = createMockOrganization({ canEditUnassignedCiphers: false });
|
||||
mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[]));
|
||||
|
||||
cipherAuthorizationService.canRestoreCipher$(cipher, true).subscribe((result) => {
|
||||
expect(result).toBe(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return false if cipher.permission.restore is false and is not an admin action", (done) => {
|
||||
const cipher = createMockCipher("org1", [], true, {
|
||||
restore: false,
|
||||
} as CipherPermissionsApi) as CipherView;
|
||||
const organization = createMockOrganization();
|
||||
mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[]));
|
||||
|
||||
cipherAuthorizationService.canRestoreCipher$(cipher, false).subscribe((result) => {
|
||||
expect(result).toBe(false);
|
||||
expect(mockCollectionService.decryptedCollectionViews$).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return true if cipher.permission.restore is true and is not an admin action", (done) => {
|
||||
const cipher = createMockCipher("org1", [], true, {
|
||||
restore: true,
|
||||
} as CipherPermissionsApi) as CipherView;
|
||||
const organization = createMockOrganization();
|
||||
mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[]));
|
||||
|
||||
cipherAuthorizationService.canRestoreCipher$(cipher, false).subscribe((result) => {
|
||||
expect(result).toBe(true);
|
||||
expect(mockCollectionService.decryptedCollectionViews$).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("canDeleteCipher$", () => {
|
||||
@@ -213,6 +289,34 @@ describe("CipherAuthorizationService", () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return true if feature flag enabled and cipher.permissions.delete is true", (done) => {
|
||||
const cipher = createMockCipher("org1", [], true, {
|
||||
delete: true,
|
||||
} as CipherPermissionsApi) as CipherView;
|
||||
const organization = createMockOrganization();
|
||||
mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[]));
|
||||
mockConfigService.getFeatureFlag$.mockReturnValue(of(true));
|
||||
|
||||
cipherAuthorizationService.canDeleteCipher$(cipher, [], false).subscribe((result) => {
|
||||
expect(result).toBe(true);
|
||||
expect(mockCollectionService.decryptedCollectionViews$).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should return false if feature flag enabled and cipher.permissions.delete is false", (done) => {
|
||||
const cipher = createMockCipher("org1", []) as CipherView;
|
||||
const organization = createMockOrganization();
|
||||
mockOrganizationService.organizations$.mockReturnValue(of([organization] as Organization[]));
|
||||
mockConfigService.getFeatureFlag$.mockReturnValue(of(true));
|
||||
|
||||
cipherAuthorizationService.canDeleteCipher$(cipher, [], false).subscribe((result) => {
|
||||
expect(result).toBe(false);
|
||||
expect(mockCollectionService.decryptedCollectionViews$).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("canCloneCipher$", () => {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { map, Observable, of, shareReplay, switchMap } from "rxjs";
|
||||
import { combineLatest, map, Observable, of, shareReplay, switchMap } from "rxjs";
|
||||
|
||||
import { CollectionService } from "@bitwarden/admin-console/common";
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { CollectionId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { getUserId } from "../../auth/services/account.service";
|
||||
import { FeatureFlag } from "../../enums/feature-flag.enum";
|
||||
import { Cipher } from "../models/domain/cipher";
|
||||
import { CipherView } from "../models/view/cipher.view";
|
||||
|
||||
@@ -28,12 +29,25 @@ export abstract class CipherAuthorizationService {
|
||||
*
|
||||
* @returns {Observable<boolean>} - An observable that emits a boolean value indicating if the user can delete the cipher.
|
||||
*/
|
||||
canDeleteCipher$: (
|
||||
abstract canDeleteCipher$: (
|
||||
cipher: CipherLike,
|
||||
allowedCollections?: CollectionId[],
|
||||
isAdminConsoleAction?: boolean,
|
||||
) => Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Determines if the user can restore the specified cipher.
|
||||
*
|
||||
* @param {CipherLike} cipher - The cipher object to evaluate for restore permissions.
|
||||
* @param {boolean} isAdminConsoleAction - Optional. A flag indicating if the action is being performed from the admin console.
|
||||
*
|
||||
* @returns {Observable<boolean>} - An observable that emits a boolean value indicating if the user can restore the cipher.
|
||||
*/
|
||||
abstract canRestoreCipher$: (
|
||||
cipher: CipherLike,
|
||||
isAdminConsoleAction?: boolean,
|
||||
) => Observable<boolean>;
|
||||
|
||||
/**
|
||||
* Determines if the user can clone the specified cipher.
|
||||
*
|
||||
@@ -42,7 +56,10 @@ export abstract class CipherAuthorizationService {
|
||||
*
|
||||
* @returns {Observable<boolean>} - An observable that emits a boolean value indicating if the user can clone the cipher.
|
||||
*/
|
||||
canCloneCipher$: (cipher: CipherLike, isAdminConsoleAction?: boolean) => Observable<boolean>;
|
||||
abstract canCloneCipher$: (
|
||||
cipher: CipherLike,
|
||||
isAdminConsoleAction?: boolean,
|
||||
) => Observable<boolean>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,13 +70,16 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer
|
||||
private collectionService: CollectionService,
|
||||
private organizationService: OrganizationService,
|
||||
private accountService: AccountService,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
|
||||
private organization$ = (cipher: CipherLike) =>
|
||||
this.accountService.activeAccount$.pipe(
|
||||
switchMap((account) => this.organizationService.organizations$(account?.id)),
|
||||
getUserId,
|
||||
switchMap((userId) => this.organizationService.organizations$(userId)),
|
||||
map((orgs) => orgs.find((org) => org.id === cipher.organizationId)),
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* {@link CipherAuthorizationService.canDeleteCipher$}
|
||||
@@ -69,12 +89,11 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer
|
||||
allowedCollections?: CollectionId[],
|
||||
isAdminConsoleAction?: boolean,
|
||||
): Observable<boolean> {
|
||||
if (cipher.organizationId == null) {
|
||||
return of(true);
|
||||
}
|
||||
|
||||
return this.organization$(cipher).pipe(
|
||||
switchMap((organization) => {
|
||||
return combineLatest([
|
||||
this.organization$(cipher),
|
||||
this.configService.getFeatureFlag$(FeatureFlag.LimitItemDeletion),
|
||||
]).pipe(
|
||||
switchMap(([organization, featureFlagEnabled]) => {
|
||||
if (isAdminConsoleAction) {
|
||||
// If the user is an admin, they can delete an unassigned cipher
|
||||
if (!cipher.collectionIds || cipher.collectionIds.length === 0) {
|
||||
@@ -86,6 +105,14 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer
|
||||
}
|
||||
}
|
||||
|
||||
if (featureFlagEnabled) {
|
||||
return of(cipher.permissions.delete);
|
||||
}
|
||||
|
||||
if (cipher.organizationId == null) {
|
||||
return of(true);
|
||||
}
|
||||
|
||||
return this.collectionService
|
||||
.decryptedCollectionViews$(cipher.collectionIds as CollectionId[])
|
||||
.pipe(
|
||||
@@ -93,7 +120,7 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer
|
||||
const shouldFilter = allowedCollections?.some(Boolean);
|
||||
|
||||
const collections = shouldFilter
|
||||
? allCollections.filter((c) => allowedCollections.includes(c.id as CollectionId))
|
||||
? allCollections.filter((c) => allowedCollections?.includes(c.id as CollectionId))
|
||||
: allCollections;
|
||||
|
||||
return collections.some((collection) => collection.manage);
|
||||
@@ -103,6 +130,29 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* {@link CipherAuthorizationService.canRestoreCipher$}
|
||||
*/
|
||||
canRestoreCipher$(cipher: CipherLike, isAdminConsoleAction?: boolean): Observable<boolean> {
|
||||
return this.organization$(cipher).pipe(
|
||||
map((organization) => {
|
||||
if (isAdminConsoleAction) {
|
||||
// If the user is an admin, they can restore an unassigned cipher
|
||||
if (!cipher.collectionIds || cipher.collectionIds.length === 0) {
|
||||
return organization?.canEditUnassignedCiphers === true;
|
||||
}
|
||||
|
||||
if (organization?.canEditAllCiphers) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return cipher.permissions.restore;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CipherAuthorizationService.canCloneCipher$}
|
||||
*/
|
||||
@@ -116,6 +166,7 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer
|
||||
// Admins and custom users can always clone when in the Admin Console
|
||||
if (
|
||||
isAdminConsoleAction &&
|
||||
organization &&
|
||||
(organization.isAdmin || organization.permissions?.editAnyCollection)
|
||||
) {
|
||||
return of(true);
|
||||
|
||||
@@ -27,6 +27,7 @@ import { CipherFileUploadService } from "../abstractions/file-upload/cipher-file
|
||||
import { FieldType } from "../enums";
|
||||
import { CipherRepromptType } from "../enums/cipher-reprompt-type";
|
||||
import { CipherType } from "../enums/cipher-type";
|
||||
import { CipherPermissionsApi } from "../models/api/cipher-permissions.api";
|
||||
import { CipherData } from "../models/data/cipher.data";
|
||||
import { Cipher } from "../models/domain/cipher";
|
||||
import { CipherCreateRequest } from "../models/request/cipher-create.request";
|
||||
@@ -57,6 +58,7 @@ const cipherData: CipherData = {
|
||||
notes: "EncryptedString",
|
||||
creationDate: "2022-01-01T12:00:00.000Z",
|
||||
deletedDate: null,
|
||||
permissions: new CipherPermissionsApi(),
|
||||
key: "EncKey",
|
||||
reprompt: CipherRepromptType.None,
|
||||
login: {
|
||||
|
||||
Reference in New Issue
Block a user