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:
Livio Spring
2024-05-23 07:04:07 +02:00
committed by GitHub
parent 12be21a3ff
commit e57a9b57c8
58 changed files with 1306 additions and 720 deletions

View File

@@ -6028,31 +6028,28 @@ message AddSAMLProviderRequest {
string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
oneof metadata {
option (validate.required) = true;
// Metadata of the SAML identity provider.
bytes metadata_xml = 2 [
(validate.rules).bytes.max_len = 500000,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Metadata of the SAML identity provider";
}
(validate.rules).bytes.max_len = 500000
];
// Url to the metadata of the SAML identity provider.
string metadata_url = 3 [
(validate.rules).string.max_len = 200,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"https://test.com/saml/metadata\""
description: "Url to the metadata of the SAML identity provider";
}
];
}
zitadel.idp.v1.SAMLBinding binding = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Binding which defines the type of communication with the identity provider";
}
];
bool with_signed_request = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Boolean which defines if the authentication requests are signed";
}
];
// Binding which defines the type of communication with the identity provider.
zitadel.idp.v1.SAMLBinding binding = 4;
// Boolean which defines if the authentication requests are signed.
bool with_signed_request = 5;
zitadel.idp.v1.Options provider_options = 6;
// Optionally specify the `nameid-format` requested.
optional zitadel.idp.v1.SAMLNameIDFormat name_id_format = 7;
// Optionally specify the name of the attribute, which will be used to map the user
// in case the nameid-format returned is `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`.
optional string transient_mapping_attribute_name = 8;
}
message AddSAMLProviderResponse {
@@ -6063,33 +6060,30 @@ message AddSAMLProviderResponse {
message UpdateSAMLProviderRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
// Metadata of the SAML identity provider.
oneof metadata {
option (validate.required) = true;
bytes metadata_xml = 3 [
(validate.rules).bytes.max_len = 500000,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Metadata of the SAML identity provider";
}
(validate.rules).bytes.max_len = 500000
];
// Url to the metadata of the SAML identity provider
string metadata_url = 4 [
(validate.rules).string.max_len = 200,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"https://test.com/saml/metadata\""
description: "Url to the metadata of the SAML identity provider";
}
];
}
zitadel.idp.v1.SAMLBinding binding = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Binding which defines the type of communication with the identity provider";
}
];
bool with_signed_request = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Boolean which defines if the authentication requests are signed";
}
];
// Binding which defines the type of communication with the identity provider.
zitadel.idp.v1.SAMLBinding binding = 5;
// Boolean which defines if the authentication requests are signed
bool with_signed_request = 6;
zitadel.idp.v1.Options provider_options = 7;
// Optionally specify the `nameid-format` requested.
optional zitadel.idp.v1.SAMLNameIDFormat name_id_format = 8;
// Optionally specify the name of the attribute, which will be used to map the user
// in case the nameid-format returned is `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`.
optional string transient_mapping_attribute_name = 9;
}
message UpdateSAMLProviderResponse {

View File

@@ -276,6 +276,13 @@ enum SAMLBinding {
SAML_BINDING_ARTIFACT = 3;
}
enum SAMLNameIDFormat {
SAML_NAME_ID_FORMAT_UNSPECIFIED = 0;
SAML_NAME_ID_FORMAT_EMAIL_ADDRESS = 1;
SAML_NAME_ID_FORMAT_PERSISTENT = 2;
SAML_NAME_ID_FORMAT_TRANSIENT = 3;
}
message ProviderConfig {
Options options = 1;
oneof config {
@@ -452,21 +459,17 @@ message LDAPConfig {
}
message SAMLConfig {
bytes metadata_xml = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Metadata of the SAML identity provider";
}
];
zitadel.idp.v1.SAMLBinding binding = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Binding which defines the type of communication with the identity provider";
}
];
bool with_signed_request = 3 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Boolean which defines if the authentication requests are signed";
}
];
// Metadata of the SAML identity provider.
bytes metadata_xml = 1;
// Binding which defines the type of communication with the identity provider.
zitadel.idp.v1.SAMLBinding binding = 2;
// Boolean which defines if the authentication requests are signed.
bool with_signed_request = 3;
// `nameid-format` for the SAML Request.
zitadel.idp.v1.SAMLNameIDFormat name_id_format = 4;
// Optional name of the attribute, which will be used to map the user
// in case the nameid-format returned is `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`.
optional string transient_mapping_attribute_name = 5;
}
message AzureADConfig {

View File

@@ -12767,31 +12767,28 @@ message AddSAMLProviderRequest {
string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
oneof metadata {
option (validate.required) = true;
// Metadata of the SAML identity provider.
bytes metadata_xml = 2 [
(validate.rules).bytes.max_len = 500000,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Metadata of the SAML identity provider";
}
(validate.rules).bytes.max_len = 500000
];
// Url to the metadata of the SAML identity provider.
string metadata_url = 3 [
(validate.rules).string.max_len = 200,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"https://test.com/saml/metadata\""
description: "Url to the metadata of the SAML identity provider";
}
];
}
zitadel.idp.v1.SAMLBinding binding = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Binding which defines the type of communication with the identity provider";
}
];
bool with_signed_request = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Boolean which defines if the authentication requests are signed";
}
];
// Binding which defines the type of communication with the identity provider.
zitadel.idp.v1.SAMLBinding binding = 4;
// Boolean which defines if the authentication requests are signed.
bool with_signed_request = 5;
zitadel.idp.v1.Options provider_options = 6;
// Optionally specify the `nameid-format` requested.
optional zitadel.idp.v1.SAMLNameIDFormat name_id_format = 7;
// Optionally specify the name of the attribute, which will be used to map the user
// in case the nameid-format returned is `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`.
optional string transient_mapping_attribute_name = 8;
}
message AddSAMLProviderResponse {
@@ -12802,33 +12799,30 @@ message AddSAMLProviderResponse {
message UpdateSAMLProviderRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
// Metadata of the SAML identity provider.
oneof metadata {
option (validate.required) = true;
bytes metadata_xml = 3 [
(validate.rules).bytes.max_len = 500000,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Metadata of the SAML identity provider";
}
(validate.rules).bytes.max_len = 500000
];
// Url to the metadata of the SAML identity provider.
string metadata_url = 4 [
(validate.rules).string.max_len = 200,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"https://test.com/saml/metadata\""
description: "Url to the metadata of the SAML identity provider";
}
];
}
zitadel.idp.v1.SAMLBinding binding = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Binding which defines the type of communication with the identity provider";
}
];
bool with_signed_request = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Boolean which defines if the authentication requests are signed";
}
];
// Binding which defines the type of communication with the identity provider.
zitadel.idp.v1.SAMLBinding binding = 5;
// Boolean which defines if the authentication requests are signed.
bool with_signed_request = 6;
zitadel.idp.v1.Options provider_options = 7;
// Optionally specify the `nameid-format` requested.
optional zitadel.idp.v1.SAMLNameIDFormat name_id_format = 8;
// Optionally specify the name of the attribute, which will be used to map the user
// in case the nameid-format returned is `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`.
optional string transient_mapping_attribute_name = 9;
}
message UpdateSAMLProviderResponse {