mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +00:00
implement user event collector
This commit is contained in:
@@ -3,7 +3,7 @@ import { BehaviorSubject, EMPTY, filter, find, from, Observable } from "rxjs";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { Account } from "../../auth/abstractions/account.service";
|
||||
import { UserEventLogProvider } from "../log/logger";
|
||||
import { UserEventCollector } from "../log/user-event-collector";
|
||||
|
||||
import { AchievementHub } from "./achievement-hub";
|
||||
import { AchievementService as AchievementServiceAbstraction } from "./achievement.service.abstraction";
|
||||
@@ -21,7 +21,7 @@ import { SendItemCreatedCountValidator } from "./validators/send-item-created-co
|
||||
import { VaultItemCreatedCountValidator } from "./validators/vault-item-created-count-validator";
|
||||
|
||||
export class NextAchievementService implements AchievementServiceAbstraction {
|
||||
constructor(private readonly eventLogs: UserEventLogProvider) {}
|
||||
constructor(private readonly eventLogs: UserEventCollector) {}
|
||||
|
||||
private hubs = new Map<string, AchievementHub>();
|
||||
|
||||
@@ -35,7 +35,7 @@ export class NextAchievementService implements AchievementServiceAbstraction {
|
||||
|
||||
// FIXME: load stored achievements
|
||||
const achievements$ = from([] as AchievementEvent[]);
|
||||
const events$ = this.eventLogs.monitor$(account);
|
||||
const events$ = this.eventLogs.events$(account);
|
||||
const hub = new AchievementHub(validators$, events$, achievements$);
|
||||
|
||||
this.hubs.set(account.id, hub);
|
||||
|
||||
48
libs/common/src/tools/log/default-user-event-collector.ts
Normal file
48
libs/common/src/tools/log/default-user-event-collector.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Observable, Subject } from "rxjs";
|
||||
|
||||
import { Account } from "../../auth/abstractions/account.service";
|
||||
import { AppIdService } from "../../platform/abstractions/app-id.service";
|
||||
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { UserActionEvent } from "../achievements/types";
|
||||
|
||||
import { UserEventMonitor } from "./user-event-monitor";
|
||||
|
||||
export class DefaultUserEventCollector {
|
||||
private eventStreams = new Map<UserId, Subject<UserActionEvent>>();
|
||||
|
||||
constructor(
|
||||
private idService: AppIdService,
|
||||
private utilService: PlatformUtilsService,
|
||||
) {}
|
||||
|
||||
private getStream(account: Account) {
|
||||
let events$ = this.eventStreams.get(account.id);
|
||||
if (!events$) {
|
||||
// FIXME: this should include a ring buffer and spool
|
||||
// when the buffer is full so that user action events
|
||||
// are not lost. Don't forget encryption...
|
||||
events$ = new Subject<UserActionEvent>();
|
||||
this.eventStreams.set(account.id, events$);
|
||||
}
|
||||
|
||||
return events$;
|
||||
}
|
||||
|
||||
monitor(account: Account): UserEventMonitor {
|
||||
const events$ = this.getStream(account);
|
||||
|
||||
const logger = new UserEventMonitor(
|
||||
this.idService,
|
||||
this.utilService,
|
||||
account,
|
||||
Date.now,
|
||||
events$,
|
||||
);
|
||||
return logger;
|
||||
}
|
||||
|
||||
events$(account: Account): Observable<UserActionEvent> {
|
||||
return this.getStream(account).asObservable();
|
||||
}
|
||||
}
|
||||
11
libs/common/src/tools/log/user-event-collector.ts
Normal file
11
libs/common/src/tools/log/user-event-collector.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { Account } from "../../auth/abstractions/account.service";
|
||||
import { UserActionEvent } from "../achievements/types";
|
||||
|
||||
import { UserEventMonitor } from "./user-event-monitor";
|
||||
|
||||
export abstract class UserEventCollector {
|
||||
abstract monitor: (account: Account) => UserEventMonitor;
|
||||
abstract events$: (account: Account) => Observable<UserActionEvent>;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BehaviorSubject, Observable, SubjectLike, from, map, zip } from "rxjs";
|
||||
import { BehaviorSubject, SubjectLike, from, map, zip } from "rxjs";
|
||||
import { Primitive } from "type-fest";
|
||||
|
||||
import { Account } from "../../auth/abstractions/account.service";
|
||||
@@ -10,11 +10,6 @@ import { ServiceFormat, UserFormat, EcsEventType } from "./ecs-format";
|
||||
import { disabledSemanticLoggerProvider } from "./factory";
|
||||
import { SemanticLogger } from "./semantic-logger.abstraction";
|
||||
|
||||
export abstract class UserEventLogProvider {
|
||||
abstract capture: (account: Account) => UserEventLogger;
|
||||
abstract monitor$: (account: Account) => Observable<UserActionEvent>;
|
||||
}
|
||||
|
||||
type BaselineType = Omit<ServiceFormat & UserFormat, "@timestamp">;
|
||||
|
||||
export type EventInfo = {
|
||||
@@ -23,7 +18,7 @@ export type EventInfo = {
|
||||
tags?: Array<string>;
|
||||
};
|
||||
|
||||
export class UserEventLogger {
|
||||
export class UserEventMonitor {
|
||||
constructor(
|
||||
idService: AppIdService,
|
||||
utilService: PlatformUtilsService,
|
||||
@@ -2,7 +2,7 @@ import { PolicyService } from "../admin-console/abstractions/policy/policy.servi
|
||||
|
||||
import { ExtensionService } from "./extension/extension.service";
|
||||
import { LogProvider } from "./log";
|
||||
import { UserEventLogProvider } from "./log/logger";
|
||||
import { UserEventCollector } from "./log/user-event-monitor";
|
||||
|
||||
/** Provides access to commonly-used cross-cutting services. */
|
||||
export type SystemServiceProvider = {
|
||||
@@ -15,5 +15,5 @@ export type SystemServiceProvider = {
|
||||
/** Event monitoring and diagnostic interfaces */
|
||||
readonly log: LogProvider;
|
||||
|
||||
readonly event: UserEventLogProvider;
|
||||
readonly event: UserEventCollector;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { firstValueFrom } from "rxjs";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { UserEventLogProvider } from "@bitwarden/common/tools/log/logger";
|
||||
import { UserEventCollector } from "@bitwarden/common/tools/log/user-event-collector";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
@@ -24,7 +24,7 @@ export class DefaultCipherFormService implements CipherFormService {
|
||||
private cipherService: CipherService = inject(CipherService);
|
||||
private accountService: AccountService = inject(AccountService);
|
||||
private apiService: ApiService = inject(ApiService);
|
||||
private system = inject(UserEventLogProvider);
|
||||
private collector = inject(UserEventCollector);
|
||||
|
||||
async decryptCipher(cipher: Cipher): Promise<CipherView> {
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
@@ -44,7 +44,7 @@ export class DefaultCipherFormService implements CipherFormService {
|
||||
null,
|
||||
config.originalCipher ?? null,
|
||||
);
|
||||
const event = this.system.create(activeUser);
|
||||
const event = this.collector.monitor(activeUser);
|
||||
const labels = {
|
||||
"vault-item-type": CipherType[cipher.type],
|
||||
"vault-item-uri-quantity": cipher.type === CipherType.Login ? cipher.login.uris.length : null,
|
||||
|
||||
Reference in New Issue
Block a user