1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-06 19:53:59 +00:00

Merge main

This commit is contained in:
Bernd Schoolmann
2026-01-13 14:03:30 +01:00
605 changed files with 32014 additions and 5329 deletions

View File

@@ -0,0 +1 @@
export * from "./org-policy.guard";

View File

@@ -0,0 +1,70 @@
import { inject } from "@angular/core";
import { CanActivateFn, Router } from "@angular/router";
import { firstValueFrom, Observable, switchMap, tap } from "rxjs";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import { ToastService } from "@bitwarden/components";
import { UserId } from "@bitwarden/user-core";
/**
* This guard is intended to prevent members of an organization from accessing
* routes based on compliance with organization
* policies. e.g Emergency access, which is a non-organization
* feature is restricted by the Auto Confirm policy.
*/
export function organizationPolicyGuard(
featureCallback: (
userId: UserId,
configService: ConfigService,
policyService: PolicyService,
) => Observable<boolean>,
): CanActivateFn {
return async () => {
const router = inject(Router);
const toastService = inject(ToastService);
const i18nService = inject(I18nService);
const accountService = inject(AccountService);
const policyService = inject(PolicyService);
const configService = inject(ConfigService);
const syncService = inject(SyncService);
const synced = await firstValueFrom(
accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) => syncService.lastSync$(userId)),
),
);
if (synced == null) {
await syncService.fullSync(false);
}
const compliant = await firstValueFrom(
accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) => featureCallback(userId, configService, policyService)),
tap((compliant) => {
if (typeof compliant !== "boolean") {
throw new Error("Feature callback must return a boolean.");
}
}),
),
);
if (!compliant) {
toastService.showToast({
variant: "error",
message: i18nService.t("noPageAccess"),
});
return router.createUrlTree(["/"]);
}
return compliant;
};
}

View File

@@ -1,30 +0,0 @@
import { TestBed } from "@angular/core/testing";
import { DefaultLoginApprovalDialogComponentService } from "./default-login-approval-dialog-component.service";
import { LoginApprovalDialogComponent } from "./login-approval-dialog.component";
describe("DefaultLoginApprovalDialogComponentService", () => {
let service: DefaultLoginApprovalDialogComponentService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [DefaultLoginApprovalDialogComponentService],
});
service = TestBed.inject(DefaultLoginApprovalDialogComponentService);
});
it("is created successfully", () => {
expect(service).toBeTruthy();
});
it("has showLoginRequestedAlertIfWindowNotVisible method that is a no-op", async () => {
const loginApprovalDialogComponent = {} as LoginApprovalDialogComponent;
const result = await service.showLoginRequestedAlertIfWindowNotVisible(
loginApprovalDialogComponent.email,
);
expect(result).toBeUndefined();
});
});

View File

@@ -1,14 +0,0 @@
import { LoginApprovalDialogComponentServiceAbstraction } from "./login-approval-dialog-component.service.abstraction";
/**
* Default implementation of the LoginApprovalDialogComponentServiceAbstraction.
*/
export class DefaultLoginApprovalDialogComponentService implements LoginApprovalDialogComponentServiceAbstraction {
/**
* No-op implementation of the showLoginRequestedAlertIfWindowNotVisible method.
* @returns
*/
async showLoginRequestedAlertIfWindowNotVisible(email?: string): Promise<void> {
return;
}
}

View File

@@ -1,3 +1 @@
export * from "./login-approval-dialog.component";
export * from "./login-approval-dialog-component.service.abstraction";
export * from "./default-login-approval-dialog-component.service";

View File

@@ -1,9 +0,0 @@
/**
* Abstraction for the LoginApprovalDialogComponent service.
*/
export abstract class LoginApprovalDialogComponentServiceAbstraction {
/**
* Shows a login requested alert if the window is not visible.
*/
abstract showLoginRequestedAlertIfWindowNotVisible: (email?: string) => Promise<void>;
}

View File

