mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 22:33:35 +00:00
[PM-5957] CLI - List items long runtime (#9589)
* Initial checking of collect many * should update to better handle parameters * cleaning up event collection params * Adding documentation * Removing commented out code saved for testing * Adding pr changes and using the account service for event collection user id * browser main.background event collection service needed the account service
This commit is contained in:
@@ -869,6 +869,7 @@ export default class MainBackground {
|
|||||||
this.organizationService,
|
this.organizationService,
|
||||||
this.eventUploadService,
|
this.eventUploadService,
|
||||||
this.authService,
|
this.authService,
|
||||||
|
this.accountService,
|
||||||
);
|
);
|
||||||
this.totpService = new TotpService(this.cryptoFunctionService, this.logService);
|
this.totpService = new TotpService(this.cryptoFunctionService, this.logService);
|
||||||
|
|
||||||
|
|||||||
@@ -128,17 +128,7 @@ export class ListCommand {
|
|||||||
ciphers = this.searchService.searchCiphersBasic(ciphers, options.search, options.trash);
|
ciphers = this.searchService.searchCiphersBasic(ciphers, options.search, options.trash);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < ciphers.length; i++) {
|
await this.eventCollectionService.collectMany(EventType.Cipher_ClientViewed, ciphers, true);
|
||||||
const c = ciphers[i];
|
|
||||||
// Set upload immediately on the last item in the ciphers collection to avoid the event collection
|
|
||||||
// service from uploading each time.
|
|
||||||
await this.eventCollectionService.collect(
|
|
||||||
EventType.Cipher_ClientViewed,
|
|
||||||
c.id,
|
|
||||||
i === ciphers.length - 1,
|
|
||||||
c.organizationId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = new ListResponse(ciphers.map((o) => new CipherResponse(o)));
|
const res = new ListResponse(ciphers.map((o) => new CipherResponse(o)));
|
||||||
return Response.success(res);
|
return Response.success(res);
|
||||||
|
|||||||
@@ -729,6 +729,7 @@ export class ServiceContainer {
|
|||||||
this.organizationService,
|
this.organizationService,
|
||||||
this.eventUploadService,
|
this.eventUploadService,
|
||||||
this.authService,
|
this.authService,
|
||||||
|
this.accountService,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.providerApiService = new ProviderApiService(this.apiService);
|
this.providerApiService = new ProviderApiService(this.apiService);
|
||||||
|
|||||||
@@ -830,6 +830,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
OrganizationServiceAbstraction,
|
OrganizationServiceAbstraction,
|
||||||
EventUploadServiceAbstraction,
|
EventUploadServiceAbstraction,
|
||||||
AuthServiceAbstraction,
|
AuthServiceAbstraction,
|
||||||
|
AccountServiceAbstraction,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { EventType } from "../../enums";
|
import { EventType } from "../../enums";
|
||||||
|
import { CipherView } from "../../vault/models/view/cipher.view";
|
||||||
|
|
||||||
export abstract class EventCollectionService {
|
export abstract class EventCollectionService {
|
||||||
|
collectMany: (
|
||||||
|
eventType: EventType,
|
||||||
|
ciphers: CipherView[],
|
||||||
|
uploadImmediately?: boolean,
|
||||||
|
) => Promise<any>;
|
||||||
collect: (
|
collect: (
|
||||||
eventType: EventType,
|
eventType: EventType,
|
||||||
cipherId?: string,
|
cipherId?: string,
|
||||||
|
|||||||
@@ -1,25 +1,76 @@
|
|||||||
import { firstValueFrom, map, from, zip } from "rxjs";
|
import { firstValueFrom, map, from, zip, Observable } from "rxjs";
|
||||||
|
|
||||||
import { EventCollectionService as EventCollectionServiceAbstraction } from "../../abstractions/event/event-collection.service";
|
import { EventCollectionService as EventCollectionServiceAbstraction } from "../../abstractions/event/event-collection.service";
|
||||||
import { EventUploadService } from "../../abstractions/event/event-upload.service";
|
import { EventUploadService } from "../../abstractions/event/event-upload.service";
|
||||||
import { OrganizationService } from "../../admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "../../admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
|
import { AccountService } from "../../auth/abstractions/account.service";
|
||||||
import { AuthService } from "../../auth/abstractions/auth.service";
|
import { AuthService } from "../../auth/abstractions/auth.service";
|
||||||
import { AuthenticationStatus } from "../../auth/enums/authentication-status";
|
import { AuthenticationStatus } from "../../auth/enums/authentication-status";
|
||||||
import { EventType } from "../../enums";
|
import { EventType } from "../../enums";
|
||||||
import { EventData } from "../../models/data/event.data";
|
import { EventData } from "../../models/data/event.data";
|
||||||
import { StateProvider } from "../../platform/state";
|
import { StateProvider } from "../../platform/state";
|
||||||
import { CipherService } from "../../vault/abstractions/cipher.service";
|
import { CipherService } from "../../vault/abstractions/cipher.service";
|
||||||
|
import { CipherView } from "../../vault/models/view/cipher.view";
|
||||||
|
|
||||||
import { EVENT_COLLECTION } from "./key-definitions";
|
import { EVENT_COLLECTION } from "./key-definitions";
|
||||||
|
|
||||||
export class EventCollectionService implements EventCollectionServiceAbstraction {
|
export class EventCollectionService implements EventCollectionServiceAbstraction {
|
||||||
|
private orgIds$: Observable<string[]>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private cipherService: CipherService,
|
private cipherService: CipherService,
|
||||||
private stateProvider: StateProvider,
|
private stateProvider: StateProvider,
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
private eventUploadService: EventUploadService,
|
private eventUploadService: EventUploadService,
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
) {}
|
private accountService: AccountService,
|
||||||
|
) {
|
||||||
|
this.orgIds$ = this.organizationService.organizations$.pipe(
|
||||||
|
map((orgs) => orgs?.filter((o) => o.useEvents)?.map((x) => x.id) ?? []),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Adds an event to the active user's event collection
|
||||||
|
* @param eventType the event type to be added
|
||||||
|
* @param ciphers The collection of ciphers to log events for
|
||||||
|
* @param uploadImmediately in some cases the recorded events should be uploaded right after being added
|
||||||
|
*/
|
||||||
|
async collectMany(
|
||||||
|
eventType: EventType,
|
||||||
|
ciphers: CipherView[],
|
||||||
|
uploadImmediately = false,
|
||||||
|
): Promise<any> {
|
||||||
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)));
|
||||||
|
const eventStore = this.stateProvider.getUser(userId, EVENT_COLLECTION);
|
||||||
|
|
||||||
|
if (!(await this.shouldUpdate(null, eventType, ciphers))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const events$ = this.orgIds$.pipe(
|
||||||
|
map((orgs) =>
|
||||||
|
ciphers
|
||||||
|
.filter((c) => orgs.includes(c.organizationId))
|
||||||
|
.map((c) => ({
|
||||||
|
type: eventType,
|
||||||
|
cipherId: c.id,
|
||||||
|
date: new Date().toISOString(),
|
||||||
|
organizationId: c.organizationId,
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await eventStore.update(
|
||||||
|
(currentEvents, newEvents) => [...(currentEvents ?? []), ...newEvents],
|
||||||
|
{
|
||||||
|
combineLatestWith: events$,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (uploadImmediately) {
|
||||||
|
await this.eventUploadService.uploadEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Adds an event to the active user's event collection
|
/** Adds an event to the active user's event collection
|
||||||
* @param eventType the event type to be added
|
* @param eventType the event type to be added
|
||||||
@@ -33,10 +84,10 @@ export class EventCollectionService implements EventCollectionServiceAbstraction
|
|||||||
uploadImmediately = false,
|
uploadImmediately = false,
|
||||||
organizationId: string = null,
|
organizationId: string = null,
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const userId = await firstValueFrom(this.stateProvider.activeUserId$);
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)));
|
||||||
const eventStore = this.stateProvider.getUser(userId, EVENT_COLLECTION);
|
const eventStore = this.stateProvider.getUser(userId, EVENT_COLLECTION);
|
||||||
|
|
||||||
if (!(await this.shouldUpdate(cipherId, organizationId, eventType))) {
|
if (!(await this.shouldUpdate(organizationId, eventType, undefined, cipherId))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,18 +113,15 @@ export class EventCollectionService implements EventCollectionServiceAbstraction
|
|||||||
* @param organizationId the organization for the event
|
* @param organizationId the organization for the event
|
||||||
*/
|
*/
|
||||||
private async shouldUpdate(
|
private async shouldUpdate(
|
||||||
cipherId: string = null,
|
|
||||||
organizationId: string = null,
|
organizationId: string = null,
|
||||||
eventType: EventType = null,
|
eventType: EventType = null,
|
||||||
|
ciphers: CipherView[] = [],
|
||||||
|
cipherId?: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const orgIds$ = this.organizationService.organizations$.pipe(
|
|
||||||
map((orgs) => orgs?.filter((o) => o.useEvents)?.map((x) => x.id) ?? []),
|
|
||||||
);
|
|
||||||
|
|
||||||
const cipher$ = from(this.cipherService.get(cipherId));
|
const cipher$ = from(this.cipherService.get(cipherId));
|
||||||
|
|
||||||
const [authStatus, orgIds, cipher] = await firstValueFrom(
|
const [authStatus, orgIds, cipher] = await firstValueFrom(
|
||||||
zip(this.authService.activeAccountStatus$, orgIds$, cipher$),
|
zip(this.authService.activeAccountStatus$, this.orgIds$, cipher$),
|
||||||
);
|
);
|
||||||
|
|
||||||
// The user must be authorized
|
// The user must be authorized
|
||||||
@@ -91,14 +139,21 @@ export class EventCollectionService implements EventCollectionServiceAbstraction
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the cipher is null there must be an organization id provided
|
// If the cipherId was provided and a cipher exists, add it to the collection
|
||||||
if (cipher == null && organizationId == null) {
|
if (cipher != null) {
|
||||||
|
ciphers.push(new CipherView(cipher));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no ciphers there must be an organization id provided
|
||||||
|
if ((ciphers == null || ciphers.length == 0) && organizationId == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the cipher is present it must be in the user's org list
|
// If the input list of ciphers is provided. Check the ciphers to see if any
|
||||||
if (cipher != null && !orgIds.includes(cipher?.organizationId)) {
|
// are in the user's org list
|
||||||
return false;
|
if (ciphers != null && ciphers.length > 0) {
|
||||||
|
const filtered = ciphers.filter((c) => orgIds.includes(c.organizationId));
|
||||||
|
return filtered.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the organization id is provided it must be in the user's org list
|
// If the organization id is provided it must be in the user's org list
|
||||||
|
|||||||
Reference in New Issue
Block a user