From 6a3455668eec82d1eaa46ab427af00ae7ce3d737 Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Fri, 24 Nov 2023 18:19:05 +0100 Subject: [PATCH 01/13] upgrade for keycloak 23 --- pom.xml | 6 ++- .../broker/spid/SpidIdentityProvider.java | 21 +++++----- .../broker/spid/SpidSAMLEndpoint.java | 38 +++++++++---------- .../SpidSpMetadataResourceProvider.java | 30 ++++++++------- .../SpidSpMetadataResourceProviderTest.java | 2 +- 5 files changed, 52 insertions(+), 45 deletions(-) diff --git a/pom.xml b/pom.xml index e79f1fc..933b352 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ 4.0.0 com.github.lscorcia keycloak-spid-provider - 1.0.15-SNAPSHOT + 1.0.16-SNAPSHOT jar Keycloak SPID Service Provider @@ -19,10 +19,12 @@ false UTF-8 - 20.0.0 + 23.0.0 1.7.30 5.8.2 4.3.1 + 17 + 17 diff --git a/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java b/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java index 542f57c..1eb058d 100644 --- a/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java +++ b/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java @@ -35,6 +35,8 @@ import org.keycloak.dom.saml.v2.assertion.SubjectType; import org.keycloak.dom.saml.v2.metadata.AttributeConsumingServiceType; import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType; +import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.dom.saml.v2.metadata.LocalizedNameType; import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; @@ -48,6 +50,7 @@ import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder; +import org.keycloak.protocol.saml.SAMLEncryptionAlgorithms; import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.protocol.saml.SamlService; import org.keycloak.protocol.saml.SamlSessionUtils; @@ -78,10 +81,10 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLStreamWriter; @@ -381,8 +384,8 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) { String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat(); - List signingKeys = new LinkedList<>(); - List encryptionKeys = new LinkedList<>(); + List signingKeys = new LinkedList<>(); + List encryptionKeys = new LinkedList<>(); session.keys().getKeysStream(realm, KeyUse.SIG, Algorithm.RS256) .filter(Objects::nonNull) @@ -392,10 +395,10 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) { try { Element element = SPMetadataDescriptor .buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); - signingKeys.add(element); + signingKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.SIGNING, null)); if (key.getStatus() == KeyStatus.ACTIVE) { - encryptionKeys.add(element); + encryptionKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.ENCRYPTION, SAMLEncryptionAlgorithms.forKeycloakIdentifier(key.getAlgorithm()).getXmlEncIdentifiers())); } } catch (ParserConfigurationException e) { logger.warn("Failed to export SAML SP Metadata!", e); @@ -408,7 +411,7 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) { XMLStreamWriter writer = StaxUtil.getXMLStreamWriter(sw); SAMLMetadataWriter metadataWriter = new SAMLMetadataWriter(writer); - EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPdescriptor( + EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPDescriptor( authnBinding, authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, wantAssertionsSigned, wantAssertionsEncrypted, entityId, nameIDPolicyFormat, signingKeys, encryptionKeys); diff --git a/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java b/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java index 07941d5..9b7d994 100755 --- a/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java +++ b/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java @@ -73,20 +73,20 @@ import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.messages.Messages; -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; -import javax.ws.rs.PathParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; import javax.xml.namespace.QName; import java.io.IOException; import java.security.Key; @@ -118,7 +118,7 @@ import java.security.cert.CertificateException; import java.util.Collections; -import javax.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.MultivaluedMap; import javax.xml.crypto.dsig.XMLSignature; import java.util.Arrays; @@ -464,7 +464,7 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h if (assertionIsEncrypted) { // This methods writes the parsed and decrypted assertion back on the responseType parameter: - assertionElement = AssertionUtil.decryptAssertion(holder, responseType, keys.getPrivateKey()); + assertionElement = AssertionUtil.decryptAssertion(responseType, keys.getPrivateKey()); } else { /* We verify the assertion using original document to handle cases where the IdP includes whitespace and/or newlines inside tags. */ @@ -523,7 +523,7 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h if(AssertionUtil.isIdEncrypted(responseType)) { // This methods writes the parsed and decrypted id back on the responseType parameter: - AssertionUtil.decryptId(responseType, keys.getPrivateKey()); + AssertionUtil.decryptId(responseType, data -> Collections.singletonList(keys.getPrivateKey())); } AssertionType assertion = responseType.getAssertions().get(0).getAssertion(); @@ -627,7 +627,7 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h */ private AuthenticationSessionModel samlIdpInitiatedSSO(final String clientUrlName) { event.event(EventType.LOGIN); - CacheControlUtil.noBackButtonCacheControlHeader(); + CacheControlUtil.noBackButtonCacheControlHeader(SpidSAMLEndpoint.this.session); Optional oClient = SpidSAMLEndpoint.this.session.clients() .searchClientsByAttributes(realm, Collections.singletonMap(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME, clientUrlName), 0, 1) .findFirst(); @@ -639,7 +639,7 @@ private AuthenticationSessionModel samlIdpInitiatedSSO(final String clientUrlNam } LoginProtocolFactory factory = (LoginProtocolFactory) session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, SamlProtocol.LOGIN_PROTOCOL); - SamlService samlService = (SamlService) factory.createProtocolEndpoint(SpidSAMLEndpoint.this.realm, event); + SamlService samlService = (SamlService) factory.createProtocolEndpoint(SpidSAMLEndpoint.this.session, event); ResteasyProviderFactory.getInstance().injectProperties(samlService); AuthenticationSessionModel authSession = samlService.getOrCreateLoginSessionForIdpInitiatedSso(session, SpidSAMLEndpoint.this.realm, oClient.get(), null); if (authSession == null) { diff --git a/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java b/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java index d3fe7ee..ca50230 100644 --- a/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java +++ b/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java @@ -28,6 +28,8 @@ import org.keycloak.dom.saml.v2.metadata.EndpointType; import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType; import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType; +import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.dom.saml.v2.metadata.LocalizedNameType; import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType; import org.keycloak.models.KeyManager; @@ -42,6 +44,7 @@ import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.processing.core.saml.v2.writers.SAMLMetadataWriter; import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature; +import org.keycloak.protocol.saml.SAMLEncryptionAlgorithms; import org.keycloak.protocol.saml.SamlService; import org.keycloak.protocol.saml.mappers.SamlMetadataDescriptorUpdater; import org.keycloak.services.resource.RealmResourceProvider; @@ -58,12 +61,12 @@ import java.util.Objects; import java.util.stream.Collectors; -import javax.ws.rs.GET; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.parsers.ParserConfigurationException; @@ -137,8 +140,8 @@ public Response get() { String attributeConsumingServiceName = firstSpidProvider.getConfig().getAttributeConsumingServiceName(); String[] attributeConsumingServiceNames = attributeConsumingServiceName != null ? attributeConsumingServiceName.split(","): null; - List signingKeys = new LinkedList<>(); - List encryptionKeys = new LinkedList<>(); + List signingKeys = new LinkedList<>(); + List encryptionKeys = new LinkedList<>(); session.keys().getKeysStream(realm, KeyUse.SIG, Algorithm.RS256) .filter(Objects::nonNull) @@ -147,11 +150,10 @@ public Response get() { .forEach(key -> { try { Element element = SPMetadataDescriptor - .buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); - signingKeys.add(element); - + .buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); + signingKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.SIGNING, null)); if (key.getStatus() == KeyStatus.ACTIVE) { - encryptionKeys.add(element); + encryptionKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.ENCRYPTION, SAMLEncryptionAlgorithms.forKeycloakIdentifier(key.getAlgorithm()).getXmlEncIdentifiers())); } } catch (ParserConfigurationException e) { logger.warn("Failed to export SAML SP Metadata!", e); @@ -159,11 +161,11 @@ public Response get() { } }); - EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPdescriptor( + EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPDescriptor( authnBinding, authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, wantAssertionsSigned, wantAssertionsEncrypted, entityId, nameIDPolicyFormat, signingKeys, encryptionKeys); - + // Create the AttributeConsumingService AttributeConsumingServiceType attributeConsumingService = new AttributeConsumingServiceType(attributeConsumingServiceIndex); attributeConsumingService.setIsDefault(true); diff --git a/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java b/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java index 42c7f31..6919ce7 100644 --- a/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java +++ b/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java @@ -54,7 +54,7 @@ import org.xmlunit.diff.Diff; import org.xmlunit.placeholder.PlaceholderDifferenceEvaluator; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.core.Response; import javax.xml.transform.Source; import java.net.URI; import java.security.InvalidKeyException; From ac263c04ca8a2afdc95e7e7ce99437b5ced10c2f Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Fri, 24 Nov 2023 18:20:30 +0100 Subject: [PATCH 02/13] update github action to use java 17 --- .github/workflows/maven.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 22167c2..8343633 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -21,7 +21,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.11 + java-version: 1.17 - name: Cache Maven packages uses: actions/cache@v2 From eba5455b2fdda94d467bddb1ee5d0b636ffd16e4 Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Fri, 24 Nov 2023 18:20:55 +0100 Subject: [PATCH 03/13] reduce build time through cache maven dependencies in docker layersenhance --- Dockerfile | 10 ++++++++++ Makefile | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Dockerfile create mode 100644 Makefile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c8dbd73 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM maven:3-eclipse-temurin-21-alpine +WORKDIR /opt/app + +# to cache dependencies +ADD pom*.xml . +RUN mvn verify --fail-never + +# build final JAR +COPY . . +RUN mvn package \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..90a240a --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +SERVICE_TARGET := keycloak-multiple-ds-user-storage + +# all our targets are phony (no files to check). +.PHONY: help package + +# suppress makes own output +#.SILENT: + +help: + @echo 'Usage: make [TARGET] ' + +build: package + +extract: + $(eval DOCKER_CONTAINER := $(shell docker create --name tc ${SERVICE_TARGET}:latest)) + mkdir -p target + docker cp ${DOCKER_CONTAINER}:/opt/app/target/spid-provider.jar target/ + docker rm tc + +package: + rm -fr target || true + -docker rm tc + docker build -t ${SERVICE_TARGET} . + $(MAKE) extract \ No newline at end of file From 83fd47a787c357443efc156085e629443a7289b1 Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Fri, 24 Nov 2023 18:27:46 +0100 Subject: [PATCH 04/13] makefile fix --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 90a240a..f3ef1ff 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -SERVICE_TARGET := keycloak-multiple-ds-user-storage +SERVICE_TARGET := spid-keycloak-provider # all our targets are phony (no files to check). .PHONY: help package From a60cab0ba07709b8884ca9a6903a81a2e3209a27 Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Fri, 24 Nov 2023 18:31:37 +0100 Subject: [PATCH 05/13] fix --- .../java/org/keycloak/broker/spid/SpidIdentityProvider.java | 2 +- .../broker/spid/metadata/SpidSpMetadataResourceProvider.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java b/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java index 1eb058d..db630ec 100644 --- a/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java +++ b/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java @@ -398,7 +398,7 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) { signingKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.SIGNING, null)); if (key.getStatus() == KeyStatus.ACTIVE) { - encryptionKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.ENCRYPTION, SAMLEncryptionAlgorithms.forKeycloakIdentifier(key.getAlgorithm()).getXmlEncIdentifiers())); + encryptionKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.ENCRYPTION, null)); } } catch (ParserConfigurationException e) { logger.warn("Failed to export SAML SP Metadata!", e); diff --git a/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java b/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java index ca50230..c151b11 100644 --- a/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java +++ b/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java @@ -153,7 +153,7 @@ public Response get() { .buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); signingKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.SIGNING, null)); if (key.getStatus() == KeyStatus.ACTIVE) { - encryptionKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.ENCRYPTION, SAMLEncryptionAlgorithms.forKeycloakIdentifier(key.getAlgorithm()).getXmlEncIdentifiers())); + encryptionKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.ENCRYPTION, null)); } } catch (ParserConfigurationException e) { logger.warn("Failed to export SAML SP Metadata!", e); From e375021764f378a8218e0dd291d425d7b32c2b02 Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Mon, 27 Nov 2023 18:10:56 +0100 Subject: [PATCH 06/13] update spid endpoint and provider to latest constructor from upstream --- .../broker/spid/SpidIdentityProvider.java | 3 +-- .../broker/spid/SpidSAMLEndpoint.java | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java b/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java index db630ec..e9898fd 100644 --- a/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java +++ b/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java @@ -50,7 +50,6 @@ import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder; -import org.keycloak.protocol.saml.SAMLEncryptionAlgorithms; import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.protocol.saml.SamlService; import org.keycloak.protocol.saml.SamlSessionUtils; @@ -117,7 +116,7 @@ public SpidIdentityProvider(KeycloakSession session, SpidIdentityProviderConfig @Override public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) { - return new SpidSAMLEndpoint(realm, this, getConfig(), callback, destinationValidator); + return new SpidSAMLEndpoint(session, this, getConfig(), callback, destinationValidator); } @Override diff --git a/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java b/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java index 9b7d994..1e69a56 100755 --- a/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java +++ b/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java @@ -110,6 +110,7 @@ import org.keycloak.saml.validators.DestinationValidator; import org.keycloak.services.util.CacheControlUtil; import org.keycloak.sessions.AuthenticationSessionModel; +import org.keycloak.utils.StringUtil; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -129,7 +130,6 @@ import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import org.keycloak.dom.saml.v2.protocol.AuthnContextComparisonType; -import org.keycloak.saml.common.util.StringUtil; import org.keycloak.util.JsonSerialization; import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification; @@ -170,12 +170,15 @@ public class SpidSAMLEndpoint { private HttpHeaders headers; - public SpidSAMLEndpoint(RealmModel realm, SpidIdentityProvider provider, SpidIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback, DestinationValidator destinationValidator) { - this.realm = realm; + public SpidSAMLEndpoint(KeycloakSession session, SpidIdentityProvider provider, SpidIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback, DestinationValidator destinationValidator) { + this.realm = session.getContext().getRealm(); this.config = config; this.callback = callback; this.provider = provider; this.destinationValidator = destinationValidator; + this.session = session; + this.clientConnection = session.getContext().getConnection(); + this.headers = session.getContext().getRequestHeaders(); } @GET @@ -426,10 +429,15 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h try { AuthenticationSessionModel authSession; - if (clientId != null && ! clientId.trim().isEmpty()) { + if (StringUtil.isNotBlank(clientId)) { authSession = samlIdpInitiatedSSO(clientId); - } else { + } else if (StringUtil.isNotBlank(relayState)) { authSession = callback.getAndVerifyAuthenticationSession(relayState); + } else { + logger.error("SAML RelayState parameter was null when it should be returned by the IDP"); + event.event(EventType.LOGIN); + event.error(Errors.INVALID_SAML_RESPONSE); + return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR); } session.getContext().setAuthenticationSession(authSession); @@ -969,7 +977,7 @@ private String verifySpidResponse(Element documentElement, Element assertionElem } // 27: Issuer element is empty (SPID check nr27) - if (!issuerElement.hasChildNodes() || !StringUtil.isNotNull(issuerElement.getFirstChild().getNodeValue()) || hasNamedChild(issuerElement)) { + if (!issuerElement.hasChildNodes() || !org.keycloak.saml.common.util.StringUtil.isNotNull(issuerElement.getFirstChild().getNodeValue()) || hasNamedChild(issuerElement)) { return "SpidSamlCheck_nr27"; } @@ -1030,7 +1038,7 @@ private String verifySpidResponse(Element documentElement, Element assertionElem } // 43: NameID element of the Assertion is empty (SPID check nr43) - if (!nameIdElement.hasChildNodes() || !StringUtil.isNotNull(nameIdElement.getFirstChild().getNodeValue()) || hasNamedChild(nameIdElement)) { + if (!nameIdElement.hasChildNodes() || !org.keycloak.saml.common.util.StringUtil.isNotNull(nameIdElement.getFirstChild().getNodeValue()) || hasNamedChild(nameIdElement)) { return "SpidSamlCheck_nr43"; } @@ -1168,7 +1176,7 @@ private String verifySpidResponse(Element documentElement, Element assertionElem } // 67: Issuer element of the Assertion is empty (SPID check nr67) - if (!assertionIssuerElement.hasChildNodes() || !StringUtil.isNotNull(assertionIssuerElement.getFirstChild().getNodeValue()) || hasNamedChild(assertionIssuerElement)) { + if (!assertionIssuerElement.hasChildNodes() || !org.keycloak.saml.common.util.StringUtil.isNotNull(assertionIssuerElement.getFirstChild().getNodeValue()) || hasNamedChild(assertionIssuerElement)) { return "SpidSamlCheck_nr67"; } @@ -1252,7 +1260,7 @@ private String verifySpidResponse(Element documentElement, Element assertionElem } // 92: AuthStatement AuthStatement AuthContextClassRef Element of the Assertion is empty (SPID check nr92) - if (!authnContextClassRef.hasChildNodes() || !StringUtil.isNotNull(authnContextClassRef.getFirstChild().getNodeValue()) || hasNamedChild(authnContextClassRef)) { + if (!authnContextClassRef.hasChildNodes() || !org.keycloak.saml.common.util.StringUtil.isNotNull(authnContextClassRef.getFirstChild().getNodeValue()) || hasNamedChild(authnContextClassRef)) { return "SpidSamlCheck_nr92"; } From 33c21f47d2ca9f280253eb7098dd9cee651e35b6 Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Mon, 27 Nov 2023 18:15:13 +0100 Subject: [PATCH 07/13] Upgrade Keycloak 23 (#3) * upgrade for keycloak 23 * update github action to use java 17 * reduce build time through cache maven dependencies in docker layersenhance * update spid endpoint and provider to latest constructor from upstream --- .github/workflows/maven.yml | 2 +- Dockerfile | 10 +++ Makefile | 24 +++++++ pom.xml | 6 +- .../broker/spid/SpidIdentityProvider.java | 22 ++++--- .../broker/spid/SpidSAMLEndpoint.java | 64 +++++++++++-------- .../SpidSpMetadataResourceProvider.java | 30 +++++---- .../SpidSpMetadataResourceProviderTest.java | 2 +- 8 files changed, 104 insertions(+), 56 deletions(-) create mode 100644 Dockerfile create mode 100644 Makefile diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 22167c2..8343633 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -21,7 +21,7 @@ jobs: - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 1.11 + java-version: 1.17 - name: Cache Maven packages uses: actions/cache@v2 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c8dbd73 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM maven:3-eclipse-temurin-21-alpine +WORKDIR /opt/app + +# to cache dependencies +ADD pom*.xml . +RUN mvn verify --fail-never + +# build final JAR +COPY . . +RUN mvn package \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f3ef1ff --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +SERVICE_TARGET := spid-keycloak-provider + +# all our targets are phony (no files to check). +.PHONY: help package + +# suppress makes own output +#.SILENT: + +help: + @echo 'Usage: make [TARGET] ' + +build: package + +extract: + $(eval DOCKER_CONTAINER := $(shell docker create --name tc ${SERVICE_TARGET}:latest)) + mkdir -p target + docker cp ${DOCKER_CONTAINER}:/opt/app/target/spid-provider.jar target/ + docker rm tc + +package: + rm -fr target || true + -docker rm tc + docker build -t ${SERVICE_TARGET} . + $(MAKE) extract \ No newline at end of file diff --git a/pom.xml b/pom.xml index e79f1fc..933b352 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ 4.0.0 com.github.lscorcia keycloak-spid-provider - 1.0.15-SNAPSHOT + 1.0.16-SNAPSHOT jar Keycloak SPID Service Provider @@ -19,10 +19,12 @@ false UTF-8 - 20.0.0 + 23.0.0 1.7.30 5.8.2 4.3.1 + 17 + 17 diff --git a/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java b/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java index 542f57c..e9898fd 100644 --- a/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java +++ b/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java @@ -35,6 +35,8 @@ import org.keycloak.dom.saml.v2.assertion.SubjectType; import org.keycloak.dom.saml.v2.metadata.AttributeConsumingServiceType; import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType; +import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.dom.saml.v2.metadata.LocalizedNameType; import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; @@ -78,10 +80,10 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLStreamWriter; @@ -114,7 +116,7 @@ public SpidIdentityProvider(KeycloakSession session, SpidIdentityProviderConfig @Override public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) { - return new SpidSAMLEndpoint(realm, this, getConfig(), callback, destinationValidator); + return new SpidSAMLEndpoint(session, this, getConfig(), callback, destinationValidator); } @Override @@ -381,8 +383,8 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) { String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat(); - List signingKeys = new LinkedList<>(); - List encryptionKeys = new LinkedList<>(); + List signingKeys = new LinkedList<>(); + List encryptionKeys = new LinkedList<>(); session.keys().getKeysStream(realm, KeyUse.SIG, Algorithm.RS256) .filter(Objects::nonNull) @@ -392,10 +394,10 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) { try { Element element = SPMetadataDescriptor .buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); - signingKeys.add(element); + signingKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.SIGNING, null)); if (key.getStatus() == KeyStatus.ACTIVE) { - encryptionKeys.add(element); + encryptionKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.ENCRYPTION, null)); } } catch (ParserConfigurationException e) { logger.warn("Failed to export SAML SP Metadata!", e); @@ -408,7 +410,7 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) { XMLStreamWriter writer = StaxUtil.getXMLStreamWriter(sw); SAMLMetadataWriter metadataWriter = new SAMLMetadataWriter(writer); - EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPdescriptor( + EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPDescriptor( authnBinding, authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, wantAssertionsSigned, wantAssertionsEncrypted, entityId, nameIDPolicyFormat, signingKeys, encryptionKeys); diff --git a/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java b/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java index 07941d5..1e69a56 100755 --- a/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java +++ b/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java @@ -73,20 +73,20 @@ import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.messages.Messages; -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; -import javax.ws.rs.PathParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; import javax.xml.namespace.QName; import java.io.IOException; import java.security.Key; @@ -110,6 +110,7 @@ import org.keycloak.saml.validators.DestinationValidator; import org.keycloak.services.util.CacheControlUtil; import org.keycloak.sessions.AuthenticationSessionModel; +import org.keycloak.utils.StringUtil; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -118,7 +119,7 @@ import java.security.cert.CertificateException; import java.util.Collections; -import javax.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.MultivaluedMap; import javax.xml.crypto.dsig.XMLSignature; import java.util.Arrays; @@ -129,7 +130,6 @@ import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import org.keycloak.dom.saml.v2.protocol.AuthnContextComparisonType; -import org.keycloak.saml.common.util.StringUtil; import org.keycloak.util.JsonSerialization; import static org.keycloak.utils.LockObjectsForModification.lockUserSessionsForModification; @@ -170,12 +170,15 @@ public class SpidSAMLEndpoint { private HttpHeaders headers; - public SpidSAMLEndpoint(RealmModel realm, SpidIdentityProvider provider, SpidIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback, DestinationValidator destinationValidator) { - this.realm = realm; + public SpidSAMLEndpoint(KeycloakSession session, SpidIdentityProvider provider, SpidIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback, DestinationValidator destinationValidator) { + this.realm = session.getContext().getRealm(); this.config = config; this.callback = callback; this.provider = provider; this.destinationValidator = destinationValidator; + this.session = session; + this.clientConnection = session.getContext().getConnection(); + this.headers = session.getContext().getRequestHeaders(); } @GET @@ -426,10 +429,15 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h try { AuthenticationSessionModel authSession; - if (clientId != null && ! clientId.trim().isEmpty()) { + if (StringUtil.isNotBlank(clientId)) { authSession = samlIdpInitiatedSSO(clientId); - } else { + } else if (StringUtil.isNotBlank(relayState)) { authSession = callback.getAndVerifyAuthenticationSession(relayState); + } else { + logger.error("SAML RelayState parameter was null when it should be returned by the IDP"); + event.event(EventType.LOGIN); + event.error(Errors.INVALID_SAML_RESPONSE); + return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR); } session.getContext().setAuthenticationSession(authSession); @@ -464,7 +472,7 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h if (assertionIsEncrypted) { // This methods writes the parsed and decrypted assertion back on the responseType parameter: - assertionElement = AssertionUtil.decryptAssertion(holder, responseType, keys.getPrivateKey()); + assertionElement = AssertionUtil.decryptAssertion(responseType, keys.getPrivateKey()); } else { /* We verify the assertion using original document to handle cases where the IdP includes whitespace and/or newlines inside tags. */ @@ -523,7 +531,7 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h if(AssertionUtil.isIdEncrypted(responseType)) { // This methods writes the parsed and decrypted id back on the responseType parameter: - AssertionUtil.decryptId(responseType, keys.getPrivateKey()); + AssertionUtil.decryptId(responseType, data -> Collections.singletonList(keys.getPrivateKey())); } AssertionType assertion = responseType.getAssertions().get(0).getAssertion(); @@ -627,7 +635,7 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h */ private AuthenticationSessionModel samlIdpInitiatedSSO(final String clientUrlName) { event.event(EventType.LOGIN); - CacheControlUtil.noBackButtonCacheControlHeader(); + CacheControlUtil.noBackButtonCacheControlHeader(SpidSAMLEndpoint.this.session); Optional oClient = SpidSAMLEndpoint.this.session.clients() .searchClientsByAttributes(realm, Collections.singletonMap(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME, clientUrlName), 0, 1) .findFirst(); @@ -639,7 +647,7 @@ private AuthenticationSessionModel samlIdpInitiatedSSO(final String clientUrlNam } LoginProtocolFactory factory = (LoginProtocolFactory) session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, SamlProtocol.LOGIN_PROTOCOL); - SamlService samlService = (SamlService) factory.createProtocolEndpoint(SpidSAMLEndpoint.this.realm, event); + SamlService samlService = (SamlService) factory.createProtocolEndpoint(SpidSAMLEndpoint.this.session, event); ResteasyProviderFactory.getInstance().injectProperties(samlService); AuthenticationSessionModel authSession = samlService.getOrCreateLoginSessionForIdpInitiatedSso(session, SpidSAMLEndpoint.this.realm, oClient.get(), null); if (authSession == null) { @@ -969,7 +977,7 @@ private String verifySpidResponse(Element documentElement, Element assertionElem } // 27: Issuer element is empty (SPID check nr27) - if (!issuerElement.hasChildNodes() || !StringUtil.isNotNull(issuerElement.getFirstChild().getNodeValue()) || hasNamedChild(issuerElement)) { + if (!issuerElement.hasChildNodes() || !org.keycloak.saml.common.util.StringUtil.isNotNull(issuerElement.getFirstChild().getNodeValue()) || hasNamedChild(issuerElement)) { return "SpidSamlCheck_nr27"; } @@ -1030,7 +1038,7 @@ private String verifySpidResponse(Element documentElement, Element assertionElem } // 43: NameID element of the Assertion is empty (SPID check nr43) - if (!nameIdElement.hasChildNodes() || !StringUtil.isNotNull(nameIdElement.getFirstChild().getNodeValue()) || hasNamedChild(nameIdElement)) { + if (!nameIdElement.hasChildNodes() || !org.keycloak.saml.common.util.StringUtil.isNotNull(nameIdElement.getFirstChild().getNodeValue()) || hasNamedChild(nameIdElement)) { return "SpidSamlCheck_nr43"; } @@ -1168,7 +1176,7 @@ private String verifySpidResponse(Element documentElement, Element assertionElem } // 67: Issuer element of the Assertion is empty (SPID check nr67) - if (!assertionIssuerElement.hasChildNodes() || !StringUtil.isNotNull(assertionIssuerElement.getFirstChild().getNodeValue()) || hasNamedChild(assertionIssuerElement)) { + if (!assertionIssuerElement.hasChildNodes() || !org.keycloak.saml.common.util.StringUtil.isNotNull(assertionIssuerElement.getFirstChild().getNodeValue()) || hasNamedChild(assertionIssuerElement)) { return "SpidSamlCheck_nr67"; } @@ -1252,7 +1260,7 @@ private String verifySpidResponse(Element documentElement, Element assertionElem } // 92: AuthStatement AuthStatement AuthContextClassRef Element of the Assertion is empty (SPID check nr92) - if (!authnContextClassRef.hasChildNodes() || !StringUtil.isNotNull(authnContextClassRef.getFirstChild().getNodeValue()) || hasNamedChild(authnContextClassRef)) { + if (!authnContextClassRef.hasChildNodes() || !org.keycloak.saml.common.util.StringUtil.isNotNull(authnContextClassRef.getFirstChild().getNodeValue()) || hasNamedChild(authnContextClassRef)) { return "SpidSamlCheck_nr92"; } diff --git a/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java b/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java index d3fe7ee..c151b11 100644 --- a/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java +++ b/src/main/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProvider.java @@ -28,6 +28,8 @@ import org.keycloak.dom.saml.v2.metadata.EndpointType; import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType; import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType; +import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.dom.saml.v2.metadata.LocalizedNameType; import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType; import org.keycloak.models.KeyManager; @@ -42,6 +44,7 @@ import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.processing.core.saml.v2.writers.SAMLMetadataWriter; import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature; +import org.keycloak.protocol.saml.SAMLEncryptionAlgorithms; import org.keycloak.protocol.saml.SamlService; import org.keycloak.protocol.saml.mappers.SamlMetadataDescriptorUpdater; import org.keycloak.services.resource.RealmResourceProvider; @@ -58,12 +61,12 @@ import java.util.Objects; import java.util.stream.Collectors; -import javax.ws.rs.GET; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriInfo; import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.parsers.ParserConfigurationException; @@ -137,8 +140,8 @@ public Response get() { String attributeConsumingServiceName = firstSpidProvider.getConfig().getAttributeConsumingServiceName(); String[] attributeConsumingServiceNames = attributeConsumingServiceName != null ? attributeConsumingServiceName.split(","): null; - List signingKeys = new LinkedList<>(); - List encryptionKeys = new LinkedList<>(); + List signingKeys = new LinkedList<>(); + List encryptionKeys = new LinkedList<>(); session.keys().getKeysStream(realm, KeyUse.SIG, Algorithm.RS256) .filter(Objects::nonNull) @@ -147,11 +150,10 @@ public Response get() { .forEach(key -> { try { Element element = SPMetadataDescriptor - .buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); - signingKeys.add(element); - + .buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); + signingKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.SIGNING, null)); if (key.getStatus() == KeyStatus.ACTIVE) { - encryptionKeys.add(element); + encryptionKeys.add(SPMetadataDescriptor.buildKeyDescriptorType(element, KeyTypes.ENCRYPTION, null)); } } catch (ParserConfigurationException e) { logger.warn("Failed to export SAML SP Metadata!", e); @@ -159,11 +161,11 @@ public Response get() { } }); - EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPdescriptor( + EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPDescriptor( authnBinding, authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, wantAssertionsSigned, wantAssertionsEncrypted, entityId, nameIDPolicyFormat, signingKeys, encryptionKeys); - + // Create the AttributeConsumingService AttributeConsumingServiceType attributeConsumingService = new AttributeConsumingServiceType(attributeConsumingServiceIndex); attributeConsumingService.setIsDefault(true); diff --git a/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java b/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java index 42c7f31..6919ce7 100644 --- a/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java +++ b/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java @@ -54,7 +54,7 @@ import org.xmlunit.diff.Diff; import org.xmlunit.placeholder.PlaceholderDifferenceEvaluator; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.core.Response; import javax.xml.transform.Source; import java.net.URI; import java.security.InvalidKeyException; From 4858edefb2759c8e16cafe78b41a6359aa1741dc Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Thu, 28 Dec 2023 20:54:54 +0100 Subject: [PATCH 08/13] update POM to keycloak 23.0.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 933b352..a196cfc 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ false UTF-8 - 23.0.0 + 23.0.3 1.7.30 5.8.2 4.3.1 From 630137873f795f704b48b0cff652bac59015bde7 Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Thu, 28 Dec 2023 20:56:18 +0100 Subject: [PATCH 09/13] WIP - move to ConfiguredProvider --- .../spid/SpidIdentityProviderConfig.java | 163 ++++++++++++++++++ .../spid/SpidIdentityProviderFactory.java | 8 +- .../messages/messages_en.properties | 64 ++++++- .../messages/messages_it.properties | 120 +++++++++---- 4 files changed, 324 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderConfig.java b/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderConfig.java index e129853..3573f77 100644 --- a/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderConfig.java +++ b/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderConfig.java @@ -16,8 +16,12 @@ */ package org.keycloak.broker.spid; +import java.util.List; + import org.keycloak.broker.saml.SAMLIdentityProviderConfig; import org.keycloak.models.IdentityProviderModel; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderConfigurationBuilder; public class SpidIdentityProviderConfig extends SAMLIdentityProviderConfig { @@ -226,5 +230,164 @@ public boolean isDebugEnabled() { public void setDebugEnabled(boolean isDebugEnabled) { getConfig().put(SPID_RESPONSE_DEBUG_ENABLED, String.valueOf(isDebugEnabled)); } + + public static List getConfigProperties() { + return ProviderConfigurationBuilder.create() + .property() + .name(ORGANIZATION_NAMES) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.organization-names") + .helpText("identity-provider.spid.organization-names.tooltip") + .add() + + .property() + .name(IDP_ENTITY_ID) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.saml.idp-entity-id") + .helpText("identity-provider.saml.idp-entity-id.tooltip") + .add() + + .property() + .name(ORGANIZATION_DISPLAY_NAMES) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.organization-display-names") + .helpText("identity-provider.spid.organization-display-names.tooltip") + .add() + + .property() + .name(ORGANIZATION_URLS) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.organization-urls") + .helpText("identity-provider.spid.organization-urls.tooltip") + .add() + + .property() + .name(OTHER_CONTACT_SP_PRIVATE) + .type(ProviderConfigProperty.BOOLEAN_TYPE) + .label("identity-provider.spid.is-sp-private") + .helpText("identity-provider.spid.is-sp-private.tooltip") + .add() + + .property() + .name(OTHER_CONTACT_IPA_CODE) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.ipaCode") + .helpText("identity-provider.spid.ipaCode.tooltip") + .add() + + .property() + .name(OTHER_CONTACT_VAT_NUMBER) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.vatNumber") + .helpText("identity-provider.spid.vatNumber.tooltip") + .add() + + .property() + .name(OTHER_CONTACT_FISCAL_CODE) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.fiscalCode") + .helpText("identity-provider.spid.fiscalCode.tooltip") + .add() + + .property() + .name(OTHER_CONTACT_COMPANY) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.contactCompany.other") + .helpText("identity-provider.spid.contactCompany.other.tooltip") + .add() + + .property() + .name(OTHER_CONTACT_PHONE) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.contactPhone.other") + .helpText("identity-provider.spid.contactPhone.other.tooltip") + .add() + + .property() + .name(OTHER_CONTACT_EMAIL) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.contactEmail.other") + .helpText("identity-provider.spid.contactEmail.other.tooltip") + .add() + + .property() + .name(BILLING_CONTACT_COMPANY) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.contactCompany.billing") + .helpText("identity-provider.spid.contactCompany.billing.tooltip") + .add() + + .property() + .name(BILLING_CONTACT_PHONE) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.contactPhone.billing") + .helpText("identity-provider.spid.contactPhone.billing.tooltip") + .add() + + .property() + .name(BILLING_CONTACT_EMAIL) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.contactEmail.billing") + .helpText("identity-provider.spid.contactEmail.billing.tooltip") + .add() + + .property() + .name(BILLING_CONTACT_REGISTRY_NAME) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.RegistryName.billing") + .helpText("identity-provider.spid.RegistryName.billing.tooltip") + .add() + + .property() + .name(BILLING_CONTACT_SITE_ADDRESS) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.site.address.billing") + .helpText("identity-provider.spid.site.address.billing.tooltip") + .add() + + .property() + .name(BILLING_CONTACT_SITE_NUMBER) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.site.number.billing") + .helpText("identity-provider.spid.site.number.billing.tooltip") + .add() + + .property() + .name(BILLING_CONTACT_SITE_CITY) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.site.city.billing") + .helpText("identity-provider.spid.site.city.billing.tooltip") + .add() + + .property() + .name(BILLING_CONTACT_SITE_ZIP_CODE) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.site.zipCode.billing") + .helpText("identity-provider.spid.site.zipCode.billing.tooltip") + .add() + + .property() + .name(BILLING_CONTACT_SITE_PROVINCE) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.site.province.billing") + .helpText("identity-provider.spid.site.province.billing.tooltip") + .add() + + .property() + .name(BILLING_CONTACT_SITE_COUNTRY) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.site.countryCode.billing") + .helpText("identity-provider.spid.site.countryCode.billing.tooltip") + .add() + + .property() + .name(SPID_RESPONSE_DEBUG_ENABLED) + .type(ProviderConfigProperty.BOOLEAN_TYPE) + .label("identity-provider.spid.debug-enabled") + .helpText("identity-provider.spid.debug-enabled.tooltip") + .add() + + .build(); + } } diff --git a/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderFactory.java b/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderFactory.java index ba01527..05f3501 100644 --- a/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderFactory.java +++ b/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderFactory.java @@ -35,6 +35,8 @@ import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; +import org.keycloak.provider.ConfiguredProvider; +import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.util.DocumentUtil; @@ -45,7 +47,7 @@ /** * @author Pedro Igor */ -public class SpidIdentityProviderFactory extends AbstractIdentityProviderFactory { +public class SpidIdentityProviderFactory extends AbstractIdentityProviderFactory implements ConfiguredProvider { public static final String PROVIDER_ID = "spid"; @@ -200,4 +202,8 @@ public void init(Scope config) { this.destinationValidator = DestinationValidator.forProtocolMap(config.getArray("knownProtocols")); } + + public List getConfigProperties() { + return SpidIdentityProviderConfig.getConfigProperties(); + } } diff --git a/src/main/resources/theme-resources/messages/messages_en.properties b/src/main/resources/theme-resources/messages/messages_en.properties index e4944f6..4a09d1c 100644 --- a/src/main/resources/theme-resources/messages/messages_en.properties +++ b/src/main/resources/theme-resources/messages/messages_en.properties @@ -118,4 +118,66 @@ SpidSamlCheck_nr97=AuthContextClassRef element set to an unexpected value (SPID SpidSamlCheck_nr98=AttributeStatement element present, but Attribute sub-element missing (SPID check nr98) SpidSamlCheck_nr99=AttributeStatement element present, but Attribute sub-element is empty (SPID check nr99) SpidSamlCheck_nr100=Assertion signed with different certificate (SPID check nr100) -SpidSamlCheck_nr103=Attribute set sent different from the one requested (SPID check nr103) \ No newline at end of file +SpidSamlCheck_nr103=Attribute set sent different from the one requested (SPID check nr103) + +############################ +# SAML Config translations # +############################ +# Different translation for saml keys +identity-provider.saml.attribute-consuming-service-name=Attribute Consuming Service Names +identity-provider.saml.attribute-consuming-service-name.tooltip=Comma separated list of localized service names. Each string should be entered in the format "|", i.e. "en|Online services,it|Servizi online" +identity-provider.saml.idp-entity-id=Identity Provider Entity ID +identity-provider.saml.idp-entity-id.tooltip=The Entity ID used to validate the Issuer for received SAML assertions. If empty, no Issuer validation is performed. +identity-provider.spid.protocol-endpoints.saml=SPID Service Provider Metadata +identity-provider.spid.protocol-endpoints.saml.tooltip=Access the SPID Service Provider metadata document aggregating the information for all SPID Service Providers registered in this realm +############################ +# SPID Config translations # +############################ +identity-provider.spid.spid-config=SPID Config +identity-provider.spid.spid-config.tooltip=SPID SAML SP configuration. +# Organization +identity-provider.spid.organization-names=Organization Names +identity-provider.spid.organization-names.tooltip=Comma separated list of localized organization names. Each string should be entered in the format "|", i.e. "en|My Organization,it|Mia Organizzazione" +identity-provider.spid.organization-display-names=Organization Display Names +identity-provider.spid.organization-display-names.tooltip=Comma separated list of localized organization display names. Each string should be entered in the format "|", i.e. "en|My Organization,it|Mia Organizzazione" +identity-provider.spid.organization-urls=Organization URLs +identity-provider.spid.organization-urls.tooltip=Comma separated list of localized organization URLs. Each string should be entered in the format "|", i.e. "en|https://www.mycompany.local/en/,it|https://www.mycompany.local/it/" +# ContactPerson Other +identity-provider.spid.contactCompany.other=Company Name (Other) +identity-provider.spid.contactCompany.other.tooltip=Company Name (Other) +identity-provider.spid.contactPhone.other=Phone (Other) +identity-provider.spid.contactPhone.other.tooltip=Phone (Other) +identity-provider.spid.contactEmail.other=Email (Other) +identity-provider.spid.contactEmail.other.tooltip=Email (Other) +identity-provider.spid.is-sp-private=Private SP +identity-provider.spid.is-sp-private.tooltip=Whether this SP belongs to a Public Administration or a private subject +identity-provider.spid.ipaCode=IPA Code (Other) +identity-provider.spid.ipaCode.tooltip=IPA Code of the Public Administration +identity-provider.spid.vatNumber=VAT Number (Other) +identity-provider.spid.vatNumber.tooltip=VAT Number of the private subject +identity-provider.spid.fiscalCode=Fiscal Code (Other) +identity-provider.spid.fiscalCode.tooltip=Fiscal Code of the private subject +# ContactPerson Billing +identity-provider.spid.contactCompany.billing=Company Name (Billing) +identity-provider.spid.contactCompany.billing.tooltip=Company Name (Billing) +identity-provider.spid.contactPhone.billing=Phone (Billing) +identity-provider.spid.contactPhone.billing.tooltip=Phone (Billing) +identity-provider.spid.contactEmail.billing=Email (Billing) +identity-provider.spid.contactEmail.billing.tooltip=Email (Billing) +identity-provider.spid.RegistryName.billing=Registry Name (Billing) +identity-provider.spid.RegistryName.billing.tooltip=Registry Name (Billing) +identity-provider.spid.site.address.billing=Headquarter Address (Billing) +identity-provider.spid.site.address.billing.tooltip=Headquarter Address (Billing) +identity-provider.spid.site.number.billing=Headquarter Address Number (Billing) +identity-provider.spid.site.number.billing.tooltip=Headquarter Address Number (Billing) +identity-provider.spid.site.city.billing=Headquarter City (Billing) +identity-provider.spid.site.city.billing.tooltip=Headquarter City (Billing) +identity-provider.spid.site.zipCode.billing=Headquarter ZIP Code (Billing) +identity-provider.spid.site.zipCode.billing.tooltip=Headquarter ZIP Code (Billing) +identity-provider.spid.site.province.billing=Headquarter Province (Billing) +identity-provider.spid.site.province.billing.tooltip=Headquarter Province (Billing) +identity-provider.spid.site.countryCode.billing=Headquarter Country Code (Billing) +identity-provider.spid.site.countryCode.billing.tooltip=Headquarter Country Code ISO alpha-2 (Billing). Default value is 'it' +# Debug +identity-provider.spid.debug-enabled=Show detailed SPID response errors +identity-provider.spid.debug-enabled.tooltip=If enabled, shows the actual SPID check error code instead of a generic error message \ No newline at end of file diff --git a/src/main/resources/theme-resources/messages/messages_it.properties b/src/main/resources/theme-resources/messages/messages_it.properties index f5bd4fd..965e266 100644 --- a/src/main/resources/theme-resources/messages/messages_it.properties +++ b/src/main/resources/theme-resources/messages/messages_it.properties @@ -1,4 +1,4 @@ -SpidFault_ErrorCode_nr2=Indisponibilit\u00E0  di sistema del gestore SPID. Riprovare pi\u00F9 tardi o utilizzare un altro metodo di autenticazione (SPID ErrorCode nr2) +SpidFault_ErrorCode_nr2=Indisponibilit\u00E0� di sistema del gestore SPID. Riprovare pi\u00F9 tardi o utilizzare un altro metodo di autenticazione (SPID ErrorCode nr2) SpidFault_ErrorCode_nr3=Errore di sistema del gestore SPID. Riprovare pi\u00F9 tardi o utilizzare un altro metodo di autenticazione (SPID ErrorCode nr3) SpidFault_ErrorCode_nr4=Formato binding non corretto. Contattare il gestore del servizio (SPID ErrorCode nr4) SpidFault_ErrorCode_nr5=Verifica della firma fallita. Contattare il gestore del servizio (SPID ErrorCode nr5) @@ -6,11 +6,11 @@ SpidFault_ErrorCode_nr6=Binding su metodo HTTP errato. Contattare il gestore del SpidFault_ErrorCode_nr7=Errore sulla verifica della firma della richiesta. Contattare il gestore del servizio (SPID ErrorCode nr7) SpidFault_ErrorCode_nr8=Formato della richiesta non conforme alle specifiche SAML. Contattare il gestore del servizio (SPID ErrorCode nr8) SpidFault_ErrorCode_nr9=Parametro version non presente, malformato o diverso da ''2.0''. Contattare il gestore del servizio (SPID ErrorCode nr9) -SpidFault_ErrorCode_nr10=Issuer non presente, malformato o non corrispondente all''entit\u00E0  che sottoscrive la richiesta. Contattare il gestore del servizio (SPID ErrorCode nr10) +SpidFault_ErrorCode_nr10=Issuer non presente, malformato o non corrispondente all''entit\u00E0� che sottoscrive la richiesta. Contattare il gestore del servizio (SPID ErrorCode nr10) SpidFault_ErrorCode_nr11=ID (Identificatore richiesta) non presente, malformato o non conforme. Contattare il gestore del servizio (SPID ErrorCode nr11) SpidFault_ErrorCode_nr12=RequestAuthnContext non presente, malformato o non previsto da SPID. Contattare il gestore del servizio (SPID ErrorCode nr12) SpidFault_ErrorCode_nr13=IssueInstant non presente, malformato o non coerente con l''orario di arrivo della richiesta. Contattare il gestore del servizio (SPID ErrorCode nr13) -SpidFault_ErrorCode_nr14=Destination non presente, malformata o non coincidente con il Gestore delle identit\u00E0  ricevente la richiesta. Contattare il gestore del servizio (SPID ErrorCode nr14) +SpidFault_ErrorCode_nr14=Destination non presente, malformata o non coincidente con il Gestore delle identit\u00E0� ricevente la richiesta. Contattare il gestore del servizio (SPID ErrorCode nr14) SpidFault_ErrorCode_nr15=Attributo isPassive presente e attualizzato al valore true. Contattare il gestore del servizio (SPID ErrorCode nr15) SpidFault_ErrorCode_nr16=AssertionConsumerService non correttamente valorizzato. Contattare il gestore del servizio (SPID ErrorCode nr16) SpidFault_ErrorCode_nr17=Attributo Format dell''elemento NameIDPolicy assente o non valorizzato secondo specifica. Contattare il gestore del servizio (SPID ErrorCode nr17) @@ -19,7 +19,7 @@ SpidFault_ErrorCode_nr19=Autenticazione fallita per ripetuta sottomissione di cr SpidFault_ErrorCode_nr20=Utente privo di credenziali compatibili con il livello richiesto dal fornitore del servizio (SPID ErrorCode nr20) SpidFault_ErrorCode_nr21=Timeout durante l''autenticazione utente (SPID ErrorCode nr21) SpidFault_ErrorCode_nr22=Utente nega il consenso all''invio di dati al SP in caso di sessione vigente (SPID ErrorCode nr22) -SpidFault_ErrorCode_nr23=Utente con identit\u00E0  sospesa/revocata o con credenziali bloccate (SPID ErrorCode nr23) +SpidFault_ErrorCode_nr23=Utente con identit\u00E0� sospesa/revocata o con credenziali bloccate (SPID ErrorCode nr23) SpidFault_ErrorCode_nr25=Processo di autenticazione annullato dall''utente (SPID ErrorCode nr25) SpidSamlCheck_GenericError=Formato richiesta non corretto - Contattare il gestore del servizio @@ -66,13 +66,13 @@ SpidSamlCheck_nr44=Elemento NameID dell''Assertion mancante (SPID check nr44) SpidSamlCheck_nr45=Attributo Format dell''elemento NameID dell''Assertion non specificato (SPID check nr45) SpidSamlCheck_nr46=Attributo Format dell''elemento NameID dell''Assertion mancante (SPID check nr46) SpidSamlCheck_nr47=Attributo Format di NameID dell''Assertion diverso da urn:oasis:names:tc:SAML:2.0:nameidformat:transient (SPID check nr47) -SpidSamlCheck_nr48=Attributo NameQualifier di NameID dell''Assertion non specificato (SPID check nr48) -SpidSamlCheck_nr49=Attributo NameQualifier di NameID dell''Assertion mancante (SPID check nr49) +SpidSamlCheck_nr48=Attributo NameQualifier�di NameID dell''Assertion non specificato (SPID check nr48) +SpidSamlCheck_nr49=Attributo NameQualifier�di NameID dell''Assertion mancante (SPID check nr49) SpidSamlCheck_nr51=Elemento SubjectConfirmation dell''Assertion non specificato (SPID check nr51) SpidSamlCheck_nr52=Elemento SubjectConfirmation dell''Assertion mancante(SPID check nr52) -SpidSamlCheck_nr53=Attributo Method di SubjectConfirmation dell''Assertion non specificato (SPID check nr53) -SpidSamlCheck_nr54=Attributo Method di SubjectConfirmation dell''Assertion mancante (SPID check nr54) -SpidSamlCheck_nr55=Attributo Method di SubjectConfirmation dell''Assertion diverso da urn:oasis:names:tc:SAML:2.0:cm:bearer (SPID check nr55) +SpidSamlCheck_nr53=Attributo Method�di SubjectConfirmation dell''Assertion non specificato (SPID check nr53) +SpidSamlCheck_nr54=Attributo Method�di SubjectConfirmation dell''Assertion mancante (SPID check nr54) +SpidSamlCheck_nr55=Attributo Method�di SubjectConfirmation dell''Assertion diverso da urn:oasis:names:tc:SAML:2.0:cm:bearer (SPID check nr55) SpidSamlCheck_nr56=Elemento SubjectConfirmationData dell''Assertion mancante (SPID check nr56) SpidSamlCheck_nr57=Attributo Recipient di SubjectConfirmationData dell''Assertion non specificato (SPID check nr57) SpidSamlCheck_nr58=Attributo Recipient di SubjectConfirmationData dell''Assertion mancante (SPID check nr58) @@ -87,35 +87,97 @@ SpidSamlCheck_nr66=Attributo NotOnOrAfter di SubjectConfirmationData precedente SpidSamlCheck_nr67=Elemento Issuer dell''Assertion non specificato (SPID check nr67) SpidSamlCheck_nr68=Elemento Issuer dell''Assertion mancante (SPID check nr68) SpidSamlCheck_nr69=Elemento Issuer dell''Assertion diverso da EntityID IdP (SPID check nr69) -SpidSamlCheck_nr70=Attributo Format di Issuer dell''Assertion non specificato (SPID check nr70) -SpidSamlCheck_nr71=Attributo Format di Issuer dell''Assertion mancante (SPID check nr71) -SpidSamlCheck_nr72=Attributo Format di Issuer dell''Assertion deve essere presente con il valore urn:oasis:names:tc:SAML:2.0:nameid-format:entity (SPID check nr72) +SpidSamlCheck_nr70=Attributo Format�di Issuer dell''Assertion non specificato (SPID check nr70) +SpidSamlCheck_nr71=Attributo Format�di Issuer dell''Assertion mancante (SPID check nr71) +SpidSamlCheck_nr72=Attributo Format�di Issuer dell''Assertion deve essere presente con il valore urn:oasis:names:tc:SAML:2.0:nameid-format:entity (SPID check nr72) SpidSamlCheck_nr73=Elemento Conditions dell''Assertion non specificato (SPID check nr73) SpidSamlCheck_nr74=Elemento Conditions dell''Assertion mancante (SPID check nr74) -SpidSamlCheck_nr75=Attributo NotBefore di Condition dell''Assertion non specificato (SPID check nr75) -SpidSamlCheck_nr76=Attributo NotBefore di Condition dell''Assertion mancante (SPID check nr76) -SpidSamlCheck_nr77=Attributo NotBefore di Condition dell''Assertion avente formato non corretto (SPID check nr77) -SpidSamlCheck_nr78=Attributo NotBefore di Condition dell''Assertion successivo all''instante di ricezione della response (SPID check nr78) -SpidSamlCheck_nr79=Attributo NotOnOrAfter di Condition dell''Assertion non specificato (SPID check nr79) -SpidSamlCheck_nr80=Attributo NotOnOrAfter di Condition dell''Assertion mancante (SPID check nr80) -SpidSamlCheck_nr81=Attributo NotOnOrAfter di Condition dell''Assertion avente formato non corretto (SPID check nr81) -SpidSamlCheck_nr82=Attributo NotOnOrAfter di Condition dell''Assertion precedente all''istante di ricezione della response (SPID check nr82) +SpidSamlCheck_nr75=Attributo NotBefore�di Condition dell''Assertion non specificato (SPID check nr75) +SpidSamlCheck_nr76=Attributo NotBefore�di Condition dell''Assertion mancante (SPID check nr76) +SpidSamlCheck_nr77=Attributo NotBefore�di Condition dell''Assertion avente formato non corretto (SPID check nr77) +SpidSamlCheck_nr78=Attributo NotBefore�di Condition dell''Assertion successivo all''instante di ricezione della response (SPID check nr78) +SpidSamlCheck_nr79=Attributo NotOnOrAfter�di Condition dell''Assertion non specificato (SPID check nr79) +SpidSamlCheck_nr80=Attributo NotOnOrAfter�di Condition dell''Assertion mancante (SPID check nr80) +SpidSamlCheck_nr81=Attributo NotOnOrAfter�di Condition dell''Assertion avente formato non corretto (SPID check nr81) +SpidSamlCheck_nr82=Attributo NotOnOrAfter�di Condition dell''Assertion precedente all''istante di ricezione della response (SPID check nr82) SpidSamlCheck_nr83=Elemento AudienceRestriction di Condition dell''Assertion non specificato (SPID check nr83) SpidSamlCheck_nr84=Elemento AudienceRestriction di Condition dell''Assertion mancante (SPID check nr84) -SpidSamlCheck_nr85=Elemento Audience di AudienceRestriction di Condition dell''Assertion non specificato (SPID check nr85) -SpidSamlCheck_nr86=Elemento Audience di AudienceRestriction di Condition dell''Assertion mancante (SPID check nr86) -SpidSamlCheck_nr87=Elemento Audience di AudienceRestriction di Condition dell''Assertion diverso da Entity Id del Service Provider (SPID check nr87) +SpidSamlCheck_nr85=Elemento Audience di AudienceRestriction�di Condition dell''Assertion non specificato (SPID check nr85) +SpidSamlCheck_nr86=Elemento Audience di AudienceRestriction�di Condition dell''Assertion mancante (SPID check nr86) +SpidSamlCheck_nr87=Elemento Audience di AudienceRestriction�di Condition dell''Assertion diverso da Entity Id del Service Provider (SPID check nr87) SpidSamlCheck_nr88=Elemento AuthStatement dell''Assertion non specificato (SPID check nr88) -SpidSamlCheck_nr89=Elemento AuthStatement dell''Assertion mancante (SPID check nr89) +SpidSamlCheck_nr89=Elemento�AuthStatement dell''Assertion mancante (SPID check nr89) SpidSamlCheck_nr90=Elemento AuthnContext di AuthStatement dell''Assertion non specificato (SPID check nr90) -SpidSamlCheck_nr91=Elemento AuthnContext di AuthStatement dell''Assertion mancante (SPID check nr91) +SpidSamlCheck_nr91=Elemento�AuthnContext di AuthStatement dell''Assertion mancante (SPID check nr91) SpidSamlCheck_nr92=Elemento AuthContextClassRef di AuthnContext di AuthStatement dell''Assertion non specificato (SPID check nr92) -SpidSamlCheck_nr93=Elemento AuthContextClassRef di AuthnContext di AuthStatement dell''Assertion mancante (SPID check nr93) +SpidSamlCheck_nr93=Elemento�AuthContextClassRef di AuthnContext di AuthStatement dell''Assertion mancante (SPID check nr93) SpidSamlCheck_nr94=Elemento AuthContextClassRef impostato su https://www.spid.gov.it/SpidL1 (SPID check nr94) SpidSamlCheck_nr95=Elemento AuthContextClassRef impostato su https://www.spid.gov.it/SpidL2 (SPID check nr95) SpidSamlCheck_nr96=Elemento AuthContextClassRef impostato su https://www.spid.gov.it/SpidL3 (SPID check nr96) SpidSamlCheck_nr97=Elemento AuthContextClassRef impostato ad un valore non previsto (SPID check nr97) -SpidSamlCheck_nr98=Elemento AttributeStatement presente, ma sottoelemento Attribute mancante (SPID check nr98) -SpidSamlCheck_nr99=Elemento AttributeStatement presente, ma sottoelemento Attribute non specificato (SPID check nr99) +SpidSamlCheck_nr98=Elemento AttributeStatement presente, ma sottoelemento�Attribute mancante (SPID check nr98) +SpidSamlCheck_nr99=Elemento AttributeStatement presente, ma sottoelemento�Attribute non specificato (SPID check nr99) SpidSamlCheck_nr100=Assertion firmata con certificato diverso (SPID check nr100) -SpidSamlCheck_nr103=Set di attributi inviato diverso da quello richiesto (SPID check nr103) \ No newline at end of file +SpidSamlCheck_nr103=Set di attributi inviato diverso da quello richiesto (SPID check nr103) + +############################ +# SAML Config translations # +############################ +# Different translation for saml keys +identity-provider.saml.attribute-consuming-service-name=Attribute Consuming Service Names +identity-provider.saml.attribute-consuming-service-name.tooltip=Comma separated list of localized service names. Each string should be entered in the format "|", i.e. "en|Online services,it|Servizi online" +identity-provider.saml.idp-entity-id=Identity Provider Entity ID +identity-provider.saml.idp-entity-id.tooltip=The Entity ID used to validate the Issuer for received SAML assertions. If empty, no Issuer validation is performed. +identity-provider.spid.protocol-endpoints.saml=SPID Service Provider Metadata +identity-provider.spid.protocol-endpoints.saml.tooltip=Access the SPID Service Provider metadata document aggregating the information for all SPID Service Providers registered in this realm +############################ +# SPID Config translations # +############################ +identity-provider.spid.spid-config=SPID Config +identity-provider.spid.spid-config.tooltip=SPID SAML SP configuration. +# Organization +identity-provider.spid.organization-names=Organization Names +identity-provider.spid.organization-names.tooltip=Comma separated list of localized organization names. Each string should be entered in the format "|", i.e. "en|My Organization,it|Mia Organizzazione" +identity-provider.spid.organization-display-names=Organization Display Names +identity-provider.spid.organization-display-names.tooltip=Comma separated list of localized organization display names. Each string should be entered in the format "|", i.e. "en|My Organization,it|Mia Organizzazione" +identity-provider.spid.organization-urls=Organization URLs +identity-provider.spid.organization-urls.tooltip=Comma separated list of localized organization URLs. Each string should be entered in the format "|", i.e. "en|https://www.mycompany.local/en/,it|https://www.mycompany.local/it/" +# ContactPerson Other +identity-provider.spid.contactCompany.other=Company Name (Other) +identity-provider.spid.contactCompany.other.tooltip=Company Name (Other) +identity-provider.spid.contactPhone.other=Phone (Other) +identity-provider.spid.contactPhone.other.tooltip=Phone (Other) +identity-provider.spid.contactEmail.other=Email (Other) +identity-provider.spid.contactEmail.other.tooltip=Email (Other) +identity-provider.spid.is-sp-private=Private SP +identity-provider.spid.is-sp-private.tooltip=Whether this SP belongs to a Public Administration or a private subject +identity-provider.spid.ipaCode=IPA Code (Other) +identity-provider.spid.ipaCode.tooltip=IPA Code of the Public Administration +identity-provider.spid.vatNumber=VAT Number (Other) +identity-provider.spid.vatNumber.tooltip=VAT Number of the private subject +identity-provider.spid.fiscalCode=Fiscal Code (Other) +identity-provider.spid.fiscalCode.tooltip=Fiscal Code of the private subject +# ContactPerson Billing +identity-provider.spid.contactCompany.billing=Company Name (Billing) +identity-provider.spid.contactCompany.billing.tooltip=Company Name (Billing) +identity-provider.spid.contactPhone.billing=Phone (Billing) +identity-provider.spid.contactPhone.billing.tooltip=Phone (Billing) +identity-provider.spid.contactEmail.billing=Email (Billing) +identity-provider.spid.contactEmail.billing.tooltip=Email (Billing) +identity-provider.spid.RegistryName.billing=Registry Name (Billing) +identity-provider.spid.RegistryName.billing.tooltip=Registry Name (Billing) +identity-provider.spid.site.address.billing=Headquarter Address (Billing) +identity-provider.spid.site.address.billing.tooltip=Headquarter Address (Billing) +identity-provider.spid.site.number.billing=Headquarter Address Number (Billing) +identity-provider.spid.site.number.billing.tooltip=Headquarter Address Number (Billing) +identity-provider.spid.site.city.billing=Headquarter City (Billing) +identity-provider.spid.site.city.billing.tooltip=Headquarter City (Billing) +identity-provider.spid.site.zipCode.billing=Headquarter ZIP Code (Billing) +identity-provider.spid.site.zipCode.billing.tooltip=Headquarter ZIP Code (Billing) +identity-provider.spid.site.province.billing=Headquarter Province (Billing) +identity-provider.spid.site.province.billing.tooltip=Headquarter Province (Billing) +identity-provider.spid.site.countryCode.billing=Headquarter Country Code (Billing) +identity-provider.spid.site.countryCode.billing.tooltip=Headquarter Country Code ISO alpha-2 (Billing). Default value is 'it' +# Debug +identity-provider.spid.debug-enabled=Show detailed SPID response errors +identity-provider.spid.debug-enabled.tooltip=If enabled, shows the actual SPID check error code instead of a generic error message \ No newline at end of file From 34e2fdb3edf760b3153de2e60478b63236ec95ee Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Thu, 28 Dec 2023 21:03:04 +0100 Subject: [PATCH 10/13] remove unused properties --- .../messages/admin-messages_en.properties | 61 ------------------- 1 file changed, 61 deletions(-) delete mode 100644 src/main/resources/theme-resources/messages/admin-messages_en.properties diff --git a/src/main/resources/theme-resources/messages/admin-messages_en.properties b/src/main/resources/theme-resources/messages/admin-messages_en.properties deleted file mode 100644 index daba58e..0000000 --- a/src/main/resources/theme-resources/messages/admin-messages_en.properties +++ /dev/null @@ -1,61 +0,0 @@ -############################ -# SAML Config translations # -############################ -# Different translation for saml keys -identity-provider.saml.attribute-consuming-service-name=Attribute Consuming Service Names -identity-provider.saml.attribute-consuming-service-name.tooltip=Comma separated list of localized service names. Each string should be entered in the format "|", i.e. "en|Online services,it|Servizi online" -identity-provider.saml.idp-entity-id=Identity Provider Entity ID -identity-provider.saml.idp-entity-id.tooltip=The Entity ID used to validate the Issuer for received SAML assertions. If empty, no Issuer validation is performed. -identity-provider.spid.protocol-endpoints.saml=SPID Service Provider Metadata -identity-provider.spid.protocol-endpoints.saml.tooltip=Access the SPID Service Provider metadata document aggregating the information for all SPID Service Providers registered in this realm -############################ -# SPID Config translations # -############################ -identity-provider.spid.spid-config=SPID Config -identity-provider.spid.spid-config.tooltip=SPID SAML SP configuration. -# Organization -identity-provider.spid.organization-names=Organization Names -identity-provider.spid.organization-names.tooltip=Comma separated list of localized organization names. Each string should be entered in the format "|", i.e. "en|My Organization,it|Mia Organizzazione" -identity-provider.spid.organization-display-names=Organization Display Names -identity-provider.spid.organization-display-names.tooltip=Comma separated list of localized organization display names. Each string should be entered in the format "|", i.e. "en|My Organization,it|Mia Organizzazione" -identity-provider.spid.organization-urls=Organization URLs -identity-provider.spid.organization-urls.tooltip=Comma separated list of localized organization URLs. Each string should be entered in the format "|", i.e. "en|https://www.mycompany.local/en/,it|https://www.mycompany.local/it/" -# ContactPerson Other -identity-provider.spid.contactCompany.other=Company Name (Other) -identity-provider.spid.contactCompany.other.tooltip=Company Name (Other) -identity-provider.spid.contactPhone.other=Phone (Other) -identity-provider.spid.contactPhone.other.tooltip=Phone (Other) -identity-provider.spid.contactEmail.other=Email (Other) -identity-provider.spid.contactEmail.other.tooltip=Email (Other) -identity-provider.spid.is-sp-private=Private SP -identity-provider.spid.is-sp-private.tooltip=Whether this SP belongs to a Public Administration or a private subject -identity-provider.spid.ipaCode=IPA Code (Other) -identity-provider.spid.ipaCode.tooltip=IPA Code of the Public Administration -identity-provider.spid.vatNumber=VAT Number (Other) -identity-provider.spid.vatNumber.tooltip=VAT Number of the private subject -identity-provider.spid.fiscalCode=Fiscal Code (Other) -identity-provider.spid.fiscalCode.tooltip=Fiscal Code of the private subject -# ContactPerson Billing -identity-provider.spid.contactCompany.billing=Company Name (Billing) -identity-provider.spid.contactCompany.billing.tooltip=Company Name (Billing) -identity-provider.spid.contactPhone.billing=Phone (Billing) -identity-provider.spid.contactPhone.billing.tooltip=Phone (Billing) -identity-provider.spid.contactEmail.billing=Email (Billing) -identity-provider.spid.contactEmail.billing.tooltip=Email (Billing) -identity-provider.spid.RegistryName.billing=Registry Name (Billing) -identity-provider.spid.RegistryName.billing.tooltip=Registry Name (Billing) -identity-provider.spid.site.address.billing=Headquarter Address (Billing) -identity-provider.spid.site.address.billing.tooltip=Headquarter Address (Billing) -identity-provider.spid.site.number.billing=Headquarter Address Number (Billing) -identity-provider.spid.site.number.billing.tooltip=Headquarter Address Number (Billing) -identity-provider.spid.site.city.billing=Headquarter City (Billing) -identity-provider.spid.site.city.billing.tooltip=Headquarter City (Billing) -identity-provider.spid.site.zipCode.billing=Headquarter ZIP Code (Billing) -identity-provider.spid.site.zipCode.billing.tooltip=Headquarter ZIP Code (Billing) -identity-provider.spid.site.province.billing=Headquarter Province (Billing) -identity-provider.spid.site.province.billing.tooltip=Headquarter Province (Billing) -identity-provider.spid.site.countryCode.billing=Headquarter Country Code (Billing) -identity-provider.spid.site.countryCode.billing.tooltip=Headquarter Country Code ISO alpha-2 (Billing). Default value is 'it' -# Debug -identity-provider.spid.debug-enabled=Show detailed SPID response errors -identity-provider.spid.debug-enabled.tooltip=If enabled, shows the actual SPID check error code instead of a generic error message From 80db3076254b8340f9be032d6f5f17406e3835a1 Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Wed, 7 Feb 2024 22:03:19 +0100 Subject: [PATCH 11/13] Update README using docker to build package (#4) Co-authored-by: Valentino Lauciani --- README.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8da2c51..77df533 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,30 @@ certainly have also to update this provider. Detailed instructions on how to install and configure this component are available in the project wiki (https://github.com/italia/spid-keycloak-provider/wiki/Installing-the-SPID-provider). -## Build requirements +## Build (without docker) +Requirements: * git * JDK8+ * Maven -## Build -Just run `mvn clean package` for a full rebuild. The output package will -be generated under `target/spid-provider.jar`. +Just run: +``` +git clone https://github.com/italia/spid-keycloak-provider.git +cd spid-keycloak-provider +mvn clean package +``` +The output package will be generated under `target/spid-provider.jar`. + +## Build (with docker) +Requirements: +* Docker + +Just run: +``` +git clone https://github.com/italia/spid-keycloak-provider.git +docker run --rm -v $(pwd)/spid-keycloak-provider:/opt/spid-keycloak-provider -w /opt/spid-keycloak-provider maven:3.8.6-openjdk-18-slim bash -c "mvn clean package" +``` +The output package will be generated under `spid-keycloak-provider/target/spid-provider.jar`. ## Deployment This provider should be deployed as a module, i.e. copied under From ae076999cb5365073a2318561a9797c9f3f9f419 Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Wed, 7 Feb 2024 22:04:59 +0100 Subject: [PATCH 12/13] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 77df533..20dfd65 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ As far as I know it has not been used in Production in any environment yet. Until the project gets to a stable release, it will be targeting the most recent release of Keycloak as published on the website (see property `version.keycloak` in file `pom.xml`). -Currently the main branch is targeting Keycloak 20.0.0. **Do not use the latest release with previous +Currently the main branch is targeting Keycloak 23.0.6. **Do not use the latest release with previous versions of Keycloak, it won't work!** Since this plugin uses some Keycloak internal modules, versions of this plugin @@ -44,7 +44,7 @@ available in the project wiki (https://github.com/italia/spid-keycloak-provider/ ## Build (without docker) Requirements: * git -* JDK8+ +* JDK17+ * Maven Just run: From 3b2a4fcd6ec09f27ed0cc253a82fd452fe948380 Mon Sep 17 00:00:00 2001 From: Nicola Beghin Date: Sun, 11 Feb 2024 13:24:25 +0100 Subject: [PATCH 13/13] 23.0.6 update libs * Compatibility with Keycloak 23.0.6 * upgrade for Keycloak 23 * update github action to use java 17 * reduce build time through cache maven dependencies in docker layersenhance * update spid endpoint and provider to latest constructor from upstream * update POM to keycloak 23.0.3 * WIP - move to ConfiguredProvider * remove unused properties * update libs to 23.0.6 * align branch to main * Update libs to Keycloak 23.0.6 * Update README.md --- README.md | 23 ++++++++++++++++------- pom.xml | 5 ++--- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 20dfd65..c97e51c 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,8 @@ sure to read it and understand the config steps and the open issues and limitations before planning your Production environment. ## Status -This project is still at an alpha stage. It is currently under development -and things may change quickly. It builds and successfully allows login/logout -to the SPID Validator test IdP (https://github.com/italia/spid-saml-check) -and to the online SPID tester (https://www.spid-validator.it). -As far as I know it has not been used in Production in any environment yet. +This project is still at a beta stage. It has been successfully tested for SPID validation and +**it's currently used in Production**. Until the project gets to a stable release, it will be targeting the most recent release of Keycloak as published on the website (see property `version.keycloak` in file `pom.xml`). @@ -38,8 +35,20 @@ Since this plugin uses some Keycloak internal modules, versions of this plugin are coupled to Keycloak versions. After (major) Keycloak upgrades, you will almost certainly have also to update this provider. -Detailed instructions on how to install and configure this component are +## Compatibility +* Keycloak 23.x.x: Release 1.0.17 +* Keycloak 19.x.x: Release 1.0.16 + +## Configuration +### Release 1.0.17 (latest, Keycloak 23.x.x compatibility) +With the latest release targeting latest Keycloak 23.x.x it's not possible to configure the plugin through the Keycloak web UI, +but only through REST services. Suggested to use https://github.com/nicolabeghin/keycloak-spid-provider-configuration-client + +### Release 1.0.6 +It's possible to configure the plugin through the Keycloak web UI, detailed instructions +on how to install and configure this component are available in the project wiki (https://github.com/italia/spid-keycloak-provider/wiki/Installing-the-SPID-provider). +To avoid errors, it's suggested to use anyway https://github.com/nicolabeghin/keycloak-spid-provider-configuration-client ## Build (without docker) Requirements: @@ -68,7 +77,7 @@ The output package will be generated under `spid-keycloak-provider/target/spid-p ## Deployment This provider should be deployed as a module, i.e. copied under -`{$KEYCLOAK_PATH}/standalone/deployments/`, with the right permissions. +`{$KEYCLOAK_PATH}/providers/`, with the right permissions. Keycloak will take care of loading the module, no restart needed. Use this command for reference: diff --git a/pom.xml b/pom.xml index 97d7315..ce7e2a2 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ 4.0.0 com.github.lscorcia keycloak-spid-provider - 1.0.16-SNAPSHOT + 1.0.17-SNAPSHOT jar Keycloak SPID Service Provider @@ -19,8 +19,7 @@ false UTF-8 - - 23.0.3 + 23.0.6 1.7.30 5.8.2 4.3.1