mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 18:07:31 +00:00
feat(saml): allow setting nameid-format and alternative mapping for transient format (#7979)
# Which Problems Are Solved ZITADEL currently always uses `urn:oasis:names:tc:SAML:2.0:nameid-format:persistent` in SAML requests, relying on the IdP to respect that flag and always return a peristent nameid in order to be able to map the external user with an existing user (idp link) in ZITADEL. In case the IdP however returns a `urn:oasis:names:tc:SAML:2.0:nameid-format:transient` (transient) nameid, the attribute will differ between each request and it will not be possible to match existing users. # How the Problems Are Solved This PR adds the following two options on SAML IdP: - **nameIDFormat**: allows to set the nameid-format used in the SAML Request - **transientMappingAttributeName**: allows to set an attribute name, which will be used instead of the nameid itself in case the returned nameid-format is transient # Additional Changes To reduce impact on current installations, the `idp_templates6_saml` table is altered with the two added columns by a setup job. New installations will automatically get the table with the two columns directly. All idp unit tests are updated to use `expectEventstore` instead of the deprecated `eventstoreExpect`. # Additional Context Closes #7483 Closes #7743 --------- Co-authored-by: peintnermax <max@caos.ch> Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
.option-form {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
max-width: 400px;
|
||||
max-width: 500px;
|
||||
padding-bottom: 1rem;
|
||||
|
||||
.checkbox-desc {
|
||||
|
@@ -70,6 +70,28 @@
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="showOptional">
|
||||
<div class="transient-info">
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.SAML.NAMEIDFORMAT' | translate }}</cnsl-label>
|
||||
<mat-select formControlName="nameIdFormat" [compareWith]="compareNameIDFormat">
|
||||
<mat-option *ngFor="let nameIdFormat of nameIDFormatValues" [value]="nameIdFormat">{{
|
||||
nameIdFormat
|
||||
}}</mat-option>
|
||||
</mat-select>
|
||||
</cnsl-form-field>
|
||||
|
||||
<cnsl-info-section>
|
||||
<div>
|
||||
<p class="transient-info-desc">{{ 'IDP.SAML.TRANSIENTMAPPINGATTRIBUTENAME_DESC' | translate }}</p>
|
||||
</div>
|
||||
|
||||
<cnsl-form-field class="formfield">
|
||||
<cnsl-label>{{ 'IDP.SAML.TRANSIENTMAPPINGATTRIBUTENAME' | translate }}</cnsl-label>
|
||||
<input cnslInput formControlName="transientMappingAttributeName" />
|
||||
</cnsl-form-field>
|
||||
</cnsl-info-section>
|
||||
</div>
|
||||
|
||||
<cnsl-provider-options
|
||||
[initialOptions]="provider?.config?.options"
|
||||
(optionsChanged)="options = $event"
|
||||
|
@@ -1,3 +1,12 @@
|
||||
.metadata-xml {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.transient-info {
|
||||
max-width: 500px;
|
||||
|
||||
.transient-info-desc {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,12 @@
|
||||
import { Component, Injector, Type } from '@angular/core';
|
||||
import { Location } from '@angular/common';
|
||||
import { AutoLinkingOption, Options, Provider, SAMLBinding } from '../../../proto/generated/zitadel/idp_pb';
|
||||
import {
|
||||
AutoLinkingOption,
|
||||
Options,
|
||||
Provider,
|
||||
SAMLBinding,
|
||||
SAMLNameIDFormat,
|
||||
} from '../../../proto/generated/zitadel/idp_pb';
|
||||
import { AbstractControl, FormGroup, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
|
||||
import { ManagementService } from '../../../services/mgmt.service';
|
||||
@@ -46,6 +52,7 @@ export class ProviderSamlSpComponent {
|
||||
// DEPRECATED: use service$ instead
|
||||
private service!: ManagementService | AdminService;
|
||||
bindingValues: string[] = Object.keys(SAMLBinding);
|
||||
nameIDFormatValues: string[] = Object.keys(SAMLNameIDFormat);
|
||||
|
||||
public justCreated$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||
public justActivated$ = new BehaviorSubject<boolean>(false);
|
||||
@@ -118,6 +125,8 @@ export class ProviderSamlSpComponent {
|
||||
metadataUrl: new UntypedFormControl('', []),
|
||||
binding: new UntypedFormControl(this.bindingValues[0], [requiredValidator]),
|
||||
withSignedRequest: new UntypedFormControl(true, [requiredValidator]),
|
||||
nameIdFormat: new UntypedFormControl(SAMLNameIDFormat.SAML_NAME_ID_FORMAT_PERSISTENT, []),
|
||||
transientMappingAttributeName: new UntypedFormControl('', []),
|
||||
},
|
||||
atLeastOneIsFilled('metadataXml', 'metadataUrl'),
|
||||
);
|
||||
@@ -196,8 +205,12 @@ export class ProviderSamlSpComponent {
|
||||
req.setWithSignedRequest(this.withSignedRequest?.value);
|
||||
// @ts-ignore
|
||||
req.setBinding(SAMLBinding[this.binding?.value]);
|
||||
// @ts-ignore
|
||||
req.setNameIdFormat(SAMLNameIDFormat[this.nameIDFormat?.value]);
|
||||
req.setTransientMappingAttributeName(this.transientMapping?.value);
|
||||
req.setProviderOptions(this.options);
|
||||
|
||||
console.log(req);
|
||||
this.loading = true;
|
||||
this.service
|
||||
.updateSAMLProvider(req)
|
||||
@@ -229,6 +242,11 @@ export class ProviderSamlSpComponent {
|
||||
// @ts-ignore
|
||||
req.setBinding(SAMLBinding[this.binding?.value]);
|
||||
req.setWithSignedRequest(this.withSignedRequest?.value);
|
||||
if (this.nameIDFormat) {
|
||||
// @ts-ignore
|
||||
req.setNameIdFormat(SAMLNameIDFormat[this.nameIDFormat.value]);
|
||||
}
|
||||
req.setTransientMappingAttributeName(this.transientMapping?.value);
|
||||
this.loading = true;
|
||||
this.service
|
||||
.addSAMLProvider(req)
|
||||
@@ -279,6 +297,14 @@ export class ProviderSamlSpComponent {
|
||||
return false;
|
||||
}
|
||||
|
||||
compareNameIDFormat(value: string, index: number) {
|
||||
console.log(value, index);
|
||||
if (value) {
|
||||
return value === Object.keys(SAMLNameIDFormat)[index];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private get name(): AbstractControl | null {
|
||||
return this.form.get('name');
|
||||
}
|
||||
@@ -298,4 +324,12 @@ export class ProviderSamlSpComponent {
|
||||
private get withSignedRequest(): AbstractControl | null {
|
||||
return this.form.get('withSignedRequest');
|
||||
}
|
||||
|
||||
private get nameIDFormat(): AbstractControl | null {
|
||||
return this.form.get('nameIdFormat');
|
||||
}
|
||||
|
||||
private get transientMapping(): AbstractControl | null {
|
||||
return this.form.get('transientMappingAttributeName');
|
||||
}
|
||||
}
|
||||
|
@@ -50,7 +50,7 @@
|
||||
|
||||
.formfield {
|
||||
display: block;
|
||||
max-width: 400px;
|
||||
max-width: 500px;
|
||||
|
||||
&.pwd {
|
||||
display: none;
|
||||
@@ -132,7 +132,7 @@
|
||||
}
|
||||
|
||||
.string-list-component-wrapper {
|
||||
max-width: 400px;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.identity-provider-content {
|
||||
@@ -144,7 +144,7 @@
|
||||
}
|
||||
|
||||
.identity-provider-2-col {
|
||||
max-width: 400px;
|
||||
max-width: 500px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 1rem;
|
||||
@@ -160,7 +160,7 @@
|
||||
.flex-line {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
max-width: 400px;
|
||||
max-width: 500px;
|
||||
|
||||
.formfield {
|
||||
flex: 1;
|
||||
|
@@ -2044,7 +2044,7 @@
|
||||
"DESCRIPTION": "Enter the credentials for your Apple Provider"
|
||||
},
|
||||
"SAML": {
|
||||
"TITLE": "Sign in with Saml SP",
|
||||
"TITLE": "Sign in with SAML SP",
|
||||
"DESCRIPTION": "Enter the credentials for your SAML Provider"
|
||||
}
|
||||
},
|
||||
@@ -2177,7 +2177,10 @@
|
||||
"METADATAXML": "Metadata Xml",
|
||||
"METADATAURL": "Metadata URL",
|
||||
"BINDING": "Binding",
|
||||
"SIGNEDREQUEST": "Signed Request"
|
||||
"SIGNEDREQUEST": "Signed Request",
|
||||
"NAMEIDFORMAT": "NameID Format",
|
||||
"TRANSIENTMAPPINGATTRIBUTENAME": "Custom Mapping Attribute Name",
|
||||
"TRANSIENTMAPPINGATTRIBUTENAME_DESC": "Alternative attribute name to map the user in case the `nameid-format` returned is `transient`, e.g. `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`"
|
||||
},
|
||||
"TOAST": {
|
||||
"SAVED": "Successfully saved.",
|
||||
|
@@ -2044,7 +2044,7 @@
|
||||
"DESCRIPTION": "Voer de inloggegevens in voor uw Apple Provider"
|
||||
},
|
||||
"SAML": {
|
||||
"TITLE": "Log in met Saml SP",
|
||||
"TITLE": "Log in met SAML SP",
|
||||
"DESCRIPTION": "Voer de inloggegevens in voor uw SAML Provider"
|
||||
}
|
||||
},
|
||||
|
Reference in New Issue
Block a user