[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">
|
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||||
<bit-form-field class="tw-col-span-6 tw-mb-0">
|
<bit-form-field class="tw-col-span-6 tw-mb-0">
|
||||||
<bit-label>{{ "defaultType" | i18n }}</bit-label>
|
<bit-label>{{ "overridePasswordTypePolicy" | i18n }}</bit-label>
|
||||||
<bit-select formControlName="defaultType" id="defaultType">
|
<bit-select formControlName="overridePasswordType" id="overrideType">
|
||||||
<bit-option *ngFor="let o of defaultTypes" [value]="o.value" [label]="o.name"></bit-option>
|
<bit-option
|
||||||
|
*ngFor="let o of overridePasswordTypeOptions"
|
||||||
|
[value]="o.value"
|
||||||
|
[label]="o.name"
|
||||||
|
></bit-option>
|
||||||
</bit-select>
|
</bit-select>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 bitTypography="h3" class="tw-mt-4">{{ "password" | i18n }}</h3>
|
<!-- password-specific policies -->
|
||||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
<div *ngIf="showPasswordPolicies$ | async">
|
||||||
<bit-form-field class="tw-col-span-6">
|
<h3 bitTypography="h3" class="tw-mt-4">{{ "password" | i18n }}</h3>
|
||||||
<bit-label>{{ "minLength" | i18n }}</bit-label>
|
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||||
<input bitInput type="number" min="5" max="128" formControlName="minLength" />
|
<bit-form-field class="tw-col-span-6">
|
||||||
</bit-form-field>
|
<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>
|
||||||
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
|
||||||
<bit-form-field class="tw-col-span-6">
|
<!-- passphrase-specific policies -->
|
||||||
<bit-label>{{ "minNumbers" | i18n }}</bit-label>
|
<div *ngIf="showPassphrasePolicies$ | async">
|
||||||
<input bitInput type="number" min="0" max="9" formControlName="minNumbers" />
|
<h3 bitTypography="h3" class="tw-mt-4">{{ "passphrase" | i18n }}</h3>
|
||||||
</bit-form-field>
|
<div class="tw-grid tw-grid-cols-12 tw-gap-4">
|
||||||
<bit-form-field class="tw-col-span-6">
|
<bit-form-field class="tw-col-span-6">
|
||||||
<bit-label>{{ "minSpecial" | i18n }}</bit-label>
|
<bit-label>{{ "minimumNumberOfWords" | i18n }}</bit-label>
|
||||||
<input bitInput type="number" min="0" max="9" formControlName="minSpecial" />
|
<input bitInput type="number" min="3" max="20" formControlName="minNumberWords" />
|
||||||
</bit-form-field>
|
</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>
|
</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>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { UntypedFormBuilder, Validators } from "@angular/forms";
|
import { UntypedFormBuilder, Validators } from "@angular/forms";
|
||||||
|
import { BehaviorSubject, map } from "rxjs";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { DefaultPassphraseBoundaries, DefaultPasswordBoundaries } from "@bitwarden/generator-core";
|
||||||
|
|
||||||
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
||||||
|
|
||||||
|
@ -19,20 +22,59 @@ export class PasswordGeneratorPolicy extends BasePolicy {
|
||||||
})
|
})
|
||||||
export class PasswordGeneratorPolicyComponent extends BasePolicyComponent {
|
export class PasswordGeneratorPolicyComponent extends BasePolicyComponent {
|
||||||
data = this.formBuilder.group({
|
data = this.formBuilder.group({
|
||||||
defaultType: [null],
|
overridePasswordType: [null],
|
||||||
minLength: [null, [Validators.min(5), Validators.max(128)]],
|
minLength: [
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
Validators.min(DefaultPasswordBoundaries.length.min),
|
||||||
|
Validators.max(DefaultPasswordBoundaries.length.max),
|
||||||
|
],
|
||||||
|
],
|
||||||
useUpper: [null],
|
useUpper: [null],
|
||||||
useLower: [null],
|
useLower: [null],
|
||||||
useNumbers: [null],
|
useNumbers: [null],
|
||||||
useSpecial: [null],
|
useSpecial: [null],
|
||||||
minNumbers: [null, [Validators.min(0), Validators.max(9)]],
|
minNumbers: [
|
||||||
minSpecial: [null, [Validators.min(0), Validators.max(9)]],
|
null,
|
||||||
minNumberWords: [null, [Validators.min(3), Validators.max(20)]],
|
[
|
||||||
|
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],
|
capitalize: [null],
|
||||||
includeNumber: [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(
|
constructor(
|
||||||
private formBuilder: UntypedFormBuilder,
|
private formBuilder: UntypedFormBuilder,
|
||||||
|
@ -40,10 +82,27 @@ export class PasswordGeneratorPolicyComponent extends BasePolicyComponent {
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.defaultTypes = [
|
this.overridePasswordTypeOptions = [
|
||||||
{ name: i18nService.t("userPreference"), value: null },
|
{ 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" },
|
{ 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": {
|
"minimumNumberOfWords": {
|
||||||
"message": "Minimum number of words"
|
"message": "Minimum number of words"
|
||||||
},
|
},
|
||||||
"defaultType": {
|
"overridePasswordTypePolicy": {
|
||||||
"message": "Default type"
|
"message": "Password Type",
|
||||||
|
"description": "Name of the password generator policy that overrides the user's password/passphrase selection."
|
||||||
},
|
},
|
||||||
"userPreference": {
|
"userPreference": {
|
||||||
"message": "User preference"
|
"message": "User preference"
|
||||||
|
|
Loading…
Reference in New Issue