@@ -16,7 +16,6 @@ import { UserId } from "@bitwarden/common/types/guid";
import { DialogRef, DIALOG_DATA, ToastService } from "@bitwarden/components";
import { LogService } from "@bitwarden/logging";
import { LoginApprovalDialogComponentServiceAbstraction } from "./login-approval-dialog-component.service.abstraction";
import { LoginApprovalDialogComponent } from "./login-approval-dialog.component";
describe("LoginApprovalDialogComponent", () => {
@@ -69,10 +68,6 @@ describe("LoginApprovalDialogComponent", () => {
{ provide: LogService, useValue: logService },
{ provide: ToastService, useValue: toastService },
{ provide: ValidationService, useValue: validationService },
{
provide: LoginApprovalDialogComponentServiceAbstraction,
useValue: mock<LoginApprovalDialogComponentServiceAbstraction>(),
},
],
}).compileComponents();

View File

@@ -24,8 +24,6 @@ import {
} from "@bitwarden/components";
import { LogService } from "@bitwarden/logging";
import { LoginApprovalDialogComponentServiceAbstraction } from "./login-approval-dialog-component.service.abstraction";
const RequestTimeOut = 60000 * 15; // 15 Minutes
const RequestTimeUpdate = 60000 * 5; // 5 Minutes
@@ -57,7 +55,6 @@ export class LoginApprovalDialogComponent implements OnInit, OnDestroy {
private devicesService: DevicesServiceAbstraction,
private dialogRef: DialogRef,
private i18nService: I18nService,
private loginApprovalDialogComponentService: LoginApprovalDialogComponentServiceAbstraction,
private logService: LogService,
private toastService: ToastService,
private validationService: ValidationService,
@@ -113,10 +110,6 @@ export class LoginApprovalDialogComponent implements OnInit, OnDestroy {
this.updateTimeText();
}, RequestTimeUpdate);
await this.loginApprovalDialogComponentService.showLoginRequestedAlertIfWindowNotVisible(
this.email,
);
this.loading = false;
}

View File

@@ -50,6 +50,7 @@
<!-- Button space (always reserved) -->
<div class="tw-my-5 tw-h-12">
<button
cdkFocusInitial
bitButton
[buttonType]="cardDetails.button.type"
[block]="true"

View File

@@ -6,6 +6,7 @@ import { filter, firstValueFrom, map } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { MasterPasswordUnlockService } from "@bitwarden/common/key-management/master-password/abstractions/master-password-unlock.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import {
LinkModule,
AsyncActionsModule,
@@ -15,6 +16,7 @@ import {
DialogService,
FormFieldModule,
IconButtonModule,
ToastService,
} from "@bitwarden/components";
/**
@@ -40,6 +42,8 @@ export class PromptMigrationPasswordComponent {
private formBuilder = inject(FormBuilder);
private masterPasswordUnlockService = inject(MasterPasswordUnlockService);
private accountService = inject(AccountService);
private toastService = inject(ToastService);
private i18nService = inject(I18nService);
migrationPasswordForm = this.formBuilder.group({
masterPassword: ["", [Validators.required]],
@@ -73,6 +77,10 @@ export class PromptMigrationPasswordComponent {
userId,
))
) {
this.toastService.showToast({
variant: "error",
message: this.i18nService.t("incorrectPassword"),
});
return;
}

View File

@@ -94,7 +94,7 @@ import {
InternalAccountService,
} from "@bitwarden/common/auth/abstractions/account.service";
import { AnonymousHubService as AnonymousHubServiceAbstraction } from "@bitwarden/common/auth/abstractions/anonymous-hub.service";
import { AuthRequestAnsweringServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction";
import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction";
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
@@ -112,7 +112,7 @@ import { SendTokenService, DefaultSendTokenService } from "@bitwarden/common/aut
import { AccountApiServiceImplementation } from "@bitwarden/common/auth/services/account-api.service";
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
import { AnonymousHubService } from "@bitwarden/common/auth/services/anonymous-hub.service";
import { NoopAuthRequestAnsweringService } from "@bitwarden/common/auth/services/auth-request-answering/noop-auth-request-answering.service";
import { DefaultAuthRequestAnsweringService } from "@bitwarden/common/auth/services/auth-request-answering/default-auth-request-answering.service";
import { PendingAuthRequestsStateService } from "@bitwarden/common/auth/services/auth-request-answering/pending-auth-requests.state";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
@@ -397,8 +397,6 @@ import {
VaultExportServiceAbstraction,
} from "@bitwarden/vault-export-core";
import { DefaultLoginApprovalDialogComponentService } from "../auth/login-approval/default-login-approval-dialog-component.service";
import { LoginApprovalDialogComponentServiceAbstraction } from "../auth/login-approval/login-approval-dialog-component.service.abstraction";
import { DefaultSetInitialPasswordService } from "../auth/password-management/set-initial-password/default-set-initial-password.service.implementation";
import { SetInitialPasswordService } from "../auth/password-management/set-initial-password/set-initial-password.service.abstraction";
import { DeviceTrustToastService as DeviceTrustToastServiceAbstraction } from "../auth/services/device-trust-toast.service.abstraction";
@@ -1040,9 +1038,15 @@ const safeProviders: SafeProvider[] = [
deps: [StateProvider],
}),
safeProvider({
provide: AuthRequestAnsweringServiceAbstraction,
useClass: NoopAuthRequestAnsweringService,
deps: [],
provide: AuthRequestAnsweringService,
useClass: DefaultAuthRequestAnsweringService,
deps: [
AccountServiceAbstraction,
AuthServiceAbstraction,
MasterPasswordServiceAbstraction,
MessagingServiceAbstraction,
PendingAuthRequestsStateService,
],
}),
safeProvider({
provide: ServerNotificationsService,
@@ -1060,7 +1064,7 @@ const safeProviders: SafeProvider[] = [
SignalRConnectionService,
AuthServiceAbstraction,
WebPushConnectionService,
AuthRequestAnsweringServiceAbstraction,
AuthRequestAnsweringService,
ConfigService,
InternalPolicyService,
],
@@ -1666,11 +1670,6 @@ const safeProviders: SafeProvider[] = [
useClass: DefaultSendPasswordService,
deps: [CryptoFunctionServiceAbstraction],
}),
safeProvider({
provide: LoginApprovalDialogComponentServiceAbstraction,
useClass: DefaultLoginApprovalDialogComponentService,
deps: [],
}),
safeProvider({
provide: LoginDecryptionOptionsService,
useClass: DefaultLoginDecryptionOptionsService,

View File

@@ -27,13 +27,13 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { Send } from "@bitwarden/common/tools/send/models/domain/send";
import { SendFileView } from "@bitwarden/common/tools/send/models/view/send-file.view";
import { SendTextView } from "@bitwarden/common/tools/send/models/view/send-text.view";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { SendType } from "@bitwarden/common/tools/send/types/send-type";
import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service";
import { DialogService, ToastService } from "@bitwarden/components";

View File

@@ -20,10 +20,10 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { SendType } from "@bitwarden/common/tools/send/types/send-type";
import { SearchService } from "@bitwarden/common/vault/abstractions/search.service";
import { DialogService, ToastService } from "@bitwarden/components";
@@ -78,7 +78,7 @@ export class SendComponent implements OnInit, OnDestroy {
protected ngZone: NgZone,
protected searchService: SearchService,
protected policyService: PolicyService,
private logService: LogService,
protected logService: LogService,
protected sendApiService: SendApiService,
protected dialogService: DialogService,
protected toastService: ToastService,

View File

@@ -194,7 +194,12 @@ export class VaultItemsComponent<C extends CipherViewLike> implements OnDestroy
return this.searchService.searchCiphers(
userId,
searchText,
[filter, this.deletedFilter, this.archivedFilter, restrictedTypeFilter],
[
filter,
this.deletedFilter,
...(this.deleted ? [] : [this.archivedFilter]),
restrictedTypeFilter,
],
allCiphers,
);
}),

View File

@@ -0,0 +1,226 @@
import { TestBed } from "@angular/core/testing";
import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject, firstValueFrom } from "rxjs";
import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm";
import { StateProvider } from "@bitwarden/common/platform/state";
import { UserId } from "@bitwarden/user-core";
import { FakeStateProvider, mockAccountServiceWith } from "../../../../../../libs/common/spec";
import { NUDGE_DISMISSED_DISK_KEY, NudgeType } from "../nudges.service";
import { AutoConfirmNudgeService } from "./auto-confirm-nudge.service";
describe("AutoConfirmNudgeService", () => {
let service: AutoConfirmNudgeService;
let autoConfirmService: MockProxy<AutomaticUserConfirmationService>;
let fakeStateProvider: FakeStateProvider;
const userId = "user-id" as UserId;
const mockAutoConfirmState = {
enabled: true,
showSetupDialog: false,
showBrowserNotification: true,
};
beforeEach(() => {
fakeStateProvider = new FakeStateProvider(mockAccountServiceWith(userId));
autoConfirmService = mock<AutomaticUserConfirmationService>();
TestBed.configureTestingModule({
providers: [
AutoConfirmNudgeService,
{
provide: StateProvider,
useValue: fakeStateProvider,
},
{
provide: AutomaticUserConfirmationService,
useValue: autoConfirmService,
},
],
});
service = TestBed.inject(AutoConfirmNudgeService);
});
describe("nudgeStatus$", () => {
it("should return all dismissed when user cannot manage auto-confirm", async () => {
autoConfirmService.configuration$.mockReturnValue(new BehaviorSubject(mockAutoConfirmState));
autoConfirmService.canManageAutoConfirm$.mockReturnValue(new BehaviorSubject(false));
const result = await firstValueFrom(service.nudgeStatus$(NudgeType.AutoConfirmNudge, userId));
expect(result).toEqual({
hasBadgeDismissed: true,
hasSpotlightDismissed: true,
});
});
it("should return all dismissed when showBrowserNotification is false", async () => {
autoConfirmService.configuration$.mockReturnValue(
new BehaviorSubject({
...mockAutoConfirmState,
showBrowserNotification: false,
}),
);
autoConfirmService.canManageAutoConfirm$.mockReturnValue(new BehaviorSubject(true));
const result = await firstValueFrom(service.nudgeStatus$(NudgeType.AutoConfirmNudge, userId));
expect(result).toEqual({
hasBadgeDismissed: true,
hasSpotlightDismissed: true,
});
});
it("should return not dismissed when showBrowserNotification is true and user can manage", async () => {
autoConfirmService.configuration$.mockReturnValue(
new BehaviorSubject({
...mockAutoConfirmState,
showBrowserNotification: true,
}),
);
autoConfirmService.canManageAutoConfirm$.mockReturnValue(new BehaviorSubject(true));
const result = await firstValueFrom(service.nudgeStatus$(NudgeType.AutoConfirmNudge, userId));
expect(result).toEqual({
hasBadgeDismissed: false,
hasSpotlightDismissed: false,
});
});
it("should return not dismissed when showBrowserNotification is undefined and user can manage", async () => {
autoConfirmService.configuration$.mockReturnValue(
new BehaviorSubject({
...mockAutoConfirmState,
showBrowserNotification: undefined,
}),
);
autoConfirmService.canManageAutoConfirm$.mockReturnValue(new BehaviorSubject(true));
const result = await firstValueFrom(service.nudgeStatus$(NudgeType.AutoConfirmNudge, userId));
expect(result).toEqual({
hasBadgeDismissed: false,
hasSpotlightDismissed: false,
});
});
it("should return stored nudge status when badge is already dismissed", async () => {
await fakeStateProvider.getUser(userId, NUDGE_DISMISSED_DISK_KEY).update(() => ({
[NudgeType.AutoConfirmNudge]: {
hasBadgeDismissed: true,
hasSpotlightDismissed: false,
},
}));
autoConfirmService.configuration$.mockReturnValue(
new BehaviorSubject({
...mockAutoConfirmState,
showBrowserNotification: true,
}),
);
autoConfirmService.canManageAutoConfirm$.mockReturnValue(new BehaviorSubject(true));
const result = await firstValueFrom(service.nudgeStatus$(NudgeType.AutoConfirmNudge, userId));
expect(result).toEqual({
hasBadgeDismissed: true,
hasSpotlightDismissed: false,
});
});
it("should return stored nudge status when spotlight is already dismissed", async () => {
await fakeStateProvider.getUser(userId, NUDGE_DISMISSED_DISK_KEY).update(() => ({
[NudgeType.AutoConfirmNudge]: {
hasBadgeDismissed: false,
hasSpotlightDismissed: true,
},
}));
autoConfirmService.configuration$.mockReturnValue(
new BehaviorSubject({
...mockAutoConfirmState,
showBrowserNotification: true,
}),
);
autoConfirmService.canManageAutoConfirm$.mockReturnValue(new BehaviorSubject(true));
const result = await firstValueFrom(service.nudgeStatus$(NudgeType.AutoConfirmNudge, userId));
expect(result).toEqual({
hasBadgeDismissed: false,
hasSpotlightDismissed: true,
});
});
it("should return stored nudge status when both badge and spotlight are already dismissed", async () => {
await fakeStateProvider.getUser(userId, NUDGE_DISMISSED_DISK_KEY).update(() => ({
[NudgeType.AutoConfirmNudge]: {
hasBadgeDismissed: true,
hasSpotlightDismissed: true,
},
}));
autoConfirmService.configuration$.mockReturnValue(
new BehaviorSubject({
...mockAutoConfirmState,
showBrowserNotification: true,
}),
);
autoConfirmService.canManageAutoConfirm$.mockReturnValue(new BehaviorSubject(true));
const result = await firstValueFrom(service.nudgeStatus$(NudgeType.AutoConfirmNudge, userId));
expect(result).toEqual({
hasBadgeDismissed: true,
hasSpotlightDismissed: true,
});
});
it("should prioritize user permissions over showBrowserNotification setting", async () => {
await fakeStateProvider.getUser(userId, NUDGE_DISMISSED_DISK_KEY).update(() => ({
[NudgeType.AutoConfirmNudge]: {
hasBadgeDismissed: false,
hasSpotlightDismissed: false,
},
}));
autoConfirmService.configuration$.mockReturnValue(
new BehaviorSubject({
...mockAutoConfirmState,
showBrowserNotification: true,
}),
);
autoConfirmService.canManageAutoConfirm$.mockReturnValue(new BehaviorSubject(false));
const result = await firstValueFrom(service.nudgeStatus$(NudgeType.AutoConfirmNudge, userId));
expect(result).toEqual({
hasBadgeDismissed: true,
hasSpotlightDismissed: true,
});
});
it("should respect stored dismissal even when user cannot manage auto-confirm", async () => {
await fakeStateProvider.getUser(userId, NUDGE_DISMISSED_DISK_KEY).update(() => ({
[NudgeType.AutoConfirmNudge]: {
hasBadgeDismissed: true,
hasSpotlightDismissed: false,
},
}));
autoConfirmService.configuration$.mockReturnValue(new BehaviorSubject(mockAutoConfirmState));
autoConfirmService.canManageAutoConfirm$.mockReturnValue(new BehaviorSubject(false));
const result = await firstValueFrom(service.nudgeStatus$(NudgeType.AutoConfirmNudge, userId));
expect(result).toEqual({
hasBadgeDismissed: true,
hasSpotlightDismissed: true,
});
});
});
});

View File

@@ -0,0 +1,41 @@
import { inject, Injectable } from "@angular/core";
import { combineLatest, map, Observable } from "rxjs";
import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm";
import { UserId } from "@bitwarden/user-core";
import { DefaultSingleNudgeService } from "../default-single-nudge.service";
import { NudgeType, NudgeStatus } from "../nudges.service";
@Injectable({ providedIn: "root" })
export class AutoConfirmNudgeService extends DefaultSingleNudgeService {
autoConfirmService = inject(AutomaticUserConfirmationService);
nudgeStatus$(nudgeType: NudgeType, userId: UserId): Observable<NudgeStatus> {
return combineLatest([
this.getNudgeStatus$(nudgeType, userId),
this.autoConfirmService.configuration$(userId),
this.autoConfirmService.canManageAutoConfirm$(userId),
]).pipe(
map(([nudgeStatus, autoConfirmState, canManageAutoConfirm]) => {
if (!canManageAutoConfirm) {
return {
hasBadgeDismissed: true,
hasSpotlightDismissed: true,
};
}
if (nudgeStatus.hasBadgeDismissed || nudgeStatus.hasSpotlightDismissed) {
return nudgeStatus;
}
const dismissed = autoConfirmState.showBrowserNotification === false;
return {
hasBadgeDismissed: dismissed,
hasSpotlightDismissed: dismissed,
};
}),
);
}
}

View File

@@ -1,4 +1,5 @@
export * from "./account-security-nudge.service";
export * from "./auto-confirm-nudge.service";
export * from "./has-items-nudge.service";
export * from "./empty-vault-nudge.service";
export * from "./vault-settings-import-nudge.service";

View File

@@ -23,6 +23,7 @@ import {
AccountSecurityNudgeService,
VaultSettingsImportNudgeService,
} from "./custom-nudges-services";
import { AutoConfirmNudgeService } from "./custom-nudges-services/auto-confirm-nudge.service";
import { DefaultSingleNudgeService } from "./default-single-nudge.service";
import { NudgesService, NudgeType } from "./nudges.service";
@@ -35,6 +36,7 @@ describe("Vault Nudges Service", () => {
EmptyVaultNudgeService,
NewAccountNudgeService,
AccountSecurityNudgeService,
AutoConfirmNudgeService,
];
beforeEach(async () => {
@@ -73,6 +75,10 @@ describe("Vault Nudges Service", () => {
provide: VaultSettingsImportNudgeService,
useValue: mock<VaultSettingsImportNudgeService>(),
},
{
provide: AutoConfirmNudgeService,
useValue: mock<AutoConfirmNudgeService>(),
},
{
provide: ApiService,
useValue: mock<ApiService>(),

View File

@@ -12,6 +12,7 @@ import {
NewItemNudgeService,
AccountSecurityNudgeService,
VaultSettingsImportNudgeService,
AutoConfirmNudgeService,
NoOpNudgeService,
} from "./custom-nudges-services";
import { DefaultSingleNudgeService, SingleNudgeService } from "./default-single-nudge.service";
@@ -39,6 +40,7 @@ export const NudgeType = {
NewNoteItemStatus: "new-note-item-status",
NewSshItemStatus: "new-ssh-item-status",
GeneratorNudgeStatus: "generator-nudge-status",
AutoConfirmNudge: "auto-confirm-nudge",
PremiumUpgrade: "premium-upgrade",
} as const;
@@ -82,6 +84,7 @@ export class NudgesService {
[NudgeType.NewIdentityItemStatus]: this.newItemNudgeService,
[NudgeType.NewNoteItemStatus]: this.newItemNudgeService,
[NudgeType.NewSshItemStatus]: this.newItemNudgeService,
[NudgeType.AutoConfirmNudge]: inject(AutoConfirmNudgeService),
};
/**
@@ -148,6 +151,7 @@ export class NudgesService {
NudgeType.EmptyVaultNudge,
NudgeType.DownloadBitwarden,
NudgeType.AutofillNudge,
NudgeType.AutoConfirmNudge,
];
const nudgeTypesWithBadge$ = nudgeTypes.map((nudge) => {

View File

@@ -51,7 +51,8 @@ export class VaultFilter {
cipherPassesFilter = CipherViewLikeUtils.isDeleted(cipher);
}
if (this.status === "archive" && cipherPassesFilter) {
cipherPassesFilter = CipherViewLikeUtils.isArchived(cipher);
cipherPassesFilter =
CipherViewLikeUtils.isArchived(cipher) && !CipherViewLikeUtils.isDeleted(cipher);
}
if (this.cipherType != null && cipherPassesFilter) {
cipherPassesFilter = CipherViewLikeUtils.getType(cipher) === this.cipherType;

View File

@@ -14,8 +14,6 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SingleUserState, StateProvider } from "@bitwarden/common/platform/state";
import { UserId } from "@bitwarden/common/types/guid";
@@ -45,7 +43,6 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti
protected policyService: PolicyService,
protected stateProvider: StateProvider,
protected accountService: AccountService,
protected configService: ConfigService,
protected i18nService: I18nService,
) {}
@@ -116,18 +113,13 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti
),
);
const orgs = await this.buildOrganizations();
const defaulCollectionsFlagEnabled = await this.configService.getFeatureFlag(
FeatureFlag.CreateDefaultLocation,
);
let collections =
organizationId == null
? storedCollections
: storedCollections.filter((c) => c.organizationId === organizationId);
if (defaulCollectionsFlagEnabled) {
collections = sortDefaultCollections(collections, orgs, this.i18nService.collator);
}
collections = sortDefaultCollections(collections, orgs, this.i18nService.collator);
const nestedCollections = await this.collectionService.getAllNested(collections);
return new DynamicTreeNode<CollectionView>({