mirror of
https://github.com/bitwarden/browser
synced 2026-02-27 18:13:29 +00:00
Merge branch 'main' into ps/extension-refresh
This commit is contained in:
@@ -6,59 +6,70 @@
|
||||
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||
<bit-form-field class="tw-col-span-6 tw-mb-0">
|
||||
<bit-label>{{ "defaultType" | i18n }}</bit-label>
|
||||
<bit-select formControlName="defaultType" id="defaultType">
|
||||
<bit-option *ngFor="let o of defaultTypes" [value]="o.value" [label]="o.name"></bit-option>
|
||||
<bit-label>{{ "overridePasswordTypePolicy" | i18n }}</bit-label>
|
||||
<bit-select formControlName="overridePasswordType" id="overrideType">
|
||||
<bit-option
|
||||
*ngFor="let o of overridePasswordTypeOptions"
|
||||
[value]="o.value"
|
||||
[label]="o.name"
|
||||
></bit-option>
|
||||
</bit-select>
|
||||
</bit-form-field>
|
||||
</div>
|
||||
|
||||
<h3 bitTypography="h3" class="tw-mt-4">{{ "password" | i18n }}</h3>
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||
<bit-form-field class="tw-col-span-6">
|
||||
<bit-label>{{ "minLength" | i18n }}</bit-label>
|
||||
<input bitInput type="number" min="5" max="128" formControlName="minLength" />
|
||||
</bit-form-field>
|
||||
<!-- password-specific policies -->
|
||||
<div *ngIf="showPasswordPolicies$ | async">
|
||||
<h3 bitTypography="h3" class="tw-mt-4">{{ "password" | i18n }}</h3>
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||
<bit-form-field class="tw-col-span-6">
|
||||
<bit-label>{{ "minLength" | i18n }}</bit-label>
|
||||
<input bitInput type="number" min="5" max="128" formControlName="minLength" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||
<bit-form-field class="tw-col-span-6">
|
||||
<bit-label>{{ "minNumbers" | i18n }}</bit-label>
|
||||
<input bitInput type="number" min="0" max="9" formControlName="minNumbers" />
|
||||
</bit-form-field>
|
||||
<bit-form-field class="tw-col-span-6">
|
||||
<bit-label>{{ "minSpecial" | i18n }}</bit-label>
|
||||
<input bitInput type="number" min="0" max="9" formControlName="minSpecial" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="useUpper" id="useUpper" />
|
||||
<bit-label>A-Z</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="useLower" id="useLower" />
|
||||
<bit-label>a-z</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="useNumbers" id="useNumbers" />
|
||||
<bit-label>0-9</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="useSpecial" id="useSpecial" />
|
||||
<bit-label>!@#$%^&*</bit-label>
|
||||
</bit-form-control>
|
||||
</div>
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||
<bit-form-field class="tw-col-span-6">
|
||||
<bit-label>{{ "minNumbers" | i18n }}</bit-label>
|
||||
<input bitInput type="number" min="0" max="9" formControlName="minNumbers" />
|
||||
</bit-form-field>
|
||||
<bit-form-field class="tw-col-span-6">
|
||||
<bit-label>{{ "minSpecial" | i18n }}</bit-label>
|
||||
<input bitInput type="number" min="0" max="9" formControlName="minSpecial" />
|
||||
</bit-form-field>
|
||||
|
||||
<!-- passphrase-specific policies -->
|
||||
<div *ngIf="showPassphrasePolicies$ | async">
|
||||
<h3 bitTypography="h3" class="tw-mt-4">{{ "passphrase" | i18n }}</h3>
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||
<bit-form-field class="tw-col-span-6">
|
||||
<bit-label>{{ "minimumNumberOfWords" | i18n }}</bit-label>
|
||||
<input bitInput type="number" min="3" max="20" formControlName="minNumberWords" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="capitalize" id="capitalize" />
|
||||
<bit-label>{{ "capitalize" | i18n }}</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="includeNumber" id="includeNumber" />
|
||||
<bit-label>{{ "includeNumber" | i18n }}</bit-label>
|
||||
</bit-form-control>
|
||||
</div>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="useUpper" id="useUpper" />
|
||||
<bit-label>A-Z</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="useLower" id="useLower" />
|
||||
<bit-label>a-z</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="useNumbers" id="useNumbers" />
|
||||
<bit-label>0-9</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="useSpecial" id="useSpecial" />
|
||||
<bit-label>!@#$%^&*</bit-label>
|
||||
</bit-form-control>
|
||||
<h3 bitTypography="h3" class="tw-mt-4">{{ "passphrase" | i18n }}</h3>
|
||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||
<bit-form-field class="tw-col-span-6">
|
||||
<bit-label>{{ "minimumNumberOfWords" | i18n }}</bit-label>
|
||||
<input bitInput type="number" min="3" max="20" formControlName="minNumberWords" />
|
||||
</bit-form-field>
|
||||
</div>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="capitalize" id="capitalize" />
|
||||
<bit-label>{{ "capitalize" | i18n }}</bit-label>
|
||||
</bit-form-control>
|
||||
<bit-form-control>
|
||||
<input type="checkbox" bitCheckbox formControlName="includeNumber" id="includeNumber" />
|
||||
<bit-label>{{ "includeNumber" | i18n }}</bit-label>
|
||||
</bit-form-control>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { UntypedFormBuilder, Validators } from "@angular/forms";
|
||||
import { BehaviorSubject, map } from "rxjs";
|
||||
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { DefaultPassphraseBoundaries, DefaultPasswordBoundaries } from "@bitwarden/generator-core";
|
||||
|
||||
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
||||
|
||||
@@ -19,20 +22,59 @@ export class PasswordGeneratorPolicy extends BasePolicy {
|
||||
})
|
||||
export class PasswordGeneratorPolicyComponent extends BasePolicyComponent {
|
||||
data = this.formBuilder.group({
|
||||
defaultType: [null],
|
||||
minLength: [null, [Validators.min(5), Validators.max(128)]],
|
||||
overridePasswordType: [null],
|
||||
minLength: [
|
||||
null,
|
||||
[
|
||||
Validators.min(DefaultPasswordBoundaries.length.min),
|
||||
Validators.max(DefaultPasswordBoundaries.length.max),
|
||||
],
|
||||
],
|
||||
useUpper: [null],
|
||||
useLower: [null],
|
||||
useNumbers: [null],
|
||||
useSpecial: [null],
|
||||
minNumbers: [null, [Validators.min(0), Validators.max(9)]],
|
||||
minSpecial: [null, [Validators.min(0), Validators.max(9)]],
|
||||
minNumberWords: [null, [Validators.min(3), Validators.max(20)]],
|
||||
minNumbers: [
|
||||
null,
|
||||
[
|
||||
Validators.min(DefaultPasswordBoundaries.minDigits.min),
|
||||
Validators.max(DefaultPasswordBoundaries.minDigits.max),
|
||||
],
|
||||
],
|
||||
minSpecial: [
|
||||
null,
|
||||
[
|
||||
Validators.min(DefaultPasswordBoundaries.minSpecialCharacters.min),
|
||||
Validators.max(DefaultPasswordBoundaries.minSpecialCharacters.max),
|
||||
],
|
||||
],
|
||||
minNumberWords: [
|
||||
null,
|
||||
[
|
||||
Validators.min(DefaultPassphraseBoundaries.numWords.min),
|
||||
Validators.max(DefaultPassphraseBoundaries.numWords.max),
|
||||
],
|
||||
],
|
||||
capitalize: [null],
|
||||
includeNumber: [null],
|
||||
});
|
||||
|
||||
defaultTypes: { name: string; value: string }[];
|
||||
overridePasswordTypeOptions: { name: string; value: string }[];
|
||||
|
||||
// These subjects cache visibility of the sub-options for passwords
|
||||
// and passphrases; without them policy controls don't show up at all.
|
||||
private showPasswordPolicies = new BehaviorSubject<boolean>(true);
|
||||
private showPassphrasePolicies = new BehaviorSubject<boolean>(true);
|
||||
|
||||
/** Emits `true` when the password policy options should be displayed */
|
||||
get showPasswordPolicies$() {
|
||||
return this.showPasswordPolicies.asObservable();
|
||||
}
|
||||
|
||||
/** Emits `true` when the passphrase policy options should be displayed */
|
||||
get showPassphrasePolicies$() {
|
||||
return this.showPassphrasePolicies.asObservable();
|
||||
}
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
@@ -40,10 +82,27 @@ export class PasswordGeneratorPolicyComponent extends BasePolicyComponent {
|
||||
) {
|
||||
super();
|
||||
|
||||
this.defaultTypes = [
|
||||
this.overridePasswordTypeOptions = [
|
||||
{ name: i18nService.t("userPreference"), value: null },
|
||||
{ name: i18nService.t("password"), value: "password" },
|
||||
{ name: i18nService.t("password"), value: PASSWORD_POLICY_VALUE },
|
||||
{ name: i18nService.t("passphrase"), value: "passphrase" },
|
||||
];
|
||||
|
||||
this.data.valueChanges
|
||||
.pipe(isEnabled(PASSWORD_POLICY_VALUE), takeUntilDestroyed())
|
||||
.subscribe(this.showPasswordPolicies);
|
||||
this.data.valueChanges
|
||||
.pipe(isEnabled(PASSPHRASE_POLICY_VALUE), takeUntilDestroyed())
|
||||
.subscribe(this.showPassphrasePolicies);
|
||||
}
|
||||
}
|
||||
|
||||
const PASSWORD_POLICY_VALUE = "password";
|
||||
const PASSPHRASE_POLICY_VALUE = "passphrase";
|
||||
|
||||
function isEnabled(enabledValue: string) {
|
||||
return map((d: { overridePasswordType: string }) => {
|
||||
const type = d?.overridePasswordType ?? enabledValue;
|
||||
return type === enabledValue;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -323,7 +323,6 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
this.syncService.setLastSync(new Date(0)),
|
||||
this.cryptoService.clearKeys(),
|
||||
this.cipherService.clear(userId),
|
||||
this.folderService.clear(userId),
|
||||
|
||||
@@ -26,11 +26,12 @@ 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 { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||
|
||||
import { flagEnabled } from "../../../utils/flags";
|
||||
import { RouterService, StateService } from "../../core";
|
||||
import { RouterService } from "../../core";
|
||||
import { AcceptOrganizationInviteService } from "../organization-invite/accept-organization.service";
|
||||
import { OrganizationInvite } from "../organization-invite/organization-invite";
|
||||
|
||||
|
||||
@@ -187,6 +187,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
|
||||
if (this.hasProvider) {
|
||||
this.formGroup.controls.businessOwned.setValue(true);
|
||||
this.formGroup.controls.clientOwnerEmail.addValidators(Validators.required);
|
||||
this.changedOwnedBusiness();
|
||||
this.provider = await this.providerApiService.getProvider(this.providerId);
|
||||
const providerDefaultPlan = this.passwordManagerPlans.find(
|
||||
|
||||
@@ -38,7 +38,6 @@ import { FileDownloadService } from "@bitwarden/common/platform/abstractions/fil
|
||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
|
||||
@@ -71,7 +70,6 @@ import { EventService } from "./event.service";
|
||||
import { InitService } from "./init.service";
|
||||
import { ModalService } from "./modal.service";
|
||||
import { RouterService } from "./router.service";
|
||||
import { StateService as WebStateService } from "./state";
|
||||
import { WebFileDownloadService } from "./web-file-download.service";
|
||||
import { WebPlatformUtilsService } from "./web-platform-utils.service";
|
||||
|
||||
@@ -135,11 +133,6 @@ const safeProviders: SafeProvider[] = [
|
||||
useClass: ModalService,
|
||||
useAngularDecorators: true,
|
||||
}),
|
||||
safeProvider(WebStateService),
|
||||
safeProvider({
|
||||
provide: StateService,
|
||||
useExisting: WebStateService,
|
||||
}),
|
||||
safeProvider({
|
||||
provide: FileDownloadService,
|
||||
useClass: WebFileDownloadService,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from "./core.module";
|
||||
export * from "./event.service";
|
||||
export * from "./router.service";
|
||||
export * from "./state/state.service";
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./state.service";
|
||||
@@ -1,55 +0,0 @@
|
||||
import { Inject, Injectable } from "@angular/core";
|
||||
|
||||
import {
|
||||
MEMORY_STORAGE,
|
||||
SECURE_STORAGE,
|
||||
STATE_FACTORY,
|
||||
} from "@bitwarden/angular/services/injection-tokens";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
import { Account } from "@bitwarden/common/platform/models/domain/account";
|
||||
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
import { StorageOptions } from "@bitwarden/common/platform/models/domain/storage-options";
|
||||
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
|
||||
import { StateService as BaseStateService } from "@bitwarden/common/platform/services/state.service";
|
||||
|
||||
@Injectable()
|
||||
export class StateService extends BaseStateService<GlobalState, Account> {
|
||||
constructor(
|
||||
storageService: AbstractStorageService,
|
||||
@Inject(SECURE_STORAGE) secureStorageService: AbstractStorageService,
|
||||
@Inject(MEMORY_STORAGE) memoryStorageService: AbstractStorageService,
|
||||
logService: LogService,
|
||||
@Inject(STATE_FACTORY) stateFactory: StateFactory<GlobalState, Account>,
|
||||
accountService: AccountService,
|
||||
environmentService: EnvironmentService,
|
||||
tokenService: TokenService,
|
||||
migrationRunner: MigrationRunner,
|
||||
) {
|
||||
super(
|
||||
storageService,
|
||||
secureStorageService,
|
||||
memoryStorageService,
|
||||
logService,
|
||||
stateFactory,
|
||||
accountService,
|
||||
environmentService,
|
||||
tokenService,
|
||||
migrationRunner,
|
||||
);
|
||||
}
|
||||
|
||||
override async getLastSync(options?: StorageOptions): Promise<string> {
|
||||
options = this.reconcileOptions(options, await this.defaultInMemoryOptions());
|
||||
return await super.getLastSync(options);
|
||||
}
|
||||
|
||||
override async setLastSync(value: string, options?: StorageOptions): Promise<void> {
|
||||
options = this.reconcileOptions(options, await this.defaultInMemoryOptions());
|
||||
return await super.setLastSync(value, options);
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,12 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
biometricsNeedsSetup: () => Promise<boolean>;
|
||||
biometricsSupportsAutoSetup(): Promise<boolean> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
biometricsSetup: () => Promise<void>;
|
||||
|
||||
supportsSecureStorage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4145,8 +4145,9 @@
|
||||
"minimumNumberOfWords": {
|
||||
"message": "Minimum number of words"
|
||||
},
|
||||
"defaultType": {
|
||||
"message": "Default type"
|
||||
"overridePasswordTypePolicy": {
|
||||
"message": "Password Type",
|
||||
"description": "Name of the password generator policy that overrides the user's password/passphrase selection."
|
||||
},
|
||||
"userPreference": {
|
||||
"message": "User preference"
|
||||
@@ -8794,6 +8795,12 @@
|
||||
"purchasedSeatsRemoved": {
|
||||
"message": "purchased seats removed"
|
||||
},
|
||||
"fileSends": {
|
||||
"message": "File Sends"
|
||||
},
|
||||
"textSends": {
|
||||
"message": "Text Sends"
|
||||
},
|
||||
"includesXMembers": {
|
||||
"message": "for $COUNT$ member",
|
||||
"placeholders": {
|
||||
|
||||
Reference in New Issue
Block a user