mirror of
https://github.com/bitwarden/browser
synced 2026-02-26 09:33:22 +00:00
Merge branch 'main' into ps/extension-refresh
This commit is contained in:
@@ -18,7 +18,8 @@ import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstraction
|
||||
import { CaptchaIFrame } from "@bitwarden/common/auth/captcha-iframe";
|
||||
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { ClientType } from "@bitwarden/common/enums";
|
||||
import { ClientType, HttpStatusCode } from "@bitwarden/common/enums";
|
||||
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
|
||||
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
@@ -26,6 +27,7 @@ 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 { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { SyncService } from "@bitwarden/common/platform/sync";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
@@ -136,6 +138,7 @@ export class LoginComponent implements OnInit, OnDestroy {
|
||||
private syncService: SyncService,
|
||||
private toastService: ToastService,
|
||||
private logService: LogService,
|
||||
private validationService: ValidationService,
|
||||
) {
|
||||
this.clientType = this.platformUtilsService.getClientType();
|
||||
this.loginViaAuthRequestSupported = this.loginComponentService.isLoginViaAuthRequestSupported();
|
||||
@@ -182,19 +185,54 @@ export class LoginComponent implements OnInit, OnDestroy {
|
||||
null,
|
||||
);
|
||||
|
||||
const authResult = await this.loginStrategyService.logIn(credentials);
|
||||
try {
|
||||
const authResult = await this.loginStrategyService.logIn(credentials);
|
||||
|
||||
await this.saveEmailSettings();
|
||||
await this.handleAuthResult(authResult);
|
||||
await this.saveEmailSettings();
|
||||
await this.handleAuthResult(authResult);
|
||||
|
||||
if (this.clientType === ClientType.Desktop) {
|
||||
if (this.captchaSiteKey) {
|
||||
const content = document.getElementById("content") as HTMLDivElement;
|
||||
content.setAttribute("style", "width:335px");
|
||||
if (this.clientType === ClientType.Desktop) {
|
||||
if (this.captchaSiteKey) {
|
||||
const content = document.getElementById("content") as HTMLDivElement;
|
||||
content.setAttribute("style", "width:335px");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.logService.error(error);
|
||||
this.handleSubmitError(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the error from the submit function.
|
||||
*
|
||||
* @param error The error object.
|
||||
*/
|
||||
private handleSubmitError(error: unknown) {
|
||||
// Handle error responses
|
||||
if (error instanceof ErrorResponse) {
|
||||
switch (error.statusCode) {
|
||||
case HttpStatusCode.BadRequest: {
|
||||
if (error.message.toLowerCase().includes("username or password is incorrect")) {
|
||||
this.formGroup.controls.masterPassword.setErrors({
|
||||
error: {
|
||||
message: this.i18nService.t("invalidMasterPassword"),
|
||||
},
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Allow all other errors to be handled by toast
|
||||
this.validationService.showError(error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Allow all other errors to be handled by toast
|
||||
this.validationService.showError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the result of the authentication process.
|
||||
*
|
||||
|
||||
@@ -8,6 +8,7 @@ export class PolicyResponse extends BaseResponse {
|
||||
type: PolicyType;
|
||||
data: any;
|
||||
enabled: boolean;
|
||||
canToggleState: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
@@ -16,5 +17,6 @@ export class PolicyResponse extends BaseResponse {
|
||||
this.type = this.getResponseProperty("Type");
|
||||
this.data = this.getResponseProperty("Data");
|
||||
this.enabled = this.getResponseProperty("Enabled");
|
||||
this.canToggleState = this.getResponseProperty("CanToggleState") ?? true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ export class DefaultSdkService implements SdkService {
|
||||
let client: BitwardenClient;
|
||||
|
||||
const createAndInitializeClient = async () => {
|
||||
if (privateKey == null || userKey == null || orgKeys == null) {
|
||||
if (privateKey == null || userKey == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ export class DefaultSdkService implements SdkService {
|
||||
kdfParams: KdfConfig,
|
||||
privateKey: EncryptedString,
|
||||
userKey: UserKey,
|
||||
orgKeys: Record<OrganizationId, EncryptedOrganizationKeyData>,
|
||||
orgKeys?: Record<OrganizationId, EncryptedOrganizationKeyData>,
|
||||
) {
|
||||
await client.crypto().initialize_user_crypto({
|
||||
email: account.email,
|
||||
@@ -169,9 +169,12 @@ export class DefaultSdkService implements SdkService {
|
||||
},
|
||||
privateKey,
|
||||
});
|
||||
|
||||
// We initialize the org crypto even if the org_keys are
|
||||
// null to make sure any existing org keys are cleared.
|
||||
await client.crypto().initialize_org_crypto({
|
||||
organizationKeys: new Map(
|
||||
Object.entries(orgKeys)
|
||||
Object.entries(orgKeys ?? {})
|
||||
.filter(([_, v]) => v.type === "organization")
|
||||
.map(([k, v]) => [k, v.key]),
|
||||
),
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<bit-card *ngFor="let credential of credentials$ | async" class="tw-mb-2">
|
||||
<div class="tw-flex tw-justify-between tw-items-center">
|
||||
<div class="tw-flex tw-flex-col">
|
||||
<h2 bitTypography="h6">{{ credential.credential }}</h2>
|
||||
<bit-color-password
|
||||
class="tw-font-mono"
|
||||
[password]="credential.credential"
|
||||
></bit-color-password>
|
||||
<span class="tw-text-muted" bitTypography="body1">{{
|
||||
credential.generationDate | date: "medium"
|
||||
}}</span>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
CardComponent,
|
||||
ColorPasswordModule,
|
||||
IconButtonModule,
|
||||
NoItemsModule,
|
||||
SectionComponent,
|
||||
@@ -21,6 +22,7 @@ import { GeneratedCredential, GeneratorHistoryService } from "@bitwarden/generat
|
||||
selector: "bit-credential-generator-history",
|
||||
templateUrl: "credential-generator-history.component.html",
|
||||
imports: [
|
||||
ColorPasswordModule,
|
||||
CommonModule,
|
||||
IconButtonModule,
|
||||
NoItemsModule,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<bit-form-field disableMargin>
|
||||
<bit-label>{{ "numWords" | i18n }}</bit-label>
|
||||
<input bitInput formControlName="numWords" id="num-words" type="number" />
|
||||
<bit-hint>{{ numWordsBoundariesHint$ | async }}</bit-hint>
|
||||
</bit-form-field>
|
||||
</bit-card>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
||||
import { OnInit, Input, Output, EventEmitter, Component, OnDestroy } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
import { BehaviorSubject, skip, takeUntil, Subject } from "rxjs";
|
||||
import { BehaviorSubject, skip, takeUntil, Subject, ReplaySubject } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
Generators,
|
||||
@@ -29,11 +30,13 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy {
|
||||
/** Instantiates the component
|
||||
* @param accountService queries user availability
|
||||
* @param generatorService settings and policy logic
|
||||
* @param i18nService localize hints
|
||||
* @param formBuilder reactive form controls
|
||||
*/
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private generatorService: CredentialGeneratorService,
|
||||
private i18nService: I18nService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
@@ -97,6 +100,13 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.toggleEnabled(Controls.capitalize, !constraints.capitalize?.readonly);
|
||||
this.toggleEnabled(Controls.includeNumber, !constraints.includeNumber?.readonly);
|
||||
|
||||
const boundariesHint = this.i18nService.t(
|
||||
"generatorBoundariesHint",
|
||||
constraints.numWords.min,
|
||||
constraints.numWords.max,
|
||||
);
|
||||
this.numWordsBoundariesHint.next(boundariesHint);
|
||||
});
|
||||
|
||||
// now that outputs are set up, connect inputs
|
||||
@@ -106,6 +116,11 @@ export class PassphraseSettingsComponent implements OnInit, OnDestroy {
|
||||
/** display binding for enterprise policy notice */
|
||||
protected policyInEffect: boolean;
|
||||
|
||||
private numWordsBoundariesHint = new ReplaySubject<string>(1);
|
||||
|
||||
/** display binding for min/max constraints of `numWords` */
|
||||
protected numWordsBoundariesHint$ = this.numWordsBoundariesHint.asObservable();
|
||||
|
||||
private toggleEnabled(setting: keyof typeof Controls, enabled: boolean) {
|
||||
if (enabled) {
|
||||
this.settings.get(setting).enable({ emitEvent: false });
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<bit-form-field disableMargin>
|
||||
<bit-label>{{ "length" | i18n }}</bit-label>
|
||||
<input bitInput formControlName="length" type="number" />
|
||||
<bit-hint>{{ lengthBoundariesHint$ | async }}</bit-hint>
|
||||
</bit-form-field>
|
||||
</bit-card>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { coerceBooleanProperty } from "@angular/cdk/coercion";
|
||||
import { OnInit, Input, Output, EventEmitter, Component, OnDestroy } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
import { BehaviorSubject, takeUntil, Subject, map, filter, tap, skip } from "rxjs";
|
||||
import { BehaviorSubject, takeUntil, Subject, map, filter, tap, skip, ReplaySubject } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import {
|
||||
Generators,
|
||||
@@ -33,11 +34,13 @@ export class PasswordSettingsComponent implements OnInit, OnDestroy {
|
||||
/** Instantiates the component
|
||||
* @param accountService queries user availability
|
||||
* @param generatorService settings and policy logic
|
||||
* @param i18nService localize hints
|
||||
* @param formBuilder reactive form controls
|
||||
*/
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private generatorService: CredentialGeneratorService,
|
||||
private i18nService: I18nService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
|
||||
@@ -147,6 +150,13 @@ export class PasswordSettingsComponent implements OnInit, OnDestroy {
|
||||
for (const [control, enabled] of toggles) {
|
||||
this.toggleEnabled(control, enabled);
|
||||
}
|
||||
|
||||
const boundariesHint = this.i18nService.t(
|
||||
"generatorBoundariesHint",
|
||||
constraints.length.min,
|
||||
constraints.length.max,
|
||||
);
|
||||
this.lengthBoundariesHint.next(boundariesHint);
|
||||
});
|
||||
|
||||
// cascade selections between checkboxes and spinboxes
|
||||
@@ -208,6 +218,11 @@ export class PasswordSettingsComponent implements OnInit, OnDestroy {
|
||||
/** display binding for enterprise policy notice */
|
||||
protected policyInEffect: boolean;
|
||||
|
||||
private lengthBoundariesHint = new ReplaySubject<string>(1);
|
||||
|
||||
/** display binding for min/max constraints of `length` */
|
||||
protected lengthBoundariesHint$ = this.lengthBoundariesHint.asObservable();
|
||||
|
||||
private toggleEnabled(setting: keyof typeof Controls, enabled: boolean) {
|
||||
if (enabled) {
|
||||
this.settings.get(setting).enable({ emitEvent: false });
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
import { BehaviorSubject, skip, Subject, takeUntil } from "rxjs";
|
||||
import { BehaviorSubject, map, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
@@ -53,9 +53,23 @@ export class SubaddressSettingsComponent implements OnInit, OnDestroy {
|
||||
const singleUserId$ = this.singleUserId$();
|
||||
const settings = await this.generatorService.settings(Generators.subaddress, { singleUserId$ });
|
||||
|
||||
settings.pipe(takeUntil(this.destroyed$)).subscribe((s) => {
|
||||
this.settings.patchValue(s, { emitEvent: false });
|
||||
});
|
||||
settings
|
||||
.pipe(
|
||||
withLatestFrom(this.accountService.activeAccount$),
|
||||
map(([settings, activeAccount]) => {
|
||||
// if the subaddress isn't specified, copy it from
|
||||
// the user's settings
|
||||
if ((settings.subaddressEmail ?? "").length < 1) {
|
||||
settings.subaddressEmail = activeAccount.email;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}),
|
||||
takeUntil(this.destroyed$),
|
||||
)
|
||||
.subscribe((s) => {
|
||||
this.settings.patchValue(s, { emitEvent: false });
|
||||
});
|
||||
|
||||
// the first emission is the current value; subsequent emissions are updates
|
||||
settings.pipe(skip(1), takeUntil(this.destroyed$)).subscribe(this.onUpdated);
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
<button
|
||||
*ngIf="hasPassword"
|
||||
class="tw-border-l-0 last:tw-rounded-r focus-visible:tw-border-l focus-visible:tw-ml-[-1px]"
|
||||
bitSuffix
|
||||
type="button"
|
||||
buttonType="danger"
|
||||
bitIconButton="bwi-minus-circle"
|
||||
|
||||
Reference in New Issue
Block a user