1
0
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:
Vicki League
2024-08-07 11:08:31 -04:00
163 changed files with 4236 additions and 1148 deletions

View File

@@ -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>!&#64;#$%^&amp;*</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>!&#64;#$%^&amp;*</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>

View File

@@ -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;
});
}

View File

@@ -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),

View File

@@ -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";

View File

@@ -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(

View File

@@ -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,

View File

@@ -1,4 +1,3 @@
export * from "./core.module";
export * from "./event.service";
export * from "./router.service";
export * from "./state/state.service";

View File

@@ -1 +0,0 @@
export * from "./state.service";

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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": {