Skip to content

Commit

Permalink
Merge pull request #59 from nicolabeghin/upgrade_keycloak_23
Browse files Browse the repository at this point in the history
Upgrade for Keycloak 23 compatibility + WIP restore UI through ConfiguredProvider
  • Loading branch information
nicolabeghin committed Feb 11, 2024
2 parents c94fe02 + 2c8c603 commit ba5c42b
Show file tree
Hide file tree
Showing 14 changed files with 446 additions and 157 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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
27 changes: 18 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,37 @@ 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`).
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
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:
* git
* JDK8+
* JDK17+
* Maven

Just run:
Expand All @@ -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:
Expand Down
6 changes: 4 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.lscorcia</groupId>
<artifactId>keycloak-spid-provider</artifactId>
<version>1.0.15-SNAPSHOT</version>
<version>1.0.17-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Keycloak SPID Service Provider</name>
Expand All @@ -19,10 +19,12 @@
<failOnMissingWebXml>false</failOnMissingWebXml>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<version.keycloak>20.0.0</version.keycloak>
<version.keycloak>23.0.6</version.keycloak>
<slf4j-api.version>1.7.30</slf4j-api.version>
<junit-jupiter.version>5.8.2</junit-jupiter.version>
<mockito.version>4.3.1</mockito.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>

<scm>
Expand Down
22 changes: 12 additions & 10 deletions src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -381,8 +383,8 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) {
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();


List<Element> signingKeys = new LinkedList<>();
List<Element> encryptionKeys = new LinkedList<>();
List<KeyDescriptorType> signingKeys = new LinkedList<>();
List<KeyDescriptorType> encryptionKeys = new LinkedList<>();

session.keys().getKeysStream(realm, KeyUse.SIG, Algorithm.RS256)
.filter(Objects::nonNull)
Expand All @@ -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);
Expand All @@ -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);
Expand Down
163 changes: 163 additions & 0 deletions src/main/java/org/keycloak/broker/spid/SpidIdentityProviderConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -226,5 +230,164 @@ public boolean isDebugEnabled() {
public void setDebugEnabled(boolean isDebugEnabled) {
getConfig().put(SPID_RESPONSE_DEBUG_ENABLED, String.valueOf(isDebugEnabled));
}

public static List<ProviderConfigProperty> 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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -45,7 +47,7 @@
/**
* @author Pedro Igor
*/
public class SpidIdentityProviderFactory extends AbstractIdentityProviderFactory<SpidIdentityProvider> {
public class SpidIdentityProviderFactory extends AbstractIdentityProviderFactory<SpidIdentityProvider> implements ConfiguredProvider {

public static final String PROVIDER_ID = "spid";

Expand Down Expand Up @@ -200,4 +202,8 @@ public void init(Scope config) {

this.destinationValidator = DestinationValidator.forProtocolMap(config.getArray("knownProtocols"));
}

public List<ProviderConfigProperty> getConfigProperties() {
return SpidIdentityProviderConfig.getConfigProperties();
}
}
Loading

0 comments on commit ba5c42b

Please sign in to comment.