[PM-10104] add generator password type policy (#10275)
* add generator password type policy * limit options display to only valid options when override in effect * remove defaultType i18n message
This commit is contained in:
parent
2ce8500391
commit
4a5a0ca537
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue