[AC-1145] Add trusted devices option to encryption settings on sso config (#5383)

* [AC-1145] Add TDE feature flag

* [AC-1145] Update sso-config to use new member decryption type and remove keyConnectorEnabled

* [AC-1145] Add new TDE option to SSO config form and update to CL radio buttons

* [AC-1145] Update checkboxes to CL checkboxes

* [AC-1145] Fix messages.json warning

* [AC-1145] Update to new form async actions

* [AC-1145] Modify key connector option display logic to check for TDE feature flag

* [AC-1145] Remove obsolete app-checkbox component

* [AC-1145] Update TDE option description to refer to master password reset policy
This commit is contained in:
Shane Melton 2023-05-10 12:51:56 -07:00 committed by GitHub
parent a64cecff68
commit ab4d8df2ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 168 additions and 190 deletions

View File

@ -5218,9 +5218,6 @@
"message": "to require all members to log in with SSO.", "message": "to require all members to log in with SSO.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'" "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Use the require single-sign-on authentication policy to require all members to log in with SSO.'"
}, },
"ssoPolicyHelpKeyConnector": {
"message": "The require SSO authentication and single organization policies are required to set up Key Connector decryption."
},
"memberDecryptionOption": { "memberDecryptionOption": {
"message": "Member decryption options" "message": "Member decryption options"
}, },
@ -5230,8 +5227,17 @@
"keyConnector": { "keyConnector": {
"message": "Key Connector" "message": "Key Connector"
}, },
"memberDecryptionKeyConnectorDesc": { "memberDecryptionKeyConnectorDescStart": {
"message": "Connect login with SSO to your self-hosted decryption key server. Using this option, members wont need to use their master passwords to decrypt vault data. Contact Bitwarden Support for set up assistance." "message": "Connect login with SSO to your self-hosted decryption key server. Using this option, members wont need to use their master passwords to decrypt vault data. The",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members wont need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'"
},
"memberDecryptionKeyConnectorDescLink": {
"message": "require SSO authentication and single organization policies",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members wont need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'"
},
"memberDecryptionKeyConnectorDescEnd": {
"message": "are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Connect login with SSO to your self-hosted decryption key server. Using this option, members wont need to use their master passwords to decrypt vault data. The require SSO authentication and single organization policies are required to set up Key Connector decryption. Contact Bitwarden Support for set up assistance.'"
}, },
"keyConnectorPolicyRestriction": { "keyConnectorPolicyRestriction": {
"message": "\"Login with SSO and Key Connector Decryption\" is activated. This policy will only apply to owners and admins." "message": "\"Login with SSO and Key Connector Decryption\" is activated. This policy will only apply to owners and admins."
@ -5535,7 +5541,7 @@
}, },
"lastSync": { "lastSync": {
"message": "Last sync", "message": "Last sync",
"Description": "Used as a prefix to indicate the last time a sync occured. Example \"Last sync 1968-11-16 00:00:00\"" "description": "Used as a prefix to indicate the last time a sync occured. Example \"Last sync 1968-11-16 00:00:00\""
}, },
"sponsorshipsSynced": { "sponsorshipsSynced": {
"message": "Self-hosted sponsorships synced." "message": "Self-hosted sponsorships synced."
@ -6781,5 +6787,20 @@
}, },
"updateKdfSettings": { "updateKdfSettings": {
"message": "Update KDF settings" "message": "Update KDF settings"
},
"trustedDeviceEncryption": {
"message": "Trusted device encryption"
},
"memberDecryptionTdeDescStart": {
"message": "Once authenticated, members will decrypt vault data using a key stored on their device. The",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescLink": {
"message": "master password reset policy",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'"
},
"memberDecryptionTdeDescEnd": {
"message": "with automatic enrollment will turn on when this option is used.",
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Once authenticated, members will decrypt vault data using a key stored on their device. The master password reset policy with automatic enrollment will turn on when this option is used.'"
} }
} }

View File

@ -1,63 +0,0 @@
import { Directive, Input, OnInit, Self } from "@angular/core";
import { ControlValueAccessor, UntypedFormControl, NgControl, Validators } from "@angular/forms";
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
@Directive()
export abstract class BaseCvaComponent implements ControlValueAccessor, OnInit {
get describedById() {
return this.showDescribedBy ? this.controlId + "Desc" : null;
}
get showDescribedBy() {
return this.helperText != null || this.controlDir.control.hasError("required");
}
get isRequired() {
return this.controlDir.control.hasValidator(Validators.required);
}
@Input() label: string;
@Input() controlId: string;
@Input() helperText: string;
internalControl = new UntypedFormControl("");
protected onChange: any;
protected onTouched: any;
constructor(@Self() public controlDir: NgControl) {
this.controlDir.valueAccessor = this;
}
ngOnInit() {
this.internalControl.valueChanges.subscribe(this.onValueChangesInternal);
}
onBlurInternal() {
this.onTouched();
}
// CVA interfaces
writeValue(value: string) {
this.internalControl.setValue(value);
}
registerOnChange(fn: any) {
this.onChange = fn;
}
registerOnTouched(fn: any) {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean) {
if (isDisabled) {
this.internalControl.disable();
} else {
this.internalControl.enable();
}
}
protected onValueChangesInternal: any = (value: string) => this.onChange(value);
// End CVA interfaces
}

View File

@ -1,16 +0,0 @@
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
[attr.id]="controlId"
[attr.aria-describedby]="describedById"
[formControl]="internalControl"
(blur)="onBlurInternal()"
/>
<label class="form-check-label" [attr.for]="controlId">{{ label }}</label>
</div>
<small *ngIf="showDescribedBy" [attr.id]="describedById" class="form-text text-muted">{{
helperText
}}</small>
</div>

View File

@ -1,10 +0,0 @@
import { Component } from "@angular/core";
import { BaseCvaComponent } from "./base-cva.component";
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
@Component({
selector: "app-input-checkbox",
templateUrl: "input-checkbox.component.html",
})
export class InputCheckboxComponent extends BaseCvaComponent {}

View File

