1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-14 23:45:37 +00:00

Merge branch 'main' into ps/extension-refresh

This commit is contained in:
Will Martin
2024-05-24 16:53:33 -04:00
committed by GitHub
715 changed files with 47663 additions and 12855 deletions

View File

@@ -12,6 +12,9 @@ import {
takeUntil,
defer,
throwError,
map,
Observable,
take,
} from "rxjs";
import {
@@ -67,6 +70,8 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
protected data?: Data;
protected loading = true;
private email$: Observable<string>;
activeAccountId: UserId;
// Remember device means for the user to trust the device
@@ -104,6 +109,14 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
async ngOnInit() {
this.loading = true;
this.activeAccountId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
this.email$ = this.accountService.activeAccount$.pipe(
map((a) => a?.email),
catchError((err: unknown) => {
this.validationService.showError(err);
return of(undefined);
}),
takeUntil(this.destroy$),
);
this.setupRememberDeviceValueChanges();
@@ -193,16 +206,8 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
}),
);
const email$ = from(this.stateService.getEmail()).pipe(
catchError((err: unknown) => {
this.validationService.showError(err);
return of(undefined);
}),
takeUntil(this.destroy$),
);
const autoEnrollStatus = await firstValueFrom(autoEnrollStatus$);
const email = await firstValueFrom(email$);
const email = await firstValueFrom(this.email$);
this.data = { state: State.NewUser, organizationId: autoEnrollStatus.id, userEmail: email };
this.loading = false;
@@ -211,17 +216,9 @@ export class BaseLoginDecryptionOptionsComponent implements OnInit, OnDestroy {
loadUntrustedDeviceData(userDecryptionOptions: UserDecryptionOptions) {
this.loading = true;
const email$ = from(this.stateService.getEmail()).pipe(
catchError((err: unknown) => {
this.validationService.showError(err);
return of(undefined);
}),
takeUntil(this.destroy$),
);
email$
this.email$
.pipe(
takeUntil(this.destroy$),
take(1),
finalize(() => {
this.loading = false;
}),

View File

@@ -1,8 +1,9 @@
import { Directive, OnDestroy, OnInit } from "@angular/core";
import { Subject, takeUntil } from "rxjs";
import { Subject, firstValueFrom, map, takeUntil } from "rxjs";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { KdfConfig } from "@bitwarden/common/auth/models/domain/kdf-config";
@@ -47,10 +48,13 @@ export class ChangePasswordComponent implements OnInit, OnDestroy {
protected dialogService: DialogService,
protected kdfConfigService: KdfConfigService,
protected masterPasswordService: InternalMasterPasswordServiceAbstraction,
protected accountService: AccountService,
) {}
async ngOnInit() {
this.email = await this.stateService.getEmail();
this.email = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
);
this.policyService
.masterPasswordPolicyOptions$()
.pipe(takeUntil(this.destroy$))
@@ -74,7 +78,9 @@ export class ChangePasswordComponent implements OnInit, OnDestroy {
return;
}
const email = await this.stateService.getEmail();
const email = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
);
if (this.kdfConfig == null) {
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
}

View File

@@ -372,7 +372,9 @@ export class LockComponent implements OnInit, OnDestroy {
(await this.vaultTimeoutSettingsService.isBiometricLockSet()) &&
((await this.cryptoService.hasUserKeyStored(KeySuffixOptions.Biometric)) ||
!this.platformUtilsService.supportsSecureStorage());
this.email = await this.stateService.getEmail();
this.email = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
);
this.webVaultHostname = (await this.environmentService.getEnvironment()).getHostname();
}

View File

@@ -1,6 +1,6 @@
import { Directive, OnDestroy, OnInit } from "@angular/core";
import { IsActiveMatchOptions, Router } from "@angular/router";
import { Subject, firstValueFrom, takeUntil } from "rxjs";
import { Subject, firstValueFrom, map, takeUntil } from "rxjs";
import {
AuthRequestLoginCredentials,
@@ -29,7 +29,6 @@ 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 { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
@@ -84,12 +83,11 @@ export class LoginViaAuthRequestComponent
platformUtilsService: PlatformUtilsService,
private anonymousHubService: AnonymousHubService,
private validationService: ValidationService,
private stateService: StateService,
private accountService: AccountService,
private loginEmailService: LoginEmailServiceAbstraction,
private deviceTrustService: DeviceTrustServiceAbstraction,
private authRequestService: AuthRequestServiceAbstraction,
private loginStrategyService: LoginStrategyServiceAbstraction,
private accountService: AccountService,
) {
super(environmentService, i18nService, platformUtilsService);
@@ -131,7 +129,9 @@ export class LoginViaAuthRequestComponent
// Pull email from state for admin auth reqs b/c it is available
// This also prevents it from being lost on refresh as the
// login service email does not persist.
this.email = await this.stateService.getEmail();
this.email = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
);
const userId = (await firstValueFrom(this.accountService.activeAccount$)).id;
if (!this.email) {

View File

@@ -1,12 +1,13 @@
import { Directive, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { firstValueFrom, map } from "rxjs";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { DialogService } from "@bitwarden/components";
@@ -22,7 +23,7 @@ export class RemovePasswordComponent implements OnInit {
constructor(
private router: Router,
private stateService: StateService,
private accountService: AccountService,
private syncService: SyncService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
@@ -33,7 +34,9 @@ export class RemovePasswordComponent implements OnInit {
async ngOnInit() {
this.organization = await this.keyConnectorService.getManagingOrganization();
this.email = await this.stateService.getEmail();
this.email = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
);
await this.syncService.fullSync(false);
this.loading = false;
}

View File

@@ -51,7 +51,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
ForceSetPasswordReason = ForceSetPasswordReason;
constructor(
private accountService: AccountService,
accountService: AccountService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
i18nService: I18nService,
cryptoService: CryptoService,
@@ -83,6 +83,7 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
dialogService,
kdfConfigService,
masterPasswordService,
accountService,
);
}

View File

@@ -4,6 +4,7 @@ import { Router } from "@angular/router";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
@@ -48,6 +49,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
dialogService: DialogService,
kdfConfigService: KdfConfigService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
accountService: AccountService,
) {
super(
i18nService,
@@ -60,6 +62,7 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
dialogService,
kdfConfigService,
masterPasswordService,
accountService,
);
}

View File

@@ -1,6 +1,6 @@
import { Directive } from "@angular/core";
import { Router } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { firstValueFrom, map } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
@@ -61,7 +61,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
protected router: Router,
dialogService: DialogService,
kdfConfigService: KdfConfigService,
private accountService: AccountService,
accountService: AccountService,
masterPasswordService: InternalMasterPasswordServiceAbstraction,
) {
super(
@@ -75,6 +75,7 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
dialogService,
kdfConfigService,
masterPasswordService,
accountService,
);
}
@@ -107,7 +108,9 @@ export class UpdateTempPasswordComponent extends BaseChangePasswordComponent {
}
async setupSubmitActions(): Promise<boolean> {
this.email = await this.stateService.getEmail();
this.email = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
);
this.kdfConfig = await this.kdfConfigService.getKdfConfig();
return true;
}

View File

@@ -193,13 +193,11 @@ import { SearchService } from "@bitwarden/common/services/search.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
import { VaultTimeoutService } from "@bitwarden/common/services/vault-timeout/vault-timeout.service";
import {
PasswordGenerationService,
PasswordGenerationServiceAbstraction,
} from "@bitwarden/common/tools/generator/password";
import {
UsernameGenerationService,
UsernameGenerationServiceAbstraction,
} from "@bitwarden/common/tools/generator/username";
legacyPasswordGenerationServiceFactory,
legacyUsernameGenerationServiceFactory,
} from "@bitwarden/common/tools/generator";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username";
import {
PasswordStrengthService,
PasswordStrengthServiceAbstraction,
@@ -559,13 +557,27 @@ const safeProviders: SafeProvider[] = [
}),
safeProvider({
provide: PasswordGenerationServiceAbstraction,
useClass: PasswordGenerationService,
deps: [CryptoServiceAbstraction, PolicyServiceAbstraction, StateServiceAbstraction],
useFactory: legacyPasswordGenerationServiceFactory,
deps: [
EncryptService,
CryptoServiceAbstraction,
PolicyServiceAbstraction,
AccountServiceAbstraction,
StateProvider,
],
}),
safeProvider({
provide: UsernameGenerationServiceAbstraction,
useClass: UsernameGenerationService,
deps: [CryptoServiceAbstraction, StateServiceAbstraction, ApiServiceAbstraction],
useFactory: legacyUsernameGenerationServiceFactory,
deps: [
ApiServiceAbstraction,
I18nServiceAbstraction,
CryptoServiceAbstraction,
EncryptService,
PolicyServiceAbstraction,
AccountServiceAbstraction,
StateProvider,
],
}),
safeProvider({
provide: ApiServiceAbstraction,

View File

@@ -1,14 +1,14 @@
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { Directive, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { BehaviorSubject } from "rxjs";
import { debounceTime, first, map } from "rxjs/operators";
import { BehaviorSubject, combineLatest, firstValueFrom, Subject } from "rxjs";
import { debounceTime, first, map, skipWhile, takeUntil } from "rxjs/operators";
import { PasswordGeneratorPolicyOptions } from "@bitwarden/common/admin-console/models/domain/password-generator-policy-options";
import { AccountService } from "@bitwarden/common/auth/abstractions/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";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { GeneratorOptions } from "@bitwarden/common/tools/generator/generator-options";
import { GeneratorType } from "@bitwarden/common/tools/generator/generator-type";
import {
PasswordGenerationServiceAbstraction,
PasswordGeneratorOptions,
@@ -21,9 +21,9 @@ import {
import { EmailForwarderOptions } from "@bitwarden/common/tools/models/domain/email-forwarder-options";
@Directive()
export class GeneratorComponent implements OnInit {
export class GeneratorComponent implements OnInit, OnDestroy {
@Input() comingFromAddEdit = false;
@Input() type: string;
@Input() type: GeneratorType | "";
@Output() onSelected = new EventEmitter<string>();
usernameGeneratingPromise: Promise<string>;
@@ -42,6 +42,9 @@ export class GeneratorComponent implements OnInit {
enforcedPasswordPolicyOptions: PasswordGeneratorPolicyOptions;
usernameWebsite: string = null;
private destroy$ = new Subject<void>();
private isInitialized$ = new BehaviorSubject(false);
// update screen reader minimum password length with 500ms debounce
// so that the user isn't flooded with status updates
private _passwordOptionsMinLengthForReader = new BehaviorSubject<number>(
@@ -52,14 +55,17 @@ export class GeneratorComponent implements OnInit {
debounceTime(500),
);
private _password = new BehaviorSubject<string>("-");
constructor(
protected passwordGenerationService: PasswordGenerationServiceAbstraction,
protected usernameGenerationService: UsernameGenerationServiceAbstraction,
protected platformUtilsService: PlatformUtilsService,
protected stateService: StateService,
protected accountService: AccountService,
protected i18nService: I18nService,
protected logService: LogService,
protected route: ActivatedRoute,
protected ngZone: NgZone,
private win: Window,
) {
this.typeOptions = [
@@ -90,59 +96,115 @@ export class GeneratorComponent implements OnInit {
];
this.subaddressOptions = [{ name: i18nService.t("random"), value: "random" }];
this.catchallOptions = [{ name: i18nService.t("random"), value: "random" }];
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.initForwardOptions();
}
async ngOnInit() {
// eslint-disable-next-line rxjs/no-async-subscribe
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
const passwordOptionsResponse = await this.passwordGenerationService.getOptions();
this.passwordOptions = passwordOptionsResponse[0];
this.enforcedPasswordPolicyOptions = passwordOptionsResponse[1];
this.avoidAmbiguous = !this.passwordOptions.ambiguous;
this.passwordOptions.type =
this.passwordOptions.type === "passphrase" ? "passphrase" : "password";
this.forwardOptions = [
{ name: "", value: "", validForSelfHosted: false },
{ name: "addy.io", value: "anonaddy", validForSelfHosted: true },
{ name: "DuckDuckGo", value: "duckduckgo", validForSelfHosted: false },
{ name: "Fastmail", value: "fastmail", validForSelfHosted: true },
{ name: "Firefox Relay", value: "firefoxrelay", validForSelfHosted: false },
{ name: "SimpleLogin", value: "simplelogin", validForSelfHosted: true },
{ name: "Forward Email", value: "forwardemail", validForSelfHosted: true },
].sort((a, b) => a.name.localeCompare(b.name));
this.usernameOptions = await this.usernameGenerationService.getOptions();
if (this.usernameOptions.type == null) {
this.usernameOptions.type = "word";
}
if (
this.usernameOptions.subaddressEmail == null ||
this.usernameOptions.subaddressEmail === ""
) {
this.usernameOptions.subaddressEmail = await this.stateService.getEmail();
}
if (this.usernameWebsite == null) {
this.usernameOptions.subaddressType = this.usernameOptions.catchallType = "random";
} else {
this.usernameOptions.website = this.usernameWebsite;
const websiteOption = { name: this.i18nService.t("websiteName"), value: "website-name" };
this.subaddressOptions.push(websiteOption);
this.catchallOptions.push(websiteOption);
}
if (this.type !== "username" && this.type !== "password") {
if (qParams.type === "username" || qParams.type === "password") {
this.type = qParams.type;
} else {
const generatorOptions = await this.stateService.getGeneratorOptions();
this.type = generatorOptions?.type ?? "password";
}
}
if (this.regenerateWithoutButtonPress()) {
await this.regenerate();
}
this._password.pipe(debounceTime(250)).subscribe((password) => {
ngZone.run(() => {
this.password = password;
});
this.passwordGenerationService.addHistory(this.password).catch((e) => {
this.logService.error(e);
});
});
}
async typeChanged() {
await this.stateService.setGeneratorOptions({ type: this.type } as GeneratorOptions);
if (this.regenerateWithoutButtonPress()) {
await this.regenerate();
cascadeOptions(navigationType: GeneratorType = undefined, accountEmail: string) {
this.avoidAmbiguous = !this.passwordOptions.ambiguous;
if (!this.type) {
if (navigationType) {
this.type = navigationType;
} else {
this.type = this.passwordOptions.type === "username" ? "username" : "password";
}
}
this.passwordOptions.type =
this.passwordOptions.type === "passphrase" ? "passphrase" : "password";
if (this.usernameOptions.type == null) {
this.usernameOptions.type = "word";
}
if (
this.usernameOptions.subaddressEmail == null ||
this.usernameOptions.subaddressEmail === ""
) {
this.usernameOptions.subaddressEmail = accountEmail;
}
if (this.usernameWebsite == null) {
this.usernameOptions.subaddressType = this.usernameOptions.catchallType = "random";
} else {
this.usernameOptions.website = this.usernameWebsite;
const websiteOption = { name: this.i18nService.t("websiteName"), value: "website-name" };
this.subaddressOptions.push(websiteOption);
this.catchallOptions.push(websiteOption);
}
}
async ngOnInit() {
combineLatest([
this.route.queryParams.pipe(first()),
this.accountService.activeAccount$.pipe(first()),
this.passwordGenerationService.getOptions$(),
this.usernameGenerationService.getOptions$(),
])
.pipe(
map(([qParams, account, [passwordOptions, passwordPolicy], usernameOptions]) => ({
navigationType: qParams.type as GeneratorType,
accountEmail: account.email,
passwordOptions,
passwordPolicy,
usernameOptions,
})),
takeUntil(this.destroy$),
)
.subscribe((options) => {
this.passwordOptions = options.passwordOptions;
this.enforcedPasswordPolicyOptions = options.passwordPolicy;
this.usernameOptions = options.usernameOptions;
this.cascadeOptions(options.navigationType, options.accountEmail);
this._passwordOptionsMinLengthForReader.next(this.passwordOptions.minLength);
if (this.regenerateWithoutButtonPress()) {
this.regenerate().catch((e) => {
this.logService.error(e);
});
}
this.isInitialized$.next(true);
});
// once initialization is complete, `ngOnInit` should return.
//
// FIXME(#6944): if a sync is in progress, wait to complete until after
// the sync completes.
await firstValueFrom(
this.isInitialized$.pipe(
skipWhile((initialized) => !initialized),
takeUntil(this.destroy$),
),
);
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
this.isInitialized$.complete();
this._passwordOptionsMinLengthForReader.complete();
}
async typeChanged() {
await this.savePasswordOptions();
}
async regenerate() {
@@ -156,7 +218,7 @@ export class GeneratorComponent implements OnInit {
async sliderChanged() {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.savePasswordOptions(false);
this.savePasswordOptions();
await this.passwordGenerationService.addHistory(this.password);
}
@@ -200,31 +262,34 @@ export class GeneratorComponent implements OnInit {
async sliderInput() {
await this.normalizePasswordOptions();
this.password = await this.passwordGenerationService.generatePassword(this.passwordOptions);
}
async savePasswordOptions(regenerate = true) {
async savePasswordOptions() {
// map navigation state into generator type
const restoreType = this.passwordOptions.type;
if (this.type === "username") {
this.passwordOptions.type = this.type;
}
// save options
await this.normalizePasswordOptions();
await this.passwordGenerationService.saveOptions(this.passwordOptions);
if (regenerate && this.regenerateWithoutButtonPress()) {
await this.regeneratePassword();
}
// restore the original format
this.passwordOptions.type = restoreType;
}
async saveUsernameOptions(regenerate = true) {
async saveUsernameOptions() {
await this.usernameGenerationService.saveOptions(this.usernameOptions);
if (this.usernameOptions.type === "forwarded") {
this.username = "-";
}
if (regenerate && this.regenerateWithoutButtonPress()) {
await this.regenerateUsername();
}
}
async regeneratePassword() {
this.password = await this.passwordGenerationService.generatePassword(this.passwordOptions);
await this.passwordGenerationService.addHistory(this.password);
this._password.next(
await this.passwordGenerationService.generatePassword(this.passwordOptions),
);
}
regenerateUsername() {
@@ -293,28 +358,5 @@ export class GeneratorComponent implements OnInit {
await this.passwordGenerationService.enforcePasswordGeneratorPoliciesOnOptions(
this.passwordOptions,
);
this._passwordOptionsMinLengthForReader.next(this.passwordOptions.minLength);
}
private async initForwardOptions() {
this.forwardOptions = [
{ name: "addy.io", value: "anonaddy", validForSelfHosted: true },
{ name: "DuckDuckGo", value: "duckduckgo", validForSelfHosted: false },
{ name: "Fastmail", value: "fastmail", validForSelfHosted: true },
{ name: "Firefox Relay", value: "firefoxrelay", validForSelfHosted: false },
{ name: "SimpleLogin", value: "simplelogin", validForSelfHosted: true },
{ name: "Forward Email", value: "forwardemail", validForSelfHosted: true },
];
this.usernameOptions = await this.usernameGenerationService.getOptions();
if (
this.usernameOptions.forwardedService == null ||
this.usernameOptions.forwardedService === ""
) {
this.forwardOptions.push({ name: "", value: null, validForSelfHosted: false });
}
this.forwardOptions = this.forwardOptions.sort((a, b) => a.name.localeCompare(b.name));
}
}

View File

@@ -23,8 +23,7 @@ export class PasswordGeneratorHistoryComponent implements OnInit {
}
clear = async () => {
this.history = [];
await this.passwordGenerationService.clear();
this.history = await this.passwordGenerationService.clear();
};
copy(password: string) {

View File

@@ -1,6 +1,6 @@
import { DatePipe } from "@angular/common";
import { Directive, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
import { concatMap, firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
import { concatMap, firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
@@ -11,6 +11,7 @@ import {
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { OrganizationUserStatusType, 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 { EventType } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service";
@@ -19,7 +20,6 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
@@ -108,7 +108,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
protected i18nService: I18nService,
protected platformUtilsService: PlatformUtilsService,
protected auditService: AuditService,
protected stateService: StateService,
protected accountService: AccountService,
protected collectionService: CollectionService,
protected messagingService: MessagingService,
protected eventCollectionService: EventCollectionService,
@@ -215,7 +215,9 @@ export class AddEditComponent implements OnInit, OnDestroy {
if (this.personalOwnershipPolicyAppliesToActiveUser) {
this.allowPersonal = false;
} else {
const myEmail = await this.stateService.getEmail();
const myEmail = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
);
this.ownershipOptions.push({ name: myEmail, value: null });
}

View File

@@ -1,9 +1,10 @@
<div class="icon" aria-hidden="true">
<div class="tw-flex tw-justify-center tw-items-center" aria-hidden="true">
<ng-container *ngIf="data$ | async as data">
<img
[src]="data.image"
[appFallbackSrc]="data.fallbackImage"
*ngIf="data.imageEnabled && data.image"
class="tw-max-h-6 tw-max-w-6 tw-rounded-md"
alt=""
decoding="async"
loading="lazy"