1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-10-28 07:49:41 +01:00

WIP updating callers

This commit is contained in:
Thomas Rittson 2024-10-28 11:06:27 +10:00
parent 902f49eb00
commit 5b0003b467
No known key found for this signature in database
GPG Key ID: CDDDA03861C35E27
20 changed files with 173 additions and 74 deletions

View File

@ -8,6 +8,7 @@ import { Observable, combineLatest, first, map, switchMap } from "rxjs";
import { CollectionService } from "@bitwarden/admin-console/common";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
@ -49,6 +50,8 @@ export class AssignCollections {
/** Params needed to populate the assign collections component */
params: CollectionAssignmentParams;
private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
constructor(
private location: Location,
private collectionService: CollectionService,
@ -70,7 +73,7 @@ export class AssignCollections {
),
);
combineLatest([cipher$, this.collectionService.decryptedCollections$])
combineLatest([cipher$, this.collectionService.decryptedCollections$(this.activeUserId$)])
.pipe(takeUntilDestroyed(), first())
.subscribe(([cipherView, collections]) => {
let availableCollections = collections.filter((c) => !c.readOnly);

View File

@ -22,6 +22,9 @@ import { BrowserApi } from "../../../../platform/browser/browser-api";
import BrowserPopupUtils from "../../../../platform/popup/browser-popup-utils";
import { VaultBrowserStateService } from "../../../services/vault-browser-state.service";
import { VaultFilterService } from "../../../services/vault-filter.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { firstValueFrom } from "rxjs";
const ComponentId = "VaultItemsComponent";
@ -49,6 +52,8 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
private applySavedState = true;
private scrollingContainer = "cdk-virtual-scroll-viewport";
private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
constructor(
searchService: SearchService,
private organizationService: OrganizationService,
@ -64,6 +69,7 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
private platformUtilsService: PlatformUtilsService,
cipherService: CipherService,
private vaultFilterService: VaultFilterService,
private accountService: AccountService,
) {
super(searchService, cipherService);
this.applySavedState =
@ -133,7 +139,10 @@ export class VaultItemsComponent extends BaseVaultItemsComponent implements OnIn
this.showVaultFilter = false;
this.collectionId = params.collectionId;
this.searchPlaceholder = this.i18nService.t("searchCollection");
const collectionNode = await this.collectionService.getNested(this.collectionId);
const allCollections = await firstValueFrom(
this.collectionService.decryptedCollections$(this.activeUserId$),
);
const collectionNode = this.collectionService.getNested(allCollections, this.collectionId);
if (collectionNode != null && collectionNode.node != null) {
this.groupingTitle = collectionNode.node.name;
this.nestedCollections =

View File

@ -8,8 +8,8 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { ProductTierType } from "@bitwarden/common/billing/enums";
import { SyncService } from "@bitwarden/common/platform/sync";
import { ObservableTracker } from "@bitwarden/common/spec";
import { CipherId } from "@bitwarden/common/types/guid";
import { mockAccountServiceWith, ObservableTracker } from "@bitwarden/common/spec";
import { CipherId, UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import { CipherType } from "@bitwarden/common/vault/enums";
@ -20,6 +20,7 @@ import { BrowserApi } from "../../../platform/browser/browser-api";
import { VaultPopupAutofillService } from "./vault-popup-autofill.service";
import { VaultPopupItemsService } from "./vault-popup-items.service";
import { VaultPopupListFiltersService } from "./vault-popup-list-filters.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
describe("VaultPopupItemsService", () => {
let testBed: TestBed;
@ -39,6 +40,7 @@ describe("VaultPopupItemsService", () => {
const collectionService = mock<CollectionService>();
const vaultAutofillServiceMock = mock<VaultPopupAutofillService>();
const syncServiceMock = mock<SyncService>();
const accountServiceMock = mockAccountServiceWith("UserId" as UserId);
beforeEach(() => {
allCiphers = cipherFactory(10);
@ -90,7 +92,7 @@ describe("VaultPopupItemsService", () => {
];
organizationServiceMock.organizations$ = new BehaviorSubject([mockOrg]);
collectionService.decryptedCollections$ = new BehaviorSubject(mockCollections);
collectionService.decryptedCollections$.mockReturnValue(new BehaviorSubject(mockCollections));
activeUserLastSync$ = new BehaviorSubject(new Date());
syncServiceMock.activeUserLastSync$.mockReturnValue(activeUserLastSync$);
@ -105,6 +107,7 @@ describe("VaultPopupItemsService", () => {
{ provide: CollectionService, useValue: collectionService },
{ provide: VaultPopupAutofillService, useValue: vaultAutofillServiceMock },
{ provide: SyncService, useValue: syncServiceMock },
{ provide: AccountService, useValue: accountServiceMock },
],
});

View File

@ -36,6 +36,8 @@ import { PopupCipherView } from "../views/popup-cipher.view";
import { VaultPopupAutofillService } from "./vault-popup-autofill.service";
import { MY_VAULT_ID, VaultPopupListFiltersService } from "./vault-popup-list-filters.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
/**
* Service for managing the various item lists on the new Vault tab in the browser popup.
@ -45,6 +47,7 @@ import { MY_VAULT_ID, VaultPopupListFiltersService } from "./vault-popup-list-fi
})
export class VaultPopupItemsService {
private _searchText$ = new BehaviorSubject<string>("");
private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
/**
* Subject that emits whenever new ciphers are being processed/filtered.
@ -90,7 +93,7 @@ export class VaultPopupItemsService {
switchMap((ciphers) =>
combineLatest([
this.organizationService.organizations$,
this.collectionService.decryptedCollections$,
this.collectionService.decryptedCollections$(this.activeUserId$),
]).pipe(
map(([organizations, collections]) => {
const orgMap = Object.fromEntries(organizations.map((org) => [org.id, org]));
@ -260,6 +263,7 @@ export class VaultPopupItemsService {
private collectionService: CollectionService,
private vaultPopupAutofillService: VaultPopupAutofillService,
private syncService: SyncService,
private accountService: AccountService,
) {}
applyFilter(newSearchText: string) {

View File

@ -7,11 +7,15 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
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 { ProductTierType } from "@bitwarden/common/billing/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { mockAccountServiceWith } from "@bitwarden/common/spec";
import { UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherType } from "@bitwarden/common/vault/enums";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
@ -26,8 +30,8 @@ describe("VaultPopupListFiltersService", () => {
const policyAppliesToActiveUser$ = new BehaviorSubject<boolean>(false);
const collectionService = {
decryptedCollections$,
getAllNested: () => Promise.resolve([]),
decryptedCollections$: () => decryptedCollections$,
getAllNested: () => [] as TreeNode<CollectionView>[],
} as unknown as CollectionService;
const folderService = {
@ -50,13 +54,15 @@ describe("VaultPopupListFiltersService", () => {
policyAppliesToActiveUser$: jest.fn(() => policyAppliesToActiveUser$),
};
const accountService = mockAccountServiceWith("userId" as UserId);
beforeEach(() => {
memberOrganizations$.next([]);
decryptedCollections$.next([]);
policyAppliesToActiveUser$.next(false);
policyService.policyAppliesToActiveUser$.mockClear();
collectionService.getAllNested = () => Promise.resolve([]);
collectionService.getAllNested = () => [] as TreeNode<CollectionView>[];
TestBed.configureTestingModule({
providers: [
{
@ -84,6 +90,10 @@ describe("VaultPopupListFiltersService", () => {
useValue: policyService,
},
{ provide: FormBuilder, useClass: FormBuilder },
{
provide: AccountService,
useValue: accountService,
},
],
});
@ -276,13 +286,11 @@ describe("VaultPopupListFiltersService", () => {
decryptedCollections$.next(testCollections);
collectionService.getAllNested = () =>
Promise.resolve(
testCollections.map((c) => ({
children: [],
node: c,
parent: null,
})),
);
testCollections.map((c) => ({
children: [],
node: c,
parent: null,
}));
});
it("returns all collections", (done) => {

View File

@ -1,15 +1,7 @@
import { Injectable } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { FormBuilder } from "@angular/forms";
import {
combineLatest,
distinctUntilChanged,
map,
Observable,
startWith,
switchMap,
tap,
} from "rxjs";
import { combineLatest, distinctUntilChanged, map, Observable, startWith, tap } from "rxjs";
import { CollectionService, Collection, CollectionView } from "@bitwarden/admin-console/common";
import { DynamicTreeNode } from "@bitwarden/angular/vault/vault-filter/models/dynamic-tree-node.model";
@ -17,6 +9,8 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
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 { ProductTierType } from "@bitwarden/common/billing/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
@ -81,6 +75,8 @@ export class VaultPopupListFiltersService {
map((ciphers) => Object.values(ciphers)),
);
private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
constructor(
private folderService: FolderService,
private cipherService: CipherService,
@ -89,6 +85,7 @@ export class VaultPopupListFiltersService {
private collectionService: CollectionService,
private formBuilder: FormBuilder,
private policyService: PolicyService,
private accountService: AccountService,
) {
this.filterForm.controls.organization.valueChanges
.pipe(takeUntilDestroyed())
@ -302,7 +299,7 @@ export class VaultPopupListFiltersService {
previousFilter.organization?.id === currentFilter.organization?.id,
),
),
this.collectionService.decryptedCollections$,
this.collectionService.decryptedCollections$(this.activeUserId$),
]).pipe(
map(([filters, allCollections]) => {
const organizationId = filters.organization?.id ?? null;
@ -314,8 +311,8 @@ export class VaultPopupListFiltersService {
return collections;
}),
switchMap(async (collections) => {
const nestedCollections = await this.collectionService.getAllNested(collections);
map((collections) => {
const nestedCollections = this.collectionService.getAllNested(collections);
return new DynamicTreeNode<CollectionView>({
fullList: collections,

View File

@ -22,7 +22,7 @@ export class VaultFilterService extends BaseVaultFilterService {
collectionService: CollectionService,
policyService: PolicyService,
stateProvider: StateProvider,
private accountService: AccountService,
accountService: AccountService,
) {
super(
organizationService,
@ -31,6 +31,7 @@ export class VaultFilterService extends BaseVaultFilterService {
collectionService,
policyService,
stateProvider,
accountService,
);
this.vaultFilter.myVaultOnly = false;
this.vaultFilter.selectedOrganizationId = null;

View File

@ -1,10 +1,11 @@
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { firstValueFrom, map } from "rxjs";
import { firstValueFrom } from "rxjs";
import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
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 { 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";
@ -25,6 +26,8 @@ export class CollectionsComponent implements OnInit {
collections: CollectionView[] = [];
organization: Organization;
protected activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
protected cipherDomain: Cipher;
constructor(
@ -45,9 +48,7 @@ export class CollectionsComponent implements OnInit {
async load() {
this.cipherDomain = await this.loadCipher();
this.collectionIds = this.loadCipherCollections();
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
const activeUserId = await firstValueFrom(this.activeUserId$);
this.cipher = await this.cipherDomain.decrypt(
await this.cipherService.getKeyForCipherKeyDecryption(this.cipherDomain, activeUserId),
);
@ -113,7 +114,9 @@ export class CollectionsComponent implements OnInit {
}
protected async loadCollections() {
const allCollections = await this.collectionService.getAllDecrypted();
const allCollections = await firstValueFrom(
this.collectionService.decryptedCollections$(this.activeUserId$),
);
return allCollections.filter(
(c) => !c.readOnly && c.organizationId === this.cipher.organizationId,
);

View File

@ -6,6 +6,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { OrganizationUserStatusType } 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 { 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";
@ -26,6 +27,7 @@ export class ShareComponent implements OnInit, OnDestroy {
organizations$: Observable<Organization[]>;
protected writeableCollections: Checkable<CollectionView>[] = [];
protected activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
private _destroy = new Subject<void>();
@ -49,7 +51,9 @@ export class ShareComponent implements OnInit, OnDestroy {
}
async load() {
const allCollections = await this.collectionService.getAllDecrypted();
const allCollections = await firstValueFrom(
this.collectionService.decryptedCollections$(this.activeUserId$),
);
this.writeableCollections = allCollections.map((c) => c).filter((c) => !c.readOnly);
this.organizations$ = this.organizationService.memberOrganizations$.pipe(

View File

@ -39,6 +39,7 @@ import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.
import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
import { DialogService } from "@bitwarden/components";
import { PasswordRepromptService } from "@bitwarden/vault";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
@Directive()
export class AddEditComponent implements OnInit, OnDestroy {
@ -98,6 +99,8 @@ export class AddEditComponent implements OnInit, OnDestroy {
private personalOwnershipPolicyAppliesToActiveUser: boolean;
private previousCipherId: string;
protected activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
get fido2CredentialCreationDateValue(): string {
const dateCreated = this.i18nService.t("dateCreated");
const creationDate = this.datePipe.transform(
@ -253,9 +256,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
if (this.cipher == null) {
if (this.editMode) {
const cipher = await this.loadCipher();
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
const activeUserId = await firstValueFrom(this.activeUserId$);
this.cipher = await cipher.decrypt(
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
);
@ -391,9 +392,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
this.cipher.id = null;
}
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
const activeUserId = await firstValueFrom(this.activeUserId$);
const cipher = await this.encryptCipher(activeUserId);
try {
this.formPromise = this.saveCipher(cipher);
@ -672,7 +671,9 @@ export class AddEditComponent implements OnInit, OnDestroy {
}
protected async loadCollections() {
const allCollections = await this.collectionService.getAllDecrypted();
const allCollections = await firstValueFrom(
this.collectionService.decryptedCollections$(this.activeUserId$),
);
return allCollections.filter((c) => !c.readOnly);
}

View File

@ -9,6 +9,8 @@ import {
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
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 { ActiveUserState, StateProvider } from "@bitwarden/common/platform/state";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
@ -30,6 +32,8 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti
private readonly collapsedGroupings$: Observable<Set<string>> =
this.collapsedGroupingsState.state$.pipe(map((c) => new Set(c)));
protected activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
constructor(
protected organizationService: OrganizationService,
protected folderService: FolderService,
@ -37,6 +41,7 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti
protected collectionService: CollectionService,
protected policyService: PolicyService,
protected stateProvider: StateProvider,
protected accountService: AccountService,
) {}
async storeCollapsedFilterNodes(collapsedFilterNodes: Set<string>): Promise<void> {
@ -85,14 +90,16 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti
}
async buildCollections(organizationId?: string): Promise<DynamicTreeNode<CollectionView>> {
const storedCollections = await this.collectionService.getAllDecrypted();
const storedCollections = await firstValueFrom(
this.collectionService.decryptedCollections$(this.activeUserId$),
);
let collections: CollectionView[];
if (organizationId != null) {
collections = storedCollections.filter((c) => c.organizationId === organizationId);
} else {
collections = storedCollections;
}
const nestedCollections = await this.collectionService.getAllNested(collections);
const nestedCollections = this.collectionService.getAllNested(collections);
return new DynamicTreeNode<CollectionView>({
fullList: collections,
nestedList: nestedCollections,

View File

@ -45,6 +45,24 @@ const LOGGED_OUT_INFO: AccountInfo = {
name: undefined,
};
/**
* An rxjs map operator that extracts the UserId from an account, or throws if the account or UserId are null.
*/
export const getUserId = map<{ id: UserId | undefined }, UserId>((account) => {
if (account?.id == null) {
throw new Error("Null account or account ID");
}
return account.id;
});
/**
* An rxjs map operator that extracts the UserId from an account, or returns undefined if the account or UserId are null.
*/
export const getOptionalUserId = map<{ id: UserId | undefined }, UserId | undefined>(
(account) => account?.id ?? undefined,
);
export class AccountServiceImplementation implements InternalAccountService {
private accountsState: GlobalState<Record<UserId, AccountInfo>>;
private activeAccountIdState: GlobalState<UserId | undefined>;

View File

@ -6,6 +6,7 @@ import { ApiService } from "../../abstractions/api.service";
import { AccountService } from "../../auth/abstractions/account.service";
import { AuthService } from "../../auth/abstractions/auth.service";
import { AuthenticationStatus } from "../../auth/enums/authentication-status";
import { getUserId } from "../../auth/services/account.service";
import {
SyncCipherNotification,
SyncFolderNotification,
@ -149,7 +150,10 @@ export abstract class CoreSyncService implements SyncService {
notification.collectionIds != null &&
notification.collectionIds.length > 0
) {
const collections = await this.collectionService.getAll();
const activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
const collections = await firstValueFrom(
this.collectionService.encryptedCollections$(activeUserId$),
);
if (collections != null) {
for (let i = 0; i < collections.length; i++) {
if (notification.collectionIds.indexOf(collections[i].id) > -1) {

View File

@ -134,9 +134,13 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
if (userId == null || userId === currentUserId) {
await this.searchService.clearIndex();
await this.folderService.clearCache();
await this.collectionService.clearActiveUserCache();
}
// TODO: is the userId ever null here? Surely a null userId cannot be authenticated?
// Also MasterPasswordService throws an exception if the userId is null and hasn't caused any issues
// TODO: why do these other services clear lockingUserId and not userId?
await this.collectionService.clearDecryptedState(userId);
await this.masterPasswordService.clearMasterKey(lockingUserId);
await this.stateService.setUserKeyAutoUnlock(null, { userId: lockingUserId });

View File

@ -72,6 +72,7 @@ import {
ImportSuccessDialogComponent,
} from "./dialog";
import { ImportLastPassComponent } from "./lastpass";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
const safeProviders: SafeProvider[] = [
safeProvider({
@ -129,6 +130,8 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
collections$: Observable<CollectionView[]>;
organizations$: Observable<Organization[]>;
protected activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
private _organizationId: string;
get organizationId(): string {
@ -205,6 +208,7 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
@Optional()
protected importCollectionService: ImportCollectionServiceAbstraction,
protected toastService: ToastService,
protected accountService: AccountService,
) {}
protected get importBlockedByPolicy(): boolean {
@ -272,15 +276,15 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit {
}
if (value) {
this.collections$ = Utils.asyncToObservable(() =>
this.collectionService
.getAllDecrypted()
.then((decryptedCollections) =>
this.collections$ = this.collectionService
.decryptedCollections$(this.activeUserId$)
.pipe(
map((decryptedCollections) =>
decryptedCollections
.filter((c2) => c2.organizationId === value && c2.manage)
.sort(Utils.getSortFunction(this.i18nService, "name")),
),
);
);
}
});
this.formGroup.controls.vaultSelector.setValue("myVault");

View File

@ -33,11 +33,14 @@ import {
import { BaseVaultExportService } from "./base-vault-export.service";
import { OrganizationVaultExportServiceAbstraction } from "./org-vault-export.service.abstraction";
import { ExportFormat } from "./vault-export.service.abstraction";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
export class OrganizationVaultExportService
extends BaseVaultExportService
implements OrganizationVaultExportServiceAbstraction
{
protected activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
constructor(
private cipherService: CipherService,
private apiService: ApiService,
@ -185,9 +188,13 @@ export class OrganizationVaultExportService
const promises = [];
promises.push(
this.collectionService.getAllDecrypted().then(async (collections) => {
decCollections = collections.filter((c) => c.organizationId == organizationId && c.manage);
}),
firstValueFrom(this.collectionService.decryptedCollections$(this.activeUserId$)).then(
(collections) => {
decCollections = collections.filter(
(c) => c.organizationId == organizationId && c.manage,
);
},
),
);
promises.push(
@ -217,9 +224,13 @@ export class OrganizationVaultExportService
const promises = [];
promises.push(
this.collectionService.getAll().then((collections) => {
encCollections = collections.filter((c) => c.organizationId == organizationId && c.manage);
}),
firstValueFrom(this.collectionService.encryptedCollections$(this.activeUserId$)).then(
(collections) => {
encCollections = collections.filter(
(c) => c.organizationId == organizationId && c.manage,
);
},
),
);
promises.push(

View File

@ -21,6 +21,8 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
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 { EventType } from "@bitwarden/common/enums";
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@ -150,6 +152,8 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
{ name: ".json (Encrypted)", value: "encrypted_json" },
];
protected activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
private destroy$ = new Subject<void>();
private onlyManagedCollections = true;
@ -167,6 +171,7 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
protected dialogService: DialogService,
protected organizationService: OrganizationService,
private collectionService: CollectionService,
protected accountService: AccountService,
) {}
async ngOnInit() {
@ -204,7 +209,7 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
}
this.organizations$ = combineLatest({
collections: this.collectionService.decryptedCollections$,
collections: this.collectionService.decryptedCollections$(this.activeUserId$),
memberOrganizations: this.organizationService.memberOrganizations$,
}).pipe(
map(({ collections, memberOrganizations }) => {

View File

@ -5,6 +5,8 @@ import { CollectionService } from "@bitwarden/admin-console/common";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { OrganizationUserStatusType, PolicyType } from "@bitwarden/common/admin-console/enums";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { CipherId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
@ -29,6 +31,9 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService {
private cipherService: CipherService = inject(CipherService);
private folderService: FolderService = inject(FolderService);
private collectionService: CollectionService = inject(CollectionService);
private accountService = inject(AccountService);
protected activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
async buildConfig(
mode: CipherFormMode,
@ -39,9 +44,9 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService {
await firstValueFrom(
combineLatest([
this.organizations$,
this.collectionService.encryptedCollections$.pipe(
this.collectionService.encryptedCollections$(this.activeUserId$).pipe(
switchMap((c) =>
this.collectionService.decryptedCollections$.pipe(
this.collectionService.decryptedCollections$(this.activeUserId$).pipe(
filter((d) => d.length === c.length), // Ensure all collections have been decrypted
),
),

View File

@ -1,13 +1,14 @@
import { CommonModule } from "@angular/common";
import { Component, Input, OnChanges, OnDestroy } from "@angular/core";
import { firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs";
import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
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 { isCardExpired } from "@bitwarden/common/autofill/utils";
import { CollectionId } from "@bitwarden/common/types/guid";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
@ -56,10 +57,13 @@ export class CipherViewComponent implements OnChanges, OnDestroy {
private destroyed$: Subject<void> = new Subject();
cardIsExpired: boolean = false;
private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
constructor(
private organizationService: OrganizationService,
private collectionService: CollectionService,
private folderService: FolderService,
private accountService: AccountService,
) {}
async ngOnChanges() {
@ -98,9 +102,13 @@ export class CipherViewComponent implements OnChanges, OnDestroy {
(!this.collections || this.collections.length === 0)
) {
this.collections = await firstValueFrom(
this.collectionService.decryptedCollectionViews$(
this.cipher.collectionIds as CollectionId[],
),
this.collectionService
.decryptedCollections$(this.activeUserId$)
.pipe(
map((allCollections) =>
allCollections.filter((c) => this.cipher.collectionIds.includes(c.id)),
),
),
);
}

View File

@ -28,8 +28,9 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction
import { OrganizationUserStatusType } 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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
import { CipherId, CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import {
@ -170,7 +171,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
private get selectedOrgId(): OrganizationId {
return this.formGroup.getRawValue().selectedOrg || this.params.organizationId;
}
private activeUserId: UserId;
private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
private destroy$ = new Subject<void>();
constructor(
@ -184,10 +185,6 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
) {}
async ngOnInit() {
this.activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
const onlyPersonalItems = this.params.ciphers.every((c) => c.organizationId == null);
if (this.selectedOrgId === MY_VAULT_ID || onlyPersonalItems) {
@ -405,7 +402,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
*/
private getCollectionsForOrganization(orgId: OrganizationId): Observable<CollectionView[]> {
return combineLatest([
this.collectionService.decryptedCollections$,
this.collectionService.decryptedCollections$(this.activeUserId$),
this.organizationService.organizations$,
]).pipe(
map(([collections, organizations]) => {
@ -429,7 +426,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
shareableCiphers,
organizationId,
selectedCollectionIds,
this.activeUserId,
await firstValueFrom(this.activeUserId$),
);
this.toastService.showToast({
@ -470,7 +467,10 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI
private async updateAssignedCollections(cipherView: CipherView) {
const { collections } = this.formGroup.getRawValue();
cipherView.collectionIds = collections.map((i) => i.id as CollectionId);
const cipher = await this.cipherService.encrypt(cipherView, this.activeUserId);
const cipher = await this.cipherService.encrypt(
cipherView,
await firstValueFrom(this.activeUserId$),
);
if (this.params.isSingleCipherAdmin) {
await this.cipherService.saveCollectionsWithServerAdmin(cipher);
} else {