diff --git a/cmd/setup/61.go b/cmd/setup/61.go new file mode 100644 index 00000000000..cf353b5e2f8 --- /dev/null +++ b/cmd/setup/61.go @@ -0,0 +1,27 @@ +package setup + +import ( + "context" + _ "embed" + + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" +) + +var ( + //go:embed 61.sql + addSAMLSignatureAlgorithm string +) + +type IDPTemplate6SAMLSignatureAlgorithm struct { + dbClient *database.DB +} + +func (mig *IDPTemplate6SAMLSignatureAlgorithm) Execute(ctx context.Context, _ eventstore.Event) error { + _, err := mig.dbClient.ExecContext(ctx, addSAMLSignatureAlgorithm) + return err +} + +func (mig *IDPTemplate6SAMLSignatureAlgorithm) String() string { + return "61_idp_templates6_add_saml_signature_algorithm" +} diff --git a/cmd/setup/61.sql b/cmd/setup/61.sql new file mode 100644 index 00000000000..79372549337 --- /dev/null +++ b/cmd/setup/61.sql @@ -0,0 +1 @@ +ALTER TABLE IF EXISTS projections.idp_templates6_saml ADD COLUMN IF NOT EXISTS signature_algorithm TEXT; diff --git a/cmd/setup/config.go b/cmd/setup/config.go index bac73b0ae58..2a7f4cf41c1 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -157,6 +157,7 @@ type Steps struct { s58ReplaceLoginNames3View *ReplaceLoginNames3View s59SetupWebkeys *SetupWebkeys s60GenerateSystemID *GenerateSystemID + s61IDPTemplate6SAMLSignatureAlgorithm *IDPTemplate6SAMLSignatureAlgorithm } func MustNewSteps(v *viper.Viper) *Steps { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index 896e0a4ac47..73489578f89 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -218,6 +218,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string) steps.s57CreateResourceCounts = &CreateResourceCounts{dbClient: dbClient} steps.s58ReplaceLoginNames3View = &ReplaceLoginNames3View{dbClient: dbClient} steps.s60GenerateSystemID = &GenerateSystemID{eventstore: eventstoreClient} + steps.s61IDPTemplate6SAMLSignatureAlgorithm = &IDPTemplate6SAMLSignatureAlgorithm{dbClient: dbClient} err = projection.Create(ctx, dbClient, eventstoreClient, config.Projections, nil, nil, nil) logging.OnError(err).Fatal("unable to start projections") @@ -266,6 +267,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string) steps.s57CreateResourceCounts, steps.s58ReplaceLoginNames3View, steps.s60GenerateSystemID, + steps.s61IDPTemplate6SAMLSignatureAlgorithm, } { setupErr = executeMigration(ctx, eventstoreClient, step, "migration failed") if setupErr != nil { diff --git a/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.html b/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.html index c5f9d3905b3..833a44231de 100644 --- a/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.html +++ b/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.html @@ -71,6 +71,15 @@
+ + {{ 'IDP.SAML.SIGNATUREALGORITHM' | translate }} + + {{ + signatureAlgorithm + }} + + + {{ 'IDP.SAML.NAMEIDFORMAT' | translate }} diff --git a/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.ts b/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.ts index cd17633112a..9db3f1a5682 100644 --- a/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.ts +++ b/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.ts @@ -6,6 +6,7 @@ import { Provider, SAMLBinding, SAMLNameIDFormat, + SAMLSignatureAlgorithm, } from '../../../proto/generated/zitadel/idp_pb'; import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum'; @@ -38,6 +39,7 @@ interface SAMLProviderForm { metadataUrl: FormControl; binding: FormControl; withSignedRequest: FormControl; + signatureAlgorithm: FormControl; nameIdFormat: FormControl; transientMappingAttributeName: FormControl; federatedLogoutEnabled: FormControl; @@ -65,6 +67,7 @@ export class ProviderSamlSpComponent { private service!: ManagementService | AdminService; bindingValues: string[] = getEnumKeys(SAMLBinding); nameIDFormatValues: string[] = getEnumKeys(SAMLNameIDFormat); + signatureAlgorithmValues: string[] = Object.keys(SAMLSignatureAlgorithm); public justCreated$: BehaviorSubject = new BehaviorSubject(''); public justActivated$ = new BehaviorSubject(false); @@ -131,6 +134,9 @@ export class ProviderSamlSpComponent { private _initializeForm(): void { const defaultBinding = getEnumKeyFromValue(SAMLBinding, SAMLBinding.SAML_BINDING_POST) || this.bindingValues[0]; + const defaultSignatureAlgorithm = + getEnumKeyFromValue(SAMLSignatureAlgorithm, SAMLSignatureAlgorithm.SAML_SIGNATURE_UNSPECIFIED) || + this.signatureAlgorithmValues[0]; const defaultNameIdFormat = getEnumKeyFromValue(SAMLNameIDFormat, SAMLNameIDFormat.SAML_NAME_ID_FORMAT_PERSISTENT) || this.nameIDFormatValues[0]; @@ -141,6 +147,7 @@ export class ProviderSamlSpComponent { metadataUrl: new FormControl('', { nonNullable: true }), binding: new FormControl(defaultBinding, { nonNullable: true, validators: [requiredValidator] }), withSignedRequest: new FormControl(true, { nonNullable: true, validators: [requiredValidator] }), + signatureAlgorithm: new FormControl(defaultSignatureAlgorithm, { nonNullable: true }), nameIdFormat: new FormControl(defaultNameIdFormat, { nonNullable: true }), transientMappingAttributeName: new FormControl('', { nonNullable: true }), federatedLogoutEnabled: new FormControl(false, { nonNullable: true }), @@ -223,6 +230,10 @@ export class ProviderSamlSpComponent { } req.setWithSignedRequest(this.withSignedRequest.value); req.setBinding(SAMLBinding[this.binding.value as keyof typeof SAMLBinding]); + req.setSignatureAlgorithm( + SAMLSignatureAlgorithm[this.signatureAlgorithm.value as keyof typeof SAMLSignatureAlgorithm], + ); + // @ts-ignore req.setNameIdFormat(SAMLNameIDFormat[this.nameIDFormat.value as keyof typeof SAMLNameIDFormat]); req.setTransientMappingAttributeName(this.transientMapping.value); req.setFederatedLogoutEnabled(this.federatedLogoutEnabled.value); @@ -260,6 +271,12 @@ export class ProviderSamlSpComponent { req.setProviderOptions(this.options); req.setBinding(SAMLBinding[this.binding.value as keyof typeof SAMLBinding]); req.setWithSignedRequest(this.withSignedRequest.value); + if (this.signatureAlgorithm) { + // @ts-ignore + req.setSignatureAlgorithm( + SAMLSignatureAlgorithm[this.signatureAlgorithm.value as keyof typeof SAMLSignatureAlgorithm], + ); + } if (this.nameIDFormat) { req.setNameIdFormat(SAMLNameIDFormat[this.nameIDFormat.value as keyof typeof SAMLNameIDFormat]); } @@ -343,6 +360,10 @@ export class ProviderSamlSpComponent { return this.form.controls.nameIdFormat; } + private get signatureAlgorithm(): FormControl { + return this.form.controls.signatureAlgorithm; + } + private get transientMapping(): FormControl { return this.form.controls.transientMappingAttributeName; } diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index 3caac2c1b2a..61fd24a21d4 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -2373,6 +2373,7 @@ "METADATAURL": "Metadata URL", "BINDING": "Binding", "SIGNEDREQUEST": "Signed Request", + "SIGNATUREALGORITHM": "Signature Algorithm", "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`" diff --git a/console/src/assets/i18n/hu.json b/console/src/assets/i18n/hu.json index dca3b1e839a..c505d82b63d 100644 --- a/console/src/assets/i18n/hu.json +++ b/console/src/assets/i18n/hu.json @@ -2368,6 +2368,7 @@ "METADATAURL": "Metaadatok URL", "BINDING": "Kötés", "SIGNEDREQUEST": "Aláírt kérés", + "SIGNATUREALGORITHM": "Aláírási algoritmus", "NAMEIDFORMAT": "NameID formátum", "TRANSIENTMAPPINGATTRIBUTENAME": "Egyéni leképezési attribútumnév", "TRANSIENTMAPPINGATTRIBUTENAME_DESC": "Alternatív attribútumnév a felhasználó leképezéséhez, ha a visszakapott `nameid-format` `transient`, pl. `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`" diff --git a/console/src/assets/i18n/id.json b/console/src/assets/i18n/id.json index d7d5b6dda4a..9be2153bb5e 100644 --- a/console/src/assets/i18n/id.json +++ b/console/src/assets/i18n/id.json @@ -2150,6 +2150,7 @@ "METADATAURL": "URL metadata", "BINDING": "Mengikat", "SIGNEDREQUEST": "Permintaan yang Ditandatangani", + "SIGNATUREALGORITHM": "Algoritma tanda tangan", "NAMEIDFORMAT": "Format ID Nama", "TRANSIENTMAPPINGATTRIBUTENAME": "Nama Atribut Pemetaan Khusus", "TRANSIENTMAPPINGATTRIBUTENAME_DESC": "Nama atribut alternatif untuk memetakan pengguna jika `format nameid` yang dikembalikan adalah `sementara`, mis. `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`" diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json index 07423e451f8..2a4dea9ff19 100644 --- a/console/src/assets/i18n/ja.json +++ b/console/src/assets/i18n/ja.json @@ -2370,6 +2370,7 @@ "METADATAURL": "Metadata URL", "BINDING": "Binding", "SIGNEDREQUEST": "署名付きリクエスト", + "SIGNATUREALGORITHM": "署名アルゴリズム", "NAMEIDFORMAT": "NameIDフォーマット", "TRANSIENTMAPPINGATTRIBUTENAME": "カスタム属性名", "TRANSIENTMAPPINGATTRIBUTENAME_DESC": "NameIDフォーマットが`transient`の場合にユーザーをマッピングするためのカスタム属性名 (例: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`)" diff --git a/console/src/assets/i18n/ko.json b/console/src/assets/i18n/ko.json index 19f9e35403e..8ebbfef3b2d 100644 --- a/console/src/assets/i18n/ko.json +++ b/console/src/assets/i18n/ko.json @@ -2370,6 +2370,7 @@ "METADATAURL": "메타데이터 URL", "BINDING": "바인딩", "SIGNEDREQUEST": "서명된 요청", + "SIGNATUREALGORITHM": "서명 알고리즘", "NAMEIDFORMAT": "NameID 형식", "TRANSIENTMAPPINGATTRIBUTENAME": "사용자 매핑 속성 이름", "TRANSIENTMAPPINGATTRIBUTENAME_DESC": "`nameid-format`이 `transient`인 경우 사용자 매핑에 사용할 대체 속성 이름, 예: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`" diff --git a/console/src/assets/i18n/nl.json b/console/src/assets/i18n/nl.json index a15488b090c..1a176feeb01 100644 --- a/console/src/assets/i18n/nl.json +++ b/console/src/assets/i18n/nl.json @@ -2369,7 +2369,8 @@ "METADATAXML": "Metadata Xml", "METADATAURL": "Metadata URL", "BINDING": "Binding", - "SIGNEDREQUEST": "Ondertekend Verzoek" + "SIGNEDREQUEST": "Ondertekend Verzoek", + "SIGNATUREALGORITHM": "Handtekeningalgoritme" }, "TOAST": { "SAVED": "Succesvol opgeslagen.", diff --git a/console/src/assets/i18n/ro.json b/console/src/assets/i18n/ro.json index 9555246294e..f8926f0e2b8 100644 --- a/console/src/assets/i18n/ro.json +++ b/console/src/assets/i18n/ro.json @@ -2368,6 +2368,7 @@ "METADATAURL": "URL metadata", "BINDING": "Legare", "SIGNEDREQUEST": "Solicitare semnată", + "SIGNATUREALGORITHM": "Algoritm de semnătură", "NAMEIDFORMAT": "Format ID nume", "TRANSIENTMAPPINGATTRIBUTENAME": "Nume atribut mapare personalizat", "TRANSIENTMAPPINGATTRIBUTENAME_DESC": "Nume alternativ de atribut pentru a mapa utilizatorul în cazul în care nameid-format returnat este tranzitoriu, de exemplu, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" diff --git a/console/src/assets/i18n/tr.json b/console/src/assets/i18n/tr.json index 39c8a73f8d5..73b360f6312 100644 --- a/console/src/assets/i18n/tr.json +++ b/console/src/assets/i18n/tr.json @@ -2373,6 +2373,7 @@ "METADATAURL": "Metadata URL", "BINDING": "Binding", "SIGNEDREQUEST": "İmzalı İstek", + "SIGNATUREALGORITHM": "İmza algoritması", "NAMEIDFORMAT": "NameID Formatı", "TRANSIENTMAPPINGATTRIBUTENAME": "Özel Eşleme Özellik Adı", "TRANSIENTMAPPINGATTRIBUTENAME_DESC": "`nameid-format` `transient` döndürülmesi durumunda kullanıcıyı eşlemek için alternatif özellik adı, örn. `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`" diff --git a/internal/api/grpc/admin/idp_converter.go b/internal/api/grpc/admin/idp_converter.go index fd7749d6647..f158f128bdd 100644 --- a/internal/api/grpc/admin/idp_converter.go +++ b/internal/api/grpc/admin/idp_converter.go @@ -3,6 +3,7 @@ package admin import ( "github.com/crewjam/saml" "github.com/muhlemmer/gu" + dsig "github.com/russellhaering/goxmldsig" idp_grpc "github.com/zitadel/zitadel/internal/api/grpc/idp" "github.com/zitadel/zitadel/internal/api/grpc/object" @@ -480,12 +481,14 @@ func addSAMLProviderToCommand(req *admin_pb.AddSAMLProviderRequest) *command.SAM if req.NameIdFormat != nil { nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat())) } + return &command.SAMLProvider{ Name: req.Name, Metadata: req.GetMetadataXml(), MetadataURL: req.GetMetadataUrl(), Binding: bindingToCommand(req.Binding), WithSignedRequest: req.WithSignedRequest, + SignatureAlgorithm: signatureAlgorithmToCommand(req.GetSignatureAlgorithm()), NameIDFormat: nameIDFormat, TransientMappingAttributeName: req.GetTransientMappingAttributeName(), FederatedLogoutEnabled: req.GetFederatedLogoutEnabled(), @@ -498,12 +501,14 @@ func updateSAMLProviderToCommand(req *admin_pb.UpdateSAMLProviderRequest) *comma if req.NameIdFormat != nil { nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat())) } + return &command.SAMLProvider{ Name: req.Name, Metadata: req.GetMetadataXml(), MetadataURL: req.GetMetadataUrl(), Binding: bindingToCommand(req.Binding), WithSignedRequest: req.WithSignedRequest, + SignatureAlgorithm: signatureAlgorithmToCommand(req.GetSignatureAlgorithm()), NameIDFormat: nameIDFormat, TransientMappingAttributeName: req.GetTransientMappingAttributeName(), FederatedLogoutEnabled: req.GetFederatedLogoutEnabled(), @@ -525,3 +530,18 @@ func bindingToCommand(binding idp_pb.SAMLBinding) string { return "" } } + +func signatureAlgorithmToCommand(signatureAlgorithm idp_pb.SAMLSignatureAlgorithm) string { + switch signatureAlgorithm { + case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_UNSPECIFIED: + return "" + case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1: + return dsig.RSASHA1SignatureMethod + case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256: + return dsig.RSASHA256SignatureMethod + case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512: + return dsig.RSASHA512SignatureMethod + default: + return "" + } +} diff --git a/internal/api/grpc/admin/idp_converter_test.go b/internal/api/grpc/admin/idp_converter_test.go index 23d378b8883..adad5f348dd 100644 --- a/internal/api/grpc/admin/idp_converter_test.go +++ b/internal/api/grpc/admin/idp_converter_test.go @@ -3,6 +3,8 @@ package admin import ( "testing" + "github.com/stretchr/testify/require" + "github.com/zitadel/zitadel/internal/test" admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin" "github.com/zitadel/zitadel/pkg/grpc/idp" @@ -157,3 +159,40 @@ func Test_updateOIDCConfigToDomain(t *testing.T) { }) } } + +func Test_signatureAlgorithmToCommand(t *testing.T) { + t.Parallel() + tests := []struct { + name string + signatureAlgorithm idp.SAMLSignatureAlgorithm + wantSignatureAlgorithm string + }{ + { + name: "signature algorithm default value", + signatureAlgorithm: 11, + wantSignatureAlgorithm: "", + }, + { + name: "RSA_SHA1", + signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1, + wantSignatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", + }, + { + name: "RSA_SHA256", + signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256, + wantSignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + }, + { + name: "RSA_SHA512", + signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512, + wantSignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := signatureAlgorithmToCommand(tt.signatureAlgorithm) + require.Equal(t, tt.wantSignatureAlgorithm, got) + }) + } +} diff --git a/internal/api/grpc/idp/converter.go b/internal/api/grpc/idp/converter.go index caab5121b8f..6e8f14359ce 100644 --- a/internal/api/grpc/idp/converter.go +++ b/internal/api/grpc/idp/converter.go @@ -3,6 +3,7 @@ package idp import ( "github.com/crewjam/saml" "github.com/muhlemmer/gu" + dsig "github.com/russellhaering/goxmldsig" "google.golang.org/protobuf/types/known/durationpb" obj_grpc "github.com/zitadel/zitadel/internal/api/grpc/object" @@ -667,6 +668,7 @@ func samlConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.SAMLI MetadataXml: template.Metadata, Binding: bindingToPb(template.Binding), WithSignedRequest: template.WithSignedRequest, + SignatureAlgorithm: gu.Ptr(signatureAlgorithmToPb(template.SignatureAlgorithm)), NameIdFormat: nameIDFormat, TransientMappingAttributeName: gu.Ptr(template.TransientMappingAttributeName), FederatedLogoutEnabled: gu.Ptr(template.FederatedLogoutEnabled), @@ -703,3 +705,16 @@ func nameIDToPb(format domain.SAMLNameIDFormat) idp_pb.SAMLNameIDFormat { return idp_pb.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_UNSPECIFIED } } + +func signatureAlgorithmToPb(signatureAlgorithm string) idp_pb.SAMLSignatureAlgorithm { + switch signatureAlgorithm { + case dsig.RSASHA1SignatureMethod: + return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1 + case dsig.RSASHA256SignatureMethod: + return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256 + case dsig.RSASHA512SignatureMethod: + return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512 + default: + return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_UNSPECIFIED + } +} diff --git a/internal/api/grpc/idp/v2/query.go b/internal/api/grpc/idp/v2/query.go index 587b1687b91..c9c763cb96b 100644 --- a/internal/api/grpc/idp/v2/query.go +++ b/internal/api/grpc/idp/v2/query.go @@ -6,6 +6,7 @@ import ( "connectrpc.com/connect" "github.com/crewjam/saml" "github.com/muhlemmer/gu" + dsig "github.com/russellhaering/goxmldsig" "google.golang.org/protobuf/types/known/durationpb" "github.com/zitadel/zitadel/internal/api/grpc/object/v2" @@ -335,6 +336,7 @@ func samlConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.SAMLIDPTemplate MetadataXml: template.Metadata, Binding: bindingToPb(template.Binding), WithSignedRequest: template.WithSignedRequest, + SignatureAlgorithm: signatureAlgorithmToPb(template.SignatureAlgorithm), NameIdFormat: nameIDFormat, TransientMappingAttributeName: gu.Ptr(template.TransientMappingAttributeName), FederatedLogoutEnabled: gu.Ptr(template.FederatedLogoutEnabled), @@ -371,3 +373,16 @@ func nameIDToPb(format domain.SAMLNameIDFormat) idp_pb.SAMLNameIDFormat { return idp_pb.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_UNSPECIFIED } } + +func signatureAlgorithmToPb(signatureAlgorithm string) idp_pb.SAMLSignatureAlgorithm { + switch signatureAlgorithm { + case dsig.RSASHA1SignatureMethod: + return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1 + case dsig.RSASHA256SignatureMethod: + return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256 + case dsig.RSASHA512SignatureMethod: + return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512 + default: + return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_UNSPECIFIED + } +} diff --git a/internal/api/grpc/management/idp_converter.go b/internal/api/grpc/management/idp_converter.go index 37169dfc6d1..6fa49d081f0 100644 --- a/internal/api/grpc/management/idp_converter.go +++ b/internal/api/grpc/management/idp_converter.go @@ -5,6 +5,7 @@ import ( "github.com/crewjam/saml" "github.com/muhlemmer/gu" + dsig "github.com/russellhaering/goxmldsig" "github.com/zitadel/zitadel/internal/api/authz" idp_grpc "github.com/zitadel/zitadel/internal/api/grpc/idp" @@ -473,12 +474,14 @@ func addSAMLProviderToCommand(req *mgmt_pb.AddSAMLProviderRequest) *command.SAML if req.NameIdFormat != nil { nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat())) } + return &command.SAMLProvider{ Name: req.Name, Metadata: req.GetMetadataXml(), MetadataURL: req.GetMetadataUrl(), Binding: bindingToCommand(req.Binding), WithSignedRequest: req.WithSignedRequest, + SignatureAlgorithm: signatureAlgorithmToCommand(req.GetSignatureAlgorithm()), NameIDFormat: nameIDFormat, TransientMappingAttributeName: req.GetTransientMappingAttributeName(), FederatedLogoutEnabled: req.GetFederatedLogoutEnabled(), @@ -491,12 +494,14 @@ func updateSAMLProviderToCommand(req *mgmt_pb.UpdateSAMLProviderRequest) *comman if req.NameIdFormat != nil { nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat())) } + return &command.SAMLProvider{ Name: req.Name, Metadata: req.GetMetadataXml(), MetadataURL: req.GetMetadataUrl(), Binding: bindingToCommand(req.Binding), WithSignedRequest: req.WithSignedRequest, + SignatureAlgorithm: signatureAlgorithmToCommand(req.GetSignatureAlgorithm()), NameIDFormat: nameIDFormat, TransientMappingAttributeName: req.GetTransientMappingAttributeName(), FederatedLogoutEnabled: req.GetFederatedLogoutEnabled(), @@ -518,3 +523,18 @@ func bindingToCommand(binding idp_pb.SAMLBinding) string { return "" } } + +func signatureAlgorithmToCommand(signatureAlgorithm idp_pb.SAMLSignatureAlgorithm) string { + switch signatureAlgorithm { + case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_UNSPECIFIED: + return "" + case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1: + return dsig.RSASHA1SignatureMethod + case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256: + return dsig.RSASHA256SignatureMethod + case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512: + return dsig.RSASHA512SignatureMethod + default: + return "" + } +} diff --git a/internal/api/grpc/management/idp_converter_test.go b/internal/api/grpc/management/idp_converter_test.go index 999be2ea069..78640aa959f 100644 --- a/internal/api/grpc/management/idp_converter_test.go +++ b/internal/api/grpc/management/idp_converter_test.go @@ -3,6 +3,8 @@ package management import ( "testing" + "github.com/stretchr/testify/require" + "github.com/zitadel/zitadel/internal/test" "github.com/zitadel/zitadel/pkg/grpc/idp" mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management" @@ -157,3 +159,40 @@ func Test_updateOIDCConfigToDomain(t *testing.T) { }) } } + +func Test_signatureAlgorithmToCommand(t *testing.T) { + t.Parallel() + tests := []struct { + name string + signatureAlgorithm idp.SAMLSignatureAlgorithm + wantSignatureAlgorithm string + }{ + { + name: "signature algorithm default value", + signatureAlgorithm: 11, + wantSignatureAlgorithm: "", + }, + { + name: "RSA_SHA1", + signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1, + wantSignatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", + }, + { + name: "RSA_SHA256", + signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256, + wantSignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + }, + { + name: "RSA_SHA512", + signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512, + wantSignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := signatureAlgorithmToCommand(tt.signatureAlgorithm) + require.Equal(t, tt.wantSignatureAlgorithm, got) + }) + } +} diff --git a/internal/command/idp.go b/internal/command/idp.go index 06d8f473c09..b53a6610e0c 100644 --- a/internal/command/idp.go +++ b/internal/command/idp.go @@ -120,6 +120,7 @@ type SAMLProvider struct { MetadataURL string Binding string WithSignedRequest bool + SignatureAlgorithm string NameIDFormat *domain.SAMLNameIDFormat TransientMappingAttributeName string FederatedLogoutEnabled bool diff --git a/internal/command/idp_intent_test.go b/internal/command/idp_intent_test.go index e0f4e2ffdb7..df976559465 100644 --- a/internal/command/idp_intent_test.go +++ b/internal/command/idp_intent_test.go @@ -737,6 +737,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) { []byte("certificate"), "", false, + "", gu.Ptr(domain.SAMLNameIDFormatUnspecified), "", false, @@ -758,6 +759,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) { }, []byte("-----BEGIN CERTIFICATE-----\nMIIC2zCCAcOgAwIBAgIIAy/jm1gAAdEwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE\nChMHWklUQURFTDAeFw0yMzA4MzAwNzExMTVaFw0yNDA4MjkwNzExMTVaMBIxEDAO\nBgNVBAoTB1pJVEFERUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDE\nd3TztGgSb3LBVZn8f60NbFCyZW+F9HPiMCr9F9T45Zc0fgmMwxId0WzRD5Y/3yc1\ndHJzt+Bsxvw12aUHbIPiothqk3lINoFzl2H/cSfIW3nehKyNOUqdBQ8B4mvaqH81\njTjoJ/JTJAwzglHk6JAWjhOyx9aep1yBqYa3QASeTaW9sxkpB0Co1L2UPNhuMwZq\n8RA9NkTfmYVcVBeNqihler5MhruFtqrv+J0ftwc1stw8uCN89ADyr4Ni+e+FeWar\nQs9Bkfc6KLF/5IXa9HCsHNPaaoYPY6I6RSaG4/DKoSKIEe1/GSVG1FTpZ8trUZxv\nU+xXS6gEalXcrJsiX8aXAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE\nDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCx\n/dRNIj0N/16zJhZR/ahkc2AkvDXYxyr4JRT5wK9GQDNl/oaX3debRuSi/tfaXFIX\naJA6PxM4J49ZaiEpLrKfxMz5kAhjKchCBEMcH3mGt+iNZH7EOyTvHjpGrP2OZrsh\nO17yrvN3HuQxIU6roJlqtZz2iAADsoPtwOO4D7hupm9XTMkSnAmlMWOo/q46Jz89\n1sMxB+dXmH/zV0wgwh0omZfLV0u89mvdq269VhcjNBpBYSnN1ccqYWd5iwziob3I\nvaavGHGfkbvRUn/tKftYuTK30q03R+e9YbmlWZ0v695owh2e/apCzowQsCKfSVC8\nOxVyt5XkHq1tWwVyBmFp\n-----END CERTIFICATE-----\n"), "", false, + "", gu.Ptr(domain.SAMLNameIDFormatUnspecified), "", false, @@ -828,6 +830,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) { []byte("certificate"), "", false, + "", gu.Ptr(domain.SAMLNameIDFormatUnspecified), "", false, @@ -849,6 +852,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) { }, []byte("-----BEGIN CERTIFICATE-----\nMIIC2zCCAcOgAwIBAgIIAy/jm1gAAdEwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE\nChMHWklUQURFTDAeFw0yMzA4MzAwNzExMTVaFw0yNDA4MjkwNzExMTVaMBIxEDAO\nBgNVBAoTB1pJVEFERUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDE\nd3TztGgSb3LBVZn8f60NbFCyZW+F9HPiMCr9F9T45Zc0fgmMwxId0WzRD5Y/3yc1\ndHJzt+Bsxvw12aUHbIPiothqk3lINoFzl2H/cSfIW3nehKyNOUqdBQ8B4mvaqH81\njTjoJ/JTJAwzglHk6JAWjhOyx9aep1yBqYa3QASeTaW9sxkpB0Co1L2UPNhuMwZq\n8RA9NkTfmYVcVBeNqihler5MhruFtqrv+J0ftwc1stw8uCN89ADyr4Ni+e+FeWar\nQs9Bkfc6KLF/5IXa9HCsHNPaaoYPY6I6RSaG4/DKoSKIEe1/GSVG1FTpZ8trUZxv\nU+xXS6gEalXcrJsiX8aXAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE\nDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCx\n/dRNIj0N/16zJhZR/ahkc2AkvDXYxyr4JRT5wK9GQDNl/oaX3debRuSi/tfaXFIX\naJA6PxM4J49ZaiEpLrKfxMz5kAhjKchCBEMcH3mGt+iNZH7EOyTvHjpGrP2OZrsh\nO17yrvN3HuQxIU6roJlqtZz2iAADsoPtwOO4D7hupm9XTMkSnAmlMWOo/q46Jz89\n1sMxB+dXmH/zV0wgwh0omZfLV0u89mvdq269VhcjNBpBYSnN1ccqYWd5iwziob3I\nvaavGHGfkbvRUn/tKftYuTK30q03R+e9YbmlWZ0v695owh2e/apCzowQsCKfSVC8\nOxVyt5XkHq1tWwVyBmFp\n-----END CERTIFICATE-----\n"), "", false, + "", gu.Ptr(domain.SAMLNameIDFormatUnspecified), "", false, diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go index 188d45e6ec4..d1cb0d592b5 100644 --- a/internal/command/idp_model.go +++ b/internal/command/idp_model.go @@ -1758,6 +1758,7 @@ type SAMLIDPWriteModel struct { Certificate []byte Binding string WithSignedRequest bool + SignatureAlgorithm string NameIDFormat *domain.SAMLNameIDFormat TransientMappingAttributeName string FederatedLogoutEnabled bool @@ -1787,6 +1788,7 @@ func (wm *SAMLIDPWriteModel) reduceAddedEvent(e *idp.SAMLIDPAddedEvent) { wm.Certificate = e.Certificate wm.Binding = e.Binding wm.WithSignedRequest = e.WithSignedRequest + wm.SignatureAlgorithm = e.SignatureAlgorithm wm.NameIDFormat = e.NameIDFormat wm.TransientMappingAttributeName = e.TransientMappingAttributeName wm.FederatedLogoutEnabled = e.FederatedLogoutEnabled @@ -1813,6 +1815,9 @@ func (wm *SAMLIDPWriteModel) reduceChangedEvent(e *idp.SAMLIDPChangedEvent) { if e.WithSignedRequest != nil { wm.WithSignedRequest = *e.WithSignedRequest } + if e.SignatureAlgorithm != nil { + wm.SignatureAlgorithm = *e.SignatureAlgorithm + } if e.NameIDFormat != nil { wm.NameIDFormat = e.NameIDFormat } @@ -1833,6 +1838,7 @@ func (wm *SAMLIDPWriteModel) NewChanges( secretCrypto crypto.EncryptionAlgorithm, binding string, withSignedRequest bool, + signatureAlgorithm string, nameIDFormat *domain.SAMLNameIDFormat, transientMappingAttributeName string, federatedLogoutEnabled bool, @@ -1861,6 +1867,9 @@ func (wm *SAMLIDPWriteModel) NewChanges( if wm.WithSignedRequest != withSignedRequest { changes = append(changes, idp.ChangeSAMLWithSignedRequest(withSignedRequest)) } + if wm.SignatureAlgorithm != signatureAlgorithm { + changes = append(changes, idp.ChangeSAMLSignatureAlgorithm(signatureAlgorithm)) + } if wm.NameIDFormat != nameIDFormat { changes = append(changes, idp.ChangeSAMLNameIDFormat(nameIDFormat)) } @@ -1902,6 +1911,9 @@ func (wm *SAMLIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Encryp if wm.Binding != "" { opts = append(opts, saml2.WithBinding(wm.Binding)) } + if wm.WithSignedRequest && wm.SignatureAlgorithm != "" { + opts = append(opts, saml2.WithSignatureAlgorithm(wm.SignatureAlgorithm)) + } if wm.NameIDFormat != nil { opts = append(opts, saml2.WithNameIDFormat(*wm.NameIDFormat)) } diff --git a/internal/command/instance_idp.go b/internal/command/instance_idp.go index 90efb3edd41..1f6536ef8c8 100644 --- a/internal/command/instance_idp.go +++ b/internal/command/instance_idp.go @@ -1793,6 +1793,7 @@ func (c *Commands) prepareAddInstanceSAMLProvider(a *instance.Aggregate, writeMo cert, provider.Binding, provider.WithSignedRequest, + provider.SignatureAlgorithm, provider.NameIDFormat, provider.TransientMappingAttributeName, provider.FederatedLogoutEnabled, @@ -1847,6 +1848,7 @@ func (c *Commands) prepareUpdateInstanceSAMLProvider(a *instance.Aggregate, writ c.idpConfigEncryption, provider.Binding, provider.WithSignedRequest, + provider.SignatureAlgorithm, provider.NameIDFormat, provider.TransientMappingAttributeName, provider.FederatedLogoutEnabled, @@ -1893,6 +1895,7 @@ func (c *Commands) prepareRegenerateInstanceSAMLProviderCertificate(a *instance. c.idpConfigEncryption, writeModel.Binding, writeModel.WithSignedRequest, + writeModel.SignatureAlgorithm, writeModel.NameIDFormat, writeModel.TransientMappingAttributeName, writeModel.FederatedLogoutEnabled, diff --git a/internal/command/instance_idp_model.go b/internal/command/instance_idp_model.go index 03d9cd9c367..262b01655bd 100644 --- a/internal/command/instance_idp_model.go +++ b/internal/command/instance_idp_model.go @@ -921,6 +921,7 @@ func (wm *InstanceSAMLIDPWriteModel) NewChangedEvent( secretCrypto crypto.EncryptionAlgorithm, binding string, withSignedRequest bool, + signatureAlgorithm string, nameIDFormat *domain.SAMLNameIDFormat, transientMappingAttributeName string, federatedLogoutEnabled bool, @@ -934,6 +935,7 @@ func (wm *InstanceSAMLIDPWriteModel) NewChangedEvent( secretCrypto, binding, withSignedRequest, + signatureAlgorithm, nameIDFormat, transientMappingAttributeName, federatedLogoutEnabled, diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go index 4805d075dd7..8e6b92bcf0f 100644 --- a/internal/command/instance_idp_test.go +++ b/internal/command/instance_idp_test.go @@ -5435,6 +5435,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) { []byte("certificate"), "", false, + "", nil, "", false, @@ -5477,6 +5478,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) { []byte("certificate"), "binding", true, + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", gu.Ptr(domain.SAMLNameIDFormatTransient), "customAttribute", true, @@ -5500,6 +5502,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) { Metadata: validSAMLMetadata, Binding: "binding", WithSignedRequest: true, + SignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient), TransientMappingAttributeName: "customAttribute", FederatedLogoutEnabled: true, @@ -5666,6 +5669,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) { []byte("certificate"), "", false, + "", nil, "", false, @@ -5705,6 +5709,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) { []byte("certificate"), "binding", false, + "", gu.Ptr(domain.SAMLNameIDFormatUnspecified), "", false, @@ -5850,6 +5855,7 @@ func TestCommandSide_RegenerateInstanceSAMLProviderCertificate(t *testing.T) { []byte("certificate"), "binding", false, + "", gu.Ptr(domain.SAMLNameIDFormatUnspecified), "", false, diff --git a/internal/command/org_idp.go b/internal/command/org_idp.go index 6cae78acc75..20e7436ad41 100644 --- a/internal/command/org_idp.go +++ b/internal/command/org_idp.go @@ -1766,6 +1766,7 @@ func (c *Commands) prepareAddOrgSAMLProvider(a *org.Aggregate, writeModel *OrgSA cert, provider.Binding, provider.WithSignedRequest, + provider.SignatureAlgorithm, provider.NameIDFormat, provider.TransientMappingAttributeName, provider.FederatedLogoutEnabled, @@ -1820,6 +1821,7 @@ func (c *Commands) prepareUpdateOrgSAMLProvider(a *org.Aggregate, writeModel *Or c.idpConfigEncryption, provider.Binding, provider.WithSignedRequest, + provider.SignatureAlgorithm, provider.NameIDFormat, provider.TransientMappingAttributeName, provider.FederatedLogoutEnabled, @@ -1866,6 +1868,7 @@ func (c *Commands) prepareRegenerateOrgSAMLProviderCertificate(a *org.Aggregate, c.idpConfigEncryption, writeModel.Binding, writeModel.WithSignedRequest, + writeModel.SignatureAlgorithm, writeModel.NameIDFormat, writeModel.TransientMappingAttributeName, writeModel.FederatedLogoutEnabled, diff --git a/internal/command/org_idp_model.go b/internal/command/org_idp_model.go index fdafb7f0871..9a7fea93c63 100644 --- a/internal/command/org_idp_model.go +++ b/internal/command/org_idp_model.go @@ -933,6 +933,7 @@ func (wm *OrgSAMLIDPWriteModel) NewChangedEvent( secretCrypto crypto.EncryptionAlgorithm, binding string, withSignedRequest bool, + signatureAlgorithm string, nameIDFormat *domain.SAMLNameIDFormat, transientMappingAttributeName string, federatedLogoutEnabled bool, @@ -946,6 +947,7 @@ func (wm *OrgSAMLIDPWriteModel) NewChangedEvent( secretCrypto, binding, withSignedRequest, + signatureAlgorithm, nameIDFormat, transientMappingAttributeName, federatedLogoutEnabled, diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go index 54f508da305..152b72a0abd 100644 --- a/internal/command/org_idp_test.go +++ b/internal/command/org_idp_test.go @@ -5517,6 +5517,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) { []byte("certificate"), "", false, + "", nil, "", false, @@ -5559,6 +5560,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) { []byte("certificate"), "binding", true, + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", gu.Ptr(domain.SAMLNameIDFormatTransient), "customAttribute", true, @@ -5583,6 +5585,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) { Metadata: validSAMLMetadata, Binding: "binding", WithSignedRequest: true, + SignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient), TransientMappingAttributeName: "customAttribute", FederatedLogoutEnabled: true, @@ -5757,6 +5760,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) { []byte("certificate"), "", false, + "", nil, "", false, @@ -5797,6 +5801,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) { []byte("certificate"), "binding", false, + "", gu.Ptr(domain.SAMLNameIDFormatUnspecified), "", false, @@ -5948,6 +5953,7 @@ func TestCommandSide_RegenerateOrgSAMLProviderCertificate(t *testing.T) { []byte("certificate"), "binding", false, + "", gu.Ptr(domain.SAMLNameIDFormatUnspecified), "", false, diff --git a/internal/idp/providers/saml/saml.go b/internal/idp/providers/saml/saml.go index 11b1d36da2d..aa138a476b9 100644 --- a/internal/idp/providers/saml/saml.go +++ b/internal/idp/providers/saml/saml.go @@ -32,6 +32,7 @@ type Provider struct { spOptions *samlsp.Options binding string + signatureAlgorithm string nameIDFormat saml.NameIDFormat transientMappingAttributeName string @@ -84,6 +85,12 @@ func WithBinding(binding string) ProviderOpts { } } +func WithSignatureAlgorithm(signatureAlgorithm string) ProviderOpts { + return func(p *Provider) { + p.signatureAlgorithm = signatureAlgorithm + } +} + func WithNameIDFormat(format domain.SAMLNameIDFormat) ProviderOpts { return func(p *Provider) { p.nameIDFormat = nameIDFormatFromDomain(format) @@ -219,6 +226,9 @@ func (p *Provider) GetSP() (*samlsp.Middleware, error) { if p.binding != "" { sp.Binding = p.binding } + if p.signatureAlgorithm != "" { + sp.ServiceProvider.SignatureMethod = p.signatureAlgorithm + } sp.ServiceProvider.MetadataValidDuration = time.Until(sp.ServiceProvider.Certificate.NotAfter) return sp, nil } diff --git a/internal/idp/providers/saml/saml_test.go b/internal/idp/providers/saml/saml_test.go index 5e76e6dcaa2..eeb9a58d6ce 100644 --- a/internal/idp/providers/saml/saml_test.go +++ b/internal/idp/providers/saml/saml_test.go @@ -165,6 +165,7 @@ func TestProvider_Options(t *testing.T) { nameIDFormat saml.NameIDFormat transientMappingAttributeName string withSignedRequest bool + signatureAlgorithm string requesttracker samlsp.RequestTracker entityID string } @@ -257,6 +258,7 @@ func TestProvider_Options(t *testing.T) { WithAutoUpdate(), WithBinding("binding"), WithSignedRequest(), + WithSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"), WithCustomRequestTracker(&requesttracker.RequestTracker{}), WithEntityID("entityID"), WithNameIDFormat(domain.SAMLNameIDFormatTransient), @@ -274,6 +276,7 @@ func TestProvider_Options(t *testing.T) { nameIDFormat: saml.TransientNameIDFormat, transientMappingAttributeName: "attribute", withSignedRequest: true, + signatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", requesttracker: &requesttracker.RequestTracker{}, }, }, @@ -297,6 +300,7 @@ func TestProvider_Options(t *testing.T) { a.Equal(tt.want.nameIDFormat, provider.nameIDFormat) a.Equal(tt.want.transientMappingAttributeName, provider.transientMappingAttributeName) a.Equal(tt.want.withSignedRequest, provider.spOptions.SignRequest) + a.Equal(tt.want.signatureAlgorithm, provider.signatureAlgorithm) a.Equal(tt.want.requesttracker, provider.requestTracker) a.Equal(tt.want.entityID, provider.spOptions.EntityID) } diff --git a/internal/query/idp_template.go b/internal/query/idp_template.go index c1c47e73bc5..25c32a14021 100644 --- a/internal/query/idp_template.go +++ b/internal/query/idp_template.go @@ -163,6 +163,7 @@ type SAMLIDPTemplate struct { Certificate []byte Binding string WithSignedRequest bool + SignatureAlgorithm string NameIDFormat sql.Null[domain.SAMLNameIDFormat] TransientMappingAttributeName string FederatedLogoutEnabled bool @@ -717,6 +718,10 @@ var ( name: projection.SAMLWithSignedRequestCol, table: samlIdpTemplateTable, } + SAMLSignatureAlgorithmCol = Column{ + name: projection.SAMLSignatureAlgorithmCol, + table: samlIdpTemplateTable, + } SAMLNameIDFormatCol = Column{ name: projection.SAMLNameIDFormatCol, table: samlIdpTemplateTable, @@ -951,6 +956,7 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla SAMLCertificateCol.identifier(), SAMLBindingCol.identifier(), SAMLWithSignedRequestCol.identifier(), + SAMLSignatureAlgorithmCol.identifier(), SAMLNameIDFormatCol.identifier(), SAMLTransientMappingAttributeNameCol.identifier(), SAMLFederatedLogoutEnabledCol.identifier(), @@ -1071,6 +1077,7 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla var samlCertificate []byte samlBinding := sql.NullString{} samlWithSignedRequest := sql.NullBool{} + samlSignatureAlgorithm := sql.NullString{} samlNameIDFormat := sql.Null[domain.SAMLNameIDFormat]{} samlTransientMappingAttributeName := sql.NullString{} samlFederatedLogoutEnabled := sql.NullBool{} @@ -1189,6 +1196,7 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla &samlCertificate, &samlBinding, &samlWithSignedRequest, + &samlSignatureAlgorithm, &samlNameIDFormat, &samlTransientMappingAttributeName, &samlFederatedLogoutEnabled, @@ -1329,6 +1337,7 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla Certificate: samlCertificate, Binding: samlBinding.String, WithSignedRequest: samlWithSignedRequest.Bool, + SignatureAlgorithm: samlSignatureAlgorithm.String, NameIDFormat: samlNameIDFormat, TransientMappingAttributeName: samlTransientMappingAttributeName.String, FederatedLogoutEnabled: samlFederatedLogoutEnabled.Bool, @@ -1463,6 +1472,7 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate SAMLCertificateCol.identifier(), SAMLBindingCol.identifier(), SAMLWithSignedRequestCol.identifier(), + SAMLSignatureAlgorithmCol.identifier(), SAMLNameIDFormatCol.identifier(), SAMLTransientMappingAttributeNameCol.identifier(), SAMLFederatedLogoutEnabledCol.identifier(), @@ -1588,6 +1598,7 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate var samlCertificate []byte samlBinding := sql.NullString{} samlWithSignedRequest := sql.NullBool{} + samlSignatureAlgorithm := sql.NullString{} samlNameIDFormat := sql.Null[domain.SAMLNameIDFormat]{} samlTransientMappingAttributeName := sql.NullString{} samlFederatedLogoutEnabled := sql.NullBool{} @@ -1706,6 +1717,7 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate &samlCertificate, &samlBinding, &samlWithSignedRequest, + &samlSignatureAlgorithm, &samlNameIDFormat, &samlTransientMappingAttributeName, &samlFederatedLogoutEnabled, @@ -1845,6 +1857,7 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate Certificate: samlCertificate, Binding: samlBinding.String, WithSignedRequest: samlWithSignedRequest.Bool, + SignatureAlgorithm: samlSignatureAlgorithm.String, NameIDFormat: samlNameIDFormat, TransientMappingAttributeName: samlTransientMappingAttributeName.String, FederatedLogoutEnabled: samlFederatedLogoutEnabled.Bool, diff --git a/internal/query/idp_template_test.go b/internal/query/idp_template_test.go index aed243c61e3..e730a765b7d 100644 --- a/internal/query/idp_template_test.go +++ b/internal/query/idp_template_test.go @@ -97,6 +97,7 @@ var ( ` projections.idp_templates6_saml.certificate,` + ` projections.idp_templates6_saml.binding,` + ` projections.idp_templates6_saml.with_signed_request,` + + ` projections.idp_templates6_saml.signature_algorithm,` + ` projections.idp_templates6_saml.name_id_format,` + ` projections.idp_templates6_saml.transient_mapping_attribute_name,` + ` projections.idp_templates6_saml.federated_logout_enabled,` + @@ -227,6 +228,7 @@ var ( "certificate", "binding", "with_signed_request", + "signature_algorithm", "name_id_format", "transient_mapping_attribute_name", "federated_logout_enabled", @@ -344,6 +346,7 @@ var ( ` projections.idp_templates6_saml.certificate,` + ` projections.idp_templates6_saml.binding,` + ` projections.idp_templates6_saml.with_signed_request,` + + ` projections.idp_templates6_saml.signature_algorithm,` + ` projections.idp_templates6_saml.name_id_format,` + ` projections.idp_templates6_saml.transient_mapping_attribute_name,` + ` projections.idp_templates6_saml.federated_logout_enabled,` + @@ -475,6 +478,7 @@ var ( "certificate", "binding", "with_signed_request", + "signature_algorithm", "name_id_format", "transient_mapping_attribute_name", "federated_logout_enabled", @@ -635,6 +639,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -790,6 +795,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -943,6 +949,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -1094,6 +1101,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -1244,6 +1252,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -1394,6 +1403,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -1545,6 +1555,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -1691,7 +1702,8 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, "binding", - false, + true, + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", domain.SAMLNameIDFormatTransient, "customAttribute", true, @@ -1751,7 +1763,8 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { Key: nil, Certificate: nil, Binding: "binding", - WithSignedRequest: false, + WithSignedRequest: true, + SignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", NameIDFormat: sql.Null[domain.SAMLNameIDFormat]{V: domain.SAMLNameIDFormatTransient, Valid: true}, TransientMappingAttributeName: "customAttribute", FederatedLogoutEnabled: true, @@ -1850,6 +1863,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config "idp-id", database.TextArray[string]{"server"}, @@ -2021,6 +2035,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -2173,6 +2188,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -2353,6 +2369,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config "idp-id", database.TextArray[string]{"server"}, @@ -2533,6 +2550,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -2686,6 +2704,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config "idp-id-ldap", database.TextArray[string]{"server"}, @@ -2801,6 +2820,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, "binding", false, + nil, domain.SAMLNameIDFormatTransient, "customAttribute", true, @@ -2922,6 +2942,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -3040,6 +3061,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -3158,6 +3180,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -3276,6 +3299,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { nil, nil, nil, + nil, // ldap config nil, nil, @@ -3382,6 +3406,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) { Certificate: nil, Binding: "binding", WithSignedRequest: false, + SignatureAlgorithm: "", NameIDFormat: sql.Null[domain.SAMLNameIDFormat]{V: domain.SAMLNameIDFormatTransient, Valid: true}, TransientMappingAttributeName: "customAttribute", FederatedLogoutEnabled: true, diff --git a/internal/query/projection/idp_template.go b/internal/query/projection/idp_template.go index 11eeb8c6137..f6fb7db3007 100644 --- a/internal/query/projection/idp_template.go +++ b/internal/query/projection/idp_template.go @@ -171,6 +171,7 @@ const ( SAMLCertificateCol = "certificate" SAMLBindingCol = "binding" SAMLWithSignedRequestCol = "with_signed_request" + SAMLSignatureAlgorithmCol = "signature_algorithm" SAMLNameIDFormatCol = "name_id_format" SAMLTransientMappingAttributeName = "transient_mapping_attribute_name" SAMLFederatedLogoutEnabled = "federated_logout_enabled" @@ -376,6 +377,7 @@ func (*idpTemplateProjection) Init() *old_handler.Check { handler.NewColumn(SAMLCertificateCol, handler.ColumnTypeBytes), handler.NewColumn(SAMLBindingCol, handler.ColumnTypeText, handler.Nullable()), handler.NewColumn(SAMLWithSignedRequestCol, handler.ColumnTypeBool, handler.Nullable()), + handler.NewColumn(SAMLSignatureAlgorithmCol, handler.ColumnTypeText, handler.Default("")), handler.NewColumn(SAMLNameIDFormatCol, handler.ColumnTypeEnum, handler.Nullable()), handler.NewColumn(SAMLTransientMappingAttributeName, handler.ColumnTypeText, handler.Nullable()), handler.NewColumn(SAMLFederatedLogoutEnabled, handler.ColumnTypeBool, handler.Default(false)), @@ -1991,6 +1993,7 @@ func (p *idpTemplateProjection) reduceSAMLIDPAdded(event eventstore.Event) (*han handler.NewCol(SAMLCertificateCol, idpEvent.Certificate), handler.NewCol(SAMLBindingCol, idpEvent.Binding), handler.NewCol(SAMLWithSignedRequestCol, idpEvent.WithSignedRequest), + handler.NewCol(SAMLSignatureAlgorithmCol, idpEvent.SignatureAlgorithm), handler.NewCol(SAMLTransientMappingAttributeName, idpEvent.TransientMappingAttributeName), handler.NewCol(SAMLFederatedLogoutEnabled, idpEvent.FederatedLogoutEnabled), } @@ -2522,6 +2525,9 @@ func reduceSAMLIDPChangedColumns(idpEvent idp.SAMLIDPChangedEvent) []handler.Col if idpEvent.WithSignedRequest != nil { SAMLCols = append(SAMLCols, handler.NewCol(SAMLWithSignedRequestCol, *idpEvent.WithSignedRequest)) } + if idpEvent.SignatureAlgorithm != nil { + SAMLCols = append(SAMLCols, handler.NewCol(SAMLSignatureAlgorithmCol, *idpEvent.SignatureAlgorithm)) + } if idpEvent.NameIDFormat != nil { SAMLCols = append(SAMLCols, handler.NewCol(SAMLNameIDFormatCol, *idpEvent.NameIDFormat)) } diff --git a/internal/query/projection/idp_template_test.go b/internal/query/projection/idp_template_test.go index 6ba6dabd471..d127e9b8475 100644 --- a/internal/query/projection/idp_template_test.go +++ b/internal/query/projection/idp_template_test.go @@ -2789,6 +2789,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { "nameIDFormat": 3, "transientMappingAttributeName": "customAttribute", "withSignedRequest": true, + "signatureAlgorithm": "", "isCreationAllowed": true, "isLinkingAllowed": true, "isAutoCreation": true, @@ -2825,7 +2826,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.idp_templates6_saml (idp_id, instance_id, metadata, key, certificate, binding, with_signed_request, transient_mapping_attribute_name, federated_logout_enabled, name_id_format) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", + expectedStmt: "INSERT INTO projections.idp_templates6_saml (idp_id, instance_id, metadata, key, certificate, binding, with_signed_request, signature_algorithm, transient_mapping_attribute_name, federated_logout_enabled, name_id_format) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -2834,6 +2835,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { anyArg{}, "binding", true, + "", "customAttribute", true, domain.SAMLNameIDFormatTransient, @@ -2863,6 +2865,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { "nameIDFormat": 3, "transientMappingAttributeName": "customAttribute", "withSignedRequest": true, + "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", "isCreationAllowed": true, "isLinkingAllowed": true, "isAutoCreation": true, @@ -2899,7 +2902,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.idp_templates6_saml (idp_id, instance_id, metadata, key, certificate, binding, with_signed_request, transient_mapping_attribute_name, federated_logout_enabled, name_id_format) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", + expectedStmt: "INSERT INTO projections.idp_templates6_saml (idp_id, instance_id, metadata, key, certificate, binding, with_signed_request, signature_algorithm, transient_mapping_attribute_name, federated_logout_enabled, name_id_format) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", expectedArgs: []interface{}{ "idp-id", "instance-id", @@ -2908,6 +2911,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { anyArg{}, "binding", true, + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", "customAttribute", true, domain.SAMLNameIDFormatTransient, @@ -2958,6 +2962,47 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { }, }, }, + { + name: "instance reduceSAMLIDPChanged - signature algorithm", + args: args{ + event: getEvent(testEvent( + instance.SAMLIDPChangedEventType, + instance.AggregateType, + []byte(`{ + "id": "idp-id", + "name": "custom-zitadel-instance", + "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" +}`), + ), instance.SAMLIDPChangedEventMapper), + }, + reduce: (&idpTemplateProjection{}).reduceSAMLIDPChanged, + want: wantReduce{ + aggregateType: eventstore.AggregateType("instance"), + sequence: 15, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.idp_templates6 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedArgs: []interface{}{ + "custom-zitadel-instance", + anyArg{}, + uint64(15), + "idp-id", + "instance-id", + }, + }, + { + expectedStmt: "UPDATE projections.idp_templates6_saml SET signature_algorithm = $1 WHERE (idp_id = $2) AND (instance_id = $3)", + expectedArgs: []interface{}{ + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + "idp-id", + "instance-id", + }, + }, + }, + }, + }, + }, { name: "instance reduceSAMLIDPChanged", args: args{ @@ -2976,6 +3021,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { "certificate": `+stringToJSONByte("certificate")+`, "binding": "binding", "withSignedRequest": true, + "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", "isCreationAllowed": true, "isLinkingAllowed": true, "isAutoCreation": true, @@ -3007,13 +3053,14 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.idp_templates6_saml SET (metadata, key, certificate, binding, with_signed_request, federated_logout_enabled) = ($1, $2, $3, $4, $5, $6) WHERE (idp_id = $7) AND (instance_id = $8)", + expectedStmt: "UPDATE projections.idp_templates6_saml SET (metadata, key, certificate, binding, with_signed_request, signature_algorithm, federated_logout_enabled) = ($1, $2, $3, $4, $5, $6, $7) WHERE (idp_id = $8) AND (instance_id = $9)", expectedArgs: []interface{}{ []byte("metadata"), anyArg{}, anyArg{}, "binding", true, + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", true, "idp-id", "instance-id", diff --git a/internal/repository/idp/saml.go b/internal/repository/idp/saml.go index 209d8371b34..b932454d927 100644 --- a/internal/repository/idp/saml.go +++ b/internal/repository/idp/saml.go @@ -16,6 +16,7 @@ type SAMLIDPAddedEvent struct { Key *crypto.CryptoValue `json:"key,omitempty"` Certificate []byte `json:"certificate,omitempty"` Binding string `json:"binding,omitempty"` + SignatureAlgorithm string `json:"signatureAlgorithm,omitempty"` WithSignedRequest bool `json:"withSignedRequest,omitempty"` NameIDFormat *domain.SAMLNameIDFormat `json:"nameIDFormat,omitempty"` TransientMappingAttributeName string `json:"transientMappingAttributeName,omitempty"` @@ -32,6 +33,7 @@ func NewSAMLIDPAddedEvent( certificate []byte, binding string, withSignedRequest bool, + signatureAlgorithm string, nameIDFormat *domain.SAMLNameIDFormat, transientMappingAttributeName string, federatedLogoutEnabled bool, @@ -46,6 +48,7 @@ func NewSAMLIDPAddedEvent( Certificate: certificate, Binding: binding, WithSignedRequest: withSignedRequest, + SignatureAlgorithm: signatureAlgorithm, NameIDFormat: nameIDFormat, TransientMappingAttributeName: transientMappingAttributeName, FederatedLogoutEnabled: federatedLogoutEnabled, @@ -84,6 +87,7 @@ type SAMLIDPChangedEvent struct { Certificate []byte `json:"certificate,omitempty"` Binding *string `json:"binding,omitempty"` WithSignedRequest *bool `json:"withSignedRequest,omitempty"` + SignatureAlgorithm *string `json:"signatureAlgorithm,omitempty"` NameIDFormat *domain.SAMLNameIDFormat `json:"nameIDFormat,omitempty"` TransientMappingAttributeName *string `json:"transientMappingAttributeName,omitempty"` FederatedLogoutEnabled *bool `json:"federatedLogoutEnabled,omitempty"` @@ -146,6 +150,12 @@ func ChangeSAMLWithSignedRequest(withSignedRequest bool) func(*SAMLIDPChangedEve } } +func ChangeSAMLSignatureAlgorithm(signatureAlgorithm string) func(*SAMLIDPChangedEvent) { + return func(e *SAMLIDPChangedEvent) { + e.SignatureAlgorithm = &signatureAlgorithm + } +} + func ChangeSAMLNameIDFormat(nameIDFormat *domain.SAMLNameIDFormat) func(*SAMLIDPChangedEvent) { return func(e *SAMLIDPChangedEvent) { e.NameIDFormat = nameIDFormat diff --git a/internal/repository/instance/idp.go b/internal/repository/instance/idp.go index f0f324cb7d3..95ffb017c86 100644 --- a/internal/repository/instance/idp.go +++ b/internal/repository/instance/idp.go @@ -1022,6 +1022,7 @@ func NewSAMLIDPAddedEvent( certificate []byte, binding string, withSignedRequest bool, + signatureAlgorithm string, nameIDFormat *domain.SAMLNameIDFormat, transientMappingAttributeName string, federatedLogoutEnabled bool, @@ -1041,6 +1042,7 @@ func NewSAMLIDPAddedEvent( certificate, binding, withSignedRequest, + signatureAlgorithm, nameIDFormat, transientMappingAttributeName, federatedLogoutEnabled, diff --git a/internal/repository/org/idp.go b/internal/repository/org/idp.go index 5f061370f17..dc402e36e85 100644 --- a/internal/repository/org/idp.go +++ b/internal/repository/org/idp.go @@ -1023,6 +1023,7 @@ func NewSAMLIDPAddedEvent( certificate []byte, binding string, withSignedRequest bool, + signatureAlgorithm string, nameIDFormat *domain.SAMLNameIDFormat, transientMappingAttributeName string, federatedLogoutEnabled bool, @@ -1043,6 +1044,7 @@ func NewSAMLIDPAddedEvent( certificate, binding, withSignedRequest, + signatureAlgorithm, nameIDFormat, transientMappingAttributeName, federatedLogoutEnabled, diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index 5bcdefe3598..ff5e1e5f846 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -7087,6 +7087,9 @@ message AddSAMLProviderRequest { // Optionally enable federated logout. If enabled, ZITADEL will send a logout request to the identity provider, // if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata. optional bool federated_logout_enabled = 9; + // Specify a Signature Algorithm that should be used to sign SAML requests and responses. + // Can be used only if the `with_signed_request` option is set to true. + zitadel.idp.v1.SAMLSignatureAlgorithm signature_algorithm = 10; } message AddSAMLProviderResponse { @@ -7124,6 +7127,9 @@ message UpdateSAMLProviderRequest { // Optionally enable federated logout. If enabled, ZITADEL will send a logout request to the identity provider, // if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata. optional bool federated_logout_enabled = 10; + // Specify a Signature Algorithm that should be used to sign SAML requests and responses. + // Can be used only if the `with_signed_request` option is set to true. + zitadel.idp.v1.SAMLSignatureAlgorithm signature_algorithm = 11; } message UpdateSAMLProviderResponse { diff --git a/proto/zitadel/idp.proto b/proto/zitadel/idp.proto index de497d8c937..e75022e5345 100644 --- a/proto/zitadel/idp.proto +++ b/proto/zitadel/idp.proto @@ -276,6 +276,13 @@ enum SAMLBinding { SAML_BINDING_ARTIFACT = 3; } +enum SAMLSignatureAlgorithm { + SAML_SIGNATURE_UNSPECIFIED = 0; + SAML_SIGNATURE_RSA_SHA1 = 1; + SAML_SIGNATURE_RSA_SHA256 = 2; + SAML_SIGNATURE_RSA_SHA512 = 3; +} + enum SAMLNameIDFormat { SAML_NAME_ID_FORMAT_UNSPECIFIED = 0; SAML_NAME_ID_FORMAT_EMAIL_ADDRESS = 1; @@ -479,9 +486,11 @@ message SAMLConfig { // 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; - // Boolean weather federated logout is enabled. If enabled, ZITADEL will send a logout request to the identity provider, + // Boolean value to indicate whether federated logout is enabled. If enabled, ZITADEL will send a logout request to the identity provider, // if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata. optional bool federated_logout_enabled = 6; + // Optional value to indicate the configured Signing Algorithm used to sign SAML requests and responses. + optional zitadel.idp.v1.SAMLSignatureAlgorithm signature_algorithm = 7; } message AzureADConfig { diff --git a/proto/zitadel/idp/v2/idp.proto b/proto/zitadel/idp/v2/idp.proto index 663581a6591..450c28ebc6c 100644 --- a/proto/zitadel/idp/v2/idp.proto +++ b/proto/zitadel/idp/v2/idp.proto @@ -64,6 +64,13 @@ enum SAMLBinding { SAML_BINDING_ARTIFACT = 3; } +enum SAMLSignatureAlgorithm { + SAML_SIGNATURE_UNSPECIFIED = 0; + SAML_SIGNATURE_RSA_SHA1 = 1; + SAML_SIGNATURE_RSA_SHA256 = 2; + SAML_SIGNATURE_RSA_SHA512 = 3; +} + enum SAMLNameIDFormat { SAML_NAME_ID_FORMAT_UNSPECIFIED = 0; SAML_NAME_ID_FORMAT_EMAIL_ADDRESS = 1; @@ -306,6 +313,9 @@ message SAMLConfig { // Boolean weather federated logout is enabled. If enabled, ZITADEL will send a logout request to the identity provider, // if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata. optional bool federated_logout_enabled = 6; + // Specify a Signature Algorithm that should used to sign SAML requests and responses. + // Can be set only if `with_signed_request` is set. + SAMLSignatureAlgorithm signature_algorithm = 7; } message AzureADConfig { diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index edaf03cfae8..19dfa9153a7 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -13794,6 +13794,9 @@ message AddSAMLProviderRequest { // Optionally enable federated logout. If enabled, ZITADEL will send a logout request to the identity provider, // if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata. optional bool federated_logout_enabled = 9; + // Specify a Signature Algorithm that should be used to sign SAML requests and responses. + // Can be used only if the `with_signed_request` option is set to true. + zitadel.idp.v1.SAMLSignatureAlgorithm signature_algorithm = 10; } message AddSAMLProviderResponse { @@ -13831,6 +13834,9 @@ message UpdateSAMLProviderRequest { // Optionally enable federated logout. If enabled, ZITADEL will send a logout request to the identity provider, // if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata. optional bool federated_logout_enabled = 10; + // Specify a Signature Algorithm that should be used to sign SAML requests and responses. + // Can be used only if the `with_signed_request` option is set to true. + zitadel.idp.v1.SAMLSignatureAlgorithm signature_algorithm = 11; } message UpdateSAMLProviderResponse {