@ -4,7 +4,6 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared/shared.module";
import { SsoComponent } from "../../auth/sso/sso.component"; import { SsoComponent } from "../../auth/sso/sso.component";
import { InputCheckboxComponent } from "./components/input-checkbox.component";
import { DomainAddEditDialogComponent } from "./manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component"; import { DomainAddEditDialogComponent } from "./manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component";
import { DomainVerificationComponent } from "./manage/domain-verification/domain-verification.component"; import { DomainVerificationComponent } from "./manage/domain-verification/domain-verification.component";
import { ScimComponent } from "./manage/scim.component"; import { ScimComponent } from "./manage/scim.component";
@ -13,7 +12,6 @@ import { OrganizationsRoutingModule } from "./organizations-routing.module";
@NgModule({ @NgModule({
imports: [SharedModule, OrganizationsRoutingModule], imports: [SharedModule, OrganizationsRoutingModule],
declarations: [ declarations: [
InputCheckboxComponent,
SsoComponent, SsoComponent,
ScimComponent, ScimComponent,
DomainVerificationComponent, DomainVerificationComponent,

View File

@ -8,32 +8,24 @@
title="{{ 'loading' | i18n }}" title="{{ 'loading' | i18n }}"
aria-hidden="true" aria-hidden="true"
></i> ></i>
<span class="sr-only">{{ "loading" | i18n }}</span> <span class="tw-sr-only">{{ "loading" | i18n }}</span>
</ng-container> </ng-container>
<form <form [formGroup]="ssoConfigForm" [bitSubmit]="submit" *ngIf="!loading">
#form
(ngSubmit)="submit()"
[formGroup]="ssoConfigForm"
[appApiAction]="formPromise"
*ngIf="!loading"
>
<p> <p>
{{ "ssoPolicyHelpStart" | i18n }} {{ "ssoPolicyHelpStart" | i18n }}
<a routerLink="../policies">{{ "ssoPolicyHelpLink" | i18n }}</a> <a routerLink="../policies">{{ "ssoPolicyHelpLink" | i18n }}</a>
{{ "ssoPolicyHelpEnd" | i18n }} {{ "ssoPolicyHelpEnd" | i18n }}
<br /> <br />
{{ "ssoPolicyHelpKeyConnector" | i18n }}
</p> </p>
<!-- Root form --> <!-- Root form -->
<ng-container> <ng-container>
<app-input-checkbox <bit-form-control>
controlId="enabled" <bit-label>{{ "allowSso" | i18n }}</bit-label>
formControlName="enabled" <input bitCheckbox type="checkbox" formControlName="enabled" id="enabled" />
[label]="'allowSso' | i18n" <bit-hint>{{ "allowSsoDesc" | i18n }}</bit-hint>
[helperText]="'allowSsoDesc' | i18n" </bit-form-control>
></app-input-checkbox>
<bit-form-field> <bit-form-field>
<bit-label>{{ "ssoIdentifier" | i18n }}</bit-label> <bit-label>{{ "ssoIdentifier" | i18n }}</bit-label>
@ -43,31 +35,25 @@
<hr /> <hr />
<div class="form-group"> <bit-radio-group formControlName="memberDecryptionType">
<label>{{ "memberDecryptionOption" | i18n }}</label> <bit-label>{{ "memberDecryptionOption" | i18n }}</bit-label>
<div class="form-check form-check-block">
<input <bit-radio-button
class="form-check-input" class="tw-block"
type="radio" id="memberDecryptionPass"
id="memberDecryptionPass" [value]="memberDecryptionType.MasterPassword"
[value]="false" >
formControlName="keyConnectorEnabled" <bit-label>{{ "masterPass" | i18n }}</bit-label>
/> </bit-radio-button>
<label class="form-check-label" for="memberDecryptionPass">
{{ "masterPass" | i18n }} <bit-radio-button
<small>{{ "memberDecryptionPassDesc" | i18n }}</small> class="tw-block"
</label> id="memberDecryptionKey"
</div> [value]="memberDecryptionType.KeyConnector"
<div class="form-check mt-2 form-check-block"> [disabled]="!organization.useKeyConnector || null"
<input *ngIf="showKeyConnectorOptions"
class="form-check-input" >
type="radio" <bit-label>
id="memberDecryptionKey"
[value]="true"
formControlName="keyConnectorEnabled"
[attr.disabled]="!organization.useKeyConnector || null"
/>
<label class="form-check-label" for="memberDecryptionKey">
{{ "keyConnector" | i18n }} {{ "keyConnector" | i18n }}
<a <a
target="_blank" target="_blank"
@ -77,13 +63,38 @@
> >
<i class="bwi bwi-question-circle" aria-hidden="true"></i> <i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a> </a>
<small>{{ "memberDecryptionKeyConnectorDesc" | i18n }}</small> </bit-label>
</label> <bit-hint>
</div> {{ "memberDecryptionKeyConnectorDescStart" | i18n }}
</div> <a routerLink="../policies">{{ "memberDecryptionKeyConnectorDescLink" | i18n }}</a>
{{ "memberDecryptionKeyConnectorDescEnd" | i18n }}
</bit-hint>
</bit-radio-button>
<bit-radio-button
class="tw-block"
id="memberDecryptionTde"
[value]="memberDecryptionType.TrustedDeviceEncryption"
*ngIf="showTdeOptions"
>
<bit-label>
{{ "trustedDeviceEncryption" | i18n }}
</bit-label>
<bit-hint>
{{ "memberDecryptionTdeDescStart" | i18n }}
<a routerLink="../policies">{{ "memberDecryptionTdeDescLink" | i18n }}</a>
{{ "memberDecryptionTdeDescEnd" | i18n }}
</bit-hint>
</bit-radio-button>
</bit-radio-group>
<!-- Key Connector --> <!-- Key Connector -->
<ng-container *ngIf="ssoConfigForm.get('keyConnectorEnabled').value"> <ng-container
*ngIf="
ssoConfigForm.value.memberDecryptionType === memberDecryptionType.KeyConnector &&
showKeyConnectorOptions
"
>
<app-callout type="warning" [useAlertRole]="true"> <app-callout type="warning" [useAlertRole]="true">
{{ "keyConnectorWarning" | i18n }} {{ "keyConnectorWarning" | i18n }}
</app-callout> </app-callout>
@ -205,11 +216,15 @@
</select> </select>
</bit-form-field> </bit-form-field>
<app-input-checkbox <bit-form-control>
controlId="getClaimsFromUserInfoEndpoint" <bit-label>{{ "getClaimsFromUserInfoEndpoint" | i18n }}</bit-label>
formControlName="getClaimsFromUserInfoEndpoint" <input
[label]="'getClaimsFromUserInfoEndpoint' | i18n" bitCheckbox
></app-input-checkbox> type="checkbox"
formControlName="getClaimsFromUserInfoEndpoint"
id="getClaimsFromUserInfoEndpoint"
/>
</bit-form-control>
<!-- Optional customizations --> <!-- Optional customizations -->
<div <div
@ -381,17 +396,25 @@
</select> </select>
</bit-form-field> </bit-form-field>
<app-input-checkbox <bit-form-control>
controlId="spWantAssertionsSigned" <bit-label>{{ "spWantAssertionsSigned" | i18n }}</bit-label>
formControlName="spWantAssertionsSigned" <input
[label]="'spWantAssertionsSigned' | i18n" bitCheckbox
></app-input-checkbox> type="checkbox"
formControlName="spWantAssertionsSigned"
id="spWantAssertionsSigned"
/>
</bit-form-control>
<app-input-checkbox <bit-form-control>
controlId="spValidateCertificates" <bit-label>{{ "spValidateCertificates" | i18n }}</bit-label>
formControlName="spValidateCertificates" <input
[label]="'spValidateCertificates' | i18n" bitCheckbox
></app-input-checkbox> type="checkbox"
formControlName="spValidateCertificates"
id="spValidateCertificates"
/>
</bit-form-control>
</div> </div>
<!-- SAML2 IDP --> <!-- SAML2 IDP -->
@ -462,21 +485,29 @@
[label]="'idpAllowUnsolicitedAuthnResponse' | i18n" [label]="'idpAllowUnsolicitedAuthnResponse' | i18n"
></app-input-checkbox> --> ></app-input-checkbox> -->
<app-input-checkbox <bit-form-control>
controlId="idpAllowOutboundLogoutRequests" <bit-label>{{ "idpAllowOutboundLogoutRequests" | i18n }}</bit-label>
formControlName="idpAllowOutboundLogoutRequests" <input
[label]="'idpAllowOutboundLogoutRequests' | i18n" bitCheckbox
></app-input-checkbox> type="checkbox"
formControlName="idpAllowOutboundLogoutRequests"
id="idpAllowOutboundLogoutRequests"
/>
</bit-form-control>
<app-input-checkbox <bit-form-control>
controlId="idpWantAuthnRequestsSigned" <bit-label>{{ "idpSignAuthenticationRequests" | i18n }}</bit-label>
formControlName="idpWantAuthnRequestsSigned" <input
[label]="'idpSignAuthenticationRequests' | i18n" bitCheckbox
></app-input-checkbox> type="checkbox"
formControlName="idpWantAuthnRequestsSigned"
id="idpWantAuthnRequestsSigned"
/>
</bit-form-control>
</div> </div>
</div> </div>
<button type="submit" buttonType="primary" bitButton [loading]="form.loading"> <button type="submit" buttonType="primary" bitButton bitFormButton>
{{ "save" | i18n }} {{ "save" | i18n }}
</button> </button>
<bit-error-summary [formGroup]="ssoConfigForm"></bit-error-summary> <bit-error-summary [formGroup]="ssoConfigForm"></bit-error-summary>

View File

@ -12,12 +12,14 @@ import { concatMap, Subject, takeUntil } from "rxjs";
import { SelectOptions } from "@bitwarden/angular/interfaces/selectOptions"; import { SelectOptions } from "@bitwarden/angular/interfaces/selectOptions";
import { ControlsOf } from "@bitwarden/angular/types/controls-of"; import { ControlsOf } from "@bitwarden/angular/types/controls-of";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { ConfigServiceAbstraction } from "@bitwarden/common/abstractions/config/config.service.abstraction";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { import {
MemberDecryptionType,
OpenIdConnectRedirectBehavior, OpenIdConnectRedirectBehavior,
Saml2BindingType, Saml2BindingType,
Saml2NameIdFormat, Saml2NameIdFormat,
@ -28,6 +30,7 @@ import { SsoConfigApi } from "@bitwarden/common/auth/models/api/sso-config.api";
import { OrganizationSsoRequest } from "@bitwarden/common/auth/models/request/organization-sso.request"; import { OrganizationSsoRequest } from "@bitwarden/common/auth/models/request/organization-sso.request";
import { OrganizationSsoResponse } from "@bitwarden/common/auth/models/response/organization-sso.response"; import { OrganizationSsoResponse } from "@bitwarden/common/auth/models/response/organization-sso.response";
import { SsoConfigView } from "@bitwarden/common/auth/models/view/sso-config.view"; import { SsoConfigView } from "@bitwarden/common/auth/models/view/sso-config.view";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { Utils } from "@bitwarden/common/misc/utils"; import { Utils } from "@bitwarden/common/misc/utils";
import { ssoTypeValidator } from "./sso-type.validator"; import { ssoTypeValidator } from "./sso-type.validator";
@ -40,6 +43,7 @@ const defaultSigningAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha2
}) })
export class SsoComponent implements OnInit, OnDestroy { export class SsoComponent implements OnInit, OnDestroy {
readonly ssoType = SsoType; readonly ssoType = SsoType;
readonly memberDecryptionType = MemberDecryptionType;
readonly ssoTypeOptions: SelectOptions[] = [ readonly ssoTypeOptions: SelectOptions[] = [
{ name: this.i18nService.t("selectType"), value: SsoType.None, disabled: true }, { name: this.i18nService.t("selectType"), value: SsoType.None, disabled: true },
@ -83,6 +87,8 @@ export class SsoComponent implements OnInit, OnDestroy {
]; ];
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
showTdeOptions = false;
showKeyConnectorOptions = false;
showOpenIdCustomizations = false; showOpenIdCustomizations = false;
@ -90,7 +96,6 @@ export class SsoComponent implements OnInit, OnDestroy {
haveTestedKeyConnector = false; haveTestedKeyConnector = false;
organizationId: string; organizationId: string;
organization: Organization; organization: Organization;
formPromise: Promise<OrganizationSsoResponse>;
callbackPath: string; callbackPath: string;
signedOutCallbackPath: string; signedOutCallbackPath: string;
@ -147,7 +152,7 @@ export class SsoComponent implements OnInit, OnDestroy {
protected ssoConfigForm = this.formBuilder.group<ControlsOf<SsoConfigView>>({ protected ssoConfigForm = this.formBuilder.group<ControlsOf<SsoConfigView>>({
configType: new FormControl(SsoType.None), configType: new FormControl(SsoType.None),
keyConnectorEnabled: new FormControl(false), memberDecryptionType: new FormControl(MemberDecryptionType.MasterPassword),
keyConnectorUrl: new FormControl(""), keyConnectorUrl: new FormControl(""),
openId: this.openIdForm, openId: this.openIdForm,
saml: this.samlForm, saml: this.samlForm,
@ -174,7 +179,8 @@ export class SsoComponent implements OnInit, OnDestroy {
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService, private i18nService: I18nService,
private organizationService: OrganizationService, private organizationService: OrganizationService,
private organizationApiService: OrganizationApiServiceAbstraction private organizationApiService: OrganizationApiServiceAbstraction,
private configService: ConfigServiceAbstraction
) {} ) {}
async ngOnInit() { async ngOnInit() {
@ -223,6 +229,15 @@ export class SsoComponent implements OnInit, OnDestroy {
takeUntil(this.destroy$) takeUntil(this.destroy$)
) )
.subscribe(); .subscribe();
const tdeFeatureFlag = await this.configService.getFeatureFlagBool(
FeatureFlag.TrustedDeviceEncryption
);
this.showTdeOptions = tdeFeatureFlag && !this.platformUtilsService.isSelfHost();
// If the tde flag is not enabled, continue showing the key connector options to keep the UI the same
// Once the flag is removed, we can rely on the platformUtilsService.isSelfHost() check alone
this.showKeyConnectorOptions = !tdeFeatureFlag || this.platformUtilsService.isSelfHost();
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -244,10 +259,10 @@ export class SsoComponent implements OnInit, OnDestroy {
this.loading = false; this.loading = false;
} }
async submit() { submit = async () => {
this.updateFormValidationState(this.ssoConfigForm); this.updateFormValidationState(this.ssoConfigForm);
if (this.ssoConfigForm.value.keyConnectorEnabled) { if (this.ssoConfigForm.value.memberDecryptionType === MemberDecryptionType.KeyConnector) {
this.haveTestedKeyConnector = false; this.haveTestedKeyConnector = false;
await this.validateKeyConnectorUrl(); await this.validateKeyConnectorUrl();
} }
@ -262,18 +277,11 @@ export class SsoComponent implements OnInit, OnDestroy {
request.identifier = this.ssoIdentifierCtrl.value === "" ? null : this.ssoIdentifierCtrl.value; request.identifier = this.ssoIdentifierCtrl.value === "" ? null : this.ssoIdentifierCtrl.value;
request.data = SsoConfigApi.fromView(this.ssoConfigForm.getRawValue()); request.data = SsoConfigApi.fromView(this.ssoConfigForm.getRawValue());
this.formPromise = this.organizationApiService.updateSso(this.organizationId, request); const response = await this.organizationApiService.updateSso(this.organizationId, request);
this.populateForm(response);
try { this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved"));
const response = await this.formPromise; };
this.populateForm(response);
this.platformUtilsService.showToast("success", null, this.i18nService.t("ssoSettingsSaved"));
} catch {
// Logged by appApiAction, do nothing
}
this.formPromise = null;
}
async validateKeyConnectorUrl() { async validateKeyConnectorUrl() {
if (this.haveTestedKeyConnector) { if (this.haveTestedKeyConnector) {
@ -313,7 +321,7 @@ export class SsoComponent implements OnInit, OnDestroy {
get enableTestKeyConnector() { get enableTestKeyConnector() {
return ( return (
this.ssoConfigForm.get("keyConnectorEnabled").value && this.ssoConfigForm.value?.memberDecryptionType === MemberDecryptionType.KeyConnector &&
!Utils.isNullOrWhitespace(this.keyConnectorUrl?.value) !Utils.isNullOrWhitespace(this.keyConnectorUrl?.value)
); );
} }

View File

@ -4,6 +4,12 @@ export enum SsoType {
Saml2 = 2, Saml2 = 2,
} }
export enum MemberDecryptionType {
MasterPassword = 0,
KeyConnector = 1,
TrustedDeviceEncryption = 2,
}
export enum OpenIdConnectRedirectBehavior { export enum OpenIdConnectRedirectBehavior {
RedirectGet = 0, RedirectGet = 0,
FormPost = 1, FormPost = 1,

View File

@ -1,5 +1,6 @@
import { BaseResponse } from "../../../models/response/base.response"; import { BaseResponse } from "../../../models/response/base.response";
import { import {
MemberDecryptionType,
OpenIdConnectRedirectBehavior, OpenIdConnectRedirectBehavior,
Saml2BindingType, Saml2BindingType,
Saml2NameIdFormat, Saml2NameIdFormat,
@ -11,8 +12,8 @@ import { SsoConfigView } from "../view/sso-config.view";
export class SsoConfigApi extends BaseResponse { export class SsoConfigApi extends BaseResponse {
static fromView(view: SsoConfigView, api = new SsoConfigApi()) { static fromView(view: SsoConfigView, api = new SsoConfigApi()) {
api.configType = view.configType; api.configType = view.configType;
api.memberDecryptionType = view.memberDecryptionType;
api.keyConnectorEnabled = view.keyConnectorEnabled;
api.keyConnectorUrl = view.keyConnectorUrl; api.keyConnectorUrl = view.keyConnectorUrl;
if (api.configType === SsoType.OpenIdConnect) { if (api.configType === SsoType.OpenIdConnect) {
@ -52,8 +53,8 @@ export class SsoConfigApi extends BaseResponse {
return api; return api;
} }
configType: SsoType; configType: SsoType;
memberDecryptionType: MemberDecryptionType;
keyConnectorEnabled: boolean;
keyConnectorUrl: string; keyConnectorUrl: string;
// OpenId // OpenId
@ -95,8 +96,8 @@ export class SsoConfigApi extends BaseResponse {
} }
this.configType = this.getResponseProperty("ConfigType"); this.configType = this.getResponseProperty("ConfigType");
this.memberDecryptionType = this.getResponseProperty("MemberDecryptionType");
this.keyConnectorEnabled = this.getResponseProperty("KeyConnectorEnabled");
this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl"); this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl");
this.authority = this.getResponseProperty("Authority"); this.authority = this.getResponseProperty("Authority");

View File

@ -1,5 +1,6 @@
import { View } from "../../../models/view/view"; import { View } from "../../../models/view/view";
import { import {
MemberDecryptionType,
OpenIdConnectRedirectBehavior, OpenIdConnectRedirectBehavior,
Saml2BindingType, Saml2BindingType,
Saml2NameIdFormat, Saml2NameIdFormat,
@ -14,7 +15,7 @@ export class SsoConfigView extends View {
configType: SsoType; configType: SsoType;
keyConnectorEnabled: boolean; memberDecryptionType: MemberDecryptionType;
keyConnectorUrl: string; keyConnectorUrl: string;
openId: { openId: {
@ -66,8 +67,8 @@ export class SsoConfigView extends View {
} }
this.configType = orgSsoResponse.data.configType; this.configType = orgSsoResponse.data.configType;
this.memberDecryptionType = orgSsoResponse.data.memberDecryptionType;
this.keyConnectorEnabled = orgSsoResponse.data.keyConnectorEnabled;
this.keyConnectorUrl = orgSsoResponse.data.keyConnectorUrl; this.keyConnectorUrl = orgSsoResponse.data.keyConnectorUrl;
if (this.configType === SsoType.OpenIdConnect) { if (this.configType === SsoType.OpenIdConnect) {

View File

@ -1,4 +1,5 @@
export enum FeatureFlag { export enum FeatureFlag {
DisplayEuEnvironmentFlag = "display-eu-environment", DisplayEuEnvironmentFlag = "display-eu-environment",
DisplayLowKdfIterationWarningFlag = "display-kdf-iteration-warning", DisplayLowKdfIterationWarningFlag = "display-kdf-iteration-warning",
TrustedDeviceEncryption = "trusted-device-encryption",
} }