diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 4242ba8..abda386 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -5,23 +5,24 @@ name: Java CI with Maven on: push: - branches: [ main ] + branches: [ main, master ] tags: '*' pull_request: - branches: [ main ] + branches: [ main, master ] jobs: build: name: Build - runs-on: ubuntu-latest + runs-on: ubuntu-latest + #runs-on: self-hosted steps: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 17 - name: Cache Maven packages uses: actions/cache@v2 @@ -31,7 +32,7 @@ jobs: restore-keys: ${{ runner.os }}-m2 - name: Build with Maven - run: mvn --batch-mode --update-snapshots package + run: ./mvnw --batch-mode --update-snapshots package - uses: actions/upload-artifact@v2 with: diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c1ac266 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +FROM registry.redhat.io/rhbk/keycloak-rhel9:22 as builder + +# Enable health and metrics support +ENV KC_HEALTH_ENABLED=true +ENV KC_METRICS_ENABLED=true + +# Configure a database vendor +ENV KC_DB=dev-file + +WORKDIR /opt/keycloak +# for demonstration purposes only, please make sure to use proper certificates in production instead +RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore + +ADD --chown=keycloak:keycloak target/spid-provider.jar /opt/keycloak/providers/spid-provider.jar +COPY --chown=keycloak:keycloak theme/keycloak-spid-only/ /opt/keycloak/themes/keycloak-spid-only/ + +RUN /opt/keycloak/bin/kc.sh build + +FROM registry.redhat.io/rhbk/keycloak-rhel9:22 +COPY --from=builder /opt/keycloak/ /opt/keycloak/ + +EXPOSE 8080 +EXPOSE 8443 + +# change these values to point to a running postgres instance +#ENV KC_DB=postgres +#ENV KC_DB_URL= +#ENV KC_DB_USERNAME= +#ENV KC_DB_PASSWORD= +#ENV KC_HOSTNAME=localhost +ENTRYPOINT ["/opt/keycloak/bin/kc.sh"] \ No newline at end of file diff --git a/README.md b/README.md index 068e45c..224ff66 100644 --- a/README.md +++ b/README.md @@ -1,116 +1,107 @@ # keycloak-spid-provider -Italian SPID authentication provider for Keycloak v.18.0.3+ (https://www.keycloak.org/) +Italian SPID authentication provider for Keycloak v.22.0+ (https://www.keycloak.org/) This guide describe the steps required to successfully integrate a Keycloak environment with the SPID federation. ## Prerequisites -- Keycloak full-working installation (version *18.0.3+*): the following instructions expect an environment variable named **$KC_HOME** to be set to the root directory of the Keycloak instance +- Keycloak full-working installation (version *22.0+*): the following instructions expect an environment variable named **$KC_HOME** to be set to the root directory of the Keycloak instance - a recent Docker (or podman) installation -- JDK 8+ +- JDK 17 - Git -- Maven ## Install keycloak-spid-provider ### Build the jar file -Clone this git repository and build it using Maven: +Clone this git repository and build it using Maven Wrapper: ```shell $ git clone https://github.com/redhat-italy/keycloak-spid-provider.git $ cd keycloak-spid-provider -$ mvn clean package +$ ./mvnw clean package ``` After a successful build you will find the `spid-provider.jar` jar file in the `target` directory. -### Deploy into Keycloak +### Add provider to Keycloak -The instructions consider a *standalone* installation but are applicable to *managed domain* installations as well (changing the target directory names where it is required). +Shutdown the Keycloak server (if running). -Shutdown the Keycloak server. +Copy the jar file into Keycloak `providers` directory. -Copy the jar file into Keycloak `deployments` directory. +```shell +$ cp target/spid-provider.jar $KC_HOME/providers/ +``` +Link the provider to the current KeyCloak installation using the `build` option: ```shell -$ cp target/spid-provider.jar $KC_HOME/standalone/deployments/ +$ {KC_HOME}/bin/kc.sh build ``` +You can check if the provider is installed correctly using the following command: -> **Note** -> -> For developing purposes the wildfly maven plugin has been included. Please, remember to update the wildfly port config section in the file pom.xml while developing to use the plugin. -> -> Each time you copy the jar file in the `deployments` directory, Keycloak automatically deploys it at the bootstrap. Nevertheless it may happen that the previous version of the same deployment doesn't get overridden. -> -> It is recommended to cleanup any existing installed deployment in $KC_HOME/standalone/data/content/ related to the same jar module, before restarting Keycloak. +```shell +$ {KC_HOME}/bin/kc.sh show-config +``` -Copy the custom theme (`keycloak-spid-only`) into Keycloak `themes` directory in order to enable the standard SPID login button in the login page. +You should expect an output similar to this one: -```shell -$ cp -r theme/keycloak-spid-only $KC_HOME/themes ``` +Current Mode: development +Current Configuration: + kc.cache = local (PersistedConfigSource) + kc.config.built = true (SysPropConfigSource) + kc.db = dev-file (PersistedConfigSource) -Since you probably installed Keycloak on your local host, define a custom hostname (e.g. `spidsp`) for it to refer to that Keycloak instance (the service provider) instead of using `localhost`. +... content omitted ... + + kc.provider.file.spid-provider.jar.last-modified = 1712329291489 (PersistedConfigSource) + +... content omitted ... + +``` + +Copy the custom theme (`keycloak-spid-only`) into Keycloak `themes` directory in order to enable the standard SPID login button in the login page. ```shell -$ echo '127.0.0.1 spidsp' >> /etc/hosts +$ cp -r theme/keycloak-spid-only $KC_HOME/themes ``` -Use this hostname (e.g. `spidsp`) to connect to the Keycloak console, also while generating the xml metadata file/url. +## Start keycloak in dev mode -Start the Keycloak server: +In order to start Keycloak server in `dev` mode you should execute this command : ```shell -$ $KC_HOME/bin/standalone.sh -b 0.0.0.0 -Djboss.socket.binding.port-offset=2 +$ ${KC_HOME}/bin/kc.sh start-dev ``` The bind address is set to *0.0.0.0* to listen on any interface, in order to relax any network configuration issue. - -Furthermore the `port-offset` value has been set in order to remap ports and avoid any collision with the ports opened by the SPID testing tool (see next sections). - During Keycloak bootstrap you should see log entries like the following: ``` -10:13:25,178 INFO [org.jboss.as.server.deployment] (MSC service thread 1-4) WFLYSRV0027: Starting deployment of "spid-provider.jar" (runtime-name: "spid-provider.jar") - -... -10:13:32,178 INFO [org.keycloak.subsystem.server.extension.KeycloakProviderDeploymentProcessor] (MSC service thread 1-4) Deploying Keycloak provider: spid-provider.jar -... +Updating the configuration and installing your custom providers, if any. Please wait. +2024-04-05 17:56:54,455 INFO [io.quarkus.deployment.QuarkusAugmentor] (main) Quarkus augmentation completed in 5404ms +... output omitted ... +2024-04-05 17:56:59,559 INFO [io.quarkus] (main) Keycloak 22.0.8.redhat-00001 on JVM (powered by Quarkus 3.2.9.Final-redhat-00002) started in 4.989s. Listening on: http://0.0.0.0:8080 +2024-04-05 17:56:59,560 INFO [io.quarkus] (main) Profile dev activated. +2024-04-05 17:56:59,560 INFO [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-h2, jdbc-mariadb, jdbc-mssql, jdbc-mysql, jdbc-oracle, jdbc-postgresql, keycloak, micrometer, narayana-jta, reactive-routes, resteasy, resteasy-jackson, smallrye-context-propagation, smallrye-health, vertx] +2024-04-05 17:56:59,563 WARN [org.keycloak.quarkus.runtime.KeycloakMain] (main) Running the server in development mode. DO NOT use this configuration in production. -10:13:34,044 INFO [org.jboss.as.server] (ServerService Thread Pool -- 33) WFLYSRV0010: Deployed "spid-provider.jar" (runtime-name : "spid-provider.jar") ``` -The SPID custom provider has been correctly deployed and to verify that the module is correctly available and active, you can open the Keycloak admin console and access the **Identity Providers** section, choose the **Add provider** dropdown and you should find the **SPID** entry. - -## Repeated deployments and cache +If you need to start KeyCloak listening on another port use the `--http-port ` command argument. -In order to deploy a modified version of the jar file, you can just repeat the deployment commands described above. However sometimes Keycloak caches don't get flushed when a new deployment occurs: in that case a quick workaround is to edit `$KC_HOME/standalone/configuration/standalone.xml` file and temporarily disable the theme/templates caching replacing the following xml block: +The SPID custom provider has been correctly deployed and to verify that the module is correctly available and active, you can open the Keycloak admin console and access the **Identity Providers** section, choose the **Add provider** dropdown, and you should find the **SPID** entry. -```xml - - 2592000 - true - true - ${jboss.home.dir}/themes - -``` - -with the following: +## Repeated deployments and cache -```xml - - -1 - false - false - ${jboss.home.dir}/themes - -``` +When starting Keycloak with the `start-dev` command line, themes resources are **not** cached. -Then restart Keycloak and it will reload the new resources from the jar package. +Then restart Keycloak, and it will reload the new resources from the jar package. Make sure you also cleared your browser caches (or use *incognito mode*) when verifying the correct deployment. After the first reload you can turn back on the caches and restart Keycloak again (if required). + ## Install and configure the local SPID SAML Check docker environment The *SPID SAML Check docker environment* ([https://github.com/italia/spid-saml-check](https://github.com/italia/spid-saml-check)) is a Docker environment that "emulates" the online SPID Test IdP ([https://demo.spid.gov.it](https://demo.spid.gov.it)). @@ -119,10 +110,16 @@ The *SPID SAML Check docker environment* ([https://github.com/italia/spid-saml-c It includes: - [spid-sp-test](https://github.com/italia/spid-sp-test) to check the SPID specifications compliance (command line executable tests) -- a web application (*spid-validator*) that provides an easy to use interface for testing success and error scenarios +- a web application (*spid-validator*) that provides an easy-to-use interface for testing success and error scenarios - a web application (*spid-demo*) that acts as a test IdP for demo purpose -Clone the SPID SAML Check project repository and build the Docker image: +You can run the official image hosted on docker hub with docker or podman + +```shell +$ docker run --name spid-saml-check-idp-demo -it -p 8443:8443 italia/spid-saml-check:1.10.4 +``` + +As an alternative you can clone the SPID SAML Check project repository and build a custom Docker image: ```shell $ git clone https://github.com/italia/spid-saml-check.git @@ -132,43 +129,39 @@ $ docker build -t italia/spid-saml-check . Visit https://github.com/italia/spid-saml-check or follow the README.md in the repo for extra instructions to set up a container with the test environment. -Run the container: - -```shell -$ docker run --name spid-saml-check-idp-demo -t -i -p 8080:8080 italia/spid-saml-check -``` +The web server of the SPID SAML Check docker environment is now available at [https://localhost:8843](https://localhost:8080). -The web server of the SPID SAML Check docker environment is now available at [https://localhost:8080](https://localhost:8080). +### Configure a custom host name inside the container -Execute the following command in order to get the IP address of the host as seen inside the container, in order to map it with the custom hostname `spidsp`, previously declared for the host machine running Keycloak. +Execute the following command in order to add a custom entry in the file hosts inside the container, in order to map it with the custom hostname `spidsp`. This should also be declared for the host machine running Keycloak. ```shell -docker exec -it spid-saml-check-idp-demo /bin/sh -c "ping host.docker.internal | head -n 1 | cut -f 2 -d '(' | cut -f 1 -d ')' | tr -d $'\n'" +docker exec spid-saml-check-idp-demo sh -c "cp -f /etc/hosts /tmp/hosts && sed -i 's/host\.docker\.internal/host\.docker\.internal spidsp/' /tmp/hosts && cp -f /tmp/hosts /etc/hosts" ``` - -It should print the IP address (for recent Docker versions on Windows/Mac it is usually 192.168.65.2). - If you are using podman replace `host.docker.internal` with `host.containers.internal` in the command above. -In this case the IP address usually is 192.168.1.192. -Then execute the following command to set the `spidsp` hostname alias (inside the container) to the IP address, replacing the string `` with the IP address printed by the previous command (e.g. 192.168.65.2). +### Configure host for testing with demo platform + +Since you probably installed Keycloak on your local host, define a custom hostname (e.g. `spidsp`) for it to refer to that Keycloak instance (the service provider) instead of using `localhost`. ```shell -docker exec -it spid-saml-check-idp-demo /bin/sh -c "echo ' spidsp' >> /etc/hosts" +$ echo '127.0.0.1 spidsp' >> /etc/hosts ``` -This way you can use URLs with `spidsp` as hostname inside the SPID SAML Check web interface (e.g. `http://spidsp:8082/auth/realms/spidrealm/spid-sp-metadata`), avoiding hostname mismatching errors. +This way you can use URLs with `spidsp` as hostname inside the SPID SAML Check web interface (e.g. `http://spidsp:8080/realms/my-spid/spid-sp-metadata`), avoiding hostname mismatching errors. -Instead you can access the SPID SAML Check using localhost (on port 8080). +## Test Identity provider The test identity provider (SPID SAML Check) exposes two metadata: -- one can be used to test the login/logout functionality and exposes the descriptor at [https://localhost:8080/demo/metadata.xml](https://localhost:8080/demo/metadata.xml). -- another one can be used to validate the service provider (Keycloak) metadata, requests and responses in order to pass all of the checks required by AGID. It exposes the descriptor at [https://localhost:8080/metadata.xml](https://localhost:8080/metadata.xml) +- one can be used to test the login/logout functionality and exposes the descriptor at [https://spidsp:8443/demo/metadata.xml](https://localhost:8080/demo/metadata.xml). +- another one can be used to validate the service provider (Keycloak) metadata, requests and responses in order to pass all the checks required by AGID. It exposes the descriptor at [https://localhost:8080/metadata.xml](https://localhost:8080/metadata.xml) -You will need these endpoints later to setup the Keycloak Identity Provider configurations. +You will need these endpoints later to set up the Keycloak Identity Provider configurations. + +Use this hostname (e.g. `spidsp`) to connect to the Keycloak console, also while generating the xml metadata file/url. -To stop the SPID SAML Check running container just kill the `docker run...` command with *CTRL+C*. +To stop the SPID SAML Check running container just kill the `docker run...` command with *CTRL+C* or, in another terminal, run `docker stop spid-saml-check-idp-demo`. ## Setup Identity Provider(s) @@ -178,36 +171,43 @@ Select the target realm (or create one if required). Select the *Realm Settings* item from the left menu, click on *Themes* tab and set `keycloak-spid-only` theme as *Login Theme*. -The following instructions can be reused to define all of the Identity Providers supported by SPID. +The following instructions can be reused to define all the Identity Providers supported by SPID. -### Setup a custom "First Broker Login" Authentication Flow +### Set up a custom "First Broker Login" Authentication Flow This step is required because we want that if a user logs in with different identity providers (even different SPID authorized IDPs), they are all linked to the same Keycloak user account (if already existent, otherwise it gets created). However, even if the username is the same, Keycloak will trigger by default an "Existing Account Verification" step with link confirmation: since this is not desirable because we trust the information from SPID IdPs, we define a new *First Broker Login* Authentication Flow to automatically set the existing user. -1. In the Keycloak admin console, select the *Authentication* item from the left menu; -2. In the *Flows* tab, select *First Broker Login* and then click *Copy*; -3. Set the name of the new flow to *First Broker Login SPID*; -4. In the newly created *First Broker Login SPID* set the *Requirement* column radio button of the *Review Profile (review profile config)* execution to *DISABLED*. This makes sure that at the first successful login the user will not be prompted to confirm his email address; -5. Search for the *First Broker Login SPID Handle Existing Account* hierarchy entry and click on the *Actions* command on the right, then select *Add Execution*; -5. Choose the provider *Automatically Set Existing User* and click *Save*; -6. With the up/down arrows, move the new execution above the *Confirm Link Existing Account* entry; -7. Set the *Requirement* column radio button of the *Automatically Set Existing User* execution to *Required* -8. Set both the *Confirm Link Existing Account* and the *First Broker Login SPID Account Verification Options* radio buttons to *Disabled*. + 1. In the Keycloak admin console, select the *Authentication* item from the left menu; + 2. In the *Flows* tab, select *First Broker Login* and then click *Duplicate*; + 3. Set the name of the new flow to *First broker login SPID*; + 4. In the newly created *First broker login SPID* set the *Requirement* column radio button of the *Review Profile (review profile config)* execution on *DISABLED*. This makes sure that at the first successful login the user will not be prompted to confirm his email address; + 5. Search for the *First broker login SPID Handle Existing Account* hierarchy entry and click on the *Add Step* command on the right by pressing the "+" button; + 6. Choose the provider *Automatically Set Existing User* and click *Add*; + 7. With the up/down arrows, move the new execution above the *Confirm Link Existing Account* entry and set it on *Required*; + 8. Set the *Requirement* column radio button of the *Automatically Set Existing User* execution on *Required*; + 9. Set both the *Confirm Link Existing Account* and the *First broker login SPID Account Verification Options* radio buttons on *Disabled*. -![Auth Flow](docs/img/auth_flow.png) +The result should be something like this: -If you need to configure all the current (May 2023) Italian SPID providers you can import, through the keycloak interface (Manage > import on realm's main menu), [this](docs/template-realm/realm-export.json) already configured realm. +![Auth Flow](docs/img/auth_flow_22.png) + +or this using the graph view: + +![Auth Flow as graph](docs/img/auth_flow_22_graph.png) -**WARNING**: after importing the template you need to configure the identity provider mappers following [this paragraph](#configure-identity-provider-mappers) for *every* provider imported. ### Identity Provider configuration +**WARNING** In the version 22 of Keycloak you can't use the web interface to configure the providers. For now, you need to use the script [configure-sh](spid-providers/configure.sh) to create all the Italian providers with the mapping information. + +Since is not possible from the UI to compile the following properties, you can skip this section and Jump directly on the [Generating and configuring Service Provider metadata](#generating-and-configuring-service-provider-metadata) section. + 1. Select the *Identity Providers* item from the left menu, click on *Add provider*, then select *SPID*; 2. In the *Add Identity Provider* page, scroll to the bottom and set the *Import from URL* field to the provider metadata url endpoint (if the import from URL does not work, use wget to download the xml file and import it as file): - - for *SPID SAML Check Demo IDP* [https://localhost:8080/demo/metadata.xml](https://localhost:8080/demo/metadata.xml) - - for *SPID SAML Check Validator IDP* [https://localhost:8080/metadata.xml](https://localhost:8080/metadata.xml) + - for *SPID SAML Check Demo IDP* [https://spidsp:8443/demo/metadata.xml](https://localhost:8080/demo/metadata.xml) + - for *SPID SAML Check Validator IDP* [https://spidsp:8443/metadata.xml](https://localhost:8080/metadata.xml) 3. Click on the Import button. Most of the fields will be filled in automatically. @@ -215,15 +215,15 @@ Most of the fields will be filled in automatically. Fill in the other fields as follows (leave the other fields as set by default). #### Main section -- **Alias**: enter a name for the provider (it will be used as an URL component, so DO NOT enter space characters). In order to use the standard SPID Login button for tests, this field must be set to `spidtestidp`, otherwise apply the right changes to the custom theme. +- **Alias**: enter a name for the provider (it will be used as a URL component, so DO NOT enter space characters). In order to use the standard SPID Login button for tests, this field must be set to `spidtestidp`, otherwise apply the right changes to the custom theme. - **Display Name**: the name of the IDP (it will be the name of the login button on the Keycloak login page) - **Trust Email**: set to `ON` -- **First Login Flow**: select `First Broker Login SPID` (defined in the previous section) +- **First Login Flow**: select `First broker login SPID` (defined in the previous section) - **Sync Mode**: select `force` #### SAML Config section -- **Service Provider Entity ID**: `http:///auth/realms/` +- **Service Provider Entity ID**: `http:///realms/` - **Single Sign-On Service URL**: - **NameID Policy Format**: set select box to `Transient` - **Principal Type**: set to `Attribute [Name]` @@ -269,21 +269,21 @@ Set the *User Name attribute*, the *Basic attributes* and, if required, one or m #### User Name attribute Click on the *Create* button and set the following attributes: -| Name | Mapper Type | Template | Sync Mode | Target | -| ---- | ---- | ---- | ---- | ---- | -| User Name | SPID Username Template Importer | ${ATTRIBUTE.fiscalNumber} | inherit | LOCAL | +| Name | Mapper Type | Template | Sync Mode | Target | +|-----------|---------------------------------|---------------------------|-----------|--------| +| User Name | SPID Username Template Importer | ${ATTRIBUTE.fiscalNumber} | inherit | LOCAL | All SPID users will have their username set to their fiscalNumber (lowercased according to the Keycloak convention). #### Basic attributes First Name and Last Name are required to identify the user and should be always mapped to special Keycloak attributes. Define the following two required mappers: -| Name | Mapper Type | Attribute Name | User Attribute Name | Sync Mode | -| ---- | ---- | ---- | ---- | ---- | -| First Name | SPID Attribute Importer | name | firstName | inherit | -| Last Name | SPID Attribute Importer | familyName | lastName | inherit | -| Tax Id | SPID Attribute Importer | fiscalNumber | spid-fiscalNumber | inherit | -| Email | SPID Attribute Importer | email | spid-email | inherit | +| Name | Mapper Type | Attribute Name | User Attribute Name | Sync Mode | +|------------|-------------------------|----------------|---------------------|-----------| +| First Name | SPID Attribute Importer | name | firstName | inherit | +| Last Name | SPID Attribute Importer | familyName | lastName | inherit | +| Tax Id | SPID Attribute Importer | fiscalNumber | spid-fiscalNumber | inherit | +| Email | SPID Attribute Importer | email | spid-email | inherit | > *NOTE** > @@ -293,62 +293,94 @@ First Name and Last Name are required to identify the user and should be always #### Other attributes -All of the other SPID attributes are optional and follow the same convention. Refer to the following table as a guide: - -| Name | Mapper Type | Attribute Name | User Attribute Name | -| ---- | ---- | ---- | ---- | -| SPID Code | SPID Attribute Importer | spidCode | spid-spidCode | -| Email | SPID Attribute Importer | email | spid-email | -| Tax Id | SPID Attribute Importer | fiscalNumber | spid-fiscalNumber | -| Gender | SPID Attribute Importer | gender | spid-gender | -| Date of Birth | SPID Attribute Importer | dateOfBirth | spid-dateOfBirth | -| Place of Birth | SPID Attribute Importer | placeOfBirth | spid-placeOfBirth | -| County of Birth | SPID Attribute Importer | countyOfBirth | spid-countyOfBirth | -| Mobile Phone | SPID Attribute Importer | mobilePhone | spid-mobilePhone | -| Address | SPID Attribute Importer | address | spid-address | -| Digital Address | SPID Attribute Importer | digitalAddress | spid-digitalAddress | -| Company Name | SPID Attribute Importer | companyName | spid-companyName | +All the other SPID attributes are optional and follow the same convention. Refer to the following table as a guide: + +| Name | Mapper Type | Attribute Name | User Attribute Name | +|-----------------|-------------------------|------------------|-----------------------| +| SPID Code | SPID Attribute Importer | spidCode | spid-spidCode | +| Email | SPID Attribute Importer | email | spid-email | +| Tax Id | SPID Attribute Importer | fiscalNumber | spid-fiscalNumber | +| Gender | SPID Attribute Importer | gender | spid-gender | +| Date of Birth | SPID Attribute Importer | dateOfBirth | spid-dateOfBirth | +| Place of Birth | SPID Attribute Importer | placeOfBirth | spid-placeOfBirth | +| County of Birth | SPID Attribute Importer | countyOfBirth | spid-countyOfBirth | +| Mobile Phone | SPID Attribute Importer | mobilePhone | spid-mobilePhone | +| Address | SPID Attribute Importer | address | spid-address | +| Digital Address | SPID Attribute Importer | digitalAddress | spid-digitalAddress | +| Company Name | SPID Attribute Importer | companyName | spid-companyName | | Company Address | SPID Attribute Importer | registeredOffice | spid-registeredOffice | -| VAT Number | SPID Attribute Importer | ivaCode | spid-ivaCode | +| VAT Number | SPID Attribute Importer | ivaCode | spid-ivaCode | ![Example mandatory mappers](docs/img/example_mandatory_mappers.png) ## Generating and configuring Service Provider metadata -The SPID Service Provider metadata (xml) document can be automatically generated clicking on the *SPID Service Provider Metadata* link available in the *Identity Provider* configuration page (the same filled in the previous section) at the label *Endpoints*. - -The link has the following standard format: +The SPID Service Provider metadata (xml) document has the following standard format: - `http(s)://:/auth/realms//spid-sp-metadata` + `http(s)://:/realms//spid-sp-metadata` -example: `http://spidsp:8082/auth/realms/spid/spid-sp-metadata` +In our case is: `http://spidsp:8080/realms/my-spid/spid-sp-metadata` > **NOTE** > -> All the "shared" data (*Organization* fields, *Company* fields, etc.) in the metadata is actually set by the first SPID IdP in alphabetical order only. Thus, there is no need to copy the same data in all of the IdPs. +> All the "shared" data (*Organization* fields, *Company* fields, etc.) in the metadata is actually set by the first SPID IdP in gui order. Thus, there is no need to copy the same data in all the IdPs. > -> The attribute mappings in the AttributeConsumingService section are automatically populated from the configured Mappers for the first SPID IdPs in alphabetical order. +> The attribute mappings in the AttributeConsumingService section are automatically populated from the configured Mappers for the first SPID IdPs in gui order. Configure the Service Provider Metadata in the *SPID SAML Check* tool as described in the README of the tool repo: Example: -1. Connect to the *SPID SAML Check* tool at [https://localhost:8080](https://localhost:8080), using `validator` as both username and password; -2. Set the SP metadata xml url (`http(s)://:/auth/realms//spid-sp-metadata`) in the *Metadata URL* field and download it, clicking *Download*; +1. Connect to the *SPID SAML Check* tool at [https://spidsp:8443](https://localhost:8080), using `validator` as both username and password; +2. Set the SP metadata xml url (`http(s)://:/realms//spid-sp-metadata`) in the *Metadata URL* field and download it, clicking *Download*; ## Testing login - logout -Now you can try to login using a configured client. For example you could use the built-in *Account* client application. +Now you can try to log in using a configured client. For example, you could use the built-in *Account* client application. -1. Browse to the Keycloak Account app: `http://:/auth/realms//account/`; +1. Browse to the Keycloak Account app: `http://:/realms//account/`; 2. The login page will appear with the standard SPID login button; 3. Click that button and choose spid-saml-check entry; 4. You should be redirected to the *SPID SAML Check* IDP login page; -5. Enter any spid level 2 users found in https://localhost:8080/demo/users (as of the time of writing you can use ada/password123); +5. Enter any spid level 2 users found in https://spidsp:8443/demo/users (as of the time of writing you can use ada/password123); 6. You should be redirected to the *Account* application page showing user data acquired from the IDP; 7. Try to click the logout button to test also this flow. +## Testing response code + +Set the configured SPID provider linked to https://spidsp:8443/metadata.xml as the first in gui order: + +![Gui Order](docs/img/gui_order.png) + +1. Browse http://spidsp:8080/realms/my-spid/account/#/ +2. Click Sign in selecting `SPID SAML Check (Public)` as a SPID provider +3. Now you can check every response checking if the codes are working as espected + +![Response Check](docs/img/response_check.png) + + ## Acknowledgements The main java code and some html/js/css snippets are taken/forked from or inspired by the same custom provider, developed by *Luca Leonardo Scorcia*, available at [https://github.com/italia/spid-keycloak-provider](https://github.com/italia/spid-keycloak-provider). This project is released under the Apache License 2.0, same as the main Keycloak package. + + +## Additional information + +The suggested configuration does not cover the creation of the provider that will be published for testing by AGID. In order to create the "production ready" provider you can use one of the json in this [folder](spid-providers/resources). +Pay particular attention to this part of the configuration that must match your specific case. + +```json +{ + "config": { + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} +``` + +After a proper configuration you can use the [configure.sh](spid-providers/configure.sh) to apply your provider to keycloak. After that, using the UI, you need to put it at the top of the SPID providers list. + diff --git a/docs/img/auth_flow_22.png b/docs/img/auth_flow_22.png new file mode 100644 index 0000000..5ecd9e4 Binary files /dev/null and b/docs/img/auth_flow_22.png differ diff --git a/docs/img/auth_flow_22_graph.png b/docs/img/auth_flow_22_graph.png new file mode 100644 index 0000000..b50bad3 Binary files /dev/null and b/docs/img/auth_flow_22_graph.png differ diff --git a/docs/img/gui_order.png b/docs/img/gui_order.png new file mode 100644 index 0000000..aeafc17 Binary files /dev/null and b/docs/img/gui_order.png differ diff --git a/docs/img/response_check.png b/docs/img/response_check.png new file mode 100644 index 0000000..8215b7e Binary files /dev/null and b/docs/img/response_check.png differ diff --git a/pom.xml b/pom.xml index f3fa24c..e6a7b78 100644 --- a/pom.xml +++ b/pom.xml @@ -6,8 +6,8 @@ http://www.apache.org/licenses/LICENSE-2.0 --> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.github.redhatitaly keycloak-spid-provider @@ -19,13 +19,15 @@ http://www.apache.org/licenses/LICENSE-2.0 https://github.com/redhat-italy/keycloak-spid-provider - 1.8 - 1.8 + 17 false UTF-8 - 18.0.6.redhat-00001 - 1.7.30 + 24.0.3.redhat-00004 + 1.7.36 4.13.2 + 3.11.0 + 3.0.0 + @@ -33,64 +35,131 @@ http://www.apache.org/licenses/LICENSE-2.0 HEAD + + + + com.redhat.quarkus.platform + quarkus-bom + 3.2.9.Final-redhat-00003 + pom + import + + + org.keycloak.bom + keycloak-bom-parent + ${version.keycloak} + pom + import + + + org.keycloak.bom + keycloak-spi-bom + ${version.keycloak} + pom + import + + + org.keycloak.bom + keycloak-misc-bom + ${version.keycloak} + pom + import + + + org.keycloak + keycloak-quarkus-parent + ${version.keycloak} + pom + import + + + + + - org.wildfly.plugins - wildfly-maven-plugin - 2.0.2.Final + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-arc + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-resteasy-jackson + test + + + io.quarkus + quarkus-junit5-mockito + test org.keycloak keycloak-core - ${version.keycloak} provided org.keycloak keycloak-saml-core - ${version.keycloak} provided + + org.keycloak keycloak-server-spi - ${version.keycloak} provided org.keycloak keycloak-server-spi-private - ${version.keycloak} provided org.keycloak keycloak-services - ${version.keycloak} provided + org.slf4j - slf4j-api - ${slf4j-api.version} + slf4j-log4j12 + 1.7.30 + test - org.slf4j - slf4j-simple - ${slf4j-api.version} + org.yaml + snakeyaml + 2.2 test - - junit - junit - ${junit.version} + io.github.hakky54 + logcaptor + 2.9.2 + test + + + + org.assertj + assertj-core + 3.25.3 test @@ -123,28 +192,136 @@ http://www.apache.org/licenses/LICENSE-2.0 spid-provider - - org.wildfly.plugins - wildfly-maven-plugin - 2.0.2.Final - - false - - 9993 - - org.apache.maven.plugins maven-release-plugin - 3.0.0-M1 + 3.0.1 true @{project.version} release + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + + + + maven-surefire-plugin + + 3.2.5 + + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + + + + ${project.build.directory}/${project.build.finalName}-runner + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + io.quarkus + quarkus-maven-plugin + 3.9.2 + true + + + + build + generate-code + generate-code-tests + + + + + + + io.quarkus + quarkus-maven-plugin + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + + org.jboss.slf4j:slf4j-jboss-logmanager + + + + + + \ No newline at end of file diff --git a/spid-providers/configure.sh b/spid-providers/configure.sh new file mode 100755 index 0000000..0500403 --- /dev/null +++ b/spid-providers/configure.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# Configuration +KCADM=${1:-/opt/eap/bin/kcadm.sh} # Location of kcadm.sh +KSERVER=${2:-http://localhost:8080} # Server host:port +KTREALM=${3:-my-spid} # Target Realm +CLEAN_UP=false # If set to true deletes every provider in the SP list + +# List of Service Providers +IDP=( +"infocertid" +"namirialid" +"lepidaid" +"arubaid" +"etnaid" +"sielteid" +"infocamereid" +"intesigroupid" +"timid" +"spiditaliaid" +"posteid" +"teamsystemid" +"test/privatespid" +"test/spidtestidp" +) + +# Counter for server calls +ADMIN_CALLS=0 + +START=$(date +%s) + +# Configure keycloak +if ! ${KCADM} config credentials --server ${KSERVER} --realm master --user admin +then + exit 1 +fi +(( ADMIN_CALLS++ )) + +echo "##########################################################################################" +echo "# Location of kcadm.sh: ${KCADM}" +echo "# Server is at ${KSERVER}" +echo "# Target realm is ${KTREALM}" +if [ "${CLEAN_UP}" == true ]; then + echo "#" + echo "# WARNING: This session will delete every Identity provider of the list. " + echo "# Press CTRL+C now if this was a mistake!" + echo "#" +fi +echo "##########################################################################################" +read -n1 -r -p "Press any key to continue..." key + +for idp in "${IDP[@]}"; +do + # sanitize idp alias + idp_alias=${idp/test\//} + if [ "${CLEAN_UP}" == true ]; then + echo "Deleting Identity Provider with alias ${idp_alias}" + ${KCADM} delete identity-provider/instances/${idp_alias} -r ${KTREALM} + (( ADMIN_CALLS++ )) + continue + fi + echo "Creating Identity Provider with alias ${idp_alias}" + ${KCADM} create identity-provider/instances -r ${KTREALM} -f resources/idp/${idp}.json + (( ADMIN_CALLS++ )) + echo "Creating custom mappers for provider ${idp_alias}" + for file in resources/mappers/*.json; + do + dest_file="/tmp/${idp_alias}-${file##*/}" + cat ${file} | sed "s/CHANGE-IT/${idp_alias}/" > ${dest_file} + ${KCADM} create identity-provider/instances/${idp_alias}/mappers -r ${KTREALM} -f ${dest_file} + (( ADMIN_CALLS++ )) + done +done + +END=$(date +%s) + +echo "Done. Keycloak server received ${ADMIN_CALLS} http requests in about $((END-START)) seconds" + diff --git a/spid-providers/resources/idp/arubaid.json b/spid-providers/resources/idp/arubaid.json new file mode 100644 index 0000000..e923574 --- /dev/null +++ b/spid-providers/resources/idp/arubaid.json @@ -0,0 +1,50 @@ +{ + "alias": "arubaid", + "displayName": "Aruba ID", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://loginspid.aruba.it/ServiceLogoutRequest", + "organizationDisplayNames": "it|ArubaPEC S.p.A.", + "organizationUrls": "it|https://www.pec.it/", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://loginspid.aruba.it", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|ArubaPEC S.p.A.", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://loginspid.aruba.it/ServiceLoginWelcome", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIExTCCA62gAwIBAgIQH32A70kY92tuXB8AGi2DdDANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG\nEwJJVDEYMBYGA1UECgwPQXJ1YmFQRUMgUy5wLkEuMSEwHwYDVQQLDBhDZXJ0aWZpY2F0aW9uIEF1\ndGhvcml0eUIxIDAeBgNVBAMMF0FydWJhUEVDIFMucC5BLiBORyBDQSAyMB4XDTIwMDEyMjAwMDAw\nMFoXDTI1MDEyMTIzNTk1OVowgaAxCzAJBgNVBAYTAklUMRYwFAYDVQQKDA1BcnViYSBQRUMgc3Bh\nMREwDwYDVQQLDAhQcm9kb3R0bzEWMBQGA1UEAwwNcGVjLml0IHBlYy5pdDEZMBcGA1UEBRMQWFhY\nWFhYMDBYMDBYMDAwWDEPMA0GA1UEKgwGcGVjLml0MQ8wDQYDVQQEDAZwZWMuaXQxETAPBgNVBC4T\nCDIwODc2Mzc5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqt2oHJhcp03l73p+QYpE\nJ+f3jYYj0W0gos0RItZx/w4vpsiKBygaqDNVWSwfo1aPdVDIX13f62O+lBki29KTt+QWv5K6SGHD\nUXYPntRdEQlicIBh2Z0HfrM7fDl+xeJrMp1s4dsSQAuB5TJOlFZq7xCQuukytGWBTvjfcN/os5aE\nsEg+RbtZHJR26SbbUcIqWb27Swgj/9jwK+tvzLnP4w8FNvEOrNfR0XwTMNDFrwbOCuWgthv5jNBs\nVZaoqNwiA/MxYt+gTOMj/o5PWKk8Wpm6o/7/+lWAoxh0v8x9OkbIi+YaFpIxuCcUqsrJJk63x2gH\nCc2nr+yclYUhsKD/AwIDAQABo4IBLDCCASgwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBTKQ3+N\nPGcXFk8nX994vMTVpba1EzBHBgNVHSAEQDA+MDwGCysGAQQBgegtAQEBMC0wKwYIKwYBBQUHAgEW\nH2h0dHBzOi8vY2EuYXJ1YmFwZWMuaXQvY3BzLmh0bWwwWAYDVR0fBFEwTzBNoEugSYZHaHR0cDov\nL2NybC5hcnViYXBlYy5pdC9BcnViYVBFQ1NwQUNlcnRpZmljYXRpb25BdXRob3JpdHlCL0xhdGVz\ndENSTC5jcmwwHwYDVR0jBBgwFoAU8v9jQBwRQv3M3/FZ9m7omYcxR3kwMwYIKwYBBQUHAQEEJzAl\nMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5hcnViYXBlYy5pdDANBgkqhkiG9w0BAQsFAAOCAQEA\nZKpor1MrrYwPw+IuPZElQAuNzXsaSWSnn/QQwJtW49c4rFM4mEud9c61p9XxIIbgQKmDmNbzC+Dm\nwJSZ8ILdCAyBHmY3BehVRAy3KRA2KQhS9kd4vywf5KVYd1L5hQa9DBrusxF7i1X/SEeLQgoKkov0\nR8v43UncqXS/ql50ovJFxi938Rv4rVwa8o0hqqc6WUcjkidB6M9aNJLIbOZN3xNUgC28qIr8y7N8\nlbxWbwVrGxqKDtpaA9J0hOOXxwuTfSd1zOtT0KSSSUQ53QGOPnxyjxYDQbJu60/lBPuUV5wb/Z2r\ngpeUH1/n7limHV5sVmOZgSnf18T+0STANCfkXg==,MIIExTCCA62gAwIBAgIQIHtEvEhGM77HwqsuvSbi9zANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG\nEwJJVDEYMBYGA1UECgwPQXJ1YmFQRUMgUy5wLkEuMSEwHwYDVQQLDBhDZXJ0aWZpY2F0aW9uIEF1\ndGhvcml0eUIxIDAeBgNVBAMMF0FydWJhUEVDIFMucC5BLiBORyBDQSAyMB4XDTE3MDEyMzAwMDAw\nMFoXDTIwMDEyMzIzNTk1OVowgaAxCzAJBgNVBAYTAklUMRYwFAYDVQQKDA1BcnViYSBQRUMgc3Bh\nMREwDwYDVQQLDAhQcm9kb3R0bzEWMBQGA1UEAwwNcGVjLml0IHBlYy5pdDEZMBcGA1UEBRMQWFhY\nWFhYMDBYMDBYMDAwWDEPMA0GA1UEKgwGcGVjLml0MQ8wDQYDVQQEDAZwZWMuaXQxETAPBgNVBC4T\nCDE2MzQ1MzgzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqt2oHJhcp03l73p+QYpE\nJ+f3jYYj0W0gos0RItZx/w4vpsiKBygaqDNVWSwfo1aPdVDIX13f62O+lBki29KTt+QWv5K6SGHD\nUXYPntRdEQlicIBh2Z0HfrM7fDl+xeJrMp1s4dsSQAuB5TJOlFZq7xCQuukytGWBTvjfcN/os5aE\nsEg+RbtZHJR26SbbUcIqWb27Swgj/9jwK+tvzLnP4w8FNvEOrNfR0XwTMNDFrwbOCuWgthv5jNBs\nVZaoqNwiA/MxYt+gTOMj/o5PWKk8Wpm6o/7/+lWAoxh0v8x9OkbIi+YaFpIxuCcUqsrJJk63x2gH\nCc2nr+yclYUhsKD/AwIDAQABo4IBLDCCASgwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBTKQ3+N\nPGcXFk8nX994vMTVpba1EzBHBgNVHSAEQDA+MDwGCysGAQQBgegtAQEBMC0wKwYIKwYBBQUHAgEW\nH2h0dHBzOi8vY2EuYXJ1YmFwZWMuaXQvY3BzLmh0bWwwWAYDVR0fBFEwTzBNoEugSYZHaHR0cDov\nL2NybC5hcnViYXBlYy5pdC9BcnViYVBFQ1NwQUNlcnRpZmljYXRpb25BdXRob3JpdHlCL0xhdGVz\ndENSTC5jcmwwHwYDVR0jBBgwFoAU8v9jQBwRQv3M3/FZ9m7omYcxR3kwMwYIKwYBBQUHAQEEJzAl\nMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5hcnViYXBlYy5pdDANBgkqhkiG9w0BAQsFAAOCAQEA\nnEw0NuaspbpDjA5wggwFtfQydU6b3Bw2/KXPRKS2JoqGmx0SYKj+L17A2KUBa2c7gDtKXYz0FLT6\n0Bv0pmBN/oYCgVMEBJKqwRwdki9YjEBwyCZwNEx1kDAyyqFEVU9vw/OQfrAdp7MTbuZGFKknVt7b\n9wOYy/Op9FiUaTg6SuOy0ep+rqhihltYNAAl4L6fY45mHvqa5vvVG30OvLW/S4uvRYUXYwY6KhWv\nNdDf5CnFugnuEZtHJrVe4wx9aO5GvFLFZ/mQ35C5mXPQ7nIb0CDdLBJdz82nUoLSA5BUbeXAUkfa\nhW/hLxLdhks68/TK694xVIuiB40pvMmJwxIyDA==", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://loginspid.aruba.it", + "attributeConsumingServiceName": "it|ArubaPEC S.p.A.", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/etnaid.json b/spid-providers/resources/idp/etnaid.json new file mode 100644 index 0000000..18555ad --- /dev/null +++ b/spid-providers/resources/idp/etnaid.json @@ -0,0 +1,50 @@ +{ + "alias": "etnaid", + "displayName": "Etna ID", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://id.eht.eu/SLS", + "organizationDisplayNames": "it|EHT,en|EHT", + "organizationUrls": "it|https://www.eht.eu/,en|https://www.eht.eu/", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://id.eht.eu", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|EtnaHitech S.C.p.A.,en|EtnaHitech S.C.p.A.", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://id.eht.eu/SSO", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIJKTCCBxGgAwIBAgIIJza0JRXSeRwwDQYJKoZIhvcNAQENBQAwgfsxCzAJBgNVBAYTAklUMQ0wCwYDVQQHDARSb21lMSYwJAYDVQQKDB1BZ2VuemlhIHBlciBsJ0l0YWxpYSBEaWdpdGFsZTEwMC4GA1UECwwnU2Vydml6aW8gQWNjcmVkaXRhbWVudG8gZSBwcm9nZXR0byBTUElEMTwwOgYDVQQDDDNQcm9nZXR0byBTUElEIC0gR2VzdG9yaSBkaSBJZGVudGl0w6AgRGlnaXRhbGUgKElkUCkxKTAnBgkqhkiG9w0BCQEWGnByb3RvY29sbG9AcGVjLmFnaWQuZ292Lml0MRowGAYDVQQFExFWQVRJVC05NzczNTAyMDU4NDAeFw0yMzAyMTcwMDAwMDBaFw0zMzAyMTYyMzU5NTlaMIGcMQswCQYDVQQGEwJJVDEQMA4GA1UECAwHQ2F0YW5pYTEQMA4GA1UEBwwHQ2F0YW5pYTEcMBoGA1UECgwTRXRuYUhpdGVjaCBTLkMucC5BLjESMBAGA1UEAwwJaWQuZWh0LmV1MRowGAYDVQRhDBFWQVRJVC0wNDMyMzIxMDg3NDEbMBkGA1UEUwwSaHR0cHM6Ly9pZC5laHQuZXUvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1JqeZUoCMKUh/Wm1X5NhvWQy2Kbp1uAAoJOmaBdR8ud2axp9Y+QABDcTVgDYgHG8o7AjOQWnPWz9LDvfzswu1CB3idEcg1cSiYVvphTMfmN62q+1HsuxvcWVuGIKq9CwGCnO2mVAOtt86rkaAsP95uou4vN9MlB8/nFY7gQjw/3h6fOZ3JS3Qdw1aY8N8SKJTPgSHvTThs8eTZy4ke5yvL09WSmosJLP3h63HZwB2BpuUue0EzWS4YnSnZ6V1p7O1LQZr3e4QKg1TOplItDWDItFgZSCQqOraBf2tfIgPtFdpgUsuJGa1sywbyjfczQ62pE9chIc7F0DKUTdUV88ygyrpAHA2z5cWALXT9+uYtau2KtTf91M0CruIkmGTu1IYSNtU+lCBdF+Pd1IlxP9HM5o5k++LUGQaDm9rsfhfYuRXFP6zp/TZsOoiygN8ryRcFOQswdyoDGEK47cXm1jYTBwOhSGVReqf990gC7paeOZwptKGSFHE/DC0Kn5b3QCuad5Yx48G/H8znshy1ZaEYR69Oh11Q/ytkKpZVNfvzVz2p43TPnNH3cQi0l4NzWV+x395sdJuObAT8ajcxl4aJ7niZna9arO54CV+RWABoJl1DKCaqVX8eG6Ha/9W3hW3vDJgMf7JG7wm7rz72vcHMKigz5UTWtX4Ilfd79rhTUCAwEAAaOCAwwwggMIMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKFmFUK23mm9Xsrqsguak005wmCtMB8GA1UdIwQYMBaAFMhfI5fCW5/U6IcEkxe+3+UDSXdfMA4GA1UdDwEB/wQEAwIGwDARBgNVHREECjAIggZpZHAuaXQwFgYDVR0SBA8wDYILc3BpZC5nb3YuaXQwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cHM6Ly9laWRhcy5hZ2lkLmdvdi5pdC9jcmwvY3JsX1NQSURfSWRQLmNybDBqBggrBgEFBQcBAQReMFwwRAYIKwYBBQUHMAKGOGh0dHA6Ly9laWRhcy5hZ2lkLmdvdi5pdC9jZXJ0aWZpY2F0aS9TdWJfQ0FfU1BJRF9JZFAuY2VyMBQGCCsGAQUFBzABhghodHRwczovLzCCAc4GA1UdIASCAcUwggHBMAkGBwQAjkYBBgIwgZUGBCtMEAYwgYwwRAYIKwYBBQUHAgIwOBo2RWxlY3Ryb25pYyBjZXJ0aWZpY2F0ZSBjb25mb3JtaW5nIHdpdGggQUdJRCBHdWlkZWxpbmVzMEQGCCsGAQUFBwICMDgaNkNlcnRpZmljYXRvIGVsZXR0cm9uaWNvIGNvbmZvcm1lIGFsbGUgTGluZWUgZ3VpZGEgQWdJRDByBgYrTBAEAQIwaDA5BggrBgEFBQcCAjAtGitTUElEOiBnZXN0b3JlIGRlbGxlIGlkZW50aXTgIGRpZ2l0YWxpIChJZFApMCsGCCsGAQUFBwICMB8aHVNQSUQ6IElkZW50aXR5IFByb3ZpZGVyIChJZFApMAgGBgQAj3oBAzBNBgQrTBAEMEUwQwYIKwYBBQUHAgEWN2h0dHBzOi8vZWlkYXMuYWdpZC5nb3YuaXQvY3BzL0FnSURfZUlEQVNfcm9vdENBX2Nwcy5wZGYwTwYGBACORgEFMEUwQwYIKwYBBQUHAgEWN2h0dHBzOi8vZWlkYXMuYWdpZC5nb3YuaXQvY3BzL0FnSURfZUlEQVNfcm9vdENBX2Nwcy5wZGYwDQYJKoZIhvcNAQENBQADggIBABlvKT22WGSr8xeLC0HcCtcZh1G2M+/4vDrptipMGNtpJQ3Oz3srPIo2WK9FqQGJ/Msd7L9n0uJz2c0/WRlFI/wom1KakyG4IxrerX7li5GhVGczse6eY+YbUBWFKsDseYE+1KTlhXJeyWfeT42VgYU24Zx4vFjvvGmTh3u2z6EYotFsUzZv2rA55uukxI7pat9u/plaBi9RIV5FUudCD6+5wpmUB6ks1sKGDnt9p7OtqDgTUnTiZKpSuW3E/TOp+EmedtYgujUhcpQ6vElbBs+nc9XH51Zw3wmuxaq/knCmdTXzZquTRn8bwVJVy2EhDgxCvCuZsFZyr7taXqKz265WqCsQJozybNJQABzzavL2c6rtqNATJJS3YyLmc3FdFk/HFIcGYMOOypdp9qZugIalg6LZmtCsio9mGA0iBk5DRmLceBmV+soJfwDjXfW8IFy5md55MkbmdWRs/qjzWP2c3sI0CpVF7PGr7fHzYSCfLaueDXC1xZh4gigHMDrjwU9Qay5SDhANe99Drumku5XnMDPky0WllHgJNfg32bYWOP0gcwCSbLNg/cB5n51oHXWi5nzP5zXE+4d3Ffhzcw8aW89IJyJYF2HwRO/PpEAEmPSKmhkgL1cI60iHoLAFs8RrAPMBVj7SlJhuRskFnBxRvHzQMUMGbLPHk0mm93gJ", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://id.eht.eu", + "attributeConsumingServiceName": "it|EtnaHitech S.C.p.A.,en|EtnaHitech S.C.p.A.", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/infocamereid.json b/spid-providers/resources/idp/infocamereid.json new file mode 100644 index 0000000..f0c127e --- /dev/null +++ b/spid-providers/resources/idp/infocamereid.json @@ -0,0 +1,51 @@ +{ + "alias": "infocamereid", + "displayName": "Infocamere ID", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://loginspid.infocamere.it/ServiceLogoutRequest", + "organizationDisplayNames": "it|InfoCamere S.C.p.A.", + "organizationUrls": "it|https://www.infocamere.it/", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://loginspid.infocamere.it", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|InfoCamere S.C.p.A.", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://loginspid.infocamere.it/ServiceLoginWelcome", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIIRDCCBiygAwIBAgIINO3vGmIYBP0wDQYJKoZIhvcNAQENBQAwgfsxCzAJBgNVBAYTAklUMQ0w\nCwYDVQQHDARSb21lMSYwJAYDVQQKDB1BZ2VuemlhIHBlciBsJ0l0YWxpYSBEaWdpdGFsZTEwMC4G\nA1UECwwnU2Vydml6aW8gQWNjcmVkaXRhbWVudG8gZSBwcm9nZXR0byBTUElEMTwwOgYDVQQDDDNQ\ncm9nZXR0byBTUElEIC0gR2VzdG9yaSBkaSBJZGVudGl0w6AgRGlnaXRhbGUgKElkUCkxKTAnBgkq\nhkiG9w0BCQEWGnByb3RvY29sbG9AcGVjLmFnaWQuZ292Lml0MRowGAYDVQQFExFWQVRJVC05Nzcz\nNTAyMDU4NDAeFw0yMzAyMjIwMDAwMDBaFw0zMzAyMjEyMzU5NTlaMIG3MQswCQYDVQQGEwJJVDEO\nMAwGA1UECAwFSXRhbHkxDzANBgNVBAcMBlBhZG92YTEcMBoGA1UECgwTSW5mb0NhbWVyZSBTLkMu\ncC5BLjElMCMGA1UEAwwcSW5mb0NhbWVyZSBJZGVudGl0eSBQcm92aWRlcjEaMBgGA1UEYQwRVkFU\nSVQtMDIzMTM4MjEwMDcxJjAkBgNVBFMMHWh0dHBzOi8vaWRzcGlkLmluZm9jYW1lcmUuaXQvMIIB\nIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo8Div4aLGUtoDoP5RWbRwqvEtjnDcCCUS+Sz\nChAsJP+UYjWl+R4R4Y7Lz+WId3LJqey+QIyvviD6vH/QloqzVRG/JabW70NZylk1UX2isss8mRvt\nceK7nYVxjTIoQpasg0OsCevgljjnFxRm8c3zUpYfjC5zzr/jZ9HjFKghGCZGjBavNNgiGIo7e7jb\ndmGH5N9z+uQ8KRG/p2JRxD0YeVy2+EV2o0cQO2duE383EganLKPcQ9AnxkLE1K0cpP7XQDtUgWTP\nqsL9+OLTl13KhVM2TMK7EkAm00WCOl1aX3E7g9Qgw+4fUm308v77OSDe77dY8hohZWPRTwjemaHA\n2QIDAQABo4IDDDCCAwgwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUddqeUDWjVXqV3PSfTyzAmlFD\nTDMwHwYDVR0jBBgwFoAUyF8jl8Jbn9TohwSTF77f5QNJd18wDgYDVR0PAQH/BAQDAgbAMBEGA1Ud\nEQQKMAiCBmlkcC5pdDAWBgNVHRIEDzANggtzcGlkLmdvdi5pdDA/BgNVHR8EODA2MDSgMqAwhi5o\ndHRwczovL2VpZGFzLmFnaWQuZ292Lml0L2NybC9jcmxfU1BJRF9JZFAuY3JsMGoGCCsGAQUFBwEB\nBF4wXDBEBggrBgEFBQcwAoY4aHR0cDovL2VpZGFzLmFnaWQuZ292Lml0L2NlcnRpZmljYXRpL1N1\nYl9DQV9TUElEX0lkUC5jZXIwFAYIKwYBBQUHMAGGCGh0dHBzOi8vMIIBzgYDVR0gBIIBxTCCAcEw\nCQYHBACORgEGAjCBlQYEK0wQBjCBjDBEBggrBgEFBQcCAjA4GjZFbGVjdHJvbmljIGNlcnRpZmlj\nYXRlIGNvbmZvcm1pbmcgd2l0aCBBR0lEIEd1aWRlbGluZXMwRAYIKwYBBQUHAgIwOBo2Q2VydGlm\naWNhdG8gZWxldHRyb25pY28gY29uZm9ybWUgYWxsZSBMaW5lZSBndWlkYSBBZ0lEMHIGBitMEAQB\nAjBoMDkGCCsGAQUFBwICMC0aK1NQSUQ6IGdlc3RvcmUgZGVsbGUgaWRlbnRpdOAgZGlnaXRhbGkg\nKElkUCkwKwYIKwYBBQUHAgIwHxodU1BJRDogSWRlbnRpdHkgUHJvdmlkZXIgKElkUCkwCAYGBACP\negEDME0GBCtMEAQwRTBDBggrBgEFBQcCARY3aHR0cHM6Ly9laWRhcy5hZ2lkLmdvdi5pdC9jcHMv\nQWdJRF9lSURBU19yb290Q0FfY3BzLnBkZjBPBgYEAI5GAQUwRTBDBggrBgEFBQcCARY3aHR0cHM6\nLy9laWRhcy5hZ2lkLmdvdi5pdC9jcHMvQWdJRF9lSURBU19yb290Q0FfY3BzLnBkZjANBgkqhkiG\n9w0BAQ0FAAOCAgEAoYZlSArAwFZDknzUG5Z3NQQUT3JKaOTT8TrNi/F8yL4mz0qjaJaJURMQauKZ\neNQiGlGvNyGp3SlgGYFHasZ9FrtpxbxGXVkNreer61kFhY/I3ZdU4DjGW2qPs9csP+W06R4k3OFF\nhua7DFyyoxAWQYIFisucT3E3+N32XuLQPDqjMwnvSdT4FLE6c4QIpJl3fQYlCsyhAxrNWlrndP1Q\n1f97oF6oB7tWR5Ae1/ixDN0q5QJeEnapNaDjvS2wEzVNRYW/RzbHPPZQ1Zs0jLEfXsuwD3A0iJiy\nD0GSgXYUibqH3VExCqQ1yjEDwjq3zF8bcSaoAQm2fRY3KIYSbI18kpPhFmNTJWbv303dQe6MzIOR\nLUzs0tSHfB+mtclrHgqqaKwZZmHiGUYTV3bziWjMDacG9gRJtyS04LYZdkSBcSOn3dYXSM18F58p\nbKifcdajFmUicUWlI/2TFArDguh5TUekLQKsTi4tMnmk5RWA4oMLjZ+q2r4jMNVuoZ0+FGFbrfdh\nz+Kyo3gWdyZyY+Uqr1aiL+QTnht8hVTVrgOf4RJW/3z5hgYLSyx3INT6GDtaSr5V+orYfSpbvU1X\nlinz+iP4vfYKmpFdF1cxjTYkNQB7/DW9nXYC4PwXjI5253rha8g/BLdsIEWD73Q1GM1HieSVX+tN\nPBbjHpKLz2UVZEM=", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://loginspid.infocamere.it", + "attributeConsumingServiceName": "it|InfoCamere S.C.p.A.", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "wantAssertionsEncrypted": "true", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/infocertid.json b/spid-providers/resources/idp/infocertid.json new file mode 100644 index 0000000..6932aa0 --- /dev/null +++ b/spid-providers/resources/idp/infocertid.json @@ -0,0 +1,50 @@ +{ + "alias": "infocertid", + "displayName": "InfoCert ID", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://identity.infocert.it/spid/samlslo", + "organizationDisplayNames": "it|InfoCert S.p.A.,en|InfoCert S.p.A.,fr|InfoCert S.p.A.,de|InfoCert S.p.A.", + "organizationUrls": "it|https://www.infocert.it,en|https://www.infocert.it/international/?lang=en,fr|https://www.infocert.it/international/?lang=fr,de|https://www.infocert.it/international/?lang=de", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://identity.infocert.it", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|InfoCert S.p.A.,en|InfoCert S.p.A.,fr|InfoCert S.p.A.,de|InfoCert S.p.A.", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://identity.infocert.it/spid/samlsso", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIHEjCCBPqgAwIBAgIDAjcDMA0GCSqGSIb3DQEBCwUAMIGTMQswCQYDVQQGEwJJVDEYMBYGA1UECgwPSW5mb0NlcnQgUy5wLkEuMR8wHQYDVQQLDBZUcnVzdCBTZXJ2aWNlIFByb3ZpZGVyMRowGAYDVQRhDBFWQVRJVC0wNzk0NTIxMTAwNjEtMCsGA1UEAwwkSW5mb0NlcnQgQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBDQSAzMB4XDTIyMDEwNTA4MjIxNFoXDTI1MDEwNTAwMDAwMFowgZkxGTAXBgNVBC4TEDIwMjI5OTk4NTBBNDk1NjAxFDASBgNVBAUTCzA3OTQ1MjExMDA2MR0wGwYDVQQDDBRpZGVudGl0eS5pbmZvY2VydC5pdDEUMBIGA1UECwwLSW5mb0NlcnQgSUQxFTATBgNVBAoMDEluZm9DZXJ0IFNwYTENMAsGA1UEBwwEUm9tYTELMAkGA1UEBhMCSVQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6s3Tl4j+1kVqyUh5evwd7+rLq7j3BcIfBV+xLKn1wPyJgHjy7UJ1khy4oF+1D38qLrz4WngJ68Rf6kSdo07bLHnS8N1iIpTm05yq600yHFaeW4qZWTgeklE+Ui7WVBxs31G7i9RZVEVHFrBPctzERgiHJ2MW0mvy2dlGszGlept4nVtQMc/CKvM1zs9W/te1opTWueZdHN5jFvW0GxEib5HufH6BMugwtX0nySBOvlE6bQSid7tkEiedDlBHUZ7Sb+f+S4D+ZZEEg3F6ikSgFxHwns2tB3YL9Xd09LfyNQF6K1PSGx2Gjq2+PsY1glmk6pt6AU2axOpfpkoe4mZbzAgMBAAGjggJlMIICYTATBgNVHSUEDDAKBggrBgEFBQcDAjCBoQYDVR0gBIGZMIGWMIGTBgYrTCQBAQgwgYgwQQYIKwYBBQUHAgIwNQwzU1NMLCBTTUlNRSBhbmQgRGlnaXRhbCBTaWduYXR1cmUgQ2xpZW50IENlcnRpZmljYXRlMEMGCCsGAQUFBwIBFjdodHRwOi8vd3d3LmZpcm1hLmluZm9jZXJ0Lml0L2RvY3VtZW50YXppb25lL21hbnVhbGkucGhwMG4GCCsGAQUFBwEBBGIwYDArBggrBgEFBQcwAYYfaHR0cDovL29jc3AuY3MuY2EzLmluZm9jZXJ0Lml0LzAxBggrBgEFBQcwAoYlaHR0cDovL2NlcnQuaW5mb2NlcnQuaXQvY2EzL2NzL0NBLmNydDCB5QYDVR0fBIHdMIHaMIHXoIHUoIHRhidodHRwOi8vY3JsLmluZm9jZXJ0Lml0L2NhMy9jcy9DUkwwMS5jcmyGgaVsZGFwOi8vbGRhcC5pbmZvY2VydC5pdC9jbiUzREluZm9DZXJ0JTIwQ2VydGlmaWNhdGlvbiUyMFNlcnZpY2VzJTIwQ0ElMjAzJTIwQ1JMMDEsb3UlM0RUcnVzdCUyMFNlcnZpY2UlMjBQcm92aWRlcixvJTNESU5GT0NFUlQlMjBTUEEsYyUzRElUP2NlcnRpZmljYXRlUmV2b2NhdGlvbkxpc3QwDgYDVR0PAQH/BAQDAgSwMB8GA1UdIwQYMBaAFHcRTQLy09eh1UxlX7hGRm7AIyq7MB0GA1UdDgQWBBSFWpMUOIyG+PVmscoEkrsnPp7JpTANBgkqhkiG9w0BAQsFAAOCAgEAB2AEW83IZGcHFrxtMkCdYaOvwFDO9AsN2uRwhK1a76GzA0LHetocUcUHOmamhnhha/Is3GRPsnmwzs63AAYEaFcS22Q9mE9e8HacxjPKCguy/6zkOg204+5jGtJAqmVI98o7gKY8utaosxRbz0CkugCO5YNRjLruj4sIbMp5BJoaA0TDTM91ilpLaGFDJeFSJQcUlJZI5OM2MrDn6/eRZxDechR+vV6rc0TwGFhTQnnYgoWg2U/CC3l46D77+R/RVPb/WW79hXTFLEnxHI5pCEZlmrWalPIBA129mIOsjXcVzjkBXfoDy1sXlziI/SMs0n3NJ/YqzUCu6bGOE5Hf++T67ynuSaQmPSYb7hbtyLm5qebg4yvowzMnfOZ/GVmoa+pKFnsMenDts7l0KgovvSspsLmMio9cYhMmaZ/uf0ckLnoeAkfjGkHufr3IbbW8Bk7s7BVN9HChw1q20WHcf95BJ4C9Yu/MVrTkJD1d3gWlfw0l9gm+gyhzCZAxT5DE2gspORygdNzzG0sLC/07Msx3+M1dPk5K5NOKaaqQBssaAPCCGnHIitCpvLlw2PoWQat88Twq4CrIscpnJ40Fa677BXDOrjHkriE7xccWhjV7dKSzEYv96ozFBPwc2Nb/1bMdCPXBfJ8dUsnAhSv15uJ6cb5UcpCpVoQ1QEW0KYU=", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://identity.infocert.it", + "attributeConsumingServiceName": "it|InfoCert S.p.A.,en|InfoCert S.p.A.,fr| InfoCert S.p.A.,de|InfoCert S.p.A.", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/intesigroupid.json b/spid-providers/resources/idp/intesigroupid.json new file mode 100644 index 0000000..afcff5e --- /dev/null +++ b/spid-providers/resources/idp/intesigroupid.json @@ -0,0 +1,50 @@ +{ + "alias": "intesigroupid", + "displayName": "Intesi Group", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://spid.intesigroup.com/saml/public/logout", + "organizationDisplayNames": "it|Intesi Group S.p.A.,en|Intesi Group S.p.A.", + "organizationUrls": "it|https://www.intesigroup.com,en|https://www.intesigroup.com", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://idp.intesigroup.com", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|Intesi Group S.p.A.,en|Intesi Group S.p.A.", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://spid.intesigroup.com/saml/public/income", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIIMDCCBhigAwIBAgIIWpERKeVBbQAwDQYJKoZIhvcNAQENBQAwgfsxCzAJBgNVBAYTAklUMQ0wCwYDVQQHDARSb21lMSYwJAYDVQQKDB1BZ2VuemlhIHBlciBsJ0l0YWxpYSBEaWdpdGFsZTEwMC4GA1UECwwnU2Vydml6aW8gQWNjcmVkaXRhbWVudG8gZSBwcm9nZXR0byBTUElEMTwwOgYDVQQDDDNQcm9nZXR0byBTUElEIC0gR2VzdG9yaSBkaSBJZGVudGl0w6AgRGlnaXRhbGUgKElkUCkxKTAnBgkqhkiG9w0BCQEWGnByb3RvY29sbG9AcGVjLmFnaWQuZ292Lml0MRowGAYDVQQFExFWQVRJVC05NzczNTAyMDU4NDAeFw0yMzA2MjEwMDAwMDBaFw0zMzA2MjAyMzU5NTlaMIGjMQswCQYDVQQIDAJNSTEPMA0GA1UEBwwGTWlsYW5vMRYwFAYDVQQDDA1TUElESW50ZXNpSURQMQswCQYDVQQGEwJJVDEcMBoGA1UECgwTSW50ZXNpIEdyb3VwIFMucC5BLjEaMBgGA1UEYQwRVkFUSVQtMDI3ODA0ODA5NjQxJDAiBgNVBFMMG2h0dHBzOi8vaXBkLmludGVzaWdyb3VwLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM9z5hL63x2486C2AStfxd5Ql+9pjY0ACCFNvjIuX6wrfR0GIW4jPa19pfw3klsvAF+ePBLAClgIBiuIC1Iac18gar/RiUg2PhfHY3Hiz2ljcvWSb6/zBVaKyM2Lry20esuy4HWunXJRGgcIxMRyK+fqw/1IOsVf0mudgr+C/sqBfGAwy+kw5/A9YoePUfnQGtRw46zJAL4/Nx35Y0prnemxPC/1UKtmxAZYOHDK3mEJ503LIDrsTOebBbEs/Xuc3peyPtnJnP0gKLZQE/bMHo3UpE29cGkkTRRudDKu6oBcOCO+EIHO0+RYzCWAUc4n04JhE+Gz8eVfYEKJJ0TwW4sCAwEAAaOCAwwwggMIMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFDiL2GZAXS1B0uYWxysigOq81khxMB8GA1UdIwQYMBaAFMhfI5fCW5/U6IcEkxe+3+UDSXdfMA4GA1UdDwEB/wQEAwIGwDARBgNVHREECjAIggZpZHAuaXQwFgYDVR0SBA8wDYILc3BpZC5nb3YuaXQwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cHM6Ly9laWRhcy5hZ2lkLmdvdi5pdC9jcmwvY3JsX1NQSURfSWRQLmNybDBqBggrBgEFBQcBAQReMFwwRAYIKwYBBQUHMAKGOGh0dHA6Ly9laWRhcy5hZ2lkLmdvdi5pdC9jZXJ0aWZpY2F0aS9TdWJfQ0FfU1BJRF9JZFAuY2VyMBQGCCsGAQUFBzABhghodHRwczovLzCCAc4GA1UdIASCAcUwggHBMAkGBwQAjkYBBgIwgZUGBCtMEAYwgYwwRAYIKwYBBQUHAgIwOBo2RWxlY3Ryb25pYyBjZXJ0aWZpY2F0ZSBjb25mb3JtaW5nIHdpdGggQUdJRCBHdWlkZWxpbmVzMEQGCCsGAQUFBwICMDgaNkNlcnRpZmljYXRvIGVsZXR0cm9uaWNvIGNvbmZvcm1lIGFsbGUgTGluZWUgZ3VpZGEgQWdJRDByBgYrTBAEAQIwaDA5BggrBgEFBQcCAjAtGitTUElEOiBnZXN0b3JlIGRlbGxlIGlkZW50aXTgIGRpZ2l0YWxpIChJZFApMCsGCCsGAQUFBwICMB8aHVNQSUQ6IElkZW50aXR5IFByb3ZpZGVyIChJZFApMAgGBgQAj3oBAzBNBgQrTBAEMEUwQwYIKwYBBQUHAgEWN2h0dHBzOi8vZWlkYXMuYWdpZC5nb3YuaXQvY3BzL0FnSURfZUlEQVNfcm9vdENBX2Nwcy5wZGYwTwYGBACORgEFMEUwQwYIKwYBBQUHAgEWN2h0dHBzOi8vZWlkYXMuYWdpZC5nb3YuaXQvY3BzL0FnSURfZUlEQVNfcm9vdENBX2Nwcy5wZGYwDQYJKoZIhvcNAQENBQADggIBAAT47ylVD9/+S6MkrqNwoagBYY/ItkJ/hkaddgoSX72squnItk9cI5y7o3t2b0hdtq72YVkNIXZc4OwPVpPXl0XjxsfJoY0Z2J5DYbir2Mb+JcAleHxCmVOhYv6lmW/+GIdttPp8mIUY0rTYbePSYFA4hYZ2WJ04fw6nU6B7IujIsU994PzOQbMaI/Wydms3ifIR7y8yuWa6bLzrECptv7Urch8EMNuG7MaUnC1CN5eQPnP49BgxhKZ6rUCgTGZz1XWNMtomO0gECwgPssA5q6ooO9QH4n912USr6GNEx7oUFcZ/GtbofMUNEY/YXxJUnr3qel2WZM5PF19fZOk2shLcWeT+b0K6urh1hyGPEfgo9jZ8wjOQYSYeY7yXO1rgjuVooROXNFNAiNwdp2pXtsa9syATKJxlC9TDCN7W+DjB7NtVOdo26H9inspDqrdRZ2nssZXYsfvyUCqCqPojzt1FNZ+YyTN7REgncCPrD80GrMCJUEux8iMqL3bMwgwy3pZtkULtCjuzU6/4umwkqY8E0lY8uziDxLhHSR7ipm6xb4dH/rZXnEdjVz/Jy0GoaxgUbNC26PN4hcnBVeDjpxPDaLVy4l8OaLNkWSvdnO9wmzTnENKJairCITI53crdE8SVr5zb4soMSBBHQr/9gFfz2KkD5XAewBjiiYFszOQc", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://loginspid.aruba.it", + "attributeConsumingServiceName": "it|Intesi Group S.p.A.,en|Intesi Group S.p.A.", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/lepidaid.json b/spid-providers/resources/idp/lepidaid.json new file mode 100644 index 0000000..3f09faa --- /dev/null +++ b/spid-providers/resources/idp/lepidaid.json @@ -0,0 +1,50 @@ +{ + "alias": "lepidaid", + "displayName": "Lepida ID", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://id.lepida.it/idp/profile/SAML2/POST/SLO", + "organizationDisplayNames": "it|Lepida S.p.A.", + "organizationUrls": "it|https://www.lepida.it/", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://id.lepida.it/idp/shibboleth", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|Lepida S.p.A.", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://id.lepida.it/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIDHDCCAgSgAwIBAgIVALisbudTRxLy3vlMcEDfaqr3iW89MA0GCSqGSIb3DQEB CwUAMBcxFTATBgNVBAMMDGlkLmxlcGlkYS5pdDAeFw0xODA4MDgxMDIzMTJaFw0z ODA4MDgxMDIzMTJaMBcxFTATBgNVBAMMDGlkLmxlcGlkYS5pdDCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMOFERgxPEYPqAjN7oW6y8oSSY6tGm2OCIU+ VyKhb2OqfNLpF8tPrytX17pgwVYHzjxRCNMTC83frbmtBapABtm9KuX7qaSPvaJx 0+UqYk9FdKCKQOEkmWcNOJfwzNMP65B+cDxP3sa1JoAMeAO0x95bnYoX0ZHcssKk wpgMb8/JHZHzqu3odxADtO5PaT3xaCyMIcqIp1O2nVn7SizUE1gNucLAdaP4kh0o 7nU61pz4pG3gQXK+uROteDD8cTU2Nxi7W1T73tQSuwst54BS2p9IBXzWrA9V0Ck1 0oiQTcIC8X9McepCrNzgCOBdap00Tifusb30t74BREARgwjp1N8CAwEAAaNfMF0w HQYDVR0OBBYEFL32/n7uf1Re14pW+gwGxZQHUZBCMDwGA1UdEQQ1MDOCDGlkLmxl cGlkYS5pdIYjaHR0cHM6Ly9pZC5sZXBpZGEuaXQvaWRwL3NoaWJib2xldGgwDQYJ KoZIhvcNAQELBQADggEBAK80B1mEWKOTJkVJOJot2xU79Lhs1+domUSYQiA+tlS4 6IAfWwDZqI1llIjgL85n7qMsKFvYTIskInoG51Iezv2dTxlB6IMI8NPRfiFXo2s8 NYjbzWyETbdXzCbDR0tKNke0TFE0oxunNfE5YRsmH4bPnjhPUjCSHX7wIhlNrLae 3FjMQp1OLDs7HmJo3AhuAVmHCoG7QV/ly4ZHcVYx4F7HUsFg5uxNYjZbo+XMutJz 4nZFOFE+uRzTwwfdR2sxny+ppkruTwIhEXyzknoiw1mGIEWZc6scnOAiwZeqTccU YVNHp+PSFs9SD8l+2PO4Oh8Y3dYT+5ojv+S6T7vy5xE=", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://id.lepida.it/idp/shibboleth", + "attributeConsumingServiceName": "it|Lepida S.p.A.", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/namirialid.json b/spid-providers/resources/idp/namirialid.json new file mode 100644 index 0000000..6a48a4a --- /dev/null +++ b/spid-providers/resources/idp/namirialid.json @@ -0,0 +1,50 @@ +{ + "alias": "namirialid", + "displayName": "Namirial ID", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://idp.namirialtsp.com/idp/profile/SAML2/POST/SLO", + "organizationDisplayNames": "it|Namirial S.p.a. Trust Service Provider", + "organizationUrls": "it|https://www.namirialtsp.com", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://idp.namirialtsp.com/idp", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|Namirial", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://idp.namirialtsp.com/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIDNzCCAh+gAwIBAgIUNGvDUjTpLSPlP4sEfO0+JARITnEwDQYJKoZIhvcNAQEL\nBQAwHjEcMBoGA1UEAwwTaWRwLm5hbWlyaWFsdHNwLmNvbTAeFw0xNzAzMDgwOTE3\nNTZaFw0zNzAzMDgwOTE3NTZaMB4xHDAaBgNVBAMME2lkcC5uYW1pcmlhbHRzcC5j\nb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDrcJvYRh49nNijgzwL\n1OOwgzeMDUWcMSwoWdtMpx3kDhZwMFQ3ITDmNvlz21I0QKaP0BDg/UAjfCbDtLqU\ny6wHtI6NWVJoqIziw+dLfg7S5Sr2nOzJ/sKhzadWH1kDsetIenOLU2ex+7Vf/+4P\n7nIrS0c+xghi9/zN8dH6+09wWYnloGmcW3qWRFMKJjR3ctBmsmqCKWNIIq2QfeFs\nzSSeG0xaNlLKBrj6TyPDxDqPAskq038W1fCuh7aejCk7XTTOxuuIwDGJiYsc8rfX\nSG9/auskAfCziGEm304/ojy5MRcNjekz4KgWxT9anMCipv0I2T7tCAivc1z9QCsE\nPk5pAgMBAAGjbTBrMB0GA1UdDgQWBBQi8+cnv0Nw0lbuICzxlSHsvBw5SzBKBgNV\nHREEQzBBghNpZHAubmFtaXJpYWx0c3AuY29thipodHRwczovL2lkcC5uYW1pcmlh\nbHRzcC5jb20vaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQELBQADggEBAEp953KM\nWY7wJbJqnPTmDkXaZJVoubcjW86IY494RgVBeZ4XzAGOifa3ScDK6a0OWfIlRTba\nKKu9lEVw9zs54vLp9oQI4JulomSaL805Glml4bYqtcLoh5qTnKaWp5qvzBgcQ7i2\nGcDC9F+qrsJYreCA7rbHXzF0hu5yIfz0BrrCRWvuWiop92WeKvtucI4oBGfoHhYO\nZsLuoTT3hZiEFJT60xS5Y2SNdz+Eia9Dgt0cvAzoOVk93Cxg+XBdyyEEiZn/zvhj\nus29KyFrzh3XYznh+4jq3ymt7Os4JKmY0aJm7yNxw+LyPjkdaB0icfo3+hD7PiuU\njC3Y67LUWQ8YgOc=", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://idp.namirialtsp.com/idp", + "attributeConsumingServiceName": "it|Namirial", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/posteid.json b/spid-providers/resources/idp/posteid.json new file mode 100644 index 0000000..b49b006 --- /dev/null +++ b/spid-providers/resources/idp/posteid.json @@ -0,0 +1,50 @@ +{ + "alias": "posteid", + "displayName": "Poste ID", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://posteid.poste.it/jod-fs/sloservicepost", + "organizationDisplayNames": "it|Poste Italiane SpA", + "organizationUrls": "it|https://www.poste.it", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://posteid.poste.it", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|Poste Italiane SpA", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://posteid.poste.it/jod-fs/ssoservicepost", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIFgzCCA2ugAwIBAgIIJSppAZKg/XQwDQYJKoZIhvcNAQELBQAwZTELMAkGA1UEBhMCSVQxHjAc\nBgNVBAoMFVBvc3RlIEl0YWxpYW5lIFMucC5BLjEaMBgGA1UEYQwRVkFUSVQtMDExMTQ2MDEwMDYx\nGjAYBgNVBAMMEVBvc3RlIEl0YWxpYW5lIENBMB4XDTIxMDIxODExNDYzMVoXDTI0MDIxOTExNDYz\nMVowQzELMAkGA1UEBhMCSVQxHjAcBgNVBAoMFVBvc3RlIEl0YWxpYW5lIFMucC5BLjEUMBIGA1UE\nAwwLaWRwLXBvc3RlaWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZFEtJoEHFAjpC\naZcj5DVWrRDyaLZyu31XApslbo87CyWz61OJMtw6QQU0MdCtrYbtSJ6vJwx7/6EUjsZ3u4x3EPLd\nlkyiGOqukPwATv4c7TVOUVs5onIqTphM9b+AHRg4ehiMGesm/9d7RIaLuN79iPUvdLn6WP3idAfE\nw+rhJ/wYEQ0h1Xm5osNUgtWcBGavZIjLssWNrDDfJYxXH3QZ0kI6feEvLCJwgjXLGkBuhFehNhM4\nfhbX9iUCWwwkJ3JsP2++Rc/iTA0LZhiUsXNNq7gBcLAJ9UX2V1dWjTzBHevfHspzt4e0VgIIwbDR\nqsRtF8VUPSDYYbLoqwbLt18XAgMBAAGjggFXMIIBUzA/BggrBgEFBQcBAQQzMDEwLwYIKwYBBQUH\nMAGGI2h0dHA6Ly9wb3N0ZWNlcnQucG9zdGUuaXQvcGktb2NzcENBMB0GA1UdDgQWBBRL64pGUJHw\nY7ok6cRMUgXvMBoLMjAfBgNVHSMEGDAWgBRs0025F7hHd0d+ULyAaELPZ7w/eTA+BgNVHSAENzA1\nMDMGCCtMMAEFAQEEMCcwJQYIKwYBBQUHAgEWGWh0dHA6Ly9wb3N0ZWNlcnQucG9zdGUuaXQwOAYD\nVR0fBDEwLzAtoCugKYYnaHR0cDovL3Bvc3RlY2VydC5wb3N0ZS5pdC9waS1DQS9jcmwuY3JsMA4G\nA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwJwYDVR0RBCAwHoEc\naWRwLXBvc3RlaWRAcG9zdGVpdGFsaWFuZS5pdDANBgkqhkiG9w0BAQsFAAOCAgEAp0EhITlTx+cO\naoXw//nBl6Q4y82MfSGfPJIw3ROV1z3tHBctaksi/RxAzyMD5beO2s8Q6lXx0sLMCcuUQmzHj3eJ\nbqn+6sIUr000dSlX/iPgVUc2dvPIZZg9xu38J8NvCfrtgAGY5iMVFMd3CZLFw0ybr+Bx/1K/NhQO\n7jxn0RSGA1J4mM2syVhEDUODs9kz3T4kXYUofwwvPL1a9xB9RBqbp7plYtbBBdftEORUQrWzH1mz\nNO4nlFkX9qgVrgFIIJJT2KadHoop1r65O9ffncK14qpNo3eTsNDq3hRlteb7ylmlJ8CoakUWZeXD\nDP9ZboWxZkyp+9903OrToRvOgeWSc+YrqcRZOv7r6tTALTk4U9OTKDG9/eNWSGQqD7Qd/9rssfF0\nuJEGHnbsk/Hvdxn8apgWN1Zwt6tsT7f/DO0Pdlaso9g7PVy8R+B3VkWAh76uCcICIPFBluC/ljaH\nV8hI+VsCLpMClo83YMCEM6E6nAPD22+fDR/DF9P73P04yUvJVHx4cnHPrpxVrPbaJoKrr9mUOLFy\nVRekX78ZRgiFiKYDNsiq9+148oRy+VehpmBoQ+T2EPeDFQ8JJ4xT8H7qdyr1swSk/9Lu4K0kw/yC\nTSb9K/wCuiHiuoSB54rzJoQxz90gS868r/+JGahYwHY5dUh1RbA4g5N8H3TDThc=", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://posteid.poste.it", + "attributeConsumingServiceName": "it|Poste Italiane SpA", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/sielteid.json b/spid-providers/resources/idp/sielteid.json new file mode 100644 index 0000000..010d564 --- /dev/null +++ b/spid-providers/resources/idp/sielteid.json @@ -0,0 +1,51 @@ +{ + "alias": "sielteid", + "displayName": "Sielte ID", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://identity.sieltecloud.it/simplesaml/saml2/idp/SLS.php", + "organizationDisplayNames": "it|http://www.sielte.it,en|http://www.sielte.it", + "organizationUrls": "it|http://www.sielte.it,en|http://www.sielte.it", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://identity.sieltecloud.it", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|Sielte S.p.A.,en|Sielte S.p.A.", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://identity.sieltecloud.it/simplesaml/saml2/idp/SSO.php", + "wantAuthnRequestsSigned": "true", + "encryptionPublicKey": "MIIDczCCAlugAwIBAgIJAMsX0iEKQM6xMA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAklUMQ4wDAYDVQQIDAVJdGFseTEgMB4GA1UEBwwXU2FuIEdyZWdvcmlvIGRpIENhdGFuaWExDzANBgNVBAoMBlNpZWx0ZTAeFw0xNTEyMTQwODE0MTVaFw0yNTEyMTMwODE0MTVaMFAxCzAJBgNVBAYTAklUMQ4wDAYDVQQIDAVJdGFseTEgMB4GA1UEBwwXU2FuIEdyZWdvcmlvIGRpIENhdGFuaWExDzANBgNVBAoMBlNpZWx0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANIRlOjM/tS9V9jYjJreqZSctuYriLfPTDgX2XdhWEbMpMpwA9p0bsbLQoC1gP0piLO+qbCsIh9+boPfb4/dLIA7E+Vmm5/+evOtzvjfHG4oXjZK6jo08QwkVV8Bm1jkakJPVZ57QFbyDSr+uBbIMY7CjA2LdgnIIwKN/kSfFhrZUMJ6ZxwegM100X5psfNPSV9WUtgHsvqlIlvydPo2rMm21sg+2d3Vtg8DthNSYRLqgazCc0NTsigrH7niSbJCO0nq/svMX2rSFdh5GFK7/pxT+c3OFWqIR8r+RX4qW+auJqkbTuNRwxV22Sm6r69ZJwV0WspvsVJi+FYqiyoWhgUCAwEAAaNQME4wHQYDVR0OBBYEFCUx063GwUhEFDllwCBe/+jdeW+XMB8GA1UdIwQYMBaAFCUx063GwUhEFDllwCBe/+jdeW+XMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADF94c3JwyBM86QBLeoUZxRYKPniba8B39FfJk0pb+LejKfZMvspOrOFgYQQ9UrS8IFkBX9Xr7/tjRbr2cPwZNjrEZhoq+NfcE09bnaWTyEl1IEKK8TWOupJj9UNVpYXX0LfIRrMwNEzAPQykOaqPOnyHxOCPTY957xXSo3jXOyvugtvPHbd+iliAzUoPm1tgiTKWS+EkQ/e22eFv5NEyT+oHiKovrQ+voPWOIvJVMjiTyxRic8fEnI9zzV0SxWvFvty77wgcYbeEuFZa3iidhojUge8o1uY/JUyQjFxcvvfAgWSIZwdHiNyWaAgwzLPmPCPsvBdR3xrlcDg/9Bd3D0=", + "validateSignature": "true", + "signingCertificate": "MIIDczCCAlugAwIBAgIJAMsX0iEKQM6xMA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAklUMQ4wDAYDVQQIDAVJdGFseTEgMB4GA1UEBwwXU2FuIEdyZWdvcmlvIGRpIENhdGFuaWExDzANBgNVBAoMBlNpZWx0ZTAeFw0xNTEyMTQwODE0MTVaFw0yNTEyMTMwODE0MTVaMFAxCzAJBgNVBAYTAklUMQ4wDAYDVQQIDAVJdGFseTEgMB4GA1UEBwwXU2FuIEdyZWdvcmlvIGRpIENhdGFuaWExDzANBgNVBAoMBlNpZWx0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANIRlOjM/tS9V9jYjJreqZSctuYriLfPTDgX2XdhWEbMpMpwA9p0bsbLQoC1gP0piLO+qbCsIh9+boPfb4/dLIA7E+Vmm5/+evOtzvjfHG4oXjZK6jo08QwkVV8Bm1jkakJPVZ57QFbyDSr+uBbIMY7CjA2LdgnIIwKN/kSfFhrZUMJ6ZxwegM100X5psfNPSV9WUtgHsvqlIlvydPo2rMm21sg+2d3Vtg8DthNSYRLqgazCc0NTsigrH7niSbJCO0nq/svMX2rSFdh5GFK7/pxT+c3OFWqIR8r+RX4qW+auJqkbTuNRwxV22Sm6r69ZJwV0WspvsVJi+FYqiyoWhgUCAwEAAaNQME4wHQYDVR0OBBYEFCUx063GwUhEFDllwCBe/+jdeW+XMB8GA1UdIwQYMBaAFCUx063GwUhEFDllwCBe/+jdeW+XMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADF94c3JwyBM86QBLeoUZxRYKPniba8B39FfJk0pb+LejKfZMvspOrOFgYQQ9UrS8IFkBX9Xr7/tjRbr2cPwZNjrEZhoq+NfcE09bnaWTyEl1IEKK8TWOupJj9UNVpYXX0LfIRrMwNEzAPQykOaqPOnyHxOCPTY957xXSo3jXOyvugtvPHbd+iliAzUoPm1tgiTKWS+EkQ/e22eFv5NEyT+oHiKovrQ+voPWOIvJVMjiTyxRic8fEnI9zzV0SxWvFvty77wgcYbeEuFZa3iidhojUge8o1uY/JUyQjFxcvvfAgWSIZwdHiNyWaAgwzLPmPCPsvBdR3xrlcDg/9Bd3D0=", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://identity.sieltecloud.it", + "attributeConsumingServiceName": "it|Sielte S.p.A.,en|Sielte S.p.A.", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/spiditaliaid.json b/spid-providers/resources/idp/spiditaliaid.json new file mode 100644 index 0000000..350c4e1 --- /dev/null +++ b/spid-providers/resources/idp/spiditaliaid.json @@ -0,0 +1,51 @@ +{ + "alias": "spiditaliaid", + "displayName": "SPIDItalia Register.it", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://spid.register.it/login/singleLogout", + "organizationDisplayNames": "it|Register.it S.p.A.", + "organizationUrls": "it|https//www.register.it", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://spid.register.it", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|Register.it S.p.A.", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://spid.register.it/login/sso", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIDazCCAlOgAwIBAgIED8R+MDANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJJVDELMAkGA1UECBMCRkkxETAPBgNVBAcTCGZsb3JlbmNlMREwDwYDVQQKEwhyZWdpc3RlcjERMA8GA1UECxMIcmVnaXN0ZXIxETAPBgNVBAMTCHJlZ2lzdGVyMB4XDTE3MDcxMDEwMzM0OVoXDTI3MDcwODEwMzM0OVowZjELMAkGA1UEBhMCSVQxCzAJBgNVBAgTAkZJMREwDwYDVQQHEwhmbG9yZW5jZTERMA8GA1UEChMIcmVnaXN0ZXIxETAPBgNVBAsTCHJlZ2lzdGVyMREwDwYDVQQDEwhyZWdpc3RlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANkYXHbm3q6xt3wrLAXnytswtj2JE1MM8aYmNXkTgDMCwO/+ahQOoQru6IBTbjfWH9jr+Woy54FDdX6bHl+5/mO6l/yAB/bKgwe5HmUjZJ5oakJjWucsSm+VkEwN2HquBZoN+mktju00xvLX5VAjmDHvZc/b8NhNr/FRKlYITboygkhGiUwGI3wLf3IaB76J0o7ugpW2WNLcywpX+p1VWZAMCdHBveBe/e42hh6WnWPqdwYUWHOgJ8HX4IzCHifiS1n6eUMgtoTQOmSvTQDwSjD0WWJE8tWSYt+txXg1t+3A3tbZOFu7T442wE7DtMdUL4+8gimQS+e8PxDK1uTqIPUCAwEAAaMhMB8wHQYDVR0OBBYEFMCgo1gzCIcUThQIs5g5ikfv1D7eMA0GCSqGSIb3DQEBCwUAA4IBAQBnGw3i3hQ37L8vyelkyZMeO3tLK65Cqti4oVrQZxClGV5zNA6fIMDY8Mci1UhLwjzp29POd/sez0vuHZ/Vmmygzoye4jTKr6c3jAh0u81FTzefBU+vIietm9RuV3sd7D9xq6EqOY1NDL+rkvBcTFtiwLEUm2kHYu/U67jk73pxOtmqxQvQeMU8oi42tehMZGLIGp3U5lGS8YGGl+GtkkQ2Z5/PSm67HGP81kTArG/QX+bX+ykypTJVg9hfb9zOFQidp1HkCRIez6YhDiP/ZLurd6Grt/wVfZPNBO8EOgy25AkRZlp+UD686BFg7qq5KKEbz3qmPrj8deHL3duacZcp", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://spid.register.it", + "attributeConsumingServiceName": "it|Register.it S.p.A.", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "wantAssertionsEncrypted": "true", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/teamsystemid.json b/spid-providers/resources/idp/teamsystemid.json new file mode 100644 index 0000000..cc06906 --- /dev/null +++ b/spid-providers/resources/idp/teamsystemid.json @@ -0,0 +1,50 @@ +{ + "alias": "teamsystemid", + "displayName": "TeamSystem ID", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://spid.teamsystem.com/idp/logout/post", + "organizationDisplayNames": "it|TeamSystem,en|TeamSystem", + "organizationUrls": "it|https://www.teamsystem.com,en|https://international.teamsystem.com/ww/", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://spid.teamsystem.com/idp", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|TeamSystem s.p.a.,en|TeamSystem s.p.a.", + "authnContextComparisonType": "minimum", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://spid.teamsystem.com/idp/sso/post", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIINTCCBh2gAwIBAgIIJz+ujRbSAYwwDQYJKoZIhvcNAQENBQAwgfsxCzAJBgNV\nBAYTAklUMQ0wCwYDVQQHDARSb21lMSYwJAYDVQQKDB1BZ2VuemlhIHBlciBsJ0l0\nYWxpYSBEaWdpdGFsZTEwMC4GA1UECwwnU2Vydml6aW8gQWNjcmVkaXRhbWVudG8g\nZSBwcm9nZXR0byBTUElEMTwwOgYDVQQDDDNQcm9nZXR0byBTUElEIC0gR2VzdG9y\naSBkaSBJZGVudGl0w6AgRGlnaXRhbGUgKElkUCkxKTAnBgkqhkiG9w0BCQEWGnBy\nb3RvY29sbG9AcGVjLmFnaWQuZ292Lml0MRowGAYDVQQFExFWQVRJVC05NzczNTAy\nMDU4NDAeFw0yMjA1MTAwMDAwMDBaFw0zMjA1MDkyMzU5NTlaMIGrMRowGAYDVQRh\nDBFWQVRJVC0wMTAzNTMxMDQxNDEcMBoGA1UEAwwTc3BpZC50ZWFtc3lzdGVtLmNv\nbTEaMBgGA1UECgwRVGVhbVN5c3RlbSBTLnAuQS4xKDAmBgNVBFMMH2h0dHBzOi8v\nc3BpZC50ZWFtc3lzdGVtLmNvbS9pZHAxCzAJBgNVBAYTAklUMQ8wDQYDVQQHDAZQ\nZXNhcm8xCzAJBgNVBAgMAlBVMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\nAQEAyNJMgyn+iquzTvLR5Z/eYBfOoyJIfI3rYcj5WOSlTzlqXXBCzdcROm/JKgrf\n3MOTEzH8RAn6XkSHXtJDtMpD7GlwYB0mo8scqDNtpszbhm/UXapJTrP7gy/UI3yf\nn99n4hvqkGOdld7w5vaAPS0w9PdcaRxY/7X4olHKBAx2cHAwiqhKuiFEDhfACRWs\nbw4gaIjVM7NuUtL/jG+PJV1NHrEn10vizE7IneMxDNqiQ14IjLL7pJMEPXwbXedz\nZsModKKAXIX5reNSegEU1Y386BCkmg4IMWd+DglmMJ4uuzcga1AppgjDuqb8yFDa\nNOKy/0Jivh2rs7u9boE4cLVBPQIDAQABo4IDCTCCAwUwCQYDVR0TBAIwADAdBgNV\nHQ4EFgQU/q5NWlPmylmZTsX0C2MwZkrx3b4wHwYDVR0jBBgwFoAUyF8jl8Jbn9To\nhwSTF77f5QNJd18wDgYDVR0PAQH/BAQDAgbAMBEGA1UdEQQKMAiCBmlkcC5pdDAW\nBgNVHRIEDzANggtzcGlkLmdvdi5pdDA/BgNVHR8EODA2MDSgMqAwhi5odHRwczov\nL2VpZGFzLmFnaWQuZ292Lml0L2NybC9jcmxfU1BJRF9JZFAuY3JsMGoGCCsGAQUF\nBwEBBF4wXDBEBggrBgEFBQcwAoY4aHR0cDovL2VpZGFzLmFnaWQuZ292Lml0L2Nl\ncnRpZmljYXRpL1N1Yl9DQV9TUElEX0lkUC5jZXIwFAYIKwYBBQUHMAGGCGh0dHBz\nOi8vMIIBzgYDVR0gBIIBxTCCAcEwCQYHBACORgEGAjCBlQYEK0wQBjCBjDBEBggr\nBgEFBQcCAjA4GjZFbGVjdHJvbmljIGNlcnRpZmljYXRlIGNvbmZvcm1pbmcgd2l0\naCBBR0lEIEd1aWRlbGluZXMwRAYIKwYBBQUHAgIwOBo2Q2VydGlmaWNhdG8gZWxl\ndHRyb25pY28gY29uZm9ybWUgYWxsZSBMaW5lZSBndWlkYSBBZ0lEMHIGBitMEAQB\nAjBoMDkGCCsGAQUFBwICMC0aK1NQSUQ6IGdlc3RvcmUgZGVsbGUgaWRlbnRpdOAg\nZGlnaXRhbGkgKElkUCkwKwYIKwYBBQUHAgIwHxodU1BJRDogSWRlbnRpdHkgUHJv\ndmlkZXIgKElkUCkwCAYGBACPegEDME0GBCtMEAQwRTBDBggrBgEFBQcCARY3aHR0\ncHM6Ly9laWRhcy5hZ2lkLmdvdi5pdC9jcHMvQWdJRF9lSURBU19yb290Q0FfY3Bz\nLnBkZjBPBgYEAI5GAQUwRTBDBggrBgEFBQcCARY3aHR0cHM6Ly9laWRhcy5hZ2lk\nLmdvdi5pdC9jcHMvQWdJRF9lSURBU19yb290Q0FfY3BzLnBkZjANBgkqhkiG9w0B\nAQ0FAAOCAgEAG9XZeAkIuqSmYb6bq5WrcI2FQtVrfbMH1CXGDKytZUsH5phkGfk/\n8UaIfkbHhnWakM4H9J2gnvfhKorfMt2FHyXFFJ38hlWR8MhFziqthXLUxyLZpUMn\nh8CcNQyFpNz7xbZk/qN5yFfJyY4Rggm1qdgCNR1LsVI3hjuaORTAzvy4kLjfuU5r\nnVYPcxpHF7feJKlN03d8JRKYaIi5U+QVYtYJpTcE7jeYmn4Ewfry2BDCOsnljeYl\ngm3fF8EEVpMfHIhvJg8evATWmKWHpXL2BRtVrl7TfhvtWqKv4tLff+Lv2YqRpmYu\noApA48/MB4QxwAPUBnmQb3CxVGs6OCbE/tdUfda9HuHP5MXYLtTVbRYu8pHEPnaN\njPA8y90KRw2wiedgjgOG8BxOkhVF/cYs3yH+0hbPS5Oji27t0P2g9eG/p9TOy4AI\ngUykFimVFk6HV9znknrFSdgsePSp+T5zy45Jdi1z4/RgJN10szJfqEBuvd8MhUu4\nmeVgfDqXrqavCVzGpSLuicdk41sTOviBz+PEgbQ/qP9KHQv67SHoF4US9Pp9tkyj\nVFUs7lBnrlFAPpOzd97XdiZfotCA5umibqlxLshy4UK7yl2LZFllpxrfiXTCDASM\nKlMMIcIsWx0lU/qw5KPpqvXELiya791kohJTi+9pyG7LXIOHHA0whr0=", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://spid.teamsystem.com/idp", + "attributeConsumingServiceName": "it|TeamSystem s.p.a.,en|TeamSystem s.p.a.", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/test/privatespid.json b/spid-providers/resources/idp/test/privatespid.json new file mode 100644 index 0000000..d66e93e --- /dev/null +++ b/spid-providers/resources/idp/test/privatespid.json @@ -0,0 +1,71 @@ +{ + "alias": "privatespid", + "displayName": "MySPIDPrivate", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "billingCodiceFiscale": "CDCFCL80P47H505G", + "billingIdPaese": "idpaese", + "authnContextClassRef": "https://www.spid.gov.it/SpidL2", + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "organizationDisplayNames": "en|Online services,it|Servizi online", + "billingSedeComune": "comune", + "billingTerzoIntermediarioSoggettoEmittente": "emittente terzo", + "loginHint": "false", + "organizationNames": "en|Online services,it|Servizi online", + "billingSedeNazione": "nazione", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "billingContactCompany": "Online services", + "billingSedeNumeroCivico": "numero", + "singleSignOnServiceUrl": "https://localhost:8443/demo/samlsso", + "wantAuthnRequestsSigned": "true", + "billingAnagraficaCodiceEORI": "eori code", + "principalAttribute": "fiscalNumber", + "billingContactPhone": "+3912345", + "attributeConsumingServiceName": "en|Online services,it|Servizi online", + "signSpMetadata": "true", + "otherContactVatNumber": "IT123456", + "otherContactIsSpPrivate": "true", + "postBindingAuthnRequest": "true", + "billingSedeIndirizzo": "indirizzo sede", + "billingContactEmail": "a@b.it", + "billingAnagraficaDenominazione": "denominazione", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://localhost:8443/demo/samlsso", + "billingSedeProvincia": "provincia", + "organizationUrls": "en|https://localhost:8443, it|https://localhost:8443", + "billingAnagraficaTitolo": "titolo", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "idpEntityId": "https://localhost:8443/demo", + "allowCreate": "true", + "billingCessionarioCommittenteExtension": "cessionario committente", + "billingAnagraficaNome": "nome", + "validateSignature": "true", + "signingCertificate": "MIIEGDCCAwCgAwIBAgIJAOrYj9oLEJCwMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTENMAsGA1UEChMEQWdJRDESMBAGA1UECxMJQWdJRCBURVNUMRQwEgYDVQQDEwthZ2lkLmdvdi5pdDAeFw0xOTA0MTExMDAyMDhaFw0yNTAzMDgxMDAyMDhaMGUxCzAJBgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTENMAsGA1UEChMEQWdJRDESMBAGA1UECxMJQWdJRCBURVNUMRQwEgYDVQQDEwthZ2lkLmdvdi5pdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8kJVo+ugRrbbv9xhXCuVrqi4B7/MQzQc62ocwlFFujJNd4m1mXkUHFbgvwhRkQqo2DAmFeHiwCkJT3K1eeXIFhNFFroEzGPzONyekLpjNvmYIs1CFvirGOj0bkEiGaKEs+/umzGjxIhy5JQlqXE96y1+Izp2QhJimDK0/KNij8I1bzxseP0Ygc4SFveKS+7QO+PrLzWklEWGMs4DM5Zc3VRK7g4LWPWZhKdImC1rnS+/lEmHSvHisdVp/DJtbSrZwSYTRvTTz5IZDSq4kAzrDfpj16h7b3t3nFGc8UoY2Ro4tRZ3ahJ2r3b79yK6C5phY7CAANuW3gDdhVjiBNYs0CAwEAAaOByjCBxzAdBgNVHQ4EFgQU3/7kV2tbdFtphbSA4LH7+w8SkcwwgZcGA1UdIwSBjzCBjIAU3/7kV2tbdFtphbSA4LH7+w8SkcyhaaRnMGUxCzAJBgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTENMAsGA1UEChMEQWdJRDESMBAGA1UECxMJQWdJRCBURVNUMRQwEgYDVQQDEwthZ2lkLmdvdi5pdIIJAOrYj9oLEJCwMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJNFqXg/V3aimJKUmUaqmQEEoSc3qvXFITvT5f5bKw9yk/NVhR6wndL+z/24h1OdRqs76blgH8k116qWNkkDtt0AlSjQOx5qvFYh1UviOjNdRI4WkYONSw+vuavcx+fB6O5JDHNmMhMySKTnmRqTkyhjrch7zaFIWUSV7hsBuxpqmrWDoLWdXbV3eFH3mINA5AoIY/m0bZtzZ7YNgiFWzxQgekpxd0vcTseMnCcXnsAlctdir0FoCZztxMuZjlBjwLTtM6Ry3/48LMM8Z+lw7NMciKLLTGQyU8XmKKSSOh0dGh5Lrlt5GxIIJkH81C0YimWebz8464QPL3RbLnTKg+c=", + "billingAnagraficaCognome": "cognome", + "billingSedeCap": "codice postale", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "entityId": "http://localhost:8080/realms/my-spid", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "billingIdCodice": "idcodice", + "wantAssertionsSigned": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code" + } +} diff --git a/spid-providers/resources/idp/test/spidtestidp.json b/spid-providers/resources/idp/test/spidtestidp.json new file mode 100644 index 0000000..d4dd4ea --- /dev/null +++ b/spid-providers/resources/idp/test/spidtestidp.json @@ -0,0 +1,48 @@ +{ + "alias": "spidtestidp", + "displayName": "MySPIDPublic", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://localhost:8443/samlsso", + "organizationDisplayNames": "en|Online services,it|Servizi online", + "organizationUrls": "en|https://localhost:8443, it|https://localhost:8443", + "xmlSigKeyInfoKeyNameTransformer": "NONE", + "allowCreate": "true", + "organizationNames": "en|Online services,it|Servizi online", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://localhost:8443/samlsso", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIIEGDCCAwCgAwIBAgIJAOrYj9oLEJCwMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTENMAsGA1UEChMEQWdJRDESMBAGA1UECxMJQWdJRCBURVNUMRQwEgYDVQQDEwthZ2lkLmdvdi5pdDAeFw0xOTA0MTExMDAyMDhaFw0yNTAzMDgxMDAyMDhaMGUxCzAJBgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTENMAsGA1UEChMEQWdJRDESMBAGA1UECxMJQWdJRCBURVNUMRQwEgYDVQQDEwthZ2lkLmdvdi5pdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8kJVo+ugRrbbv9xhXCuVrqi4B7/MQzQc62ocwlFFujJNd4m1mXkUHFbgvwhRkQqo2DAmFeHiwCkJT3K1eeXIFhNFFroEzGPzONyekLpjNvmYIs1CFvirGOj0bkEiGaKEs+/umzGjxIhy5JQlqXE96y1+Izp2QhJimDK0/KNij8I1bzxseP0Ygc4SFveKS+7QO+PrLzWklEWGMs4DM5Zc3VRK7g4LWPWZhKdImC1rnS+/lEmHSvHisdVp/DJtbSrZwSYTRvTTz5IZDSq4kAzrDfpj16h7b3t3nFGc8UoY2Ro4tRZ3ahJ2r3b79yK6C5phY7CAANuW3gDdhVjiBNYs0CAwEAAaOByjCBxzAdBgNVHQ4EFgQU3/7kV2tbdFtphbSA4LH7+w8SkcwwgZcGA1UdIwSBjzCBjIAU3/7kV2tbdFtphbSA4LH7+w8SkcyhaaRnMGUxCzAJBgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTENMAsGA1UEChMEQWdJRDESMBAGA1UECxMJQWdJRCBURVNUMRQwEgYDVQQDEwthZ2lkLmdvdi5pdIIJAOrYj9oLEJCwMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJNFqXg/V3aimJKUmUaqmQEEoSc3qvXFITvT5f5bKw9yk/NVhR6wndL+z/24h1OdRqs76blgH8k116qWNkkDtt0AlSjQOx5qvFYh1UviOjNdRI4WkYONSw+vuavcx+fB6O5JDHNmMhMySKTnmRqTkyhjrch7zaFIWUSV7hsBuxpqmrWDoLWdXbV3eFH3mINA5AoIY/m0bZtzZ7YNgiFWzxQgekpxd0vcTseMnCcXnsAlctdir0FoCZztxMuZjlBjwLTtM6Ry3/48LMM8Z+lw7NMciKLLTGQyU8XmKKSSOh0dGh5Lrlt5GxIIJkH81C0YimWebz8464QPL3RbLnTKg+c=", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "idpEntityId": "https://localhost:8443", + "entityId": "https://localhost:8443", + "attributeConsumingServiceName": "en|Online services,it|Servizi online", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Online services", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/idp/timid.json b/spid-providers/resources/idp/timid.json new file mode 100644 index 0000000..51132a4 --- /dev/null +++ b/spid-providers/resources/idp/timid.json @@ -0,0 +1,51 @@ +{ + "alias": "timid", + "displayName": "Tim ID", + "providerId": "saml-spid", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "First broker login SPID", + "config": { + "authnContextClassRefs": "[\"https://www.spid.gov.it/SpidL2\"]", + "postBindingLogout": "true", + "postBindingResponse": "true", + "singleLogoutServiceUrl": "https://login.id.tim.it/affwebservices/public/saml2slo", + "organizationDisplayNames": "it|Trust Technologies srl", + "organizationUrls": "it|https://www.trusttechnologies.it", + "xmlSigKeyInfoKeyNameTransformer": "KEY_ID", + "idpEntityId": "https://login.id.tim.it/affwebservices/public/saml2sso", + "loginHint": "false", + "allowCreate": "true", + "organizationNames": "it|TI Trust Technologies srl", + "authnContextComparisonType": "exact", + "syncMode": "FORCE", + "singleSignOnServiceUrl": "https://login.id.tim.it/affwebservices/public/saml2sso", + "wantAuthnRequestsSigned": "true", + "validateSignature": "true", + "signingCertificate": "MIID0jCCArqgAwIBAgIUXDUOKL3WuolxDw96Fk9es8rIt6kwDQYJKoZIhvcNAQELBQAwgYsxCzAJBgNVBAYTAklUMS4wLAYDVQQKDCVUZWxlY29tIEl0YWxpYSBUcnVzdCBUZWNobm9sb2dpZXMgc3JsMSgwJgYDVQQLDB9TZXJ2aXppIHBlciBsJ2lkZW50aXRhIGRpZ2l0YWxlMSIwIAYDVQQDDBlUSSBUcnVzdCBUZWNobm9sb2dpZXMgc3JsMB4XDTIxMTExMTE3MDMyMFoXDTI1MTExMDE3MDMyMFowgYsxCzAJBgNVBAYTAklUMS4wLAYDVQQKDCVUZWxlY29tIEl0YWxpYSBUcnVzdCBUZWNobm9sb2dpZXMgc3JsMSgwJgYDVQQLDB9TZXJ2aXppIHBlciBsJ2lkZW50aXRhIGRpZ2l0YWxlMSIwIAYDVQQDDBlUSSBUcnVzdCBUZWNobm9sb2dpZXMgc3JsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6sIS3+3iZSaAIyVywahlbpua2uJ/XmpV68P1e1STJpHoaj32STdHhqZnnb4Y/FshP1NUolzNolPXAYDmDduW1OnGndJZ+G9Hjh1PCkdiRw+p0FjhQAsGJkn8NdgTIHLJjqN1qQwtOsVGab8ScyA3mtmj3xKYuBhUoweuATzC7f5r7FfIoc3cy6N5lgrpZpfeAChxLwoHVjoAVgIBuemi6HAzmd4/BI06KzOcR7+dBVi4+uiseldxrJ5bhnjZKIwgkX14y9UA84Y+e+rMtyT8cT3XXi9NazZl5Ej5/bQPqqVsbg6tXzQSfEJD6JEjuYeC0RUKMS/EJn3hL5VLzTJ1NwIDAQABoywwKjAdBgNVHQ4EFgQUfctFZ8bRtmEvXPRlqgVDuggY/ZwwCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAQEA0lszHadknPfE17IWGWsgvlXOdKMnWcl9H5rEYmsWwDB9FJG9XAZvPMcVv1kkWi6XZI/8N2Twhu1BdZkdvntDRscuck8wxxIpkRV7CwlcqNFZ/IwjDBxOBa8Q1J850p+qP8A9apsLLPUlu/oLygNDWIXzcOjMqnPkEP+XXUNYPto5iV+OyDzLLacCYqDDHcvDewWLmEjt35X967KcM+m7K2zGRLWfqcZPIjJJOkpNjgcs+MaisMrGDyOKiD16v0LpwVyIpTqXvDk7KHo8CUNXDxyLxZzB6WffgnOgjXTfU3vluweOx0qQy/VxIupDlNBKiZB4gnt1oAfnaMbqla9wcw==", + "nameIDPolicyFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + "principalAttribute": "fiscalNumber", + "entityId": "https://login.id.tim.it/affwebservices/public/saml2sso", + "attributeConsumingServiceName": "it|TI Trust Technologies srl", + "signSpMetadata": "true", + "signatureAlgorithm": "RSA_SHA256", + "wantAssertionsEncrypted": "true", + "useJwksUrl": "true", + "wantAssertionsSigned": "true", + "postBindingAuthnRequest": "true", + "forceAuthn": "true", + "attributeConsumingServiceIndex": "1", + "addExtensionsElementWithKeyInfo": "false", + "principalType": "ATTRIBUTE", + "otherContactCompany": "Example S.p.a.", + "otherContactPhone": "+39123456789", + "otherContactEmail": "info@example.it", + "otherContactIpaCode": "ipa_code", + "otherContactIsSpPrivate": "false" + } +} diff --git a/spid-providers/resources/mappers/address_mm.json b/spid-providers/resources/mappers/address_mm.json new file mode 100644 index 0000000..a80854f --- /dev/null +++ b/spid-providers/resources/mappers/address_mm.json @@ -0,0 +1,11 @@ +{ + "name": "Address", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-address", + "attribute": "address", + "attribute.name": "address" + } +} diff --git a/spid-providers/resources/mappers/companyname_mm.json b/spid-providers/resources/mappers/companyname_mm.json new file mode 100644 index 0000000..bf8f033 --- /dev/null +++ b/spid-providers/resources/mappers/companyname_mm.json @@ -0,0 +1,11 @@ +{ + "name": "Company Name", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-companyName", + "attribute": "companyName", + "attribute.name": "companyName" + } +} diff --git a/spid-providers/resources/mappers/countyofbirth_mm.json b/spid-providers/resources/mappers/countyofbirth_mm.json new file mode 100644 index 0000000..92e3bd6 --- /dev/null +++ b/spid-providers/resources/mappers/countyofbirth_mm.json @@ -0,0 +1,11 @@ +{ + "name": "County Of Birth", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-countyOfBirth", + "attribute": "countyOfBirth", + "attribute.name": "countyOfBirth" + } +} diff --git a/spid-providers/resources/mappers/dateofbirth_mm.json b/spid-providers/resources/mappers/dateofbirth_mm.json new file mode 100644 index 0000000..193364f --- /dev/null +++ b/spid-providers/resources/mappers/dateofbirth_mm.json @@ -0,0 +1,11 @@ +{ + "name": "Date Of Birth", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-dateOfBirth", + "attribute": "dateOfBirth", + "attribute.name": "dateOfBirth" + } +} diff --git a/spid-providers/resources/mappers/digitaladdress_mm.json b/spid-providers/resources/mappers/digitaladdress_mm.json new file mode 100644 index 0000000..9fbecf2 --- /dev/null +++ b/spid-providers/resources/mappers/digitaladdress_mm.json @@ -0,0 +1,11 @@ +{ + "name": "Digital Address", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-digitalAddress", + "attribute": "digitalAddress", + "attribute.name": "digitalAddress" + } +} diff --git a/spid-providers/resources/mappers/email_mm.json b/spid-providers/resources/mappers/email_mm.json new file mode 100644 index 0000000..05f2408 --- /dev/null +++ b/spid-providers/resources/mappers/email_mm.json @@ -0,0 +1,11 @@ +{ + "name": "Email", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-email", + "attribute": "email", + "attribute.name": "email" + } +} \ No newline at end of file diff --git a/spid-providers/resources/mappers/firstname_mm.json b/spid-providers/resources/mappers/firstname_mm.json new file mode 100644 index 0000000..fa3d960 --- /dev/null +++ b/spid-providers/resources/mappers/firstname_mm.json @@ -0,0 +1,10 @@ +{ + "name": "First Name", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "firstName", + "attribute.name": "name" + } +} \ No newline at end of file diff --git a/spid-providers/resources/mappers/gender_mm.json b/spid-providers/resources/mappers/gender_mm.json new file mode 100644 index 0000000..5042722 --- /dev/null +++ b/spid-providers/resources/mappers/gender_mm.json @@ -0,0 +1,11 @@ +{ + "name": "Gender", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-gender", + "attribute": "gender", + "attribute.name": "gender" + } +} diff --git a/spid-providers/resources/mappers/lastname_mm.json b/spid-providers/resources/mappers/lastname_mm.json new file mode 100644 index 0000000..449284d --- /dev/null +++ b/spid-providers/resources/mappers/lastname_mm.json @@ -0,0 +1,11 @@ +{ + "name": "Last Name", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "lastName", + "attribute": "familyName", + "attribute.name": "familyName" + } +} \ No newline at end of file diff --git a/spid-providers/resources/mappers/mobilephone_mm.json b/spid-providers/resources/mappers/mobilephone_mm.json new file mode 100644 index 0000000..47b71a1 --- /dev/null +++ b/spid-providers/resources/mappers/mobilephone_mm.json @@ -0,0 +1,11 @@ +{ + "name": "Mobile Phone", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-mobilePhone", + "attribute": "mobilePhone", + "attribute.name": "mobilePhone" + } +} diff --git a/spid-providers/resources/mappers/placeofbirth_mm.json b/spid-providers/resources/mappers/placeofbirth_mm.json new file mode 100644 index 0000000..3f0a97f --- /dev/null +++ b/spid-providers/resources/mappers/placeofbirth_mm.json @@ -0,0 +1,11 @@ +{ + "name": "Place Of Birth", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-placeOfBirth", + "attribute": "placeOfBirth", + "attribute.name": "placeOfBirth" + } +} diff --git a/spid-providers/resources/mappers/spidcode_mm.json b/spid-providers/resources/mappers/spidcode_mm.json new file mode 100644 index 0000000..5b30ccd --- /dev/null +++ b/spid-providers/resources/mappers/spidcode_mm.json @@ -0,0 +1,11 @@ +{ + "name": "SPID Code", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-spidCode", + "attribute": "spidCode", + "attribute.name": "spidCode" + } +} diff --git a/spid-providers/resources/mappers/taxid_mm.json b/spid-providers/resources/mappers/taxid_mm.json new file mode 100644 index 0000000..48e40c3 --- /dev/null +++ b/spid-providers/resources/mappers/taxid_mm.json @@ -0,0 +1,11 @@ +{ + "name": "Fiscal Number", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-fiscalNumber", + "attribute": "fiscalNumber", + "attribute.name": "fiscalNumber" + } +} diff --git a/spid-providers/resources/mappers/username_mm.json b/spid-providers/resources/mappers/username_mm.json new file mode 100644 index 0000000..41e7df9 --- /dev/null +++ b/spid-providers/resources/mappers/username_mm.json @@ -0,0 +1,10 @@ +{ + "name": "Username", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-saml-username-idp-mapper", + "config": { + "template": "${ATTRIBUTE.fiscalNumber}", + "syncMode": "INHERIT", + "target": "BROKER_USERNAME" + } +} \ No newline at end of file diff --git a/spid-providers/resources/mappers/vatnumber_mm.json b/spid-providers/resources/mappers/vatnumber_mm.json new file mode 100644 index 0000000..2560749 --- /dev/null +++ b/spid-providers/resources/mappers/vatnumber_mm.json @@ -0,0 +1,11 @@ +{ + "name": "VAT Number", + "identityProviderAlias": "CHANGE-IT", + "identityProviderMapper": "spid-user-attribute-idp-mapper", + "config": { + "syncMode": "INHERIT", + "user.attribute": "spid-ivaCode", + "attribute": "ivaCode", + "attribute.name": "ivaCode" + } +} diff --git a/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java b/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java index b26cab8..cec2d3c 100644 --- a/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java +++ b/src/main/java/org/keycloak/broker/spid/SpidIdentityProvider.java @@ -35,12 +35,15 @@ 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; import org.keycloak.dom.saml.v2.protocol.ResponseType; import org.keycloak.events.EventBuilder; import org.keycloak.models.FederatedIdentityModel; +import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.KeyManager; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; @@ -52,6 +55,7 @@ import org.keycloak.protocol.saml.SamlSessionUtils; import org.keycloak.protocol.saml.mappers.SamlMetadataDescriptorUpdater; import org.keycloak.protocol.saml.preprocessor.SamlAuthenticationPreprocessor; +import org.keycloak.protocol.saml.SAMLEncryptionAlgorithms; import org.keycloak.saml.SAML2AuthnRequestBuilder; import org.keycloak.saml.SAML2LogoutRequestBuilder; import org.keycloak.saml.SAML2NameIDBuilder; @@ -78,10 +82,11 @@ 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; @@ -89,11 +94,15 @@ import java.io.StringWriter; import java.net.URI; import java.security.KeyPair; +import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.Iterator; import java.util.List; import java.util.Objects; +import java.util.Map.Entry; +import java.util.stream.Collectors; /** * @author Pedro Igor @@ -110,7 +119,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 @@ -271,7 +280,7 @@ public void authenticationFinished(AuthenticationSessionModel authSession, Broke @Override public Response retrieveToken(KeycloakSession session, FederatedIdentityModel identity) { - return Response.ok(identity.getToken()).build(); + return Response.ok(identity.getToken()).type(MediaType.TEXT_PLAIN_TYPE).build(); } @Override @@ -305,7 +314,7 @@ public Response keycloakInitiatedBrowserLogout(KeycloakSession session, UserSess if (getConfig().isBackchannelSupported()) { backchannelLogout(session, userSession, uriInfo, realm); return null; - } else { + } else { try { LogoutRequestType logoutRequest = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl); if (logoutRequest.getDestination() != null) { @@ -365,12 +374,17 @@ private JaxrsSAML2BindingBuilder buildLogoutBinding(KeycloakSession session, Use public Response export(UriInfo uriInfo, RealmModel realm, String format) { try { - URI authnBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.getUri(); + URI authnResponseBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.getUri(); if (getConfig().isPostBindingAuthnRequest()) { - authnBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri(); + authnResponseBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri(); } + URI logoutBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.getUri(); + + if (getConfig().isPostBindingLogout()) { + logoutBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri(); + } URI endpoint = uriInfo.getBaseUriBuilder() .path("realms").path(realm.getName()) .path("broker") @@ -383,76 +397,92 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) { boolean wantAssertionsEncrypted = getConfig().isWantAssertionsEncrypted(); String entityId = getEntityId(uriInfo, realm); String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat(); - int attributeConsumingServiceIndex = getConfig().getAttributeConsumingServiceIndex() != null ? getConfig().getAttributeConsumingServiceIndex(): 1; - String attributeConsumingServiceName = getConfig().getAttributeConsumingServiceName(); - List signingKeys = new LinkedList<>(); - List encryptionKeys = new LinkedList<>(); - session.keys().getKeysStream(realm, KeyUse.SIG, Algorithm.RS256) - .filter(Objects::nonNull) + // We export all keys for algorithm RS256, both active and passive so IDP is able to verify signature even + // if a key rotation happens in the meantime + List signingKeys = session.keys().getKeysStream(realm, KeyUse.SIG, Algorithm.RS256) .filter(key -> key.getCertificate() != null) .sorted(SamlService::compareKeys) - .forEach(key -> { + .map(key -> { try { - Element element = SPMetadataDescriptor - .buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); - signingKeys.add(element); - - if (key.getStatus() == KeyStatus.ACTIVE) { - encryptionKeys.add(element); - } + return SPMetadataDescriptor.buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); + } catch (ParserConfigurationException e) { + logger.warn("Failed to export SAML SP Metadata!", e); + throw new RuntimeException(e); + } + }) + .map(key -> SPMetadataDescriptor.buildKeyDescriptorType(key, KeyTypes.SIGNING, (String) null)) + .collect(Collectors.toList()); + + // We export only active ENC keys so IDP uses different key as soon as possible if a key rotation happens + String encAlg = getConfig().getEncryptionAlgorithm(); + List encryptionKeys = session.keys().getKeysStream(realm) + .filter(key -> key.getStatus().isActive() && KeyUse.ENC.equals(key.getUse()) + && (encAlg == null || Objects.equals(encAlg, key.getAlgorithmOrDefault())) + && SAMLEncryptionAlgorithms.forKeycloakIdentifier(key.getAlgorithm()) != null + && key.getCertificate() != null) + .sorted(SamlService::compareKeys) + .map(key -> { + Element keyInfo; + try { + keyInfo = SPMetadataDescriptor.buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); } catch (ParserConfigurationException e) { logger.warn("Failed to export SAML SP Metadata!", e); throw new RuntimeException(e); } - }); + + return SPMetadataDescriptor.buildKeyDescriptorType(keyInfo, KeyTypes.ENCRYPTION, SAMLEncryptionAlgorithms.forKeycloakIdentifier(key.getAlgorithm()).getXmlEncIdentifiers()); + }) + .collect(Collectors.toList()); // Prepare the metadata descriptor model StringWriter sw = new StringWriter(); XMLStreamWriter writer = StaxUtil.getXMLStreamWriter(sw); SAMLMetadataWriter metadataWriter = new SAMLMetadataWriter(writer); - EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPdescriptor( - authnBinding, authnBinding, endpoint, endpoint, + EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPDescriptor( + authnResponseBinding, logoutBinding, endpoint, endpoint, wantAuthnRequestsSigned, wantAssertionsSigned, wantAssertionsEncrypted, entityId, nameIDPolicyFormat, signingKeys, encryptionKeys); - // Create the AttributeConsumingService - AttributeConsumingServiceType attributeConsumingService = new AttributeConsumingServiceType(attributeConsumingServiceIndex); - attributeConsumingService.setIsDefault(true); - - if (attributeConsumingServiceName != null && attributeConsumingServiceName.length() > 0) - { - String currentLocale = realm.getDefaultLocale() == null ? "en": realm.getDefaultLocale(); + // Create the AttributeConsumingService if at least one attribute importer mapper exists + List> metadataAttrProviders = new ArrayList<>(); + realm.getIdentityProviderMappersByAliasStream(getConfig().getAlias()) + .forEach(mapper -> { + IdentityProviderMapper target = (IdentityProviderMapper) session.getKeycloakSessionFactory().getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper()); + if (target instanceof SamlMetadataDescriptorUpdater) + metadataAttrProviders.add(new java.util.AbstractMap.SimpleEntry<>(mapper, (SamlMetadataDescriptorUpdater)target)); + }); + + if (!metadataAttrProviders.isEmpty()) { + int attributeConsumingServiceIndex = getConfig().getAttributeConsumingServiceIndex() != null ? getConfig().getAttributeConsumingServiceIndex() : 1; + String attributeConsumingServiceName = getConfig().getAttributeConsumingServiceName(); + //default value for attributeConsumingServiceName + if (attributeConsumingServiceName == null) + attributeConsumingServiceName = realm.getDisplayName() != null ? realm.getDisplayName() : realm.getName() ; + AttributeConsumingServiceType attributeConsumingService = new AttributeConsumingServiceType(attributeConsumingServiceIndex); + attributeConsumingService.setIsDefault(true); + + String currentLocale = realm.getDefaultLocale() == null ? "en" : realm.getDefaultLocale(); LocalizedNameType attributeConsumingServiceNameElement = new LocalizedNameType(currentLocale); attributeConsumingServiceNameElement.setValue(attributeConsumingServiceName); attributeConsumingService.addServiceName(attributeConsumingServiceNameElement); - } - // Look for the SP descriptor and add the attribute consuming service - for (EntityDescriptorType.EDTChoiceType choiceType: entityDescriptor.getChoiceType()) { - List descriptors = choiceType.getDescriptors(); - - if (descriptors != null) { - for (EntityDescriptorType.EDTDescriptorChoiceType descriptor: descriptors) { - if (descriptor.getSpDescriptor() != null) { - descriptor.getSpDescriptor().addAttributeConsumerService(attributeConsumingService); - } + // Look for the SP descriptor and add the attribute consuming service + for (EntityDescriptorType.EDTChoiceType choiceType : entityDescriptor.getChoiceType()) { + List descriptors = choiceType.getDescriptors(); + for (EntityDescriptorType.EDTDescriptorChoiceType descriptor : descriptors) { + descriptor.getSpDescriptor().addAttributeConsumerService(attributeConsumingService); } } - } - - // Add the attribute mappers - realm.getIdentityProviderMappersByAliasStream(getConfig().getAlias()) - .forEach(mapper -> { - IdentityProviderMapper target = (IdentityProviderMapper) session.getKeycloakSessionFactory().getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper()); - if (target instanceof SamlMetadataDescriptorUpdater) - { - SamlMetadataDescriptorUpdater metadataAttrProvider = (SamlMetadataDescriptorUpdater)target; - metadataAttrProvider.updateMetadata(mapper, entityDescriptor); - } + + // Add the attribute mappers + metadataAttrProviders.forEach(mapper -> { + SamlMetadataDescriptorUpdater metadataAttrProvider = mapper.getValue(); + metadataAttrProvider.updateMetadata(mapper.getKey(), entityDescriptor); }); + } // Write the metadata and export it to a string metadataWriter.writeEntityDescriptor(entityDescriptor); @@ -463,13 +493,15 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) { if (getConfig().isSignSpMetadata()) { KeyManager.ActiveRsaKey activeKey = session.keys().getActiveRsaKey(realm); - String keyName = getConfig().getXmlSigKeyInfoKeyNameTransformer().getKeyName(activeKey.getKid(), activeKey.getCertificate()); + X509Certificate certificate = activeKey.getCertificate(); + String keyName = getConfig().getXmlSigKeyInfoKeyNameTransformer().getKeyName(activeKey.getKid(), certificate); KeyPair keyPair = new KeyPair(activeKey.getPublicKey(), activeKey.getPrivateKey()); Document metadataDocument = DocumentUtil.getDocument(descriptor); SAML2Signature signatureHelper = new SAML2Signature(); signatureHelper.setSignatureMethod(getSignatureAlgorithm().getXmlSignatureMethod()); signatureHelper.setDigestMethod(getSignatureAlgorithm().getXmlSignatureDigestMethod()); + signatureHelper.setX509Certificate(certificate); Node nextSibling = metadataDocument.getDocumentElement().getFirstChild(); signatureHelper.setNextSibling(nextSibling); @@ -500,3 +532,4 @@ public IdentityProviderDataMarshaller getMarshaller() { return new SAMLDataMarshaller(); } } + diff --git a/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderConfig.java b/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderConfig.java index 3f036a4..0ea5dd9 100644 --- a/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderConfig.java +++ b/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderConfig.java @@ -18,14 +18,23 @@ import static org.keycloak.common.util.UriUtils.checkUrl; +import java.util.List; +import java.util.spi.ResourceBundleProvider; + import org.keycloak.common.enums.SslRequired; import org.keycloak.dom.saml.v2.protocol.AuthnContextComparisonType; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.RealmModel; import org.keycloak.protocol.saml.SamlPrincipalType; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.provider.ProviderConfigurationBuilder; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.util.XmlKeyInfoKeyNameTransformer; +import org.keycloak.services.messages.Messages; +/** + * @author Pedro Igor + */ public class SpidIdentityProviderConfig extends IdentityProviderModel { public static final XmlKeyInfoKeyNameTransformer DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER = XmlKeyInfoKeyNameTransformer.NONE; @@ -41,6 +50,7 @@ public class SpidIdentityProviderConfig extends IdentityProviderModel { public static final String POST_BINDING_LOGOUT = "postBindingLogout"; public static final String POST_BINDING_RESPONSE = "postBindingResponse"; public static final String SIGNATURE_ALGORITHM = "signatureAlgorithm"; + public static final String ENCRYPTION_ALGORITHM = "encryptionAlgorithm"; public static final String SIGNING_CERTIFICATE_KEY = "signingCertificate"; public static final String SINGLE_LOGOUT_SERVICE_URL = "singleLogoutServiceUrl"; public static final String SINGLE_SIGN_ON_SERVICE_URL = "singleSignOnServiceUrl"; @@ -51,7 +61,7 @@ public class SpidIdentityProviderConfig extends IdentityProviderModel { public static final String WANT_ASSERTIONS_SIGNED = "wantAssertionsSigned"; public static final String WANT_AUTHN_REQUESTS_SIGNED = "wantAuthnRequestsSigned"; public static final String XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER = "xmlSigKeyInfoKeyNameTransformer"; - public static final String ENABLED_FROM_METADATA = "enabledFromMetadata"; + public static final String ENABLED_FROM_METADATA = "enabledFromMetadata"; public static final String AUTHN_CONTEXT_COMPARISON_TYPE = "authnContextComparisonType"; public static final String AUTHN_CONTEXT_CLASS_REFS = "authnContextClassRefs"; public static final String AUTHN_CONTEXT_DECL_REFS = "authnContextDeclRefs"; @@ -129,7 +139,7 @@ public void setSingleLogoutServiceUrl(String singleLogoutServiceUrl) { } public boolean isValidateSignature() { - return Boolean.valueOf(getConfig().get(VALIDATE_SIGNATURE)); + return Boolean.parseBoolean(getConfig().get(VALIDATE_SIGNATURE)); } public void setValidateSignature(boolean validateSignature) { @@ -137,7 +147,7 @@ public void setValidateSignature(boolean validateSignature) { } public boolean isForceAuthn() { - return Boolean.valueOf(getConfig().get(FORCE_AUTHN)); + return Boolean.parseBoolean(getConfig().get(FORCE_AUTHN)); } public void setForceAuthn(boolean forceAuthn) { @@ -146,7 +156,6 @@ public void setForceAuthn(boolean forceAuthn) { /** * @deprecated Prefer {@link #getSigningCertificates()}} - * @param signingCertificate */ @Deprecated public String getSigningCertificate() { @@ -168,8 +177,7 @@ public void addSigningCertificate(String signingCertificate) { getConfig().put(SIGNING_CERTIFICATE_KEY, signingCertificate); } else { // Note that "," is not coding character per PEM format specification: - // see https://tools.ietf.org/html/rfc1421, section 4.3.2.4 Step 4: Printable - // Encoding + // see https://tools.ietf.org/html/rfc1421, section 4.3.2.4 Step 4: Printable Encoding getConfig().put(SIGNING_CERTIFICATE_KEY, crt + "," + signingCertificate); } } @@ -177,11 +185,10 @@ public void addSigningCertificate(String signingCertificate) { public String[] getSigningCertificates() { String crt = getConfig().get(SIGNING_CERTIFICATE_KEY); if (crt == null || crt.isEmpty()) { - return new String[] {}; + return new String[] { }; } // Note that "," is not coding character per PEM format specification: - // see https://tools.ietf.org/html/rfc1421, section 4.3.2.4 Step 4: Printable - // Encoding + // see https://tools.ietf.org/html/rfc1421, section 4.3.2.4 Step 4: Printable Encoding return crt.split(","); } @@ -194,7 +201,7 @@ public void setNameIDPolicyFormat(String nameIDPolicyFormat) { } public boolean isWantAuthnRequestsSigned() { - return Boolean.valueOf(getConfig().get(WANT_AUTHN_REQUESTS_SIGNED)); + return Boolean.parseBoolean(getConfig().get(WANT_AUTHN_REQUESTS_SIGNED)); } public void setWantAuthnRequestsSigned(boolean wantAuthnRequestsSigned) { @@ -202,7 +209,7 @@ public void setWantAuthnRequestsSigned(boolean wantAuthnRequestsSigned) { } public boolean isWantAssertionsSigned() { - return Boolean.valueOf(getConfig().get(WANT_ASSERTIONS_SIGNED)); + return Boolean.parseBoolean(getConfig().get(WANT_ASSERTIONS_SIGNED)); } public void setWantAssertionsSigned(boolean wantAssertionsSigned) { @@ -210,7 +217,7 @@ public void setWantAssertionsSigned(boolean wantAssertionsSigned) { } public boolean isWantAssertionsEncrypted() { - return Boolean.valueOf(getConfig().get(WANT_ASSERTIONS_ENCRYPTED)); + return Boolean.parseBoolean(getConfig().get(WANT_ASSERTIONS_ENCRYPTED)); } public void setWantAssertionsEncrypted(boolean wantAssertionsEncrypted) { @@ -218,7 +225,7 @@ public void setWantAssertionsEncrypted(boolean wantAssertionsEncrypted) { } public boolean isAddExtensionsElementWithKeyInfo() { - return Boolean.valueOf(getConfig().get(ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO)); + return Boolean.parseBoolean(getConfig().get(ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO)); } public void setAddExtensionsElementWithKeyInfo(boolean addExtensionsElementWithKeyInfo) { @@ -233,6 +240,14 @@ public void setSignatureAlgorithm(String signatureAlgorithm) { getConfig().put(SIGNATURE_ALGORITHM, signatureAlgorithm); } + public String getEncryptionAlgorithm() { + return getConfig().get(ENCRYPTION_ALGORITHM); + } + + public void setEncryptionAlgorithm(String encryptionAlgorithm) { + getConfig().put(ENCRYPTION_ALGORITHM, encryptionAlgorithm); + } + public String getEncryptionPublicKey() { return getConfig().get(ENCRYPTION_PUBLIC_KEY); } @@ -242,7 +257,7 @@ public void setEncryptionPublicKey(String encryptionPublicKey) { } public boolean isPostBindingAuthnRequest() { - return Boolean.valueOf(getConfig().get(POST_BINDING_AUTHN_REQUEST)); + return Boolean.parseBoolean(getConfig().get(POST_BINDING_AUTHN_REQUEST)); } public void setPostBindingAuthnRequest(boolean postBindingAuthnRequest) { @@ -250,7 +265,7 @@ public void setPostBindingAuthnRequest(boolean postBindingAuthnRequest) { } public boolean isPostBindingResponse() { - return Boolean.valueOf(getConfig().get(POST_BINDING_RESPONSE)); + return Boolean.parseBoolean(getConfig().get(POST_BINDING_RESPONSE)); } public void setPostBindingResponse(boolean postBindingResponse) { @@ -260,12 +275,11 @@ public void setPostBindingResponse(boolean postBindingResponse) { public boolean isPostBindingLogout() { String postBindingLogout = getConfig().get(POST_BINDING_LOGOUT); if (postBindingLogout == null) { - // To maintain unchanged behavior when adding this field, we set the inital - // value to equal that + // To maintain unchanged behavior when adding this field, we set the inital value to equal that // of the binding for the response: return isPostBindingResponse(); } - return Boolean.valueOf(postBindingLogout); + return Boolean.parseBoolean(postBindingLogout); } public void setPostBindingLogout(boolean postBindingLogout) { @@ -273,7 +287,7 @@ public void setPostBindingLogout(boolean postBindingLogout) { } public boolean isBackchannelSupported() { - return Boolean.valueOf(getConfig().get(BACKCHANNEL_SUPPORTED)); + return Boolean.parseBoolean(getConfig().get(BACKCHANNEL_SUPPORTED)); } public void setBackchannelSupported(boolean backchannel) { @@ -282,20 +296,17 @@ public void setBackchannelSupported(boolean backchannel) { /** * Always returns non-{@code null} result. - * - * @return Configured ransformer of - * {@link #DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER} if not set. + * @return Configured ransformer of {@link #DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER} if not set. */ public XmlKeyInfoKeyNameTransformer getXmlSigKeyInfoKeyNameTransformer() { - return XmlKeyInfoKeyNameTransformer.from(getConfig().get(XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER), - DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER); + return XmlKeyInfoKeyNameTransformer.from(getConfig().get(XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER), DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER); } public void setXmlSigKeyInfoKeyNameTransformer(XmlKeyInfoKeyNameTransformer xmlSigKeyInfoKeyNameTransformer) { getConfig().put(XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER, - xmlSigKeyInfoKeyNameTransformer == null - ? null - : xmlSigKeyInfoKeyNameTransformer.name()); + xmlSigKeyInfoKeyNameTransformer == null + ? null + : xmlSigKeyInfoKeyNameTransformer.name()); } public int getAllowedClockSkew() { @@ -328,9 +339,9 @@ public SamlPrincipalType getPrincipalType() { public void setPrincipalType(SamlPrincipalType principalType) { getConfig().put(PRINCIPAL_TYPE, - principalType == null - ? null - : principalType.name()); + principalType == null + ? null + : principalType.name()); } public String getPrincipalAttribute() { @@ -342,16 +353,15 @@ public void setPrincipalAttribute(String principalAttribute) { } public boolean isEnabledFromMetadata() { - return Boolean.valueOf(getConfig().get(ENABLED_FROM_METADATA)); + return Boolean.valueOf(getConfig().get(ENABLED_FROM_METADATA )); } public void setEnabledFromMetadata(boolean enabled) { - getConfig().put(ENABLED_FROM_METADATA, String.valueOf(enabled)); + getConfig().put(ENABLED_FROM_METADATA , String.valueOf(enabled)); } public AuthnContextComparisonType getAuthnContextComparisonType() { - return AuthnContextComparisonType.fromValue( - getConfig().getOrDefault(AUTHN_CONTEXT_COMPARISON_TYPE, AuthnContextComparisonType.EXACT.value())); + return AuthnContextComparisonType.fromValue(getConfig().getOrDefault(AUTHN_CONTEXT_COMPARISON_TYPE, AuthnContextComparisonType.EXACT.value())); } public void setAuthnContextComparisonType(AuthnContextComparisonType authnContextComparisonType) { @@ -381,7 +391,7 @@ public boolean isSignSpMetadata() { public void setSignSpMetadata(boolean signSpMetadata) { getConfig().put(SIGN_SP_METADATA, String.valueOf(signSpMetadata)); } - + public boolean isAllowCreate() { return Boolean.valueOf(getConfig().get(ALLOW_CREATE)); } @@ -452,13 +462,10 @@ public void validate(RealmModel realm) { checkUrl(sslRequired, getSingleLogoutServiceUrl(), SINGLE_LOGOUT_SERVICE_URL); checkUrl(sslRequired, getSingleSignOnServiceUrl(), SINGLE_SIGN_ON_SERVICE_URL); - // transient name id format is not accepted together with principaltype - // SubjectnameId - if (JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get().equals(getNameIDPolicyFormat()) - && SamlPrincipalType.SUBJECT == getPrincipalType()) - throw new IllegalArgumentException( - "Can not have Transient NameID Policy Format together with SUBJECT Principal Type"); - + //transient name id format is not accepted together with principaltype SubjectnameId + if (JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get().equals(getNameIDPolicyFormat()) && SamlPrincipalType.SUBJECT == getPrincipalType()) + throw new IllegalArgumentException("Can not have Transient NameID Policy Format together with SUBJECT Principal Type"); + } public boolean isSpPrivate() { @@ -660,4 +667,160 @@ public String getBillingAnagraficaCodiceEORI() { public void setBillingAnagraficaCodiceEORI(String billingAnagraficaCodiceEORI) { getConfig().put(BILLING_ANAGRAFICA_CODICE_EORI, billingAnagraficaCodiceEORI); } + + + 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.spid.idpEntityId") + .helpText("identity-provider.spid.idpEntityId.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_CODICE_FISCALE) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.cessionarioCommittente.codiceFiscale.billing") + .helpText("identity-provider.spid.cessionarioCommittente.codiceFiscale.billing.tooltip") + .add() + + .property() + .name(BILLING_SEDE_INDIRIZZO) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.cessionarioCommittente.sede") + .helpText("identity-provider.spid.cessionarioCommittente.sede.tooltip") + .add() + + .property() + .name(BILLING_ID_CODICE) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.cessionarioCommittente.idCodice.billing") + .helpText("identity-provider.spid.cessionarioCommittente.idCodice.billing.tooltip") + .add() + + .property() + .name(BILLING_ID_PAESE) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.cessionarioCommittente.idPaese.billing") + .helpText("identity-provider.spid.cessionarioCommittente.idPaese.billing.tooltip") + .add() + + .property() + .name(BILLING_SEDE_CAP) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.cessionarioCommittente.sedeCap.billing") + .helpText("identity-provider.spid.cessionarioCommittente.sedeCap.billing.tooltip") + .add() + + .property() + .name(BILLING_SEDE_PROVINCIA) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.cessionarioCommittente.sedeProvincia.billing") + .helpText("identity-provider.spid.cessionarioCommittente.sedeProvincia.billing.tooltip") + .add() + + .property() + .name(BILLING_SEDE_NAZIONE) + .type(ProviderConfigProperty.STRING_TYPE) + .label("identity-provider.spid.cessionarioCommittente.sedeComune.billing") + .helpText("identity-provider.spid.cessionarioCommittente.sedeComune.billing.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 aac20b5..b4fe6e1 100644 --- a/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderFactory.java +++ b/src/main/java/org/keycloak/broker/spid/SpidIdentityProviderFactory.java @@ -16,25 +16,16 @@ */ package org.keycloak.broker.spid; -import java.io.InputStream; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.namespace.QName; - +import org.jboss.logging.Logger; import org.keycloak.Config.Scope; import org.keycloak.broker.provider.AbstractIdentityProviderFactory; +import org.keycloak.broker.saml.SAMLIdentityProviderFactory; import org.keycloak.dom.saml.v2.assertion.AttributeType; -import org.keycloak.dom.saml.v2.metadata.EndpointType; -import org.keycloak.dom.saml.v2.metadata.EntitiesDescriptorType; -import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType; -import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType; -import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType; -import org.keycloak.dom.saml.v2.metadata.KeyTypes; +import org.keycloak.dom.saml.v2.metadata.*; 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; @@ -42,12 +33,20 @@ import org.keycloak.saml.validators.DestinationValidator; import org.w3c.dom.Element; +import javax.xml.namespace.QName; +import java.io.InputStream; +import java.util.*; +import java.util.stream.Collectors; + /** * @author Pedro Igor */ -public class SpidIdentityProviderFactory extends AbstractIdentityProviderFactory { +public class SpidIdentityProviderFactory extends AbstractIdentityProviderFactory + implements ConfiguredProvider { - public static final String PROVIDER_ID = "spid"; + protected static final Logger logger = Logger.getLogger(SpidIdentityProviderFactory.class); + + public static final String PROVIDER_ID = SAMLIdentityProviderFactory.PROVIDER_ID + "-spid"; private static final String MACEDIR_ENTITY_CATEGORY = "http://macedir.org/entity-category"; private static final String REFEDS_HIDE_FROM_DISCOVERY = "http://refeds.org/category/hide-from-discovery"; @@ -76,7 +75,8 @@ public Map parseConfig(KeycloakSession session, InputStream inpu EntityDescriptorType entityType; if (parsedObject instanceof EntitiesDescriptorType) { - entityType = (EntityDescriptorType) ((EntitiesDescriptorType) parsedObject).getEntityDescriptor().get(0); + entityType = (EntityDescriptorType) ((EntitiesDescriptorType) parsedObject).getEntityDescriptor() + .get(0); } else { entityType = (EntityDescriptorType) parsedObject; } @@ -86,12 +86,13 @@ public Map parseConfig(KeycloakSession session, InputStream inpu if (!choiceType.isEmpty()) { IDPSSODescriptorType idpDescriptor = null; - //Metadata documents can contain multiple Descriptors (See ADFS metadata documents) such as RoleDescriptor, SPSSODescriptor, IDPSSODescriptor. - //So we need to loop through to find the IDPSSODescriptor. - for(EntityDescriptorType.EDTChoiceType edtChoiceType : entityType.getChoiceType()) { + // Metadata documents can contain multiple Descriptors (See ADFS metadata + // documents) such as RoleDescriptor, SPSSODescriptor, IDPSSODescriptor. + // So we need to loop through to find the IDPSSODescriptor. + for (EntityDescriptorType.EDTChoiceType edtChoiceType : entityType.getChoiceType()) { List descriptors = edtChoiceType.getDescriptors(); - if(!descriptors.isEmpty() && descriptors.get(0).getIdpDescriptor() != null) { + if (!descriptors.isEmpty() && descriptors.get(0).getIdpDescriptor() != null) { idpDescriptor = descriptors.get(0).getIdpDescriptor(); } } @@ -102,21 +103,25 @@ public Map parseConfig(KeycloakSession session, InputStream inpu boolean postBindingResponse = false; boolean postBindingLogout = false; for (EndpointType endpoint : idpDescriptor.getSingleSignOnService()) { - if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) { + if (endpoint.getBinding().toString() + .equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) { singleSignOnServiceUrl = endpoint.getLocation().toString(); postBindingResponse = true; break; - } else if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){ + } else if (endpoint.getBinding().toString() + .equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())) { singleSignOnServiceUrl = endpoint.getLocation().toString(); } } String singleLogoutServiceUrl = null; for (EndpointType endpoint : idpDescriptor.getSingleLogoutService()) { - if (postBindingResponse && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) { + if (postBindingResponse && endpoint.getBinding().toString() + .equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) { singleLogoutServiceUrl = endpoint.getLocation().toString(); postBindingLogout = true; break; - } else if (!postBindingResponse && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){ + } else if (!postBindingResponse && endpoint.getBinding().toString() + .equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())) { singleLogoutServiceUrl = endpoint.getLocation().toString(); break; } @@ -143,13 +148,14 @@ public Map parseConfig(KeycloakSession session, InputStream inpu if (keyDescriptor != null) { for (KeyDescriptorType keyDescriptorType : keyDescriptor) { Element keyInfo = keyDescriptorType.getKeyInfo(); - Element x509KeyInfo = DocumentUtil.getChildElement(keyInfo, new QName("dsig", "X509Certificate")); + Element x509KeyInfo = DocumentUtil.getChildElement(keyInfo, + new QName("dsig", "X509Certificate")); if (KeyTypes.SIGNING.equals(keyDescriptorType.getUse())) { samlIdentityProviderConfig.addSigningCertificate(x509KeyInfo.getTextContent()); } else if (KeyTypes.ENCRYPTION.equals(keyDescriptorType.getUse())) { samlIdentityProviderConfig.setEncryptionPublicKey(x509KeyInfo.getTextContent()); - } else if (keyDescriptorType.getUse() == null) { + } else if (keyDescriptorType.getUse() == null) { defaultCertificate = x509KeyInfo.getTextContent(); } } @@ -166,23 +172,26 @@ public Map parseConfig(KeycloakSession session, InputStream inpu } samlIdentityProviderConfig.setEnabledFromMetadata(entityType.getValidUntil() == null - || entityType.getValidUntil().toGregorianCalendar().getTime().after(new Date(System.currentTimeMillis()))); + || entityType.getValidUntil().toGregorianCalendar().getTime() + .after(new Date(System.currentTimeMillis()))); // check for hide on login attribute - if (entityType.getExtensions() != null && entityType.getExtensions().getEntityAttributes() != null) { - for (AttributeType attribute : entityType.getExtensions().getEntityAttributes().getAttribute()) { + if (entityType.getExtensions() != null + && entityType.getExtensions().getEntityAttributes() != null) { + for (AttributeType attribute : entityType.getExtensions().getEntityAttributes() + .getAttribute()) { if (MACEDIR_ENTITY_CATEGORY.equals(attribute.getName()) - && attribute.getAttributeValue().contains(REFEDS_HIDE_FROM_DISCOVERY)) { + && attribute.getAttributeValue().contains(REFEDS_HIDE_FROM_DISCOVERY)) { samlIdentityProviderConfig.setHideOnLogin(true); } } } - return samlIdentityProviderConfig.getConfig(); } } } catch (ParsingException pe) { + logger.error("Could not parse IdP SAML Metadata", pe); throw new RuntimeException("Could not parse IdP SAML Metadata", pe); } @@ -197,7 +206,28 @@ public String getId() { @Override public void init(Scope config) { super.init(config); - this.destinationValidator = DestinationValidator.forProtocolMap(config.getArray("knownProtocols")); } + + @Override + public List getConfigProperties() { + + ResourceBundle bundle = ResourceBundle.getBundle("provider-config.messages", currentLocale()); + + return SpidIdentityProviderConfig.getConfigProperties() + .stream() + .map(p -> new ProviderConfigProperty(p.getName(), bundle.getString(p.getLabel()), + bundle.getString(p.getHelpText()), p.getType(), p.getDefaultValue())) + .collect(Collectors.toList()); + } + + /** + * This method returns the current locale. + * This method is a stub. We need to find a method to access to realm's current locale + * @return Locale, current locale + */ + private Locale currentLocale(){ + return Locale.ITALIAN; + } + } diff --git a/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java b/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java index 151ebff..56cf4a1 100755 --- a/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java +++ b/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpoint.java @@ -17,41 +17,32 @@ package org.keycloak.broker.spid; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.*; import org.jboss.logging.Logger; import org.jboss.resteasy.annotations.cache.NoCache; - import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.broker.provider.BrokeredIdentityContext; import org.keycloak.broker.provider.IdentityBrokerException; import org.keycloak.broker.provider.IdentityProvider; import org.keycloak.common.ClientConnection; import org.keycloak.common.VerificationException; -import org.keycloak.dom.saml.v2.assertion.AssertionType; -import org.keycloak.dom.saml.v2.assertion.AttributeStatementType; -import org.keycloak.dom.saml.v2.assertion.AttributeType; -import org.keycloak.dom.saml.v2.assertion.AuthnStatementType; -import org.keycloak.dom.saml.v2.assertion.NameIDType; -import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationDataType; -import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationType; -import org.keycloak.dom.saml.v2.assertion.SubjectType; -import org.keycloak.dom.saml.v2.protocol.*; +import org.keycloak.dom.saml.v2.assertion.*; +import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; +import org.keycloak.dom.saml.v2.protocol.RequestAbstractType; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.dom.saml.v2.protocol.StatusResponseType; import org.keycloak.events.Details; import org.keycloak.events.Errors; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; -import org.keycloak.models.ClientModel; -import org.keycloak.models.KeyManager; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserSessionModel; +import org.keycloak.models.*; import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.LoginProtocolFactory; -import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder; -import org.keycloak.protocol.saml.SamlProtocol; -import org.keycloak.protocol.saml.SamlProtocolUtils; -import org.keycloak.protocol.saml.SamlService; -import org.keycloak.protocol.saml.SamlSessionUtils; +import org.keycloak.protocol.saml.*; import org.keycloak.protocol.saml.preprocessor.SamlAuthenticationPreprocessor; +import org.keycloak.rotation.HardcodedKeyLocator; +import org.keycloak.rotation.KeyLocator; import org.keycloak.saml.SAML2LogoutResponseBuilder; import org.keycloak.saml.SAMLRequestParser; import org.keycloak.saml.common.constants.GeneralConstants; @@ -60,66 +51,40 @@ import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.common.util.DocumentUtil; -import org.keycloak.saml.common.util.StringUtil; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants; import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil; +import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; import org.keycloak.saml.processing.core.util.XMLSignatureUtil; import org.keycloak.saml.processing.web.util.PostBindingUtil; +import org.keycloak.saml.validators.ConditionsValidator; +import org.keycloak.saml.validators.DestinationValidator; import org.keycloak.services.ErrorPage; import org.keycloak.services.Urls; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.messages.Messages; +import org.keycloak.services.util.CacheControlUtil; +import org.keycloak.sessions.AuthenticationSessionModel; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; -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 javax.xml.datatype.*; +import javax.xml.crypto.dsig.XMLSignature; import javax.xml.namespace.QName; import java.io.IOException; +import java.net.URI; import java.security.Key; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.time.Instant; -import java.time.format.DateTimeParseException; import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Predicate; -import java.util.stream.Collectors; - -import org.keycloak.protocol.saml.SamlPrincipalType; -import org.keycloak.rotation.HardcodedKeyLocator; -import org.keycloak.rotation.KeyLocator; -import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; -import org.keycloak.saml.validators.ConditionsValidator; -import org.keycloak.saml.validators.DestinationValidator; -import org.keycloak.services.util.CacheControlUtil; -import org.keycloak.sessions.AuthenticationSessionModel; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.net.URI; -import java.security.cert.CertificateException; - -import javax.ws.rs.core.MultivaluedMap; -import javax.xml.crypto.dsig.XMLSignature; /** * @author Bill Burke * @version $Revision: 1 $ */ + public class SpidSAMLEndpoint { protected static final Logger logger = Logger.getLogger(SpidSAMLEndpoint.class); public static final String SAML_FEDERATED_SESSION_INDEX = "SAML_FEDERATED_SESSION_INDEX"; @@ -135,30 +100,35 @@ public class SpidSAMLEndpoint { public static final String ASSERTION_NAMEID_FORMAT = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"; public static final String ASSERTION_ISSUER_FORMAT = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"; - protected RealmModel realm; + protected final RealmModel realm; protected EventBuilder event; - protected SpidIdentityProviderConfig config; - protected IdentityProvider.AuthenticationCallback callback; - protected SpidIdentityProvider provider; + protected final SpidIdentityProviderConfig config; + protected final IdentityProvider.AuthenticationCallback callback; + protected final SpidIdentityProvider provider; private final DestinationValidator destinationValidator; + private final KeycloakSession session; + + private final ClientConnection clientConnection; - @Context - private KeycloakSession session; + private final HttpHeaders headers; - @Context - private ClientConnection clientConnection; + public static final String ENCRYPTION_DEPRECATED_MODE_PROPERTY = "keycloak.saml.deprecated.encryption"; + private final boolean DEPRECATED_ENCRYPTION = Boolean.getBoolean(ENCRYPTION_DEPRECATED_MODE_PROPERTY); - @Context - private HttpHeaders headers; + private final SpidSAMLEndpointHelper helper; - 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(); + this.helper = new SpidSAMLEndpointHelper(config); } @GET @@ -281,10 +251,16 @@ protected Response handleSamlRequest(String samlRequest, String relayState) { // validate destination if (isDestinationRequired() && requestAbstractType.getDestination() == null && containsUnencryptedSignature(holder)) { - return getResponse(Errors.MISSING_REQUIRED_DESTINATION, Errors.INVALID_REQUEST, Messages.INVALID_REQUEST); + event.event(EventType.IDENTITY_PROVIDER_RESPONSE); + event.detail(Details.REASON, Errors.MISSING_REQUIRED_DESTINATION); + event.error(Errors.INVALID_REQUEST); + return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST); } if (! destinationValidator.validate(getExpectedDestination(config.getAlias(), null), requestAbstractType.getDestination())) { - return getResponse(Errors.INVALID_DESTINATION, Errors.INVALID_SAML_RESPONSE, Messages.INVALID_REQUEST); + event.event(EventType.IDENTITY_PROVIDER_RESPONSE); + event.detail(Details.REASON, Errors.INVALID_DESTINATION); + event.error(Errors.INVALID_SAML_RESPONSE); + return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST); } if (config.isValidateSignature()) { try { @@ -297,10 +273,9 @@ protected Response handleSamlRequest(String samlRequest, String relayState) { } } - if (requestAbstractType instanceof LogoutRequestType) { + if (requestAbstractType instanceof LogoutRequestType logout) { logger.debug("** logout request"); event.event(EventType.LOGOUT); - LogoutRequestType logout = (LogoutRequestType) requestAbstractType; return logoutRequest(logout, relayState); } else { @@ -317,7 +292,7 @@ protected Response logoutRequest(LogoutRequestType request, String relayState) { session.sessions().getUserSessionByBrokerUserIdStream(realm, brokerUserId) .filter(userSession -> userSession.getState() != UserSessionModel.State.LOGGING_OUT && userSession.getState() != UserSessionModel.State.LOGGED_OUT) - .collect(Collectors.toList()) // collect to avoid concurrent modification as backchannelLogout removes the user sessions. + .toList() // collect to avoid concurrent modification as backchannelLogout removes the user sessions. .forEach(processLogout(ref)); request = ref.get(); @@ -367,11 +342,7 @@ protected Response logoutRequest(LogoutRequestType request, String relayState) { } else { return binding.redirectBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl()); } - } catch (ConfigurationException e) { - throw new RuntimeException(e); - } catch (ProcessingException e) { - throw new RuntimeException(e); - } catch (IOException e) { + } catch (ConfigurationException | ProcessingException | IOException e) { throw new RuntimeException(e); } @@ -409,23 +380,13 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h // questo blocco impedisce la definizione granulare dei messaggi -// if (! isSuccessfulSamlResponse(responseType)) { -// // Translate SPID error codes to meaningful messages -// boolean isSpidFault = responseType.getStatus() != null -// && responseType.getStatus().getStatusMessage() != null -// && responseType.getStatus().getStatusMessage().startsWith("ErrorCode nr"); -// if (isSpidFault) -// return callback.error("SpidFault_" + responseType.getStatus().getStatusMessage().replace(' ', '_')); - // questo blocco impedisce la definizione granulare dei messaggi -// else -// { - -// String statusMessage = responseType.getStatus() == null ? Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR : responseType.getStatus().getStatusMessage(); -// return callback.error(statusMessage); -// } -// } - if (responseType.getAssertions() == null || responseType.getAssertions().isEmpty()) { - return callback.error(Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR); + String checkSpidStatus = helper.checkSpidStatus(responseType); + if (checkSpidStatus != null){ + return callback.error(checkSpidStatus); + } + String checkAssertions = helper.checkAssertions(responseType); + if (checkAssertions != null) { + return callback.error(checkAssertions); } boolean assertionIsEncrypted = AssertionUtil.isAssertionEncrypted(responseType); @@ -440,8 +401,8 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h Element assertionElement; if (assertionIsEncrypted) { - // This methods writes the parsed and decrypted assertion back on the responseType parameter: - assertionElement = AssertionUtil.decryptAssertion(holder, responseType, keys.getPrivateKey()); + // These methods write the parsed and decrypted assertion back on the responseType parameter: + 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. */ @@ -450,11 +411,15 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h // Apply SPID-specific response validation rules String spidExpectedRequestId = authSession.getClientNote(SamlProtocol.SAML_REQUEST_ID_BROKER); - String spidResponseValidationError = verifySpidResponse(holder.getSamlDocument().getDocumentElement(), + String requestIssueInstantNote = authSession.getClientNote(JBossSAMLConstants.ISSUE_INSTANT.name()); + String assertionConsumerServiceURL = authSession.getClientNote(JBossSAMLConstants.ASSERTION_CONSUMER_SERVICE_URL.name()); + String spidResponseValidationError = helper.verifySpidResponse( + holder.getSamlDocument().getDocumentElement(), assertionElement, spidExpectedRequestId, responseType, - authSession); + requestIssueInstantNote, + assertionConsumerServiceURL); if (spidResponseValidationError != null) { @@ -466,7 +431,7 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h // Validate InResponseTo attribute: must match the generated request ID String expectedRequestId = authSession.getClientNote(SamlProtocol.SAML_REQUEST_ID_BROKER); - final boolean inResponseToValidationSuccess = validateInResponseToAttribute(responseType, expectedRequestId); + final boolean inResponseToValidationSuccess = helper.validateInResponseToAttribute(responseType, expectedRequestId); if (!inResponseToValidationSuccess) { event.event(EventType.IDENTITY_PROVIDER_RESPONSE); @@ -497,7 +462,6 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h return ErrorPage.error(session, authSession, Response.Status.BAD_REQUEST, Messages.INVALID_REQUESTER); } - //Map notes = new HashMap<>(); BrokeredIdentityContext identity = new BrokeredIdentityContext(principal); identity.getContextData().put(SAML_LOGIN_RESPONSE, responseType); identity.getContextData().put(SAML_ASSERTION, assertion); @@ -519,7 +483,7 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h try { String issuerURL = getEntityId(session.getContext().getUri(), realm); cvb.addAllowedAudience(URI.create(issuerURL)); - // getDestination has been validated to match request URL already so it matches SAML endpoint + // getDestination has been validated to match request URL already, so it matches SAML endpoint if (responseType.getDestination() != null) { cvb.addAllowedAudience(URI.create(responseType.getDestination())); } @@ -553,7 +517,7 @@ protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder h identity.setIdp(provider); if (authn != null && authn.getSessionIndex() != null) { identity.setBrokerSessionId(config.getAlias() + "." + authn.getSessionIndex()); - } + } return callback.authenticated(identity); @@ -586,20 +550,22 @@ private AuthenticationSessionModel getAuthenticationSessionModel(String relaySta */ private AuthenticationSessionModel samlIdpInitiatedSSO(final String clientUrlName) { event.event(EventType.LOGIN); - CacheControlUtil.noBackButtonCacheControlHeader(); + CacheControlUtil.noBackButtonCacheControlHeader(session); + Optional oClient = SpidSAMLEndpoint.this.session.clients() - .searchClientsByAttributes(realm, Collections.singletonMap(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME, clientUrlName), 0, 1) - .findFirst(); + .searchClientsByAttributes(realm, Collections.singletonMap(SamlProtocol.SAML_IDP_INITIATED_SSO_URL_NAME, clientUrlName), 0, 1) + .findFirst(); - if (! oClient.isPresent()) { + if (oClient.isEmpty()) { event.error(Errors.CLIENT_NOT_FOUND); Response response = ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.CLIENT_NOT_FOUND); throw new WebApplicationException(response); } 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(session, event); ResteasyProviderFactory.getInstance().injectProperties(samlService); + AuthenticationSessionModel authSession = samlService.getOrCreateLoginSessionForIdpInitiatedSso(session, SpidSAMLEndpoint.this.realm, oClient.get(), null); if (authSession == null) { event.error(Errors.INVALID_REDIRECT_URI); @@ -610,16 +576,6 @@ private AuthenticationSessionModel samlIdpInitiatedSSO(final String clientUrlNam return authSession; } - - private boolean isSuccessfulSamlResponse(ResponseType responseType) { - return responseType != null - && responseType.getStatus() != null - && responseType.getStatus().getStatusCode() != null - && responseType.getStatus().getStatusCode().getValue() != null - && Objects.equals(responseType.getStatus().getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get()); - } - - public Response handleSamlResponse(String samlResponse, String relayState, String clientId) { SAMLDocumentHolder holder = extractResponseDocument(samlResponse); if (holder == null) { @@ -691,7 +647,6 @@ public Response handleSamlResponse(String samlResponse, String relayState, Strin // todo need to check that it is actually a LogoutResponse return handleLogoutResponse(holder, statusResponse, relayState); } - //throw new RuntimeException("Unknown response type"); } @@ -726,13 +681,6 @@ private String getExpectedDestination(String providerAlias, String clientId) { } } - private Response getResponse(String invalidReason, String invalidSamlResponse, String errorDisplayed) { - event.event(EventType.IDENTITY_PROVIDER_RESPONSE); - event.detail(Details.REASON, invalidReason); - event.error(invalidSamlResponse); - return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, errorDisplayed); - } - protected class PostBinding extends Binding { @Override protected boolean containsUnencryptedSignature(SAMLDocumentHolder documentHolder) { @@ -742,8 +690,7 @@ protected boolean containsUnencryptedSignature(SAMLDocumentHolder documentHolder @Override protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException { - if ((! containsUnencryptedSignature(documentHolder)) && (documentHolder.getSamlObject() instanceof ResponseType)) { - ResponseType responseType = (ResponseType) documentHolder.getSamlObject(); + if ((! containsUnencryptedSignature(documentHolder)) && (documentHolder.getSamlObject() instanceof ResponseType responseType)) { List assertions = responseType.getAssertions(); if (! assertions.isEmpty() ) { // Only relax verification if the response is an authnresponse and contains (encrypted/plaintext) assertion. @@ -847,13 +794,15 @@ private String getFirstMatchingAttribute(AssertionType assertion, Predicate { return principalType.name(); - case ATTRIBUTE: - case FRIENDLY_ATTRIBUTE: + } + case ATTRIBUTE, FRIENDLY_ATTRIBUTE -> { return String.format("%s(%s)", principalType.name(), config.getPrincipalAttribute()); - default: + } + default -> { return null; + } } } @@ -863,579 +812,4 @@ private NameIDType getSubjectNameID(final AssertionType assertion) { return subType != null ? (NameIDType) subType.getBaseID() : null; } - private boolean validateInResponseToAttribute(ResponseType responseType, String expectedRequestId) { - // If we are not expecting a request ID, don't bother - if (expectedRequestId == null || expectedRequestId.isEmpty()) - return true; - - // We are expecting a request ID so we are in SP-initiated login, attribute InResponseTo must be present - if (responseType.getInResponseTo() == null) { - logger.error("Response Validation Error: InResponseTo attribute was expected but not present in received response"); - return false; - } - - // Attribute is present, proceed with validation - // 1) Attribute Response > InResponseTo must not be empty - String responseInResponseToValue = responseType.getInResponseTo(); - if (responseInResponseToValue.isEmpty()) { - logger.error("Response Validation Error: InResponseTo attribute was expected but it is empty in received response"); - return false; - } - - // 2) Attribute Response > InResponseTo must match request ID - if (!responseInResponseToValue.equals(expectedRequestId)) { - logger.error("Response Validation Error: received InResponseTo attribute does not match the expected request ID"); - return false; - } - - // If present, Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo must also be validated - if (responseType.getAssertions().isEmpty()) - return true; - - SubjectType subjectElement = responseType.getAssertions().get(0).getAssertion().getSubject(); - if (subjectElement != null) { - if (subjectElement.getConfirmation() != null && !subjectElement.getConfirmation().isEmpty()) - { - SubjectConfirmationType subjectConfirmationElement = subjectElement.getConfirmation().get(0); - - if (subjectConfirmationElement != null) { - SubjectConfirmationDataType subjectConfirmationDataElement = subjectConfirmationElement.getSubjectConfirmationData(); - - if (subjectConfirmationDataElement != null) { - if (subjectConfirmationDataElement.getInResponseTo() != null) { - // 3) Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo is empty - String subjectConfirmationDataInResponseToValue = subjectConfirmationDataElement.getInResponseTo(); - if (subjectConfirmationDataInResponseToValue.isEmpty()) { - logger.error("Response Validation Error: SubjectConfirmationData InResponseTo attribute was expected but it is empty in received response"); - return false; - } - - // 4) Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo does not match request ID - if (!subjectConfirmationDataInResponseToValue.equals(expectedRequestId)) { - logger.error("Response Validation Error: received SubjectConfirmationData InResponseTo attribute does not match the expected request ID"); - return false; - } - } - } - } - } - } - - return true; - } - - /** - * This method verifies the correctness of the response sent by the IdP. - * The comments written in italian are the actual copy of the error statements - * given by the AGID testing tool. In this way it is possible to keep track which code block - * belongs to which test, and its requirement. - * - * @param documentElement - * @param assertionElement - * @param expectedRequestId - * @param responseType - * @param authSession - * @return spidcode response error string - */ - private String verifySpidResponse(Element documentElement, - Element assertionElement, - String expectedRequestId, - ResponseType responseType, - AuthenticationSessionModel authSession) - { - //2: Unsigned Response - if (responseType.getSignature() == null) { - return "SpidSamlCheck_02"; - } - - //3: Unsigned Assertion - if (responseType.getAssertions().size() > 0 && - responseType.getAssertions().get(0).getAssertion().getSignature() == null) { - return "SpidSamlCheck_03"; - } - //8: Null ID - if (StringUtil.isNullOrEmpty(responseType.getID())) { - return "SpidSamlCheck_08"; - } - - - String requestIssueInstantNote = authSession.getClientNote(JBossSAMLConstants.ISSUE_INSTANT.name()); - try { - XMLGregorianCalendar requestIssueInstant = DatatypeFactory.newInstance(). - newXMLGregorianCalendar(requestIssueInstantNote); - - // 13: IssueInstant correct UTC format -> non valid UTC format throws DateTimeParseException - Instant.parse(responseType.getIssueInstant().toString()); - - - XMLGregorianCalendar responseIssueInstant = responseType.getIssueInstant(); - - //14: Issue Instant req < Issue Instant Response - if(responseIssueInstant.compare(requestIssueInstant) != DatatypeConstants.GREATER){ - return "SpidSamlCheck_14"; - } - - //15: Response Attribute IssueInstant within three minutes of request IssueInstant - //https://github.com/italia/spid-saml-check/issues/73 - //max tolerance of three minutes - long responseTimeMillis = responseIssueInstant.toGregorianCalendar().getTimeInMillis(); - long requestTimeMillis = requestIssueInstant.toGregorianCalendar().getTimeInMillis(); - - if((responseTimeMillis-requestTimeMillis)>0 && (responseTimeMillis-requestTimeMillis)>180000){ - return "SpidSamlCheck_15"; - - } - - - GregorianCalendar now = new GregorianCalendar(); - XMLGregorianCalendar nowXmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(now); - if(responseIssueInstant.compare(nowXmlGregorianCalendar) == DatatypeConstants.GREATER){ - return "SpidSamlCheck_15"; - } - - //110 IssueInstant must not have milliseconds - int responseIssueInstantMillisecond = responseIssueInstant.getMillisecond(); - if(responseIssueInstantMillisecond>0){ - return "SpidSamlCheck_110"; - } - - - } catch (DatatypeConfigurationException e) { - logger.error("Could not convert request IssueInstant to XMLGregorianCalendar, wrong format?"); - return "SpidFault_ErrorCode_nr3"; - } catch (DateTimeParseException e){ - return "SpidSamlCheck_13"; - } - - // 17: Response > InResponseTo missing - if (!documentElement.hasAttribute("InResponseTo")) { - return "SpidSamlCheck_nr17"; - } - - // 16: Response > InResponseTo empty - String responseInResponseToValue = documentElement.getAttribute("InResponseTo"); - if (responseInResponseToValue.isEmpty()) { - return "SpidSamlCheck_nr16"; - } - - // 18: Response > InResponseTo does not match request ID - if (!responseInResponseToValue.equals(expectedRequestId)) { - return "SpidSamlCheck_nr18"; - } - - //22 Unspecified Element Status - if(responseType.getStatus()!=null && - responseType.getStatus().getStatusCode()==null && - responseType.getStatus().getStatusDetail()==null && - responseType.getStatus().getStatusMessage() == null){ - return "SpidSamlCheck_22"; - } - - //23 Missing Element Status - if(responseType.getStatus()==null){ - return "SpidSamlCheck_23"; - } - - //24 Unspecified Element StatusCode - if(responseType.getStatus()!=null && - responseType.getStatus().getStatusCode()!=null && - StringUtil.isNullOrEmpty(responseType.getStatus().getStatusCode().getValue().toString())){ - return "SpidSamlCheck_24"; - } - - //25 Missing StatusCode: note-> The test fails with code 22 because the - // element sent by the response is the same. (See response xml from the SPID testing tool) - - if(responseType.getStatus()!=null && - responseType.getStatus().getStatusCode()==null){ - return "SpidSamlCheck_25"; - } - - //26 StatusCode element != Success - if(responseType.getStatus()!=null && - responseType.getStatus().getStatusCode()!=null && - !responseType.getStatus().getStatusCode().getValue().toString().substring(responseType.getStatus().getStatusCode().getValue().toString().lastIndexOf(":")+1).equals("Success")){ - return "SpidSamlCheck_26"; - } - - //27 Unspecified Issuer element - if(responseType.getIssuer()!=null && - StringUtil.isNullOrEmpty(responseType.getIssuer().getValue())){ - return "SpidSamlCheck_27"; - } - - //28 Missing element Issuer - // the test fails with code 1 (test2) because the testing tool sends an unsigned response - // the control block is included anyhow - if(responseType.getIssuer()==null){ - return "SpidSamlCheck_28"; - } - - //29 Element Issuer != EntityID IdP - if(!responseType.getIssuer().getValue().equalsIgnoreCase(config.getIdpEntityId())){ - return "SpidSamlCheck_29"; - } - - //30/31 Format di Issuer attribute must be omitted or have the value urn:oasis:names:tc:SAML:2.0:nameid-format:entity - if(responseType.getIssuer()!=null && - responseType.getIssuer().getFormat()!=null && - !responseType.getIssuer().getFormat().toString().equals(ISSUER_FORMAT)){ - return "SpidSamlCheck_30"; - } - - //33 Assertion attribute ID unspecified - //32/34 checked by keycloak - String assertionID = assertionElement.getAttribute("ID"); - if(assertionID.equals("")){ - return "SpidSamlCheck_33"; - } - - String assertionIssueInstant = assertionElement.getAttribute("IssueInstant"); - - - if(!StringUtil.isNullOrEmpty(assertionIssueInstant)){ - try { - XMLGregorianCalendar requestIssueInstant = DatatypeFactory.newInstance(). - newXMLGregorianCalendar(requestIssueInstantNote); - XMLGregorianCalendar assertionIssueInstantXML = DatatypeFactory.newInstance(). - newXMLGregorianCalendar(assertionIssueInstant); - //39 Assertion IssueInstant attribute < Request IssueInstant - - if(assertionIssueInstantXML.compare(requestIssueInstant) == DatatypeConstants.LESSER){ - return "SpidSamlCheck_39"; - } - - //40. Assertion IssueInstant attribute > later than 3 minutes from request - //https://github.com/italia/spid-saml-check/issues/73 - //max tolerance of three minutes - long assertionTimeMillis = assertionIssueInstantXML.toGregorianCalendar().getTimeInMillis(); - long requestTimemillis = requestIssueInstant.toGregorianCalendar().getTimeInMillis(); - - if((assertionTimeMillis-requestTimemillis)>0 && (assertionTimeMillis-requestTimemillis)>180000){ - return "SpidSamlCheck_40"; - - } - - //110 Assertion IssueInstant with milliseconds - int assertionIssueInstantXMLMillisecond = assertionIssueInstantXML.getMillisecond(); - if (assertionIssueInstantXMLMillisecond > 0){ - return "SpidSamlCheck_110"; - } - - } catch (DatatypeConfigurationException e) { - logger.error("Could not convert request IssueInstant to XMLGregorianCalendar, wrong format?"); - return "SpidFault_ErrorCode_nr3"; - } - } - - // 42: Assertion > Subject missing - Element subjectElement = getDocumentElement(assertionElement, "Subject"); - if (subjectElement == null) { - return "SpidSamlCheck_nr42"; - } - - // 41: Assertion > Subject empty (Keycloak returns error earlier) - if (!hasNamedChild(subjectElement)) { - return "SpidSamlCheck_nr41"; - } - - //44 Assertion NameID missing - Element nameID = getDocumentElement(assertionElement, "NameID"); - if(nameID==null){ - return "SpidSamlCheck_44"; - } - - //43 Assertion NameID unspecified - if(nameID.getFirstChild() != null && StringUtil.isNullOrEmpty(nameID.getFirstChild().getNodeValue().trim())){ - return "SpidSamlCheck_43"; - } - - //45/46 Format NameID attribute missing or unspecified - if(StringUtil.isNullOrEmpty(nameID.getAttribute("Format"))){ - return "SpidSamlCheck_4546"; - } - - //47 Format NameID attribute != urn:oasis:names:tc:SAML:2.0:nameidformat:transient - if(!StringUtil.isNullOrEmpty(nameID.getAttribute("Format")) && !nameID.getAttribute("Format").equals(ASSERTION_NAMEID_FORMAT)){ - return "SpidSamlCheck_47"; - - } - //48/49 Assertion NameQualifier unspecified - if(StringUtil.isNullOrEmpty(nameID.getAttribute("NameQualifier"))){ - return "SpidSamlCheck_4849"; - } - - // 52: Assertion > Subject > Confirmation missing - Element subjectConfirmationElement = getDocumentElement(subjectElement, "SubjectConfirmation"); - - if (subjectConfirmationElement == null) { - return "SpidSamlCheck_nr52"; - } - - // 51: Assertion > Subject > Confirmation empty - if (!hasNamedChild(subjectConfirmationElement)) { - return "SpidSamlCheck_nr51"; - } - - // 53: Assertion > Subject > Confirmation > Method missing - if (!subjectConfirmationElement.hasAttribute("Method")) { - return "SpidSamlCheck_nr54"; - } - - // 54: Assertion > Subject > Confirmation > Method empty - String subjectConfirmationMethodValue = subjectConfirmationElement.getAttribute("Method"); - if (subjectConfirmationMethodValue.isEmpty()) { - return "SpidSamlCheck_nr53"; - } - - // 55: Assertion > Subject > Confirmation > Method is not JBossSAMLURIConstants.SUBJECT_CONFIRMATION_BEARER - if (!subjectConfirmationMethodValue.equals(JBossSAMLURIConstants.SUBJECT_CONFIRMATION_BEARER.get())) { - return "SpidSamlCheck_nr55"; - } - - // 56: Assertion > Subject > Confirmation > SubjectConfirmationData missing. Testing tool xml snippet same as 51 - Element subjectConfirmationDataElement = getDocumentElement(subjectConfirmationElement, "SubjectConfirmationData"); - - if (subjectConfirmationDataElement == null) { - return "SpidSamlCheck_nr56"; - } - - // 58: Assertion > Subject > Confirmation > SubjectConfirmationData > Recipient missing - if (!subjectConfirmationDataElement.hasAttribute("Recipient")) { - return "SpidSamlCheck_nr58"; - } - - // 59: Assertion > Subject > Confirmation > SubjectConfirmationData > different than AssertionConsumerServiceURL - String assertionConsumerServiceURL = authSession.getClientNote(JBossSAMLConstants.ASSERTION_CONSUMER_SERVICE_URL.name()); - String recipient = subjectConfirmationDataElement.getAttribute("Recipient"); - if(!StringUtil.isNullOrEmpty(recipient) && !recipient.trim().equals(assertionConsumerServiceURL.trim())){ - return "SpidSamlCheck_59"; - } - - // 57: Assertion > Subject > Confirmation > SubjectConfirmationData > Recipient is empty - String subjectConfirmationDataRecipientValue = subjectConfirmationDataElement.getAttribute("Recipient"); - if (subjectConfirmationDataRecipientValue.isEmpty()) { - return "SpidSamlCheck_nr57"; - } - - // 61: Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo missing - if (!subjectConfirmationDataElement.hasAttribute("InResponseTo")) { - return "SpidSamlCheck_nr61"; - } - - // 60: Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo is empty - String subjectConfirmationDataInResponseToValue = subjectConfirmationDataElement.getAttribute("InResponseTo"); - if (subjectConfirmationDataInResponseToValue.isEmpty()) { - return "SpidSamlCheck_nr60"; - } - - // 62: Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo does not match request ID - if (!subjectConfirmationDataInResponseToValue.equals(expectedRequestId)) { - return "SpidSamlCheck_nr62"; - } - - // 64: Assertion > Subject > Confirmation > SubjectConfirmationData > NotOnOrAfter missing - String notOnOrAfter = subjectConfirmationDataElement.getAttribute("NotOnOrAfter"); - if(StringUtil.isNullOrEmpty(notOnOrAfter.trim())){ - return "SpidSamlCheck_64"; - } - - try { - // 66: Assertion > Subject > Confirmation > SubjectConfirmationData > NotOnOrAfter before response reception - XMLGregorianCalendar notOnOrAfterXMLGregorian = DatatypeFactory.newInstance(). - newXMLGregorianCalendar(notOnOrAfter); - XMLGregorianCalendar now = DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar()); - - if(notOnOrAfterXMLGregorian.compare(now) == DatatypeConstants.LESSER){ - return "SpidSamlCheck_66"; - } - - } catch (DatatypeConfigurationException e) { - logger.error("Could not convert request NotOnOrAfter to XMLGregorianCalendar, wrong format?"); - return "SpidFault_ErrorCode_nr3"; - } - // 67: Assertion > Issuer non specified - Element issuerElement = getDocumentElement(assertionElement, "Issuer"); - if(issuerElement!=null && - (issuerElement.getFirstChild()==null || - StringUtil.isNullOrEmpty(issuerElement.getFirstChild().getNodeValue()))){ - return "SpidSamlCheck_67"; - - } - // 68: Assertion > Issuer missing - if(issuerElement==null){ - return "SpidSamlCheck_68"; - - } - - //69 Assertion > Issuer != entityID idp - if(!issuerElement.getFirstChild().getNodeValue().equals(config.getIdpEntityId())){ - return "SpidSamlCheck_69"; - } - //70 71 Assertion > Issuer > Format not specified or null - String format = issuerElement.getAttribute("Format"); - if(StringUtil.isNullOrEmpty(format)){ - return "SpidSamlCheck_7071"; - } - - //72 Assertion > Issuer > Format different than constant - if(!format.equals(ASSERTION_ISSUER_FORMAT)){ - return "SpidSamlCheck_72"; - } - - //73 Assertion > Conditions missing - Element conditionsElement = getDocumentElement(assertionElement, "Conditions"); - if(conditionsElement != null && !hasNamedChild(conditionsElement)){ - return "SpidSamlCheck_73"; - } - //74 Assertion > Conditions is null - if(conditionsElement==null){ - return "SpidSamlCheck_74"; - } - //75-76 Assertion > Conditions > NotBefore null or empty - String notBefore = conditionsElement.getAttribute("NotBefore"); - if(StringUtil.isNullOrEmpty(notBefore)){ - return "SpidSamlCheck_7576"; - } - - //78 Assertion > Condition > NotBefore after response - try { - XMLGregorianCalendar notBeforeXmlGregorian = DatatypeFactory.newInstance(). - newXMLGregorianCalendar(notBefore); - XMLGregorianCalendar now = DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar()); - - if(notBeforeXmlGregorian.compare(now) == DatatypeConstants.GREATER){ - return "SpidSamlCheck_78"; - } - - } catch (DatatypeConfigurationException e) { - logger.error("Could not convert request NotOnOrAfter to XMLGregorianCalendar, wrong format?"); - return "SpidFault_ErrorCode_nr3"; - } - - //79-80 Assertion > Condition > NotOnOrAfter missing or not specified - String conditionsNotOnOrAfter = conditionsElement.getAttribute("NotOnOrAfter"); - if(StringUtil.isNullOrEmpty(conditionsNotOnOrAfter.trim())){ - return "SpidSamlCheck_7980"; - } - - //82 Assertion > Condition > NotOnOrAfter before response - try { - XMLGregorianCalendar conditionsNotOnOrAfterXmlGregorian = DatatypeFactory.newInstance(). - newXMLGregorianCalendar(conditionsNotOnOrAfter); - XMLGregorianCalendar now = DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar()); - - if(conditionsNotOnOrAfterXmlGregorian.compare(now) == DatatypeConstants.LESSER){ - return "SpidSamlCheck_82"; - } - - } catch (DatatypeConfigurationException e) { - logger.error("Could not convert request NotOnOrAfter to XMLGregorianCalendar, wrong format?"); - return "SpidFault_ErrorCode_nr3"; - } - - //86 Assertion > Condition > Audience > AudienceRestriction missing (note: testing tool xml same as #83) - Element audienceRestrictionElement = getDocumentElement(conditionsElement, "AudienceRestriction"); - Element audience = getDocumentElement(audienceRestrictionElement, "Audience"); - - //83 Assertion > Condition > AudienceRestriction not specified - if(!hasNamedChild(audienceRestrictionElement)){ - return "SpidSamlCheck_83"; - } - - //85 86 Assertion > Condition > AudienceRestriction > Audience not specified or missing (note: testing tool 86 xml same as #83) - if(audience == null || audience.getFirstChild() == null || StringUtil.isNullOrEmpty(audience.getFirstChild().getNodeValue()) ){ - return "SpidSamlCheck_8586"; - } - - - - //84 Assertion > Condition > AudienceRestriction null (testing tool yaml snippet same as #73) - if(audienceRestrictionElement==null){ - return "SpidSamlCheck_84"; - } - - //87 Assertion > Condition > AudienceRestriction > Audience != EntityID SP - String spEntityId = config.getEntityId(); - if(audience.getFirstChild()!= null && - !StringUtil.isNullOrEmpty(audience.getFirstChild().getNodeValue()) && - !audience.getFirstChild().getNodeValue().equals(spEntityId)){ - - return "SpidSamlCheck_87"; - - } - - //88 Assertion > AuthnStatement not specified - Element authnStatement = getDocumentElement(assertionElement, "AuthnStatement"); - if(authnStatement!= null && !hasNamedChild(authnStatement)){ - return "SpidSamlCheck_88"; - } - //89 Assertion > AuthnStatement null - if(authnStatement==null){ - return "SpidSamlCheck_89"; - - } - //90 Assertion > AuthnStatement > AuthnContext not specified - Element authnContextElement = getDocumentElement(authnStatement, "AuthnContext"); - if(authnContextElement!= null && !hasNamedChild(authnContextElement)){ - return "SpidSamlCheck_90"; - } - //91 Assertion > AuthnContext > AuthnStatement null note: from IDP same xml response block as #88 - if(authnContextElement==null){ - return "SpidSamlCheck_91"; - - } - - //92 Assertion > AuthnStatement > AuthnContextClassRef unspecified - Element authnContextClassRef = getDocumentElement(authnContextElement, "AuthnContextClassRef"); - if(authnContextClassRef!= null && - (authnContextClassRef.getFirstChild() == null || - StringUtil.isNullOrEmpty(authnContextClassRef.getFirstChild().getNodeValue()))){ - return "SpidSamlCheck_92"; - - } - - //93 Assertion > AuthnStatement > AuthnContextClassRef missing note: response snippet same as #90 - if(authnContextClassRef==null){ - return "SpidSamlCheck_93"; - } - /** - *nota: se vi sono più spidLevel specificati in keycloak, la response avrà sempre e solo il primo - * Non essendo specificato nel tool il preciso comportamento in casi di vari livelli configurati ma solo uno - * inviato dalla request, si sceglie di controllare che il livello della response sia contenuto tra i livelli - * configurati su kc (quindi nella config della request) - */ - //94 Assertion > AuthContextClassRef spid level different from request. This block also implies #95 #96 #97 - String responseSpidLevel = authnContextClassRef.getFirstChild().getNodeValue(); - List requestSpidLevels =Arrays.asList(config.getAuthnContextClassRefs().replaceAll("[\"\\[\\](){}]","").trim().split(",")); - if(!requestSpidLevels.contains(responseSpidLevel)){ - return "SpidSamlCheck_94"; - } - - //98,99,100,103,104,105,106,107,108 caught by kc - //109 ok - - return null; - } - - private Element getDocumentElement(Element assertionElement, String subject) { - return DocumentUtil.getChildElement(assertionElement, - new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(), subject)); - } - - private boolean hasNamedChild(Element element) - { - NodeList childNodes = element.getChildNodes(); - if (childNodes == null) return false; - - for (int i = 0; i < childNodes.getLength(); ++i) - { - Node node = childNodes.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName() != null) - return true; - } - - return false; - } - } diff --git a/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpointHelper.java b/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpointHelper.java new file mode 100644 index 0000000..27d2d42 --- /dev/null +++ b/src/main/java/org/keycloak/broker/spid/SpidSAMLEndpointHelper.java @@ -0,0 +1,670 @@ +package org.keycloak.broker.spid; + +import org.jboss.logging.Logger; +import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationDataType; +import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationType; +import org.keycloak.dom.saml.v2.assertion.SubjectType; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.saml.common.constants.JBossSAMLURIConstants; +import org.keycloak.saml.common.util.DocumentUtil; +import org.keycloak.saml.common.util.StringUtil; +import org.keycloak.services.messages.Messages; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeConstants; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.namespace.QName; +import java.time.Instant; +import java.time.format.DateTimeParseException; +import java.util.Arrays; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.Objects; + +/** + * Externalized the SPID validation method. Using this approach is much simpler create some useful unit tests. + */ +public class SpidSAMLEndpointHelper { + + protected static final Logger logger = Logger.getLogger(SpidSAMLEndpointHelper.class); + + private final SpidIdentityProviderConfig config; + + public SpidSAMLEndpointHelper(SpidIdentityProviderConfig config) { + this.config = config; + } + + /** + * This method verifies the correctness of the response sent by the IdP. + * The comments written in italian are the actual copy of the error statements + * given by the AGID testing tool. In this way it is possible to keep track which code block + * belongs to which test, and its requirement. + * Last two parameters are obtained from AuthenticationSessionModel + *
+     *   String requestIssueInstantNote = authSession.getClientNote(JBossSAMLConstants.ISSUE_INSTANT.name());
+     *   String assertionConsumerServiceURL = authSession.getClientNote(JBossSAMLConstants.ASSERTION_CONSUMER_SERVICE_URL.name());
+     * 
+ * + * @param documentElement complete document + * @param assertionElement single assertion + * @param expectedRequestId request id + * @param responseType response type + * @param requestIssueInstantNote request Issue instant note (extrated from + * @param assertionConsumerServiceURL assertion consumer service url + * @return spidcode response error string + */ + public String verifySpidResponse(Element documentElement, + Element assertionElement, + String expectedRequestId, + ResponseType responseType, + String requestIssueInstantNote, + String assertionConsumerServiceURL) { + //2: Unsigned Response + if (responseType.getSignature() == null) { + return "SpidSamlCheck_02"; + } + + //3: Unsigned Assertion + if (!responseType.getAssertions().isEmpty() && + responseType.getAssertions().get(0).getAssertion().getSignature() == null) { + return "SpidSamlCheck_03"; + } + //8: Null ID + if (StringUtil.isNullOrEmpty(responseType.getID())) { + return "SpidSamlCheck_08"; + } + + try { + XMLGregorianCalendar requestIssueInstant = DatatypeFactory.newInstance(). + newXMLGregorianCalendar(requestIssueInstantNote); + + // 13: IssueInstant correct UTC format -> non valid UTC format throws DateTimeParseException + Instant.parse(responseType.getIssueInstant().toString()); + + + XMLGregorianCalendar responseIssueInstant = responseType.getIssueInstant(); + + //14: Issue Instant req < Issue Instant Response + if (responseIssueInstant.compare(requestIssueInstant) != DatatypeConstants.GREATER) { + return "SpidSamlCheck_14"; + } + + //15: Response Attribute IssueInstant within three minutes of request IssueInstant + //https://github.com/italia/spid-saml-check/issues/73 + //max tolerance of three minutes + long responseTimeMillis = responseIssueInstant.toGregorianCalendar().getTimeInMillis(); + long requestTimeMillis = requestIssueInstant.toGregorianCalendar().getTimeInMillis(); + + if ((responseTimeMillis - requestTimeMillis) > 0 && (responseTimeMillis - requestTimeMillis) > 180000) { + return "SpidSamlCheck_15"; + + } + + + GregorianCalendar now = new GregorianCalendar(); + XMLGregorianCalendar nowXmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(now); + if (responseIssueInstant.compare(nowXmlGregorianCalendar) == DatatypeConstants.GREATER) { + return "SpidSamlCheck_15"; + } + + //110 IssueInstant must not have milliseconds, this is not an error anymore https://www.spid.gov.it/wp-content/uploads/2021/07/SPID_QAD.pdf + int responseIssueInstantMillisecond = responseIssueInstant.getMillisecond(); + if (responseIssueInstantMillisecond > 0) { + logger.debug("responseIssueInstant has milliseconds, SpidSamlCheck_110"); + //return "SpidSamlCheck_110"; + } + + + } catch (DatatypeConfigurationException e) { + logger.error("Could not convert request IssueInstant to XMLGregorianCalendar, wrong format?"); + return "SpidFault_ErrorCode_nr3"; + } catch (DateTimeParseException e) { + //FIXME: no one emits this exception + return "SpidSamlCheck_13"; + } + + // 17: Response > InResponseTo missing + if (!documentElement.hasAttribute("InResponseTo")) { + return "SpidSamlCheck_17"; + } + + // 16: Response > InResponseTo empty + String responseInResponseToValue = documentElement.getAttribute("InResponseTo"); + if (responseInResponseToValue.isEmpty()) { + return "SpidSamlCheck_16"; + } + + // 18: Response > InResponseTo does not match request ID + if (!responseInResponseToValue.equals(expectedRequestId)) { + return "SpidSamlCheck_18"; + } + + //22 Unspecified Element Status + if (responseType.getStatus() != null && + responseType.getStatus().getStatusCode() == null && + responseType.getStatus().getStatusDetail() == null && + responseType.getStatus().getStatusMessage() == null) { + return "SpidSamlCheck_22"; + } + + //23 Missing Element Status + if (responseType.getStatus() == null) { + return "SpidSamlCheck_23"; + } + + //24 Unspecified Element StatusCode + if (responseType.getStatus() != null && + responseType.getStatus().getStatusCode() != null && + StringUtil.isNullOrEmpty(responseType.getStatus().getStatusCode().getValue().toString())) { + return "SpidSamlCheck_24"; + } + + //25 Missing StatusCode: note-> The test fails with code 22 because the + // element sent by the response is the same. (See response xml from the SPID testing tool) + + if (responseType.getStatus() != null && + responseType.getStatus().getStatusCode() == null) { + return "SpidSamlCheck_25"; + } + + //26 StatusCode element != Success + if (responseType.getStatus() != null && + responseType.getStatus().getStatusCode() != null && + !responseType.getStatus().getStatusCode().getValue().toString().substring(responseType.getStatus().getStatusCode().getValue().toString().lastIndexOf(":") + 1).equals("Success")) { + return "SpidSamlCheck_26"; + } + + //27 Unspecified Issuer element + if (responseType.getIssuer() != null && + StringUtil.isNullOrEmpty(responseType.getIssuer().getValue())) { + return "SpidSamlCheck_27"; + } + + //28 Missing element Issuer + // the test fails with code 1 (test2) because the testing tool sends an unsigned response + // the control block is included anyhow + if (responseType.getIssuer() == null) { + return "SpidSamlCheck_28"; + } + + //29 Element Issuer != EntityID IdP + if (!responseType.getIssuer().getValue().equalsIgnoreCase(config.getIdpEntityId())) { + return "SpidSamlCheck_29"; + } + + //30/31 Format di Issuer attribute must be omitted or have the value urn:oasis:names:tc:SAML:2.0:nameid-format:entity + if (responseType.getIssuer() != null && + responseType.getIssuer().getFormat() != null && + !responseType.getIssuer().getFormat().toString().equals(SpidSAMLEndpoint.ISSUER_FORMAT)) { + return "SpidSamlCheck_30"; + } + + //33 Assertion attribute ID unspecified + //32/34 checked by keycloak + String assertionID = assertionElement.getAttribute("ID"); + if (assertionID.isEmpty()) { + return "SpidSamlCheck_33"; + } + + String assertionIssueInstant = assertionElement.getAttribute("IssueInstant"); + + + if (!StringUtil.isNullOrEmpty(assertionIssueInstant)) { + try { + XMLGregorianCalendar requestIssueInstant = DatatypeFactory.newInstance(). + newXMLGregorianCalendar(requestIssueInstantNote); + XMLGregorianCalendar assertionIssueInstantXML = DatatypeFactory.newInstance(). + newXMLGregorianCalendar(assertionIssueInstant); + //39 Assertion IssueInstant attribute < Request IssueInstant + + if (assertionIssueInstantXML.compare(requestIssueInstant) == DatatypeConstants.LESSER) { + return "SpidSamlCheck_39"; + } + + //40. Assertion IssueInstant attribute > later than 3 minutes from request + //https://github.com/italia/spid-saml-check/issues/73 + //max tolerance of three minutes + long assertionTimeMillis = assertionIssueInstantXML.toGregorianCalendar().getTimeInMillis(); + long requestTimemillis = requestIssueInstant.toGregorianCalendar().getTimeInMillis(); + + if ((assertionTimeMillis - requestTimemillis) > 0 && (assertionTimeMillis - requestTimemillis) > 180000) { + return "SpidSamlCheck_40"; + + } + + //110 Assertion IssueInstant with milliseconds, this is not an error anymore https://www.spid.gov.it/wp-content/uploads/2021/07/SPID_QAD.pdf + int assertionIssueInstantXMLMillisecond = assertionIssueInstantXML.getMillisecond(); + if (assertionIssueInstantXMLMillisecond > 0) { + logger.debug("responseIssueInstant (in assertion) has milliseconds, SpidSamlCheck_110"); + //return "SpidSamlCheck_110"; + } + + } catch (DatatypeConfigurationException e) { + logger.error("Could not convert request IssueInstant to XMLGregorianCalendar, wrong format?"); + return "SpidFault_ErrorCode_nr3"; + } + } + + // 42: Assertion > Subject missing + Element subjectElement = getDocumentElement(assertionElement, "Subject"); + if (subjectElement == null) { + return "SpidSamlCheck_42"; + } + + // 41: Assertion > Subject empty (Keycloak returns error earlier) + if (!hasNamedChild(subjectElement)) { + return "SpidSamlCheck_41"; + } + + //44 Assertion NameID missing + Element nameID = getDocumentElement(assertionElement, "NameID"); + if (nameID == null) { + return "SpidSamlCheck_44"; + } + + //43 Assertion NameID unspecified + if (nameID.getFirstChild() != null && StringUtil.isNullOrEmpty(nameID.getFirstChild().getNodeValue().trim())) { + return "SpidSamlCheck_43"; + } + + //45/46 Format NameID attribute missing or unspecified + if (StringUtil.isNullOrEmpty(nameID.getAttribute("Format"))) { + return "SpidSamlCheck_4546"; + } + + //47 Format NameID attribute != urn:oasis:names:tc:SAML:2.0:nameidformat:transient + if (!StringUtil.isNullOrEmpty(nameID.getAttribute("Format")) && !nameID.getAttribute("Format").equals(SpidSAMLEndpoint.ASSERTION_NAMEID_FORMAT)) { + return "SpidSamlCheck_47"; + + } + //48/49 Assertion NameQualifier unspecified + if (StringUtil.isNullOrEmpty(nameID.getAttribute("NameQualifier"))) { + return "SpidSamlCheck_4849"; + } + + // 52: Assertion > Subject > Confirmation missing + Element subjectConfirmationElement = getDocumentElement(subjectElement, "SubjectConfirmation"); + + if (subjectConfirmationElement == null) { + return "SpidSamlCheck_52"; + } + + // 51: Assertion > Subject > Confirmation empty + if (!hasNamedChild(subjectConfirmationElement)) { + return "SpidSamlCheck_51"; + } + + // 53: Assertion > Subject > Confirmation > Method missing + if (!subjectConfirmationElement.hasAttribute("Method")) { + return "SpidSamlCheck_53"; + } + + // 54: Assertion > Subject > Confirmation > Method empty + String subjectConfirmationMethodValue = subjectConfirmationElement.getAttribute("Method"); + if (subjectConfirmationMethodValue.isEmpty()) { + return "SpidSamlCheck_54"; + } + + // 55: Assertion > Subject > Confirmation > Method is not JBossSAMLURIConstants.SUBJECT_CONFIRMATION_BEARER + if (!subjectConfirmationMethodValue.equals(JBossSAMLURIConstants.SUBJECT_CONFIRMATION_BEARER.get())) { + return "SpidSamlCheck_55"; + } + + // 56: Assertion > Subject > Confirmation > SubjectConfirmationData missing. Testing tool xml snippet same as 51 + Element subjectConfirmationDataElement = getDocumentElement(subjectConfirmationElement, "SubjectConfirmationData"); + + if (subjectConfirmationDataElement == null) { + return "SpidSamlCheck_56"; + } + + // 58: Assertion > Subject > Confirmation > SubjectConfirmationData > Recipient missing + if (!subjectConfirmationDataElement.hasAttribute("Recipient")) { + return "SpidSamlCheck_58"; + } + + // 59: Assertion > Subject > Confirmation > SubjectConfirmationData > different than AssertionConsumerServiceURL + String recipient = subjectConfirmationDataElement.getAttribute("Recipient"); + if (!StringUtil.isNullOrEmpty(recipient) && !recipient.trim().equals(assertionConsumerServiceURL.trim())) { + return "SpidSamlCheck_59"; + } + + // 57: Assertion > Subject > Confirmation > SubjectConfirmationData > Recipient is empty + String subjectConfirmationDataRecipientValue = subjectConfirmationDataElement.getAttribute("Recipient"); + if (subjectConfirmationDataRecipientValue.isEmpty()) { + return "SpidSamlCheck_57"; + } + + // 61: Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo missing + if (!subjectConfirmationDataElement.hasAttribute("InResponseTo")) { + return "SpidSamlCheck_61"; + } + + // 60: Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo is empty + String subjectConfirmationDataInResponseToValue = subjectConfirmationDataElement.getAttribute("InResponseTo"); + if (subjectConfirmationDataInResponseToValue.isEmpty()) { + return "SpidSamlCheck_60"; + } + + // 62: Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo does not match request ID + if (!subjectConfirmationDataInResponseToValue.equals(expectedRequestId)) { + return "SpidSamlCheck_62"; + } + + // 64: Assertion > Subject > Confirmation > SubjectConfirmationData > NotOnOrAfter missing + String notOnOrAfter = subjectConfirmationDataElement.getAttribute("NotOnOrAfter"); + if (StringUtil.isNullOrEmpty(notOnOrAfter.trim())) { + return "SpidSamlCheck_64"; + } + + try { + // 66: Assertion > Subject > Confirmation > SubjectConfirmationData > NotOnOrAfter before response reception + XMLGregorianCalendar notOnOrAfterXMLGregorian = DatatypeFactory.newInstance(). + newXMLGregorianCalendar(notOnOrAfter); + XMLGregorianCalendar now = DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar()); + + if (notOnOrAfterXMLGregorian.compare(now) == DatatypeConstants.LESSER) { + return "SpidSamlCheck_66"; + } + + } catch (DatatypeConfigurationException e) { + logger.error("Could not convert request NotOnOrAfter to XMLGregorianCalendar, wrong format?"); + return "SpidFault_ErrorCode_nr3"; + } + // 67: Assertion > Issuer non specified + Element issuerElement = getDocumentElement(assertionElement, "Issuer"); + if (issuerElement != null && + (issuerElement.getFirstChild() == null || + StringUtil.isNullOrEmpty(issuerElement.getFirstChild().getNodeValue()))) { + return "SpidSamlCheck_67"; + + } + // 68: Assertion > Issuer missing + if (issuerElement == null) { + return "SpidSamlCheck_68"; + + } + + //69 Assertion > Issuer != entityID idp + if (!issuerElement.getFirstChild().getNodeValue().equals(config.getIdpEntityId())) { + return "SpidSamlCheck_69"; + } + //70 71 Assertion > Issuer > Format not specified or null + String format = issuerElement.getAttribute("Format"); + if (StringUtil.isNullOrEmpty(format)) { + return "SpidSamlCheck_7071"; + } + + //72 Assertion > Issuer > Format different than constant + if (!format.equals(SpidSAMLEndpoint.ASSERTION_ISSUER_FORMAT)) { + return "SpidSamlCheck_72"; + } + + //73 Assertion > Conditions missing + Element conditionsElement = getDocumentElement(assertionElement, "Conditions"); + if (conditionsElement != null && !hasNamedChild(conditionsElement)) { + return "SpidSamlCheck_73"; + } + //74 Assertion > Conditions is null + if (conditionsElement == null) { + return "SpidSamlCheck_74"; + } + //75-76 Assertion > Conditions > NotBefore null or empty + String notBefore = conditionsElement.getAttribute("NotBefore"); + if (StringUtil.isNullOrEmpty(notBefore)) { + return "SpidSamlCheck_7576"; + } + + //78 Assertion > Condition > NotBefore after response + try { + XMLGregorianCalendar notBeforeXmlGregorian = DatatypeFactory.newInstance(). + newXMLGregorianCalendar(notBefore); + XMLGregorianCalendar now = DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar()); + + if (notBeforeXmlGregorian.compare(now) == DatatypeConstants.GREATER) { + return "SpidSamlCheck_78"; + } + + } catch (DatatypeConfigurationException e) { + logger.error("Could not convert request NotOnOrAfter to XMLGregorianCalendar, wrong format?"); + return "SpidFault_ErrorCode_nr3"; + } + + //79-80 Assertion > Condition > NotOnOrAfter missing or not specified + String conditionsNotOnOrAfter = conditionsElement.getAttribute("NotOnOrAfter"); + if (StringUtil.isNullOrEmpty(conditionsNotOnOrAfter.trim())) { + return "SpidSamlCheck_7980"; + } + + //82 Assertion > Condition > NotOnOrAfter before response + try { + XMLGregorianCalendar conditionsNotOnOrAfterXmlGregorian = DatatypeFactory.newInstance(). + newXMLGregorianCalendar(conditionsNotOnOrAfter); + XMLGregorianCalendar now = DatatypeFactory.newInstance().newXMLGregorianCalendar(new GregorianCalendar()); + + if (conditionsNotOnOrAfterXmlGregorian.compare(now) == DatatypeConstants.LESSER) { + return "SpidSamlCheck_82"; + } + + } catch (DatatypeConfigurationException e) { + logger.error("Could not convert request NotOnOrAfter to XMLGregorianCalendar, wrong format?"); + return "SpidFault_ErrorCode_nr3"; + } + + //86 Assertion > Condition > Audience > AudienceRestriction missing (note: testing tool xml same as #83) + Element audienceRestrictionElement = getDocumentElement(conditionsElement, "AudienceRestriction"); + Element audience = getDocumentElement(audienceRestrictionElement, "Audience"); + + //83 Assertion > Condition > AudienceRestriction not specified + if (!hasNamedChild(audienceRestrictionElement)) { + return "SpidSamlCheck_83"; + } + + //85 86 Assertion > Condition > AudienceRestriction > Audience not specified or missing (note: testing tool 86 xml same as #83) + if (audience == null || audience.getFirstChild() == null || StringUtil.isNullOrEmpty(audience.getFirstChild().getNodeValue())) { + return "SpidSamlCheck_8586"; + } + + + //84 Assertion > Condition > AudienceRestriction null (testing tool yaml snippet same as #73) + if (audienceRestrictionElement == null) { + return "SpidSamlCheck_84"; + } + + //87 Assertion > Condition > AudienceRestriction > Audience != EntityID SP + String spEntityId = config.getEntityId(); + if (audience.getFirstChild() != null && + !StringUtil.isNullOrEmpty(audience.getFirstChild().getNodeValue()) && + !audience.getFirstChild().getNodeValue().equals(spEntityId)) { + + return "SpidSamlCheck_87"; + + } + + //88 Assertion > AuthnStatement not specified + Element authnStatement = getDocumentElement(assertionElement, "AuthnStatement"); + if (authnStatement != null && !hasNamedChild(authnStatement)) { + return "SpidSamlCheck_88"; + } + //89 Assertion > AuthnStatement null + if (authnStatement == null) { + return "SpidSamlCheck_89"; + + } + //90 Assertion > AuthnStatement > AuthnContext not specified + Element authnContextElement = getDocumentElement(authnStatement, "AuthnContext"); + if (authnContextElement != null && !hasNamedChild(authnContextElement)) { + return "SpidSamlCheck_90"; + } + //91 Assertion > AuthnContext > AuthnStatement null note: from IDP same xml response block as #88 + if (authnContextElement == null) { + return "SpidSamlCheck_91"; + + } + + //92 Assertion > AuthnStatement > AuthnContextClassRef unspecified + Element authnContextClassRef = getDocumentElement(authnContextElement, "AuthnContextClassRef"); + if (authnContextClassRef != null && + (authnContextClassRef.getFirstChild() == null || + StringUtil.isNullOrEmpty(authnContextClassRef.getFirstChild().getNodeValue()))) { + return "SpidSamlCheck_92"; + + } + + //93 Assertion > AuthnStatement > AuthnContextClassRef missing note: response snippet same as #90 + if (authnContextClassRef == null) { + return "SpidSamlCheck_93"; + } + /* + *nota: se vi sono più spidLevel specificati in keycloak, la response avrà sempre e solo il primo + * Non essendo specificato nel tool il preciso comportamento in casi di vari livelli configurati ma solo uno + * inviato dalla request, si sceglie di controllare che il livello della response sia contenuto tra i livelli + * configurati su kc (quindi nella config della request) + */ + + //94 Assertion > AuthContextClassRef spid level different from request. This block also implies #95 #96 #97 + String responseSpidLevel = authnContextClassRef.getFirstChild().getNodeValue(); + List requestSpidLevels = Arrays.asList(config.getAuthnContextClassRefs().replaceAll("[\"\\[\\](){}]", "").trim().split(",")); + + int spidLevelFromResponse = extractSpidLevel(authnContextClassRef.getFirstChild().getNodeValue()); + // only the first on configuration + int spidLevelFromConfig = extractSpidLevel(requestSpidLevels.get(0)); + if (spidLevelFromResponse < 1 || spidLevelFromConfig < 1) { + return "SpidSamlCheck_97"; + } + + int comparison = switch (config.getAuthnContextComparisonType()) { + case BETTER -> !responseSpidLevel.equals(config.getAuthnContextClassRefs()) ? spidLevelFromResponse : 0; + case MAXIMUM -> spidLevelFromResponse > spidLevelFromConfig ? spidLevelFromResponse : 0; + case MINIMUM -> spidLevelFromResponse < spidLevelFromConfig ? spidLevelFromResponse : 0; + case EXACT -> spidLevelFromResponse != spidLevelFromConfig ? spidLevelFromResponse : 0; + }; + + //98,99,100,103,104,105,106,107,108 caught by kc + //109 ok + return switch (comparison) { + case 1 -> "SpidSamlCheck_94"; + case 2 -> "SpidSamlCheck_95"; + case 3 -> "SpidSamlCheck_96"; + default -> null; + }; + + } + + private int extractSpidLevel(String value) { + return switch (value) { + case "https://www.spid.gov.it/SpidL1" -> 1; + case "https://www.spid.gov.it/SpidL2" -> 2; + case "https://www.spid.gov.it/SpidL3" -> 3; + default -> 0; + }; + } + + private Element getDocumentElement(Element assertionElement, String subject) { + return DocumentUtil.getChildElement(assertionElement, + new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(), subject)); + } + + private boolean hasNamedChild(Element element) { + NodeList childNodes = element.getChildNodes(); + if (childNodes == null) return false; + + for (int i = 0; i < childNodes.getLength(); ++i) { + Node node = childNodes.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName() != null) + return true; + } + + return false; + } + + public String checkSpidStatus(ResponseType responseType) { + if (!isSuccessfulSamlResponse(responseType)) { + // Translate SPID error codes to meaningful messages + boolean isSpidFault = responseType.getStatus() != null && responseType.getStatus().getStatusMessage() != null && responseType.getStatus().getStatusMessage().startsWith("ErrorCode nr"); + if (isSpidFault) { + return "SpidFault_" + responseType.getStatus().getStatusMessage().replace(' ', '_'); + } else { + return responseType.getStatus() == null ? "SpidSamlCheck_23" : responseType.getStatus().getStatusMessage(); + } + } + return null; + } + + private boolean isSuccessfulSamlResponse(ResponseType responseType) { + return responseType != null + && responseType.getStatus() != null + && responseType.getStatus().getStatusCode() != null + && responseType.getStatus().getStatusCode().getValue() != null + && Objects.equals(responseType.getStatus().getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get()); + } + + // Validate InResponseTo attribute: must match the generated request ID + public boolean validateInResponseToAttribute(ResponseType responseType, String expectedRequestId) { + + // If we are not expecting a request ID, don't bother + if (expectedRequestId == null || expectedRequestId.isEmpty()) + return true; + + // We are expecting a request ID so we are in SP-initiated login, attribute InResponseTo must be present + if (responseType.getInResponseTo() == null) { + logger.error("Response Validation Error: InResponseTo attribute was expected but not present in received response"); + return false; + } + + // Attribute is present, proceed with validation + // 1) Attribute Response > InResponseTo must not be empty + String responseInResponseToValue = responseType.getInResponseTo(); + if (responseInResponseToValue.isEmpty()) { + logger.error("Response Validation Error: InResponseTo attribute was expected but it is empty in received response"); + return false; + } + + // 2) Attribute Response > InResponseTo must match request ID + if (!responseInResponseToValue.equals(expectedRequestId)) { + logger.error("Response Validation Error: received InResponseTo attribute does not match the expected request ID"); + return false; + } + + // If present, Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo must also be validated + if (responseType.getAssertions().isEmpty()) + return true; + + SubjectType subjectElement = responseType.getAssertions().get(0).getAssertion().getSubject(); + if (subjectElement != null) { + if (subjectElement.getConfirmation() != null && !subjectElement.getConfirmation().isEmpty()) { + SubjectConfirmationType subjectConfirmationElement = subjectElement.getConfirmation().get(0); + + if (subjectConfirmationElement != null) { + SubjectConfirmationDataType subjectConfirmationDataElement = subjectConfirmationElement.getSubjectConfirmationData(); + + if (subjectConfirmationDataElement != null) { + if (subjectConfirmationDataElement.getInResponseTo() != null) { + // 3) Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo is empty + String subjectConfirmationDataInResponseToValue = subjectConfirmationDataElement.getInResponseTo(); + if (subjectConfirmationDataInResponseToValue.isEmpty()) { + logger.error("Response Validation Error: SubjectConfirmationData InResponseTo attribute was expected but it is empty in received response"); + return false; + } + + // 4) Assertion > Subject > Confirmation > SubjectConfirmationData > InResponseTo does not match request ID + if (!subjectConfirmationDataInResponseToValue.equals(expectedRequestId)) { + logger.error("Response Validation Error: received SubjectConfirmationData InResponseTo attribute does not match the expected request ID"); + return false; + } + } + } + } + } + } + + return true; + } + + public String checkAssertions(ResponseType responseType) { + return responseType.getAssertions() == null || responseType.getAssertions().isEmpty() ? "SpidSamlCheck_32" : null; + } +} diff --git a/src/main/java/org/keycloak/broker/spid/mappers/SpidUsernameTemplateMapper.java b/src/main/java/org/keycloak/broker/spid/mappers/SpidUsernameTemplateMapper.java index 92ad9ed..19be296 100644 --- a/src/main/java/org/keycloak/broker/spid/mappers/SpidUsernameTemplateMapper.java +++ b/src/main/java/org/keycloak/broker/spid/mappers/SpidUsernameTemplateMapper.java @@ -64,7 +64,7 @@ public void preprocessFederatedIdentity(KeycloakSession session, RealmModel real private void setUserNameFromTemplate(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) { AssertionType assertion = (AssertionType)context.getContextData().get("SAML_ASSERTION"); - String template = (String)mapperModel.getConfig().get("template"); + String template = mapperModel.getConfig().get("template"); Matcher m = SUBSTITUTION.matcher(template); StringBuffer sb = new StringBuffer(); @@ -76,14 +76,14 @@ private void setUserNameFromTemplate(IdentityProviderMapperModel mapperModel, Br var10001.getClass(); UnaryOperator transformer = (UnaryOperator)var10000.map(var10001::get).orElse(UnaryOperator.identity()); if (variable.equals("ALIAS")) { - m.appendReplacement(sb, (String)transformer.apply(context.getIdpConfig().getAlias())); + m.appendReplacement(sb, transformer.apply(context.getIdpConfig().getAlias())); } else if (variable.equals("UUID")) { - m.appendReplacement(sb, (String)transformer.apply(KeycloakModelUtils.generateId())); + m.appendReplacement(sb, transformer.apply(KeycloakModelUtils.generateId())); } else if (variable.equals("NAMEID")) { SubjectType subject = assertion.getSubject(); SubjectType.STSubType subType = subject.getSubType(); NameIDType subjectNameID = (NameIDType)subType.getBaseID(); - m.appendReplacement(sb, (String)transformer.apply(subjectNameID.getValue())); + m.appendReplacement(sb, transformer.apply(subjectNameID.getValue())); } else if (!variable.startsWith("ATTRIBUTE.")) { m.appendReplacement(sb, m.group(1)); } else { @@ -114,7 +114,7 @@ private void setUserNameFromTemplate(IdentityProviderMapperModel mapperModel, Br value = value.split("^(?i)TINIT-")[1]; - m.appendReplacement(sb, (String)transformer.apply(value)); + m.appendReplacement(sb,transformer.apply(value)); break; } } 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 c613030..a1db6e5 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.EntityDescriptorType; import org.keycloak.dom.saml.v2.metadata.ExtensionsType; 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.LocalizedURIType; import org.keycloak.dom.saml.v2.metadata.OrganizationType; @@ -54,18 +56,10 @@ import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; +import java.util.*; +import java.util.function.Function; 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 javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLStreamWriter; @@ -73,6 +67,14 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; + +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 org.keycloak.broker.provider.IdentityProviderMapper; import org.keycloak.broker.spid.SpidIdentityProvider; import org.keycloak.broker.spid.SpidIdentityProviderConfig; @@ -105,13 +107,13 @@ public Response get() { List lstSpidIdentityProviders = realm.getIdentityProvidersStream() .filter(t -> t.getProviderId().equals(SpidIdentityProviderFactory.PROVIDER_ID) && t.isEnabled()) - .sorted((o1, o2) -> o1.getAlias().compareTo(o2.getAlias())) - .collect(Collectors.toList()); + .sorted(this::compareByGuiOrderOrAlias) + .toList(); if (lstSpidIdentityProviders.isEmpty()) throw new Exception("No SPID providers found!"); - // Create an instance of the first SPID Identity Provider in alphabetical order + // Create an instance of the first SPID Identity Provider in gui or, as a fallback, alphabetical order SpidIdentityProviderFactory providerFactory = new SpidIdentityProviderFactory(); SpidIdentityProvider firstSpidProvider = providerFactory.create(session, lstSpidIdentityProviders.get(0)); @@ -147,8 +149,8 @@ public Response get() { ? 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) @@ -159,10 +161,10 @@ public Response get() { Element element = SPMetadataDescriptor .buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())); - signingKeys.add(element); + signingKeys.add(asSigningKeyDescriptor(element)); if (key.getStatus() == KeyStatus.ACTIVE) { - encryptionKeys.add(element); + encryptionKeys.add(asEncryptionKeyDescriptor(element)); } } catch (ParserConfigurationException e) { logger.warn("Failed to export SAML SP Metadata!", e); @@ -175,7 +177,7 @@ public Response get() { 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); @@ -297,7 +299,6 @@ public Response get() { if (firstSpidProvider.getConfig().isSignSpMetadata()) { KeyWrapper activeKey = session.keys().getActiveKey(realm, KeyUse.SIG, Algorithm.RS256); - // KeyManager.ActiveRsaKey activeKey = session.keys().getActiveRsaKey(realm); String keyName = firstSpidProvider.getConfig().getXmlSigKeyInfoKeyNameTransformer() .getKeyName(activeKey.getKid(), activeKey.getCertificate()); KeyPair keyPair = new KeyPair((PublicKey) activeKey.getPublicKey(), @@ -324,6 +325,39 @@ public Response get() { } } + private int compareByGuiOrderOrAlias(IdentityProviderModel id1, IdentityProviderModel id2){ + String guiOrder1 = id1.getConfig().get("guiOrder"); + String guiOrder2 = id2.getConfig().get("guiOrder"); + + if (guiOrder1 != null && guiOrder2 != null){ + int o1 = 0; + int o2 = 0; + try{ + o1 = Integer.parseInt(guiOrder1); + o2 = Integer.parseInt(guiOrder2); + }catch ( NumberFormatException e){ + logger.warn("Cannot parse guiOrder"); + } + return Integer.compare(o1, o2); + }else{ + return id1.getAlias().compareTo(id2.getAlias()); + } + } + + private static KeyDescriptorType asSigningKeyDescriptor(Element value){ + KeyDescriptorType result = new KeyDescriptorType(); + result.setKeyInfo(value); + result.setUse(KeyTypes.SIGNING); + return result; + } + + private static KeyDescriptorType asEncryptionKeyDescriptor(Element value){ + KeyDescriptorType result = new KeyDescriptorType(); + result.setKeyInfo(value); + result.setUse(KeyTypes.ENCRYPTION); + return result; + } + private String getEntityId(String configEntityId, UriInfo uriInfo, RealmModel realm) { if (configEntityId == null || configEntityId.isEmpty()) return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString(); diff --git a/src/main/resources/META-INF/jboss-deployment-structure.xml b/src/main/resources/META-INF/jboss-deployment-structure.xml deleted file mode 100644 index 978c8d5..0000000 --- a/src/main/resources/META-INF/jboss-deployment-structure.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/theme-resources/messages/admin-messages_en.properties b/src/main/resources/provider-config/messages_en.properties similarity index 99% rename from src/main/resources/theme-resources/messages/admin-messages_en.properties rename to src/main/resources/provider-config/messages_en.properties index 56efed2..9f862d1 100644 --- a/src/main/resources/theme-resources/messages/admin-messages_en.properties +++ b/src/main/resources/provider-config/messages_en.properties @@ -2,6 +2,7 @@ identity-provider.saml.attribute-consuming-service-index=Attribute Consuming Ser identity-provider.saml.attribute-consuming-service-index.tooltip=Index of the Attribute Consuming Service to be used for assertions, as described from SP metadata 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" +# from here 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 @@ -91,3 +92,4 @@ identity-provider.spid.vatNumber.tooltip=VAT Number of the private subject identity-provider.spid.fiscalCode=Fiscal Code identity-provider.spid.fiscalCode.tooltip=Fiscal Code of the private subject identity-provider.spid.idpEntityId=IDP EntityID +identity-provider.spid.idpEntityId.tooltip=IDP EntityID diff --git a/src/main/resources/provider-config/messages_it.properties b/src/main/resources/provider-config/messages_it.properties new file mode 100644 index 0000000..9f862d1 --- /dev/null +++ b/src/main/resources/provider-config/messages_it.properties @@ -0,0 +1,95 @@ +identity-provider.saml.attribute-consuming-service-index=Attribute Consuming Service Index +identity-provider.saml.attribute-consuming-service-index.tooltip=Index of the Attribute Consuming Service to be used for assertions, as described from SP metadata +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" +# from here +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/" +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.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) +#CessionarioCommittente +identity-provider.spid.cessionarioCommittente=Cessionario Committente (Billing) +identity-provider.spid.cessionarioCommittente.tooltip=Cessionario Committente (Billing) +identity-provider.spid.cessionarioCommittente.datiAnagrafici=Dati Anagrafici (Billing) +identity-provider.spid.cessionarioCommittente.datiAnagrafici.tooltip=Dati Anagrafici (Billing) +identity-provider.spid.cessionarioCommittente.datiAnagrafici.idFiscaleIva=Id Fiscale IVA (Billing) +identity-provider.spid.cessionarioCommittente.datiAnagrafici.idFiscaleIva.tooltip=Id Fiscale IVA (Billing) +identity-provider.spid.cessionarioCommittente.anagrafica=Anagrafica (Billing) +identity-provider.spid.cessionarioCommittente.anagrafica.tooltip=Anagrafica (Billing) +identity-provider.spid.cessionarioCommittente.sede=Sede (Billing) +identity-provider.spid.cessionarioCommittente.sede.tooltip=Sede (Billing) +#billingIdPaese +identity-provider.spid.cessionarioCommittente.idPaese.billing=Id Paese (Billing) +identity-provider.spid.cessionarioCommittente.idPaese.billing.tooltip=Id Paese (Billing) +#billingIdCodice +identity-provider.spid.cessionarioCommittente.idCodice.billing=Id Codice (Billing) +identity-provider.spid.cessionarioCommittente.idCodice.billing.tooltip=Id Codice (Billing) +#billingCodiceFiscale +identity-provider.spid.cessionarioCommittente.codiceFiscale.billing=Fiscal Code (Billing) +identity-provider.spid.cessionarioCommittente.codiceFiscale.billing.tooltip=Fiscal Code (Billing). This field is optional. +#billingAnagraficaDenominazione +identity-provider.spid.cessionarioCommittente.anagraficaDenominazione.billing=Denomination (Billing) +identity-provider.spid.cessionarioCommittente.anagraficaDenominazione.billing.tooltip=Denomination (Billing). Should be empty if you fill in Name (Billing) and Surname (Billing). +#billingAnagraficaNome +identity-provider.spid.cessionarioCommittente.anagraficaNome.billing=Name (Billing) +identity-provider.spid.cessionarioCommittente.anagraficaNome.billing.tooltip=You should fill in also the Surname (Billing) field. Denomination (Billing) should be empty. +#billingAnagraficaCognome +identity-provider.spid.cessionarioCommittente.anagraficaCognome.billing=Surname (Billing) +identity-provider.spid.cessionarioCommittente.anagraficaCognome.billing.tooltip=You should fill in also the Name (Billing) field. Denomination (Billing) should be empty. +#billingAnagraficaTitolo +identity-provider.spid.cessionarioCommittente.anagraficaTitolo.billing=Title (Billing) +identity-provider.spid.cessionarioCommittente.anagraficaTitolo.billing.tooltip=Title (Billing). This field is optional. +#billingAnagraficaCodiceEORI +identity-provider.spid.cessionarioCommittente.anagraficaCodiceEORI.billing=EORI Code (Billing) +identity-provider.spid.cessionarioCommittente.anagraficaCodiceEORI.billing.tooltip=EORI Code (Billing). This field is optional. +#billingSedeIndirizzo +identity-provider.spid.cessionarioCommittente.sedeIndirizzo.billing=Site Address (Billing) +identity-provider.spid.cessionarioCommittente.sedeIndirizzo.billing.tooltip=Site Address (Billing) +#billingSedeNumeroCivico +identity-provider.spid.cessionarioCommittente.sedeNumeroCivico.billing=Site Number (Billing) +identity-provider.spid.cessionarioCommittente.sedeNumeroCivico.billing.title=Site Number (Billing). This field is optional. +#billingSedeCap +identity-provider.spid.cessionarioCommittente.sedeCap.billing=Site Postal Code (Billing) +identity-provider.spid.cessionarioCommittente.sedeCap.billing.tooltip=Site Postal Code (Billing) +#billingSedeComune +identity-provider.spid.cessionarioCommittente.sedeComune.billing=Site Municipality (Billing) +identity-provider.spid.cessionarioCommittente.sedeComune.billing.tooltip=Site Municipality (Billing) +#billingSedeProvincia +identity-provider.spid.cessionarioCommittente.sedeProvincia.billing=Site Province (Billing) +identity-provider.spid.cessionarioCommittente.sedeProvincia.billing.tooltip=Site Province (Billing). This field is optional. +#billingSedeNazione +identity-provider.spid.cessionarioCommittente.sedeNazione.billing=Site Nation (Billing) +identity-provider.spid.cessionarioCommittente.sedeNazione.billing.tooltip=Site Nation (Billing) +#TerzoIntermediarioSoggettoEmittente +identity-provider.spid.terzoIntermediario.soggettoEmittente=Terzo Intermediario soggetto emittente (Billing) +identity-provider.spid.terzoIntermediario.soggettoEmittente.tooltip=Terzo Intermediario soggetto emittente (Billing) +#billingTerzoIntermediarioSoggettoEmittente +identity-provider.spid.terzoIntermediario.soggettoEmittente.billing=Emitter subject (Billing) +identity-provider.spid.terzoIntermediario.soggettoEmittente.billing.tooltip=Emitter subject (Billing). This field is optional. +#Other +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 +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 +identity-provider.spid.ipaCode.tooltip=IPA Code of the Public Administration +identity-provider.spid.vatNumber=VAT Number +identity-provider.spid.vatNumber.tooltip=VAT Number of the private subject +identity-provider.spid.fiscalCode=Fiscal Code +identity-provider.spid.fiscalCode.tooltip=Fiscal Code of the private subject +identity-provider.spid.idpEntityId=IDP EntityID +identity-provider.spid.idpEntityId.tooltip=IDP EntityID diff --git a/src/main/resources/theme-resources/messages/messages_en.properties b/src/main/resources/theme-resources/messages/messages_en.properties index 08d7e22..610e91f 100644 --- a/src/main/resources/theme-resources/messages/messages_en.properties +++ b/src/main/resources/theme-resources/messages/messages_en.properties @@ -1,26 +1,26 @@ -SpidFault_ErrorCode_nr2=SPID Identity Provider currently unavailable. Please try again later or use another authentication method (SPID ErrorCode nr2) -SpidFault_ErrorCode_nr3=Internal error from the SPID Identity Provider. Please try again later or use another authentication method (SPID ErrorCode nr3) -SpidFault_ErrorCode_nr4=Unexpected HTTP Binding method. Please refer to the Service Provider for assistance (SPID ErrorCode nr4) -SpidFault_ErrorCode_nr5=Request signature verification failed. Please refer to the Service Provider for assistance (SPID ErrorCode nr5) -SpidFault_ErrorCode_nr6=Mismatched HTTP Binding method. Please refer to the Service Provider for assistance (SPID ErrorCode nr6) -SpidFault_ErrorCode_nr7=Error verifying request signature. Please refer to the Service Provider for assistance (SPID ErrorCode nr7) -SpidFault_ErrorCode_nr8=Request format not compliant with SAML specifications. Please refer to the Service Provider for assistance (SPID ErrorCode nr8) -SpidFault_ErrorCode_nr9=Version attribute missing or different from ''2.0''. Please refer to the Service Provider for assistance (SPID ErrorCode nr9) -SpidFault_ErrorCode_nr10=Issuer missing or not matching the entity that signed the request. Please refer to the Service Provider for assistance (SPID ErrorCode nr10) -SpidFault_ErrorCode_nr11=Request ID missing or value not allowed. Please refer to the Service Provider for assistance (SPID ErrorCode nr11) -SpidFault_ErrorCode_nr12=RequestAuthnContext missing or does not match the values allowed by SPID. Please refer to the Service Provider for assistance (SPID ErrorCode nr12) -SpidFault_ErrorCode_nr13=IssueInstant element missing or not matching the time of the received request. Please refer to the Service Provider for assistance (SPID ErrorCode nr13) -SpidFault_ErrorCode_nr14=Destination element missing or not matching the receiving Identity Provider. Please refer to the Service Provider for assistance (SPID ErrorCode nr14) -SpidFault_ErrorCode_nr15=isPassive attribute has value true. Please refer to the Service Provider for assistance (SPID ErrorCode nr15) -SpidFault_ErrorCode_nr16=Error in the AssertionConsumerService value. Please refer to the Service Provider for assistance (SPID ErrorCode nr16) -SpidFault_ErrorCode_nr17=Attribute Format of the NameIDPolicy element is missing or has an unexpected value. Please refer to the Service Provider for assistance (SPID ErrorCode nr17) -SpidFault_ErrorCode_nr18=AttributeConsumerServiceIndex does not match any value declared in the SP metadata. Please refer to the Service Provider for assistance (SPID ErrorCode nr18) -SpidFault_ErrorCode_nr19=Exceeded the maximum number of login attempts (SPID ErrorCode nr19) -SpidFault_ErrorCode_nr20=User has no matching credentials for the Service Provider required authentication level (SPID ErrorCode nr20) -SpidFault_ErrorCode_nr21=Timeout during user authentication (SPID ErrorCode nr21) -SpidFault_ErrorCode_nr22=User declined consent to send data during current session (SPID ErrorCode nr22) -SpidFault_ErrorCode_nr23=Identity suspended, revoked or credentials locked (SPID ErrorCode nr23) -SpidFault_ErrorCode_nr25=Authentication canceled by user (SPID ErrorCode nr25) +SpidFault_ErrorCode_nr2=SPID Identity Provider currently unavailable. Please try again later or use another authentication method +SpidFault_ErrorCode_nr3=Internal error from the SPID Identity Provider. Please try again later or use another authentication method +SpidFault_ErrorCode_nr4=Unexpected HTTP Binding method. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr5=Request signature verification failed. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr6=Mismatched HTTP Binding method. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr7=Error verifying request signature. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr8=Request format not compliant with SAML specifications. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr9=Version attribute missing or different from ''2.0''. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr10=Issuer missing or not matching the entity that signed the request. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr11=Request ID missing or value not allowed. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr12=RequestAuthnContext missing or does not match the values allowed by SPID. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr13=IssueInstant element missing or not matching the time of the received request. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr14=Destination element missing or not matching the receiving Identity Provider. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr15=isPassive attribute has value true. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr16=Error in the AssertionConsumerService value. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr17=Attribute Format of the NameIDPolicy element is missing or has an unexpected value. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr18=AttributeConsumerServiceIndex does not match any value declared in the SP metadata. Please refer to the Service Provider for assistance +SpidFault_ErrorCode_nr19=Exceeded the maximum number of login attempts +SpidFault_ErrorCode_nr20=User has no matching credentials for the Service Provider required authentication level +SpidFault_ErrorCode_nr21=Timeout during user authentication +SpidFault_ErrorCode_nr22=User declined consent to send data during current session +SpidFault_ErrorCode_nr23=Identity suspended, revoked or credentials locked +SpidFault_ErrorCode_nr25=Authentication canceled by user SpidSamlCheck_02=Error in the SPID Identity Provider response (Unsigned Response-SPID check nr1) @@ -28,84 +28,84 @@ SpidSamlCheck_03=Error in the SPID Identity Provider response (Signed Response, SpidSamlCheck_04=Error in the SPID Identity Provider response (Response signed with different certificate from the one registered on the SP-SPID check nr3) SpidSamlCheck_08=Error in the SPID Identity Provider response (Unspecified ID attribute-SPID check nr8) SpidSamlCheck_09=Error in the SPID Identity Provider response (Missing ID attribute-SPID check nr9) -SpidSamlCheck_10=Error in the SPID Identity Provider response (Attribute Version different from 2.0-SPID check nr10) -SpidSamlCheck_11=Error in the SPID Identity Provider response (Unspecified IssueInstant attribute-SPID check nr11) -SpidSamlCheck_12=Error in the SPID Identity Provider response (Missing IssueInstant attribute-SPID check nr12) -SpidSamlCheck_13=Error in the SPID Identity Provider response (IssueInstant attribute with wrong format-SPID check nr13) -SpidSamlCheck_14=Error in the SPID Identity Provider response (Response IssueInstant before Request IssueInstant-SPID check nr14) -SpidSamlCheck_15=Error in the SPID Identity Provider response (IssueInstant attribute time is after the moment of reception-SPID check nr15) -SpidSamlCheck_nr16=Error in the SPID Identity Provider response (InResponseTo attribute is empty-SPID check nr16) -SpidSamlCheck_nr17=Error in the SPID Identity Provider response (InResponseTo attribute is missing-SPID check nr17) -SpidSamlCheck_nr18=Error in the SPID Identity Provider response (InResponseTo attribute does not match expected request ID-SPID check nr18) -SpidSamlCheck_22=Error in the SPID Identity Provider response (Unspecified Status element-SPID check nr22) -SpidSamlCheck_23=Error in the SPID Identity Provider response (Missing Status element-SPID check nr23) -SpidSamlCheck_24=Error in the SPID Identity Provider response (Unspecified StatusCode element-SPID check nr24) -SpidSamlCheck_25=Error in the SPID Identity Provider response (Missing StatusCode element-SPID check nr25) -SpidSamlCheck_26=Error in the SPID Identity Provider response (StatusCode element different from Success (non valid)-SPID check nr26) -SpidSamlCheck_27=Error in the SPID Identity Provider response (Issuer element not specified-SPID check nr27) -SpidSamlCheck_28=Error in the SPID Identity Provider response (Missing Issuer element-SPID check nr28) -SpidSamlCheck_29=Error in the SPID Identity Provider response (Issuer element different from EntityID IdP-SPID check nr29) -SpidSamlCheck_30=Error in the SPID Identity Provider response (The Issuer Format attribuet must be omitted or have the value urn:oasis:names:tc:SAML:2.0:nameid-format:entity-SPID check nr30) -SpidSamlCheck_32=Error in the SPID Identity Provider response (Assertion element missing and positive authentication outcome-SPID check nr32) -SpidSamlCheck_33=Error in the SPID Identity Provider response (ID Assertion attribute not specified-SPID check nr33) -SpidSamlCheck_34=Error in the SPID Identity Provider response (ID Assertion attribute missing-SPID check nr34) -SpidSamlCheck_35=Error in the SPID Identity Provider response (Version Assertion attribute different from 2.0-SPID check nr35) -SpidSamlCheck_36=Error in the SPID Identity Provider response (IssueInstant Assertion attribute not specified-SPID check nr36) -SpidSamlCheck_37=Error in the SPID Identity Provider response (IssueInstant Assertion attribute missing-SPID check nr37) -SpidSamlCheck_38=Error in the SPID Identity Provider response (IssueInstant Assertion attribute with wrong format-SPID check nr38) -SpidSamlCheck_39=Error in the SPID Identity Provider response (IssueInstant Assertion attribute before IssueInstant Assertion Request attribute-SPID check nr39) -SpidSamlCheck_40=Error in the SPID Identity Provider response (IssueInstant Assertion attribute before Request IssueInstant-SPID check nr40) -SpidSamlCheck_nr41=Error in the SPID Identity Provider response (Subject element is empty-SPID check nr41) -SpidSamlCheck_nr42=Error in the SPID Identity Provider response (Subject element is missing-SPID check nr42) -SpidSamlCheck_43=Error in the SPID Identity Provider response (Unspecified Assertion element NameID-SPID check nr43) -SpidSamlCheck_44=Error in the SPID Identity Provider response (Missing Assertion element NameID-SPID check nr44) -SpidSamlCheck_4546=Error in the SPID Identity Provider response (Assertion Format attribute missing or non specified-SPID check nr45 or nr46) -SpidSamlCheck_47=Error in the SPID Identity Provider response (Assertion Format attribute diffrent than urn:oasis:names:tc:SAML:2.0:nameidformat:transient-SPID check nr47) -SpidSamlCheck_4849=Error in the SPID Identity Provider response (Assertion Format attribute missing or non specified-SPID check nr48 or nr49) -SpidSamlCheck_nr51=Error in the SPID Identity Provider response (SubjectConfirmation element is empty-SPID check nr51) -SpidSamlCheck_nr52=Error in the SPID Identity Provider response (SubjectConfirmation element is missing-SPID check nr52) -SpidSamlCheck_nr53=Error in the SPID Identity Provider response (Method attribute of SubjectConfirmation element is empty-SPID check nr53) -SpidSamlCheck_nr54=Error in the SPID Identity Provider response (Method attribute of SubjectConfirmation element is missing-SPID check nr54) -SpidSamlCheck_nr55=Error in the SPID Identity Provider response (Method attribute of SubjectConfirmation element method does not match expected value (urn:oasis:names:tc:SAML:2.0:cm:bearer)-SPID check nr55) -SpidSamlCheck_nr56=Error in the SPID Identity Provider response (Element SubjectConfirmationData is missing-SPID check nr56) -SpidSamlCheck_nr57=Error in the SPID Identity Provider response (Recipient attribute of SubjectConfirmationData element is empty-SPID check nr57) -SpidSamlCheck_nr58=Error in the SPID Identity Provider response (Recipient attribute of SubjectConfirmationData element is missing-SPID check nr58) -SpidSamlCheck_59=Error in the SPID Identity Provider response (Recipient attribute of SubjectConfirmationData from Assertion different than AssertionConsumerServiceURL-SPID check nr59) -SpidSamlCheck_nr60=Error in the SPID Identity Provider response (Attribute InResponseTo of SubjectConfirmationData element is empty-SPID check nr60) -SpidSamlCheck_nr61=Error in the SPID Identity Provider response (Attribute InResponseTo of SubjectConfirmationData element is missing-SPID check nr61) -SpidSamlCheck_nr62=Error in the SPID Identity Provider response (Attribute InResponseTo of SubjectConfirmationData element does not match expected request ID-SPID check nr62) -SpidSamlCheck_64=Error in the SPID Identity Provider response (Assertion attribute NotOnOrAfter of SubjectConfirmationData missing-SPID check nr64) -SpidSamlCheck_66=Error in the SPID Identity Provider response (SubjectConfirmationData Attribute NotOnOrAfter before reception of response-SPID check nr66) -SpidSamlCheck_67=Error in the SPID Identity Provider response (Issuer Assertion element not specified-SPID check nr67) -SpidSamlCheck_68=Error in the SPID Identity Provider response (Assertion Issuer element missing-SPID check nr68) -SpidSamlCheck_69=Error in the SPID Identity Provider response (Assertion Issuer element different thant EntityID IdP-SPID check nr69) -SpidSamlCheck_7071=Error in the SPID Identity Provider response (Assertion Issuer Format attribute not specified or missing-SPID check nr70 or nr71) -SpidSamlCheck_72=Error in the SPID Identity Provider response (Assertion Issuer Format must have the value urn:oasis:names:tc:SAML:2.0:nameid-format:entity-SPID check nr72) -SpidSamlCheck_73=Error in the SPID Identity Provider response (Assertion Conditions element not specified-SPID check nr73) -SpidSamlCheck_74=Error in the SPID Identity Provider response (Assertion Conditions element is missing-SPID check nr74) -SpidSamlCheck_7576=Error in the SPID Identity Provider response (Assertion Condition NotBefore attribute not specified or missing-SPID Check nr 75 or 76) -SpidSamlCheck_77=Error in the SPID Identity Provider response (Assertion Condition NotBefore attribute wrong format-SPID check nr77) -SpidSamlCheck_78=Error in the SPID Identity Provider response (Assertion Condition NotBefore attribute after response reception-SPID check nr78) -SpidSamlCheck_7980=Error in the SPID Identity Provider response (Assertion Condition NotOnOrAFter missing or not specified-SPID check nr79 or nr80) -SpidSamlCheck_82=Error in the SPID Identity Provider response (Assertion Condition NotOnOrAfter before response reception-SPID check nr82) -SpidSamlCheck_83=Error in the SPID Identity Provider response (Assertion Condition AudienceRestriction element not specified-SPID check nr83) -SpidSamlCheck_84=Error in the SPID Identity Provider response (Assertion Condition AudienceRestriction element missing-SPID check nr84) -SpidSamlCheck_8586=Error in the SPID Identity Provider response (Assertion Condition AudienceRestriction Audience element missing or not specified-SPID check nr85 o nr86) -SpidSamlCheck_87=Error in the SPID Identity Provider response (Assertion Condition AudienceRestriction Audience value different from Service Provider EntityID-SPID check nr87) -SpidSamlCheck_88=Error in the SPID Identity Provider response (Assertion AuthnStatement not specified-SPID check nr88) -SpidSamlCheck_89=Error in the SPID Identity Provider response (Assertion AuthnStatement is missing-SPID check nr89) -SpidSamlCheck_90=Error in the SPID Identity Provider response (Assertion AuthnContext not specified-SPID check nr90) -SpidSamlCheck_91=Error in the SPID Identity Provider response (Assertion AuthnContext null-SPID check nr91) -SpidSamlCheck_92=Error in the SPID Identity Provider response (Assertion AuthnContext AuthnContextClassRef not specified-SPID check nr92) -SpidSamlCheck_93=Error in the SPID Identity Provider response (Assertion AuthnContext AuthnContextClassRef missing-SPID check nr93) -SpidSamlCheck_94=Error in the SPID Identity Provider response (Assertion AuthnContext AuthContextClassRef set with different param than requested-SPID check nr94) -SpidSamlCheck_97=Error in the SPID Identity Provider response (Assertion AuthnContext AuthnContextClassRef wrong value-SPID check nr97) -SpidSamlCheck_98=Error in the SPID Identity Provider response (AttributeStatement element present, but Attribute subelement missing-SPID check nr98) -SpidSamlCheck_99=Error in the SPID Identity Provider response (AttributeStatement element present, but Attribute subelement not specified-SPID check nr99) -SpidSamlCheck_100=Error in the SPID Identity Provider response (Assertion signed with different certificate-SPID check nr100) -SpidSamlCheck_103=Error in the SPID Identity Provider response (Attribute set posted different from required set-SPID check nr103) -SpidSamlCheck_110=Error in the SPID Identity Provider response (IssueInstant attribute specified with milliseconds-SPID check nr110) +SpidSamlCheck_10=Error in the SPID Identity Provider response (Attribute Version different from 2.0) +SpidSamlCheck_11=Error in the SPID Identity Provider response (Unspecified IssueInstant attribute) +SpidSamlCheck_12=Error in the SPID Identity Provider response (Missing IssueInstant attribute) +SpidSamlCheck_13=Error in the SPID Identity Provider response (IssueInstant attribute with wrong format) +SpidSamlCheck_14=Error in the SPID Identity Provider response (Response IssueInstant before Request IssueInstant) +SpidSamlCheck_15=Error in the SPID Identity Provider response (IssueInstant attribute time is after the moment of reception) +SpidSamlCheck_16=Error in the SPID Identity Provider response (InResponseTo attribute is empty) +SpidSamlCheck_17=Error in the SPID Identity Provider response (InResponseTo attribute is missing) +SpidSamlCheck_18=Error in the SPID Identity Provider response (InResponseTo attribute does not match expected request ID) +SpidSamlCheck_22=Error in the SPID Identity Provider response (Unspecified Status element) +SpidSamlCheck_23=Error in the SPID Identity Provider response (Missing Status element) +SpidSamlCheck_24=Error in the SPID Identity Provider response (Unspecified StatusCode element) +SpidSamlCheck_25=Error in the SPID Identity Provider response (Missing StatusCode element) +SpidSamlCheck_26=Error in the SPID Identity Provider response (StatusCode element different from Success) +SpidSamlCheck_27=Error in the SPID Identity Provider response (Issuer element not specified) +SpidSamlCheck_28=Error in the SPID Identity Provider response (Missing Issuer element) +SpidSamlCheck_29=Error in the SPID Identity Provider response (Issuer element different from EntityID IdP) +SpidSamlCheck_30=Error in the SPID Identity Provider response (The Issuer Format attribuet must be omitted or have the value urn:oasis:names:tc:SAML:2.0:nameid-format:entity) +SpidSamlCheck_32=Error in the SPID Identity Provider response (Assertion element missing ) +SpidSamlCheck_33=Error in the SPID Identity Provider response (ID Assertion attribute not specified) +SpidSamlCheck_34=Error in the SPID Identity Provider response (ID Assertion attribute missing) +SpidSamlCheck_35=Error in the SPID Identity Provider response (Version Assertion attribute different from 2.0) +SpidSamlCheck_36=Error in the SPID Identity Provider response (IssueInstant Assertion attribute not specified) +SpidSamlCheck_37=Error in the SPID Identity Provider response (IssueInstant Assertion attribute missing) +SpidSamlCheck_38=Error in the SPID Identity Provider response (IssueInstant Assertion attribute with wrong format) +SpidSamlCheck_39=Error in the SPID Identity Provider response (IssueInstant Assertion attribute before IssueInstant Assertion Request attribute) +SpidSamlCheck_40=Error in the SPID Identity Provider response (IssueInstant Assertion attribute before Request IssueInstant) +SpidSamlCheck_41=Error in the SPID Identity Provider response (Subject element is empty) +SpidSamlCheck_42=Error in the SPID Identity Provider response (Subject element is missing) +SpidSamlCheck_43=Error in the SPID Identity Provider response (Unspecified Assertion element NameID) +SpidSamlCheck_44=Error in the SPID Identity Provider response (Missing Assertion element NameID) +SpidSamlCheck_4546=Error in the SPID Identity Provider response (Assertion Format attribute missing or non specified or nr46) +SpidSamlCheck_47=Error in the SPID Identity Provider response (Assertion Format attribute diffrent than urn:oasis:names:tc:SAML:2.0:nameidformat:transient) +SpidSamlCheck_4849=Error in the SPID Identity Provider response (Assertion Format attribute missing or non specified ) +SpidSamlCheck_51=Error in the SPID Identity Provider response (SubjectConfirmation element is empty) +SpidSamlCheck_52=Error in the SPID Identity Provider response (SubjectConfirmation element is missing) +SpidSamlCheck_53=Error in the SPID Identity Provider response (Method attribute of SubjectConfirmation element is empty) +SpidSamlCheck_54=Error in the SPID Identity Provider response (Method attribute of SubjectConfirmation element is missing) +SpidSamlCheck_55=Error in the SPID Identity Provider response (Method attribute of SubjectConfirmation element method does not match expected value (urn:oasis:names:tc:SAML:2.0:cm:bearer)) +SpidSamlCheck_56=Error in the SPID Identity Provider response (Element SubjectConfirmationData is missing) +SpidSamlCheck_57=Error in the SPID Identity Provider response (Recipient attribute of SubjectConfirmationData element is empty) +SpidSamlCheck_58=Error in the SPID Identity Provider response (Recipient attribute of SubjectConfirmationData element is missing) +SpidSamlCheck_59=Error in the SPID Identity Provider response (Recipient attribute of SubjectConfirmationData from Assertion different from AssertionConsumerServiceURL) +SpidSamlCheck_60=Error in the SPID Identity Provider response (Attribute InResponseTo of SubjectConfirmationData element is empty) +SpidSamlCheck_61=Error in the SPID Identity Provider response (Attribute InResponseTo of SubjectConfirmationData element is missing) +SpidSamlCheck_62=Error in the SPID Identity Provider response (Attribute InResponseTo of SubjectConfirmationData element does not match expected request ID) +SpidSamlCheck_64=Error in the SPID Identity Provider response (Assertion attribute NotOnOrAfter of SubjectConfirmationData missing) +SpidSamlCheck_66=Error in the SPID Identity Provider response (SubjectConfirmationData Attribute NotOnOrAfter before reception of response) +SpidSamlCheck_67=Error in the SPID Identity Provider response (Issuer Assertion element not specified) +SpidSamlCheck_68=Error in the SPID Identity Provider response (Assertion Issuer element missing) +SpidSamlCheck_69=Error in the SPID Identity Provider response (Assertion Issuer element different thant EntityID IdP) +SpidSamlCheck_7071=Error in the SPID Identity Provider response (Assertion Issuer Format attribute not specified or missing) +SpidSamlCheck_72=Error in the SPID Identity Provider response (Assertion Issuer Format must have the value urn:oasis:names:tc:SAML:2.0:nameid-format:entity) +SpidSamlCheck_73=Error in the SPID Identity Provider response (Assertion Conditions element not specified) +SpidSamlCheck_74=Error in the SPID Identity Provider response (Assertion Conditions element is missing) +SpidSamlCheck_7576=Error in the SPID Identity Provider response (Assertion Condition NotBefore attribute not specified or missing) +SpidSamlCheck_77=Error in the SPID Identity Provider response (Assertion Condition NotBefore attribute wrong format) +SpidSamlCheck_78=Error in the SPID Identity Provider response (Assertion Condition NotBefore attribute after response reception) +SpidSamlCheck_7980=Error in the SPID Identity Provider response (Assertion Condition NotOnOrAFter missing or not specified or nr80) +SpidSamlCheck_82=Error in the SPID Identity Provider response (Assertion Condition NotOnOrAfter before response reception) +SpidSamlCheck_83=Error in the SPID Identity Provider response (Assertion Condition AudienceRestriction element not specified) +SpidSamlCheck_84=Error in the SPID Identity Provider response (Assertion Condition AudienceRestriction element missing) +SpidSamlCheck_8586=Error in the SPID Identity Provider response (Assertion Condition AudienceRestriction Audience element missing or not specified) +SpidSamlCheck_87=Error in the SPID Identity Provider response (Assertion Condition AudienceRestriction Audience value different from Service Provider EntityID) +SpidSamlCheck_88=Error in the SPID Identity Provider response (Assertion AuthnStatement not specified) +SpidSamlCheck_89=Error in the SPID Identity Provider response (Assertion AuthnStatement is missing) +SpidSamlCheck_90=Error in the SPID Identity Provider response (Assertion AuthnContext not specified) +SpidSamlCheck_91=Error in the SPID Identity Provider response (Assertion AuthnContext null) +SpidSamlCheck_92=Error in the SPID Identity Provider response (Assertion AuthnContext AuthnContextClassRef not specified) +SpidSamlCheck_93=Error in the SPID Identity Provider response (Assertion AuthnContext AuthnContextClassRef missing) +SpidSamlCheck_94=Error in the SPID Identity Provider response (Element AuthContextClassRef set on https://www.spid.gov.it/SpidL1) +SpidSamlCheck_95=Error in the SPID Identity Provider response (Element AuthContextClassRef set on https://www.spid.gov.it/SpidL2) +SpidSamlCheck_96=Error in the SPID Identity Provider response (Element AuthContextClassRef set on https://www.spid.gov.it/SpidL3) +SpidSamlCheck_97=Error in the SPID Identity Provider response (Element AuthContextClassRef set on an invalid value) +SpidSamlCheck_98=Error in the SPID Identity Provider response (AttributeStatement element present, but Attribute sub-element missing) +SpidSamlCheck_99=Error in the SPID Identity Provider response (AttributeStatement element present, but Attribute sub-element not specified) +SpidSamlCheck_100=Error in the SPID Identity Provider response (Assertion signed with different certificate) +SpidSamlCheck_103=Error in the SPID Identity Provider response (Attribute set posted different from required) +SpidSamlCheck_110=Error in the SPID Identity Provider response (IssueInstant attribute specified with milliseconds) SpidSamlCheck_GenericResponseParsingError=Error in the SPID Identity Provider response - - diff --git a/src/main/resources/theme-resources/messages/messages_it.properties b/src/main/resources/theme-resources/messages/messages_it.properties index 9e987e2..1798002 100644 --- a/src/main/resources/theme-resources/messages/messages_it.properties +++ b/src/main/resources/theme-resources/messages/messages_it.properties @@ -1,126 +1,128 @@ -SpidFault_ErrorCode_nr2=Indisponibilità di sistema del gestore SPID. Riprovare più tardi o utilizzare un altro metodo di autenticazione (SPID ErrorCode nr2) -SpidFault_ErrorCode_nr3=Errore di sistema del gestore SPID. Riprovare più 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) -SpidFault_ErrorCode_nr6=Binding su metodo HTTP errato. Contattare il gestore del servizio (SPID ErrorCode nr6) -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à 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à 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) -SpidFault_ErrorCode_nr18=AttributeConsumerServiceIndex malformato o che riferisce a un valore non registrato nei metadati di SP. Contattare il gestore del servizio (SPID ErrorCode nr18) -SpidFault_ErrorCode_nr19=Autenticazione fallita per ripetuta sottomissione di credenziali errate (SPID ErrorCode nr19) -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à sospesa/revocata o con credenziali bloccate (SPID ErrorCode nr23) -SpidFault_ErrorCode_nr25=Processo di autenticazione annullato dall''utente (SPID ErrorCode nr25) +SpidFault_ErrorCode_nr2=Indisponibilit\u00E0 di sistema del gestore SPID. Riprovare pi\u00F9 tardi o utilizzare un altro metodo di autenticazione +SpidFault_ErrorCode_nr3=Errore di sistema del gestore SPID. Riprovare pi\u00F9 tardi o utilizzare un altro metodo di autenticazione +SpidFault_ErrorCode_nr4=Formato binding non corretto. Contattare il gestore del servizio +SpidFault_ErrorCode_nr5=Verifica della firma fallita. Contattare il gestore del servizio +SpidFault_ErrorCode_nr6=Binding su metodo HTTP errato. Contattare il gestore del servizio +SpidFault_ErrorCode_nr7=Errore sulla verifica della firma della richiesta. Contattare il gestore del servizio +SpidFault_ErrorCode_nr8=Formato della richiesta non conforme alle specifiche SAML. Contattare il gestore del servizio +SpidFault_ErrorCode_nr9=Parametro version non presente, malformato o diverso da ''2.0''. Contattare il gestore del servizio +SpidFault_ErrorCode_nr10=Issuer non presente, malformato o non corrispondente all''entit\u00E0 che sottoscrive la richiesta. Contattare il gestore del servizio +SpidFault_ErrorCode_nr11=ID (Identificatore richiesta) non presente, malformato o non conforme. Contattare il gestore del servizio +SpidFault_ErrorCode_nr12=RequestAuthnContext non presente, malformato o non previsto da SPID. Contattare il gestore del servizio +SpidFault_ErrorCode_nr13=IssueInstant non presente, malformato o non coerente con l''orario di arrivo della richiesta. Contattare il gestore del servizio +SpidFault_ErrorCode_nr14=Destination non presente, malformata o non coincidente con il Gestore delle identit\u00E0 ricevente la richiesta. Contattare il gestore del servizio +SpidFault_ErrorCode_nr15=Attributo isPassive presente e attualizzato al valore true. Contattare il gestore del servizio +SpidFault_ErrorCode_nr16=AssertionConsumerService non correttamente valorizzato. Contattare il gestore del servizio +SpidFault_ErrorCode_nr17=Attributo Format dell''elemento NameIDPolicy assente o non valorizzato secondo specifica. Contattare il gestore del servizio +SpidFault_ErrorCode_nr18=AttributeConsumerServiceIndex malformato o che riferisce a un valore non registrato nei metadati di SP. Contattare il gestore del servizio +SpidFault_ErrorCode_nr19=Autenticazione fallita per ripetuta sottomissione di credenziali errate +SpidFault_ErrorCode_nr20=Utente privo di credenziali compatibili con il livello richiesto dal fornitore del servizio +SpidFault_ErrorCode_nr21=Timeout durante l''autenticazione utente +SpidFault_ErrorCode_nr22=Utente nega il consenso all''invio di dati al SP in caso di sessione vigente +SpidFault_ErrorCode_nr23=Utente con identit\u00E0 sospesa/revocata o con credenziali bloccate +SpidFault_ErrorCode_nr25=Processo di autenticazione annullato dall''utente -SpidSamlCheck_02=Errore nella risposta dell''Identity Provider SPID (Response non firmata-SPID check nr01) -SpidSamlCheck_03=Errore nella risposta dell''Identity Provider SPID (Response firmata, Assertion non firmata-SPID check nr02) -SpidSamlCheck_04=Errore nella risposta dell''Identity Provider SPID (Response firmata con certificato diverso da quello registrato su SP-SPID check nr03) -SpidSamlCheck_08=Errore nella risposta dell''Identity Provider SPID (Attributo ID non specificato-SPID check nr08) -SpidSamlCheck_09=Errore nella risposta dell''Identity Provider SPID (Attributo ID mancante-SPID check nr09) -SpidSamlCheck_10=Errore nella risposta dell''Identity Provider SPID (Attributo Version diverso da 2.0-SPID check nr10) -SpidSamlCheck_11=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant non specificato-SPID check nr11) -SpidSamlCheck_12=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant mancante-SPID check nr12) -SpidSamlCheck_13=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant avente formato non corretto-SPID check nr13) -SpidSamlCheck_14=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant precedente a IssueInstant della request-SPID check nr14) -SpidSamlCheck_15=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant successivo all''istante di ricezione-SPID check nr15) -SpidSamlCheck_16=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo non specificato-SPID check nr16) -SpidSamlCheck_17=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo mancante-SPID check nr17) -SpidSamlCheck_18=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo diverso da ID request-SPID check nr18) -SpidSamlCheck_19=Errore nella risposta dell''Identity Provider SPID (Attributo Destination non specificato-SPID check nr19) -SpidSamlCheck_20=Errore nella risposta dell''Identity Provider SPID (Attributo Destination mancante-SPID check nr20) -SpidSamlCheck_21=Errore nella risposta dell''Identity Provider SPID (Attributo Destination diverso da AssertionConsumerServiceURL-SPID check nr21) -SpidSamlCheck_22=Errore nella risposta dell''Identity Provider SPID (Elemento Status non specificato-SPID check nr22) -SpidSamlCheck_23=Errore nella risposta dell''Identity Provider SPID (Elemento Status mancante-SPID check nr23) -SpidSamlCheck_24=Errore nella risposta dell''Identity Provider SPID (Elemento StatusCode non specificato-SPID check nr24) -SpidSamlCheck_25=Errore nella risposta dell''Identity Provider SPID (Elemento StatusCode mancante-SPID check nr25) -SpidSamlCheck_26=Errore nella risposta dell''Identity Provider SPID (Elemento StatusCode diverso da Success-non valido)-SPID check nr26) -SpidSamlCheck_27=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer non specificato-SPID check nr27) -SpidSamlCheck_28=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer mancante-SPID check nr28) -SpidSamlCheck_29=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer diverso da EntityID IdP-SPID check nr29) -SpidSamlCheck_30=Errore nella risposta dell''Identity Provider SPID (L''attributo Format di Issuer deve essere omesso o assumere valore urn:oasis:names:tc:SAML:2.0:nameid-format:entity-SPID check nr30) -SpidSamlCheck_32=Errore nella risposta dell''Identity Provider SPID (Elemento Assertion mancante ed esito positivo autenticazione-SPID check nr32) -SpidSamlCheck_33=Errore nella risposta dell''Identity Provider SPID (Attributo ID dell''Assertion non specificato-SPID check nr33) -SpidSamlCheck_34=Errore nella risposta dell''Identity Provider SPID (Attributo ID dell''Assertion mancante-SPID check nr34) -SpidSamlCheck_35=Errore nella risposta dell''Identity Provider SPID (Attributo Version dell''Assertion diverso da 2.0-SPID check nr35) -SpidSamlCheck_36=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant dell''Assertion non specificato-SPID check nr36) -SpidSamlCheck_37=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant dell''Assertion mancante-SPID check nr37) -SpidSamlCheck_38=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant dell''Assertion avente formato non corretto-SPID check nr38) -SpidSamlCheck_39=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant dell''Assertion precedente a IssueInstant della Request-SPID check nr39) -SpidSamlCheck_40=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant dell''Assertion successivo a IssueInstant della Request-SPID check nr40) -SpidSamlCheck_41=Errore nella risposta dell''Identity Provider SPID (Elemento Subject dell''Assertion non specificato-SPID check nr41) -SpidSamlCheck_42=Errore nella risposta dell''Identity Provider SPID (Elemento Subject dell''Assertion mancante-SPID check nr42) -SpidSamlCheck_43=Errore nella risposta dell''Identity Provider SPID (Elemento NameID dell''Assertion non specificato-SPID check nr43) -SpidSamlCheck_44=Errore nella risposta dell''Identity Provider SPID (Elemento NameID dell''Assertion mancante-SPID check nr44) -SpidSamlCheck_45=Errore nella risposta dell''Identity Provider SPID (Attributo Format dell''elemento NameID dell''Assertion non specificato-SPID check nr45) -SpidSamlCheck_46=Errore nella risposta dell''Identity Provider SPID (Attributo Format dell''elemento NameID dell''Assertion mancante-SPID check nr46) -SpidSamlCheck_4546=Errore nella risposta dell''Identity Provider SPID (Attributo Format dell''elemento NameID non specificato o mancante-SPID check nr45 or nr46) -SpidSamlCheck_47=Errore nella risposta dell''Identity Provider SPID (Attributo Format di NameID dell''Assertion diverso da urn:oasis:names:tc:SAML:2.0:nameidformat:transient-SPID check nr47) -SpidSamlCheck_48=Errore nella risposta dell''Identity Provider SPID (Attributo NameQualifier di NameID dell''Assertion non specificato-SPID check nr48) -SpidSamlCheck_49=Errore nella risposta dell''Identity Provider SPID (Attributo NameQualifier di NameID dell''Assertion mancante-SPID check nr49) -SpidSamlCheck_4849=Errore nella risposta dell''Identity Provider SPID (Attributo NameQualifier di NameID dell''Assertion non specificato o mancante-SPID check nr48 or nr49) -SpidSamlCheck_51=Errore nella risposta dell''Identity Provider SPID (Elemento SubjectConfirmation dell''Assertion non specificato-SPID check nr51) -SpidSamlCheck_52=Errore nella risposta dell''Identity Provider SPID (Elemento SubjectConfirmation dell''Assertion mancante-SPID check nr52) -SpidSamlCheck_53=Errore nella risposta dell''Identity Provider SPID (Attributo Method di SubjectConfirmation dell''Assertion non specificato-SPID check nr53) -SpidSamlCheck_54=Errore nella risposta dell''Identity Provider SPID (Attributo Method di SubjectConfirmation dell''Assertion mancante-SPID check nr54) -SpidSamlCheck_55=Errore nella risposta dell''Identity Provider SPID (Attributo Method di SubjectConfirmation dell''Assertion diverso da urn:oasis:names:tc:SAML:2.0:cm:bearer-SPID check nr55) -SpidSamlCheck_56=Errore nella risposta dell''Identity Provider SPID (Elemento SubjectConfirmationData dell''Assertion mancante-SPID check nr56) -SpidSamlCheck_57=Errore nella risposta dell''Identity Provider SPID (Attributo Recipient di SubjectConfirmationData dell''Assertion non specificato-SPID check nr57) -SpidSamlCheck_58=Errore nella risposta dell''Identity Provider SPID (Attributo Recipient di SubjectConfirmationData dell''Assertion mancante-SPID check nr58) -SpidSamlCheck_59=Errore nella risposta dell''Identity Provider SPID (Attributo Recipient di SubjectConfirmationData dell''Assertion diverso da AssertionConsumerServiceURL-SPID check nr59) -SpidSamlCheck_60=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo di SubjectConfirmationData dell''Assertion non specificato-SPID check nr60) -SpidSamlCheck_61=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo di SubjectConfirmationData dell''Assertion mancante-SPID check nr61) -SpidSamlCheck_62=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo di SubjectConfirmationData dell''Assertion diverso da ID request-SPID check nr62) -SpidSamlCheck_63=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di SubjectConfirmationData dell''Assertion non specificato-SPID check nr63) -SpidSamlCheck_64=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di SubjectConfirmationData mancante-SPID check nr64) -SpidSamlCheck_65=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di SubjectConfirmationData avente formato non corretto-SPID check nr65) -SpidSamlCheck_66=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di SubjectConfirmationData precedente all''istante di ricezione della response-SPID check nr66) -SpidSamlCheck_67=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer dell''Assertion non specificato-SPID check nr67) -SpidSamlCheck_68=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer dell''Assertion mancante-SPID check nr68) -SpidSamlCheck_69=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer dell''Assertion diverso da EntityID IdP-SPID check nr69) -SpidSamlCheck_70=Errore nella risposta dell''Identity Provider SPID (Attributo Format di Issuer dell''Assertion non specificato-SPID check nr70) -SpidSamlCheck_71=Errore nella risposta dell''Identity Provider SPID (Attributo Format di Issuer dell''Assertion mancante-SPID check nr71) -SpidSamlCheck_7071=Errore nella risposta dell''Identity Provider SPID (Attributo Format di Issuer dell''Assertion non specificato o mancante-SPID check nr70 or nr71) -SpidSamlCheck_72=Errore nella risposta dell''Identity Provider SPID (L''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_73=Errore nella risposta dell''Identity Provider SPID (Elemento Conditions dell''Assertion non specificato-SPID check nr73) -SpidSamlCheck_74=Errore nella risposta dell''Identity Provider SPID (Elemento Conditions dell''Assertion mancante-SPID check nr74) -SpidSamlCheck_75=Errore nella risposta dell''Identity Provider SPID (Attributo NotBefore di Condition dell''Assertion non specificato-SPID check nr75) -SpidSamlCheck_76=Errore nella risposta dell''Identity Provider SPID (Attributo NotBefore di Condition dell''Assertion mancante-SPID check nr76) -SpidSamlCheck_7576=Errore nella risposta dell''Identity Provider SPID (Attributo NotBefore di Condition dell'Assertion non specificato o mancante-SPID Check nr 75 o 76) -SpidSamlCheck_77=Errore nella risposta dell''Identity Provider SPID (Attributo NotBefore di Condition dell''Assertion avente formato non corretto-SPID check nr77) -SpidSamlCheck_78=Errore nella risposta dell''Identity Provider SPID (Attributo NotBefore di Condition dell''Assertion successivo all''instante di ricezione della response-SPID check nr78) -SpidSamlCheck_79=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di Condition dell''Assertion non specificato-SPID check nr79) -SpidSamlCheck_80=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di Condition dell''Assertion mancante-SPID check nr80) -SpidSamlCheck_7980=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di Condition dell''Assertion non specificato o mancante-SPID check nr79 o nr80) -SpidSamlCheck_81=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di Condition dell''Assertion avente formato non corretto-SPID check nr81) -SpidSamlCheck_82=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di Condition dell''Assertion precedente all''istante di ricezione della response-SPID check nr82) -SpidSamlCheck_83=Errore nella risposta dell''Identity Provider SPID (Elemento AudienceRestriction di Condition dell''Assertion non specificato-SPID check nr83) -SpidSamlCheck_84=Errore nella risposta dell''Identity Provider SPID (Elemento AudienceRestriction di Condition dell''Assertion mancante-SPID check nr84) -SpidSamlCheck_85=Errore nella risposta dell''Identity Provider SPID (Elemento Audience di AudienceRestriction di Condition dell''Assertion non specificato-SPID check nr85) -SpidSamlCheck_86=Errore nella risposta dell''Identity Provider SPID (Elemento Audience di AudienceRestriction di Condition dell''Assertion mancante-SPID check nr86) -SpidSamlCheck_8586=Errore nella risposta dell''Identity Provider SPID (Elemento Audience di AudienceRestriction di Condition dell''Assertion non specificato o mancante-SPID check nr85 o nr86) -SpidSamlCheck_87=Errore nella risposta dell''Identity Provider SPID (Elemento Audience di AudienceRestriction di Condition dell''Assertion diverso da Entity Id del Service Provider-SPID check nr87) -SpidSamlCheck_88=Errore nella risposta dell''Identity Provider SPID (Elemento AuthStatement dell''Assertion non specificato-SPID check nr88) -SpidSamlCheck_89=Errore nella risposta dell''Identity Provider SPID (Elemento AuthStatement dell''Assertion mancante-SPID check nr89) -SpidSamlCheck_90=Errore nella risposta dell''Identity Provider SPID (Elemento AuthnContext di AuthStatement dell''Assertion non specificato-SPID check nr90) -SpidSamlCheck_91=Errore nella risposta dell''Identity Provider SPID (Elemento AuthnContext di AuthStatement dell''Assertion mancante-SPID check nr91) -SpidSamlCheck_92=Errore nella risposta dell''Identity Provider SPID (Elemento AuthContextClassRef di AuthnContext di AuthStatement dell''Assertion non specificato-SPID check nr92) -SpidSamlCheck_93=Errore nella risposta dell''Identity Provider SPID (Elemento AuthContextClassRef di AuthnContext di AuthStatement dell''Assertion mancante-SPID check nr93) -SpidSamlCheck_94=Errore nella risposta dell''Identity Provider SPID (Elemento AuthContextClassRef impostato su livello diverso da quello richiesto dalla request-SPID check nr93) -SpidSamlCheck_97=Errore nella risposta dell''Identity Provider SPID (Elemento AuthContextClassRef impostato ad un valore non previsto-SPID check nr97) -SpidSamlCheck_98=Errore nella risposta dell''Identity Provider SPID (Elemento AttributeStatement presente, ma sottoelemento Attribute mancante-SPID check nr98) -SpidSamlCheck_99=Errore nella risposta dell''Identity Provider SPID (Elemento AttributeStatement presente, ma sottoelemento Attribute non specificato-SPID check nr99) -SpidSamlCheck_100=Errore nella risposta dell''Identity Provider SPID (Assertion firmata con certificato diverso-SPID check nr100) -SpidSamlCheck_103=Errore nella risposta dell''Identity Provider SPID (Set di attributi inviato diverso da quello richiesto-SPID check nr103) -SpidSamlCheck_110=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant specificato con millisecondi-SPID check nr110) +SpidSamlCheck_02=Errore nella risposta dell''Identity Provider SPID (Response non firmata) +SpidSamlCheck_03=Errore nella risposta dell''Identity Provider SPID (Response firmata, Assertion non firmata) +SpidSamlCheck_04=Errore nella risposta dell''Identity Provider SPID (Response firmata con certificato diverso da quello registrato su SP) +SpidSamlCheck_08=Errore nella risposta dell''Identity Provider SPID (Attributo ID non specificato) +SpidSamlCheck_09=Errore nella risposta dell''Identity Provider SPID (Attributo ID mancante) +SpidSamlCheck_10=Errore nella risposta dell''Identity Provider SPID (Attributo Version diverso da 2.0) +SpidSamlCheck_11=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant non specificato) +SpidSamlCheck_12=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant mancante) +SpidSamlCheck_13=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant avente formato non corretto) +SpidSamlCheck_14=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant precedente a IssueInstant della request) +SpidSamlCheck_15=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant successivo all''istante di ricezione) +SpidSamlCheck_16=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo non specificato) +SpidSamlCheck_17=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo mancante) +SpidSamlCheck_18=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo diverso da ID request) +SpidSamlCheck_19=Errore nella risposta dell''Identity Provider SPID (Attributo Destination non specificato) +SpidSamlCheck_20=Errore nella risposta dell''Identity Provider SPID (Attributo Destination mancante) +SpidSamlCheck_21=Errore nella risposta dell''Identity Provider SPID (Attributo Destination diverso da AssertionConsumerServiceURL) +SpidSamlCheck_22=Errore nella risposta dell''Identity Provider SPID (Elemento Status non specificato) +SpidSamlCheck_23=Errore nella risposta dell''Identity Provider SPID (Elemento Status mancante) +SpidSamlCheck_24=Errore nella risposta dell''Identity Provider SPID (Elemento StatusCode non specificato) +SpidSamlCheck_25=Errore nella risposta dell''Identity Provider SPID (Elemento StatusCode mancante) +SpidSamlCheck_26=Errore nella risposta dell''Identity Provider SPID (Elemento StatusCode diverso da Success) +SpidSamlCheck_27=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer non specificato) +SpidSamlCheck_28=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer mancante) +SpidSamlCheck_29=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer diverso da EntityID IdP) +SpidSamlCheck_30=Errore nella risposta dell''Identity Provider SPID (L''attributo Format di Issuer deve essere omesso o assumere valore urn:oasis:names:tc:SAML:2.0:nameid-format:entity) +SpidSamlCheck_32=Errore nella risposta dell''Identity Provider SPID (Elemento Assertion mancante) +SpidSamlCheck_33=Errore nella risposta dell''Identity Provider SPID (Attributo ID dell''Assertion non specificato) +SpidSamlCheck_34=Errore nella risposta dell''Identity Provider SPID (Attributo ID dell''Assertion mancante) +SpidSamlCheck_35=Errore nella risposta dell''Identity Provider SPID (Attributo Version dell''Assertion diverso da 2.0) +SpidSamlCheck_36=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant dell''Assertion non specificato) +SpidSamlCheck_37=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant dell''Assertion mancante) +SpidSamlCheck_38=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant dell''Assertion avente formato non corretto) +SpidSamlCheck_39=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant dell''Assertion precedente a IssueInstant della Request) +SpidSamlCheck_40=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant dell''Assertion successivo a IssueInstant della Request) +SpidSamlCheck_41=Errore nella risposta dell''Identity Provider SPID (Elemento Subject dell''Assertion non specificato) +SpidSamlCheck_42=Errore nella risposta dell''Identity Provider SPID (Elemento Subject dell''Assertion mancante) +SpidSamlCheck_43=Errore nella risposta dell''Identity Provider SPID (Elemento NameID dell''Assertion non specificato) +SpidSamlCheck_44=Errore nella risposta dell''Identity Provider SPID (Elemento NameID dell''Assertion mancante) +SpidSamlCheck_45=Errore nella risposta dell''Identity Provider SPID (Attributo Format dell''elemento NameID dell''Assertion non specificato) +SpidSamlCheck_46=Errore nella risposta dell''Identity Provider SPID (Attributo Format dell''elemento NameID dell''Assertion mancante) +SpidSamlCheck_4546=Errore nella risposta dell''Identity Provider SPID (Attributo Format dell''elemento NameID non specificato o mancante) +SpidSamlCheck_47=Errore nella risposta dell''Identity Provider SPID (Attributo Format di NameID dell''Assertion diverso da urn:oasis:names:tc:SAML:2.0:nameidformat:transient) +SpidSamlCheck_48=Errore nella risposta dell''Identity Provider SPID (Attributo NameQualifier di NameID dell''Assertion non specificato) +SpidSamlCheck_49=Errore nella risposta dell''Identity Provider SPID (Attributo NameQualifier di NameID dell''Assertion mancante) +SpidSamlCheck_4849=Errore nella risposta dell''Identity Provider SPID (Attributo NameQualifier di NameID dell''Assertion non specificato o mancante) +SpidSamlCheck_51=Errore nella risposta dell''Identity Provider SPID (Elemento SubjectConfirmation dell''Assertion non specificato) +SpidSamlCheck_52=Errore nella risposta dell''Identity Provider SPID (Elemento SubjectConfirmation dell''Assertion mancante) +SpidSamlCheck_53=Errore nella risposta dell''Identity Provider SPID (Attributo Method di SubjectConfirmation dell''Assertion non specificato) +SpidSamlCheck_54=Errore nella risposta dell''Identity Provider SPID (Attributo Method di SubjectConfirmation dell''Assertion mancante) +SpidSamlCheck_55=Errore nella risposta dell''Identity Provider SPID (Attributo Method di SubjectConfirmation dell''Assertion diverso da urn:oasis:names:tc:SAML:2.0:cm:bearer) +SpidSamlCheck_56=Errore nella risposta dell''Identity Provider SPID (Elemento SubjectConfirmationData dell''Assertion mancante) +SpidSamlCheck_57=Errore nella risposta dell''Identity Provider SPID (Attributo Recipient di SubjectConfirmationData dell''Assertion non specificato) +SpidSamlCheck_58=Errore nella risposta dell''Identity Provider SPID (Attributo Recipient di SubjectConfirmationData dell''Assertion mancante) +SpidSamlCheck_59=Errore nella risposta dell''Identity Provider SPID (Attributo Recipient di SubjectConfirmationData dell''Assertion diverso da AssertionConsumerServiceURL) +SpidSamlCheck_60=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo di SubjectConfirmationData dell''Assertion non specificato) +SpidSamlCheck_61=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo di SubjectConfirmationData dell''Assertion mancante) +SpidSamlCheck_62=Errore nella risposta dell''Identity Provider SPID (Attributo InResponseTo di SubjectConfirmationData dell''Assertion diverso da ID request) +SpidSamlCheck_63=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di SubjectConfirmationData dell''Assertion non specificato) +SpidSamlCheck_64=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di SubjectConfirmationData mancante) +SpidSamlCheck_65=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di SubjectConfirmationData avente formato non corretto) +SpidSamlCheck_66=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di SubjectConfirmationData precedente all''istante di ricezione della response) +SpidSamlCheck_67=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer dell''Assertion non specificato) +SpidSamlCheck_68=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer dell''Assertion mancante) +SpidSamlCheck_69=Errore nella risposta dell''Identity Provider SPID (Elemento Issuer dell''Assertion diverso da EntityID IdP) +SpidSamlCheck_70=Errore nella risposta dell''Identity Provider SPID (Attributo Format di Issuer dell''Assertion non specificato) +SpidSamlCheck_71=Errore nella risposta dell''Identity Provider SPID (Attributo Format di Issuer dell''Assertion mancante) +SpidSamlCheck_7071=Errore nella risposta dell''Identity Provider SPID (Attributo Format di Issuer dell''Assertion non specificato o mancante) +SpidSamlCheck_72=Errore nella risposta dell''Identity Provider SPID (L''attributo Format di Issuer dell''Assertion deve essere presente con il valore urn:oasis:names:tc:SAML:2.0:nameid-format:entity) +SpidSamlCheck_73=Errore nella risposta dell''Identity Provider SPID (Elemento Conditions dell''Assertion non specificato) +SpidSamlCheck_74=Errore nella risposta dell''Identity Provider SPID (Elemento Conditions dell''Assertion mancante) +SpidSamlCheck_75=Errore nella risposta dell''Identity Provider SPID (Attributo NotBefore di Condition dell''Assertion non specificato) +SpidSamlCheck_76=Errore nella risposta dell''Identity Provider SPID (Attributo NotBefore di Condition dell''Assertion mancante) +SpidSamlCheck_7576=Errore nella risposta dell''Identity Provider SPID (Attributo NotBefore di Condition dell'Assertion non specificato o mancante) +SpidSamlCheck_77=Errore nella risposta dell''Identity Provider SPID (Attributo NotBefore di Condition dell''Assertion avente formato non corretto) +SpidSamlCheck_78=Errore nella risposta dell''Identity Provider SPID (Attributo NotBefore di Condition dell''Assertion successivo all''instante di ricezione della response) +SpidSamlCheck_79=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di Condition dell''Assertion non specificato) +SpidSamlCheck_80=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di Condition dell''Assertion mancante) +SpidSamlCheck_7980=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di Condition dell''Assertion non specificato o mancante) +SpidSamlCheck_81=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di Condition dell''Assertion avente formato non corretto) +SpidSamlCheck_82=Errore nella risposta dell''Identity Provider SPID (Attributo NotOnOrAfter di Condition dell''Assertion precedente all''istante di ricezione della response) +SpidSamlCheck_83=Errore nella risposta dell''Identity Provider SPID (Elemento AudienceRestriction di Condition dell''Assertion non specificato) +SpidSamlCheck_84=Errore nella risposta dell''Identity Provider SPID (Elemento AudienceRestriction di Condition dell''Assertion mancante) +SpidSamlCheck_85=Errore nella risposta dell''Identity Provider SPID (Elemento Audience di AudienceRestriction di Condition dell''Assertion non specificato) +SpidSamlCheck_86=Errore nella risposta dell''Identity Provider SPID (Elemento Audience di AudienceRestriction di Condition dell''Assertion mancante) +SpidSamlCheck_8586=Errore nella risposta dell''Identity Provider SPID (Elemento Audience di AudienceRestriction di Condition dell''Assertion non specificato o mancante) +SpidSamlCheck_87=Errore nella risposta dell''Identity Provider SPID (Elemento Audience di AudienceRestriction di Condition dell''Assertion diverso da Entity Id del Service Provider) +SpidSamlCheck_88=Errore nella risposta dell''Identity Provider SPID (Elemento AuthStatement dell''Assertion non specificato) +SpidSamlCheck_89=Errore nella risposta dell''Identity Provider SPID (Elemento AuthStatement dell''Assertion mancante) +SpidSamlCheck_90=Errore nella risposta dell''Identity Provider SPID (Elemento AuthnContext di AuthStatement dell''Assertion non specificato) +SpidSamlCheck_91=Errore nella risposta dell''Identity Provider SPID (Elemento AuthnContext di AuthStatement dell''Assertion mancante) +SpidSamlCheck_92=Errore nella risposta dell''Identity Provider SPID (Elemento AuthContextClassRef di AuthnContext di AuthStatement dell''Assertion non specificato) +SpidSamlCheck_93=Errore nella risposta dell''Identity Provider SPID (Elemento AuthContextClassRef di AuthnContext di AuthStatement dell''Assertion mancante) +SpidSamlCheck_94=Errore nella risposta dell''Identity Provider SPID (Elemento AuthContextClassRef impostato su https://www.spid.gov.it/SpidL1) +SpidSamlCheck_95=Errore nella risposta dell''Identity Provider SPID (Elemento AuthContextClassRef impostato su https://www.spid.gov.it/SpidL2) +SpidSamlCheck_96=Errore nella risposta dell''Identity Provider SPID (Elemento AuthContextClassRef impostato su https://www.spid.gov.it/SpidL3) +SpidSamlCheck_97=Errore nella risposta dell''Identity Provider SPID (Elemento AuthContextClassRef impostato ad un valore non previsto) +SpidSamlCheck_98=Errore nella risposta dell''Identity Provider SPID (Elemento AttributeStatement presente, ma sottoelemento Attribute mancante) +SpidSamlCheck_99=Errore nella risposta dell''Identity Provider SPID (Elemento AttributeStatement presente, ma sottoelemento Attribute non specificato) +SpidSamlCheck_100=Errore nella risposta dell''Identity Provider SPID (Assertion firmata con certificato diverso) +SpidSamlCheck_103=Errore nella risposta dell''Identity Provider SPID (Set di attributi inviato diverso da quello richiesto) +SpidSamlCheck_110=Errore nella risposta dell''Identity Provider SPID (Attributo IssueInstant specificato con millisecondi 0) -SpidSamlCheck_GenericResponseParsingError=Errore nella risposta dell''Identity Provider SPID \ No newline at end of file +SpidSamlCheck_GenericResponseParsingError=Errore nella risposta dell''Identity Provider SPID diff --git a/src/test/java/org/keycloak/broker/spid/ObjectMother.java b/src/test/java/org/keycloak/broker/spid/ObjectMother.java new file mode 100644 index 0000000..03c77ea --- /dev/null +++ b/src/test/java/org/keycloak/broker/spid/ObjectMother.java @@ -0,0 +1,135 @@ +package org.keycloak.broker.spid; + +import org.keycloak.dom.saml.v2.assertion.*; +import org.keycloak.dom.saml.v2.protocol.AuthnContextComparisonType; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.dom.saml.v2.protocol.StatusCodeType; +import org.keycloak.dom.saml.v2.protocol.StatusType; +import org.keycloak.saml.common.exceptions.ConfigurationException; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.exceptions.ProcessingException; +import org.keycloak.saml.common.util.DocumentUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.namespace.QName; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +public class ObjectMother { + + public static class SamlResponseTypes { + + public static SpidSAMLEndpointHelper CustomHelper(String authnContextClassRefs, AuthnContextComparisonType authnContextComparison) { + SpidIdentityProviderConfig config = new SpidIdentityProviderConfig(); + config.setIdpEntityId("https://id.lepida.it/idp/shibboleth"); + config.setEntityId("https://spid.agid.gov.it"); + config.setAuthnContextClassRefs(authnContextClassRefs); + config.setAuthnContextComparisonType(authnContextComparison); + return new SpidSAMLEndpointHelper(config); + } + + + public static SpidSAMLEndpointHelper DefaultHelper() { + return CustomHelper("https://www.spid.gov.it/SpidL2", AuthnContextComparisonType.EXACT); + } + + public static Element samlResponseElement() { + return samlResponse().getDocumentElement(); + } + + private static Document samlResponse() { + try { + return DocumentUtil.getDocument(Files.newInputStream(Path.of("src/test/resources/saml_response.xml"))); + } catch (ParsingException | IOException | ConfigurationException | ProcessingException e) { + throw new RuntimeException(e); + } + } + + public static Element samlAssertion() { + return DocumentUtil.getElement(samlResponse(), new QName("Assertion")); + } + + public static ResponseType EmptyResponseType() { + try { + return new ResponseType("empty", DatatypeFactory.newInstance().newXMLGregorianCalendar()); + } catch (DatatypeConfigurationException e) { + throw new RuntimeException(e); + } + } + + public static ResponseType CompleteResponseType(String id, String responseTime) { + try { + Document samlResponse = samlResponse(); + Element samlAssertion = samlAssertion(); + + XMLGregorianCalendar responseTimeCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(responseTime); + ResponseType responseType = new ResponseType(id, responseTimeCalendar); + responseType.setSignature(DocumentUtil.getElement(samlResponse, new QName("Signature"))); + + + SubjectConfirmationDataType subjectConfirmationData = new SubjectConfirmationDataType(); + subjectConfirmationData.setAddress("11.22.33.44"); + subjectConfirmationData.setInResponseTo("spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21"); + subjectConfirmationData.setNotOnOrAfter(DatatypeFactory.newInstance().newXMLGregorianCalendar("2024-04-10T09:27:28.000Z")); + subjectConfirmationData.setRecipient("https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + SubjectConfirmationType subjectConfirmation = new SubjectConfirmationType(); + subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:bearer"); + subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData); + + SubjectType subject = new SubjectType(); + subject.addConfirmation(subjectConfirmation); + + AssertionType id1 = new AssertionType("id", responseTimeCalendar); + id1.setSubject(subject); + + responseType.addAssertion(new ResponseType.RTChoiceType(id1)); + responseType.getAssertions().get(0).getAssertion().setSignature(DocumentUtil.getChildElement(samlAssertion, new QName("Signature"))); + StatusType statusType = new StatusType(); + StatusCodeType statusCodeType = new StatusCodeType(); + statusCodeType.setValue(URI.create("urn:oasis:names:tc:SAML:2.0:status:Success")); + statusType.setStatusCode(statusCodeType); + responseType.setStatus(statusType); + NameIDType issuer = new NameIDType(); + issuer.setValue("https://id.lepida.it/idp/shibboleth"); + issuer.setFormat(URI.create(SpidSAMLEndpoint.ISSUER_FORMAT)); + responseType.setIssuer(issuer); + responseType.setInResponseTo("spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21"); + + + return responseType; + } catch (DatatypeConfigurationException e) { + throw new RuntimeException(e); + } + } + + public static ResponseType CompleteResponseType() { + return CompleteResponseType("id", "2024-04-10T09:23:28.000Z"); + } + + public static ResponseType UserAnomalies(String statusMessage) { + ResponseType userAnomaly = CompleteResponseType(); + + StatusType status = new StatusType(); + + StatusCodeType responderStatusCode = new StatusCodeType(); + responderStatusCode.setValue(URI.create("urn:oasis:names:tc:SAML:2.0:status:Responder")); + StatusCodeType authnFailedStatusCode = new StatusCodeType(); + responderStatusCode.setValue(URI.create("urn:oasis:names:tc:SAML:2.0:status:AuthnFailed")); + responderStatusCode.setStatusCode(authnFailedStatusCode); + + status.setStatusCode(responderStatusCode); + status.setStatusMessage(statusMessage); + + userAnomaly.setStatus(status); + + return userAnomaly; + } + } +} diff --git a/src/test/java/org/keycloak/broker/spid/SpidIdentityProviderConfigTest.java b/src/test/java/org/keycloak/broker/spid/SpidIdentityProviderConfigTest.java new file mode 100644 index 0000000..67787cd --- /dev/null +++ b/src/test/java/org/keycloak/broker/spid/SpidIdentityProviderConfigTest.java @@ -0,0 +1,39 @@ +package org.keycloak.broker.spid; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.keycloak.provider.ProviderConfigProperty; + +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class SpidIdentityProviderConfigTest { + + + @ParameterizedTest + @MethodSource("provideLocales") + void testConfigPropertiesExistsInBundles(Locale locale) { + List actualConfigProperties = SpidIdentityProviderConfig.getConfigProperties(); + + ResourceBundle bundle = ResourceBundle.getBundle("provider-config.messages", locale); + actualConfigProperties.forEach(pcp -> + assertAll("Label and help text exists in bundle ", + () -> assertTrue(bundle.containsKey(pcp.getLabel())), + () -> assertTrue(bundle.containsKey(pcp.getHelpText())) + + )); + } + + private static Stream provideLocales() { + return Stream.of( + Arguments.of(Locale.ITALIAN), + Arguments.of(Locale.ENGLISH) + ); + } +} \ No newline at end of file diff --git a/src/test/java/org/keycloak/broker/spid/SpidIdentityProviderFactoryTest.java b/src/test/java/org/keycloak/broker/spid/SpidIdentityProviderFactoryTest.java new file mode 100644 index 0000000..4743253 --- /dev/null +++ b/src/test/java/org/keycloak/broker/spid/SpidIdentityProviderFactoryTest.java @@ -0,0 +1,212 @@ +package org.keycloak.broker.spid; + +import org.junit.jupiter.api.Test; +import org.keycloak.models.KeycloakSession; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.services.DefaultKeycloakSession; +import org.keycloak.services.DefaultKeycloakSessionFactory; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +import static org.junit.jupiter.api.Assertions.*; + +class SpidIdentityProviderFactoryTest { + + private final SpidIdentityProviderFactory factory = new SpidIdentityProviderFactory(); + + @Test + void testCreate() { + + SpidIdentityProviderConfig model = new SpidIdentityProviderConfig(); + model.setBillingAnagraficaNome("Billing Name"); + model.setBillingAnagraficaCognome("Billing Surname"); + + SpidIdentityProvider spidIdentityProvider = factory.create(createSession(), model); + assertConfigIsEqual(model, spidIdentityProvider.getConfig()); + } + + @Test + void testCreateConfig() { + var expectedConfig = new SpidIdentityProviderConfig(); + var actualConfig = factory.createConfig(); + assertConfigIsEqual(expectedConfig, actualConfig); + } + + /** + * Check the whole config + * + * @param expectedConfig config you expect + * @param actualConfig config you get + */ + private void assertConfigIsEqual(SpidIdentityProviderConfig expectedConfig, + SpidIdentityProviderConfig actualConfig) { + // sed -E 's/^(.*)/\(\) -> assertEquals(expectedConfig.\1,actualConfig.\1\),/p' + assertAll("Config is equal", + () -> assertEquals(expectedConfig.getAllowedClockSkew(), + actualConfig.getAllowedClockSkew()), + () -> assertEquals(expectedConfig.getAttributeConsumingServiceIndex(), + actualConfig.getAttributeConsumingServiceIndex()), + () -> assertEquals(expectedConfig.getAttributeConsumingServiceName(), + actualConfig.getAttributeConsumingServiceName()), + () -> assertEquals(expectedConfig.getAuthnContextClassRefs(), + actualConfig.getAuthnContextClassRefs()), + () -> assertEquals(expectedConfig.getAuthnContextComparisonType(), + actualConfig.getAuthnContextComparisonType()), + () -> assertEquals(expectedConfig.getAuthnContextDeclRefs(), + actualConfig.getAuthnContextDeclRefs()), + () -> assertEquals(expectedConfig.getBillingAnagraficaCodiceEORI(), + actualConfig.getBillingAnagraficaCodiceEORI()), + () -> assertEquals(expectedConfig.getBillingAnagraficaCognome(), + actualConfig.getBillingAnagraficaCognome()), + () -> assertEquals(expectedConfig.getBillingAnagraficaDenominazione(), + actualConfig.getBillingAnagraficaDenominazione()), + () -> assertEquals(expectedConfig.getBillingAnagraficaNome(), + actualConfig.getBillingAnagraficaNome()), + () -> assertEquals(expectedConfig.getBillingAnagraficaTitolo(), + actualConfig.getBillingAnagraficaTitolo()), + () -> assertEquals(expectedConfig.getBillingCodiceFiscale(), + actualConfig.getBillingCodiceFiscale()), + () -> assertEquals(expectedConfig.getBillingContactCompany(), + actualConfig.getBillingContactCompany()), + () -> assertEquals(expectedConfig.getBillingContactEmail(), + actualConfig.getBillingContactEmail()), + () -> assertEquals(expectedConfig.getBillingContactPhone(), + actualConfig.getBillingContactPhone()), + () -> assertEquals(expectedConfig.getBillingIdCodice(), + actualConfig.getBillingIdCodice()), + () -> assertEquals(expectedConfig.getBillingIdPaese(), + actualConfig.getBillingIdPaese()), + () -> assertEquals(expectedConfig.getBillingSedeCap(), + actualConfig.getBillingSedeCap()), + () -> assertEquals(expectedConfig.getBillingSedeComune(), + actualConfig.getBillingSedeComune()), + () -> assertEquals(expectedConfig.getBillingSedeIndirizzo(), + actualConfig.getBillingSedeIndirizzo()), + () -> assertEquals(expectedConfig.getBillingSedeNazione(), + actualConfig.getBillingSedeNazione()), + () -> assertEquals(expectedConfig.getBillingSedeNumeroCivico(), + actualConfig.getBillingSedeNumeroCivico()), + () -> assertEquals(expectedConfig.getBillingSedeProvincia(), + actualConfig.getBillingSedeProvincia()), + () -> assertEquals(expectedConfig.getBillingTerzoIntermediarioSoggettoEmittente(), + actualConfig.getBillingTerzoIntermediarioSoggettoEmittente()), + () -> assertEquals(expectedConfig.getEncryptionPublicKey(), + actualConfig.getEncryptionPublicKey()), + () -> assertEquals(expectedConfig.getFiscalCode(), actualConfig.getFiscalCode()), + () -> assertEquals(expectedConfig.getIdpEntityId(), actualConfig.getIdpEntityId()), + () -> assertEquals(expectedConfig.getIpaCode(), actualConfig.getIpaCode()), + () -> assertEquals(expectedConfig.getNameIDPolicyFormat(), + actualConfig.getNameIDPolicyFormat()), + () -> assertEquals(expectedConfig.getOrganizationDisplayNames(), + actualConfig.getOrganizationDisplayNames()), + () -> assertEquals(expectedConfig.getOrganizationNames(), + actualConfig.getOrganizationNames()), + () -> assertEquals(expectedConfig.getOrganizationUrls(), + actualConfig.getOrganizationUrls()), + () -> assertEquals(expectedConfig.getOtherContactCompany(), + actualConfig.getOtherContactCompany()), + () -> assertEquals(expectedConfig.getOtherContactEmail(), + actualConfig.getOtherContactEmail()), + () -> assertEquals(expectedConfig.getOtherContactPhone(), + actualConfig.getOtherContactPhone()), + () -> assertEquals(expectedConfig.getPrincipalAttribute(), + actualConfig.getPrincipalAttribute()), + () -> assertEquals(expectedConfig.getPrincipalType(), actualConfig.getPrincipalType()), + () -> assertEquals(expectedConfig.getSignatureAlgorithm(), + actualConfig.getSignatureAlgorithm()), + () -> assertEquals(expectedConfig.getSigningCertificate(), + actualConfig.getSigningCertificate()), + () -> assertEquals(expectedConfig.getSingleLogoutServiceUrl(), + actualConfig.getSingleLogoutServiceUrl()), + () -> assertEquals(expectedConfig.getSingleSignOnServiceUrl(), + actualConfig.getSingleSignOnServiceUrl()), + () -> assertEquals(expectedConfig.getVatNumber(), actualConfig.getVatNumber()), + () -> assertEquals(expectedConfig.getXmlSigKeyInfoKeyNameTransformer(), + actualConfig.getXmlSigKeyInfoKeyNameTransformer()), + () -> assertEquals(expectedConfig.isAddExtensionsElementWithKeyInfo(), + actualConfig.isAddExtensionsElementWithKeyInfo()), + () -> assertEquals(expectedConfig.isAllowCreate(), actualConfig.isAllowCreate()), + () -> assertEquals(expectedConfig.isBackchannelSupported(), + actualConfig.isBackchannelSupported()), + () -> assertEquals(expectedConfig.isEnabledFromMetadata(), + actualConfig.isEnabledFromMetadata()), + () -> assertEquals(expectedConfig.isForceAuthn(), actualConfig.isForceAuthn()), + () -> assertEquals(expectedConfig.isPostBindingAuthnRequest(), + actualConfig.isPostBindingAuthnRequest()), + () -> assertEquals(expectedConfig.isPostBindingLogout(), + actualConfig.isPostBindingLogout()), + () -> assertEquals(expectedConfig.isPostBindingResponse(), + actualConfig.isPostBindingResponse()), + () -> assertEquals(expectedConfig.isSignSpMetadata(), actualConfig.isSignSpMetadata()), + () -> assertEquals(expectedConfig.isSpPrivate(), actualConfig.isSpPrivate()), + () -> assertEquals(expectedConfig.isValidateSignature(), + actualConfig.isValidateSignature()), + () -> assertEquals(expectedConfig.isWantAssertionsEncrypted(), + actualConfig.isWantAssertionsEncrypted()), + () -> assertEquals(expectedConfig.isWantAssertionsSigned(), + actualConfig.isWantAssertionsSigned()), + () -> assertEquals(expectedConfig.isWantAuthnRequestsSigned(), + actualConfig.isWantAuthnRequestsSigned())); + } + + @Test + void testGetId() { + String expected = "saml-spid"; + assertEquals(expected, factory.getId()); + } + + @Test + void testGetName() { + String expected = "SPID"; + assertEquals(expected, factory.getName()); + } + + @Test + void testParseConfig() throws IOException { + + InputStream configStream = Files.newInputStream(Path.of("src/test/resources/real-metadata.xml")); + InputStream expectedConfigStream = Files + .newInputStream(Path.of("src/test/resources/parsed-metadata.yaml")); + + Map actualConfig = factory.parseConfig(createSession(), configStream); + + Yaml parseExpected = new Yaml(); + Map expectedConfig = parseExpected.load(expectedConfigStream); + + assertEquals(expectedConfig.size(), actualConfig.size()); + + actualConfig.forEach( + (K, V) -> assertEquals(expectedConfig.get(K).toString(), V) + ); + + } + + @Test + void testConfigPropertiesAreResolvedInBundles() { + List actualConfigProperties = factory.getConfigProperties(); + + ResourceBundle bundle = ResourceBundle.getBundle("provider-config.messages", Locale.ITALIAN); + List bundleValues = bundle.keySet().stream().map(bundle::getString).toList(); + + actualConfigProperties.forEach(pcp -> + assertAll("Label and help text exists in bundle ", + () -> assertTrue(bundleValues.contains(pcp.getLabel())), + () -> assertTrue(bundleValues.contains(pcp.getHelpText())) + + )); + } + + private KeycloakSession createSession() { + KeycloakSession session = new DefaultKeycloakSession(new DefaultKeycloakSessionFactory()); + session.setAttribute("TestAttribute", "only4Testing"); + return session; + } +} diff --git a/src/test/java/org/keycloak/broker/spid/SpidSAMLEndpointHelperTest.java b/src/test/java/org/keycloak/broker/spid/SpidSAMLEndpointHelperTest.java new file mode 100644 index 0000000..04fe9e7 --- /dev/null +++ b/src/test/java/org/keycloak/broker/spid/SpidSAMLEndpointHelperTest.java @@ -0,0 +1,1624 @@ +package org.keycloak.broker.spid; + +import nl.altindag.log.LogCaptor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.keycloak.dom.saml.v2.assertion.NameIDType; +import org.keycloak.dom.saml.v2.protocol.AuthnContextComparisonType; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.dom.saml.v2.protocol.StatusType; +import org.keycloak.saml.common.exceptions.ConfigurationException; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.exceptions.ProcessingException; +import org.keycloak.saml.common.util.DocumentUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.namespace.QName; +import java.io.IOException; +import java.net.URI; +import java.time.Duration; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.keycloak.broker.spid.ObjectMother.SamlResponseTypes.*; + +class SpidSAMLEndpointHelperTest { + + LogCaptor logCaptor; + + @BeforeEach + void init() { + logCaptor = LogCaptor.forClass(SpidSAMLEndpointHelper.class); + } + + @AfterEach + void tearDown() { + logCaptor.clearLogs(); + logCaptor.close(); + } + + + @Test + void raiseSpidSamlCheck02() throws DatatypeConfigurationException { + // Given + String expected = "SpidSamlCheck_02"; + ResponseType responseType = CompleteResponseType(); + responseType.setSignature(null); + // When + String actual = DefaultHelper().verifySpidResponse(null, null, null, responseType, null, null); + // Then + assertEquals(expected, actual); + } + + @Test + void raiseSpidSamlCheck03() throws DatatypeConfigurationException, ConfigurationException, ProcessingException, IOException, ParsingException { + + // Given + String expectedError = "SpidSamlCheck_03"; + + ResponseType responseType = CompleteResponseType(); + responseType.getAssertions().get(0).getAssertion().setSignature(null); + + // When + String actualError = DefaultHelper().verifySpidResponse(null, null, null, responseType, null, null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck08() throws DatatypeConfigurationException { + String expectedError = "SpidSamlCheck_08"; + + ResponseType responseType = CompleteResponseType("", "2024-04-10T09:22:28.065Z"); + + // When + String actualError = DefaultHelper().verifySpidResponse(null, null, null, responseType, null, null); + + // Then + assertEquals(expectedError, actualError); + } + + + @Test + void raiseSpidSamlCheck14() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_14"; + String requestAfterResponse = "2024-04-10T09:24:27.065Z"; + + // When + String actualError = DefaultHelper().verifySpidResponse(null, null, null, CompleteResponseType(), requestAfterResponse, null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck15() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_15"; + String requestMoreThan3MinutesBefore = "2024-04-10T09:18:28.065Z"; + + // When + String actualError = DefaultHelper().verifySpidResponse(null, null, null, CompleteResponseType(), requestMoreThan3MinutesBefore, null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck15Future() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_15"; + String requestTimeInRange = "2124-04-10T09:20:28.065Z"; + String responseTimeInFuture = "2124-04-10T09:22:28.065Z"; + + ResponseType responseType = CompleteResponseType("responseTimeInFuture", responseTimeInFuture); + + // When + String actualError = DefaultHelper().verifySpidResponse(null, null, null, responseType, requestTimeInRange, null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + @Disabled("Not checked by new AGID rules") + void raiseSpidSamlCheck110() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_110"; + + ResponseType responseType = CompleteResponseType("responseTimeInFuture", "2024-04-10T09:22:28.999Z"); + + // When + String actualError = DefaultHelper().verifySpidResponse(null, null, null, responseType, "2024-04-10T09:22:28.065Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + + @Test + void raiseSpidSamlCheck17() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_17"; + + Element samlResponse = samlResponseElement(); + samlResponse.removeAttribute("InResponseTo"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponse, null, null, CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck16() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_16"; + + Element samlResponse = samlResponseElement(); + samlResponse.setAttribute("InResponseTo", ""); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponse, null, null, CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck18() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_18"; + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), null, "not-expected-in-response-to", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck22() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_22"; + + ResponseType responseType = ObjectMother.SamlResponseTypes.CompleteResponseType(); + responseType.setStatus(new StatusType()); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), null, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", responseType, "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck23() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_23"; + + ResponseType responseType = CompleteResponseType(); + responseType.setStatus(null); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), null, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", responseType, "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck24() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_24"; + + ResponseType responseType = CompleteResponseType(); + responseType.getStatus().getStatusCode().setValue(URI.create("")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), null, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", responseType, "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck25() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_25"; + + ResponseType responseType = CompleteResponseType(); + responseType.getStatus().setStatusCode(null); + responseType.getStatus().setStatusMessage("not null"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), null, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", responseType, "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck26() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_26"; + + ResponseType responseType = CompleteResponseType(); + responseType.getStatus().getStatusCode().setValue(URI.create("uri:not:valid")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), null, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", responseType, "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck27() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_27"; + + ResponseType responseType = CompleteResponseType(); + responseType.setIssuer(new NameIDType()); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), null, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", responseType, "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck28() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_28"; + + ResponseType responseType = CompleteResponseType(); + responseType.setIssuer(null); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), null, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", responseType, "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck29() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_29"; + + ResponseType responseType = CompleteResponseType(); + responseType.getIssuer().setValue("wrong issuer"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), null, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", responseType, "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck30() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_30"; + + ResponseType responseType = CompleteResponseType(); + responseType.getIssuer().setFormat(URI.create("uri:not:correct")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), null, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", responseType, "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck33() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_33"; + + Element samlAssertion = samlAssertion(); + samlAssertion.setAttribute("ID", ""); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck39() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_39"; + + Element samlAssertion = samlAssertion(); + samlAssertion.setAttribute("IssueInstant", "2024-04-10T08:22:28.065Z"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck40() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_40"; + + Element samlAssertion = samlAssertion(); + // more than 3 minutes + samlAssertion.setAttribute("IssueInstant", "2024-04-10T09:27:28.065Z"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + @Disabled("Not checked by new AGID rules") + void raiseSpidSamlCheck110FromAssertion() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_110"; + + Element samlAssertion = samlAssertion(); + // with milliseconds + samlAssertion.setAttribute("IssueInstant", "2024-04-10T09:23:28.065Z"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + + @Test + void raiseSpidSamlCheck42() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_42"; + + Element samlAssertion = samlAssertion(); + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + samlAssertion.removeChild(subject); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck41() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_41"; + + Element samlAssertion = samlAssertion(); + // Remove Subject + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + subject.getParentNode().removeChild(subject); + // Add empty Subject + samlAssertion.appendChild(samlAssertion.getOwnerDocument().createElementNS("saml2", "Subject")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck44() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_44"; + + Element samlAssertion = samlAssertion(); + // Remove NameID + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + for (int i = 0; i < subject.getChildNodes().getLength(); i++) { + Node node = subject.getChildNodes().item(i); + if (node.getNodeName().equals("saml2:NameID")) { + node.getParentNode().removeChild(node); + } + } + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck43() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_43"; + + Element samlAssertion = samlAssertion(); + + // Remove NameID + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + for (int i = 0; i < subject.getChildNodes().getLength(); i++) { + Node node = subject.getChildNodes().item(i); + if (node.getNodeName().equals("saml2:NameID")) { + node.getFirstChild().setNodeValue(" "); + } + } + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck4546() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_4546"; + + Element samlAssertion = samlAssertion(); + + // Remove NameID + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element nameID = DocumentUtil.getChildElement(subject, new QName("NameID")); + nameID.setAttribute("Format", ""); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck47() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_47"; + + Element samlAssertion = samlAssertion(); + + // Remove NameID + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element nameID = DocumentUtil.getChildElement(subject, new QName("NameID")); + nameID.setAttribute("Format", "uri:wrong:format"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck4849() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_4849"; + + Element samlAssertion = samlAssertion(); + + // Remove NameID + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element nameID = DocumentUtil.getChildElement(subject, new QName("NameID")); + nameID.setAttribute("NameQualifier", ""); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck52() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_52"; + + Element samlAssertion = samlAssertion(); + + // Remove SubjectConfirmation + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + for (int i = 0; i < subject.getChildNodes().getLength(); i++) { + Node node = subject.getChildNodes().item(i); + if (node.getNodeName().equals("saml2:SubjectConfirmation")) { + node.getParentNode().removeChild(node); + } + } + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck51() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_51"; + + Element samlAssertion = samlAssertion(); + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + subjectConfirmation.getParentNode().removeChild(subjectConfirmation); + // Add empty SubjectConfirmation + subject.appendChild(samlAssertion.getOwnerDocument().createElementNS("saml2", "SubjectConfirmation")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck53() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_53"; + + Element samlAssertion = samlAssertion(); + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + subjectConfirmation.removeAttribute("Method"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + + @Test + void raiseSpidSamlCheck54() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_54"; + + Element samlAssertion = samlAssertion(); + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + subjectConfirmation.setAttribute("Method", ""); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck55() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_55"; + + Element samlAssertion = samlAssertion(); + + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + subjectConfirmation.setAttribute("Method", "wrong value"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck56() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_56"; + + Element samlAssertion = samlAssertion(); + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + subjectConfirmation.removeChild(subjectConfirmationData); + subjectConfirmation.appendChild(samlAssertion.getOwnerDocument().createElement("PlaceHolder")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck58() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_58"; + + Element samlAssertion = samlAssertion(); + + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + subjectConfirmationData.removeAttribute("Recipient"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", null); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck59() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_59"; + + Element samlAssertion = samlAssertion(); + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + subjectConfirmationData.setAttribute("Recipient", "wrong-recipient"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck57() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_57"; + + Element samlAssertion = samlAssertion(); + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + subjectConfirmationData.setAttribute("Recipient", ""); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck61() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_61"; + + Element samlAssertion = samlAssertion(); + + // Remove InResponseTo + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + subjectConfirmationData.removeAttribute("InResponseTo"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + + @Test + void raiseSpidSamlCheck60() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_60"; + + Element samlAssertion = samlAssertion(); + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + subjectConfirmationData.setAttribute("InResponseTo", ""); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck62() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_62"; + + Element samlAssertion = samlAssertion(); + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + subjectConfirmationData.setAttribute("InResponseTo", "wrong-value"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck64() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_64"; + + Element samlAssertion = samlAssertion(); + + // Remove SubjectConfirmation children + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + subjectConfirmationData.setAttribute("NotOnOrAfter", ""); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck66() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_66"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + subjectConfirmationData.setAttribute("NotOnOrAfter", "2024-04-10T09:22:28.089Z"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck67() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_67"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element issuerElement = DocumentUtil.getChildElement(samlAssertion, new QName("Issuer")); + issuerElement.getFirstChild().setNodeValue(null); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck68() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_68"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element issuerElement = DocumentUtil.getChildElement(samlAssertion, new QName("Issuer")); + issuerElement.getParentNode().removeChild(issuerElement); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck69() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_69"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element issuerElement = DocumentUtil.getChildElement(samlAssertion, new QName("Issuer")); + issuerElement.getFirstChild().setNodeValue("wrong-value"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck7071() throws DatatypeConfigurationException, ConfigurationException, ProcessingException { + // Given + String expectedError = "SpidSamlCheck_7071"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element issuerElement = DocumentUtil.getChildElement(samlAssertion, new QName("Issuer")); + issuerElement.setAttribute("Format", ""); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck72() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_72"; + + Element samlAssertion = samlAssertion(); + + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element issuerElement = DocumentUtil.getChildElement(samlAssertion, new QName("Issuer")); + issuerElement.setAttribute("Format", "wrong-format"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck73() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_73"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element element = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + element.getParentNode().removeChild(element); + samlAssertion.appendChild(samlAssertion.getOwnerDocument().createElementNS("saml2", "Conditions")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck74() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_74"; + + Element samlAssertion = samlAssertion(); + + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element element = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + element.getParentNode().removeChild(element); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck7576() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_7576"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element element = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + element.removeAttribute("NotBefore"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck78() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_78"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element element = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + element.setAttribute("NotBefore", "2124-04-10T09:22:28.065Z"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck7980() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_7980"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element element = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + element.removeAttribute("NotOnOrAfter"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck82() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_82"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element element = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + element.setAttribute("NotBefore", "2024-04-10T08:22:28.065Z"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck83() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_83"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element element = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + element.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element audienceRestriction = DocumentUtil.getChildElement(element, new QName("AudienceRestriction")); + audienceRestriction.getParentNode().removeChild(audienceRestriction); + element.appendChild(samlAssertion.getOwnerDocument().createElementNS("saml", "AudienceRestriction")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + + @Test + void raiseSpidSamlCheck8586() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_8586"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element element = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + element.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element audienceRestriction = DocumentUtil.getChildElement(element, new QName("AudienceRestriction")); + Element audience = DocumentUtil.getChildElement(audienceRestriction, new QName("Audience")); + audience.getParentNode().removeChild(audience); + audienceRestriction.appendChild(samlAssertion.getOwnerDocument().createElement("DummyElement")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck87() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_87"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element element = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + element.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element audienceRestriction = DocumentUtil.getChildElement(element, new QName("AudienceRestriction")); + Element audience = DocumentUtil.getChildElement(audienceRestriction, new QName("Audience")); + audience.getFirstChild().setNodeValue("wrong-value"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck88() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_88"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element conditions = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + conditions.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element authnStatement = DocumentUtil.getChildElement(samlAssertion, new QName("AuthnStatement")); + authnStatement.getParentNode().removeChild(authnStatement); + samlAssertion.appendChild(samlAssertion.getOwnerDocument().createElementNS("saml2", "AuthnStatement")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck89() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_89"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element conditions = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + conditions.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element authnStatement = DocumentUtil.getChildElement(samlAssertion, new QName("AuthnStatement")); + authnStatement.getParentNode().removeChild(authnStatement); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck90() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_90"; + + Element samlAssertion = samlAssertion(); + + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element conditions = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + conditions.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element authnStatement = DocumentUtil.getChildElement(samlAssertion, new QName("AuthnStatement")); + Element authnContext = DocumentUtil.getChildElement(authnStatement, new QName("AuthnContext")); + authnContext.getParentNode().removeChild(authnContext); + authnStatement.appendChild(samlAssertion.getOwnerDocument().createElementNS("saml2", "AuthnContext")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck91() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_91"; + + Element samlAssertion = samlAssertion(); + + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element conditions = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + conditions.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element authnStatement = DocumentUtil.getChildElement(samlAssertion, new QName("AuthnStatement")); + Element authnContext = DocumentUtil.getChildElement(authnStatement, new QName("AuthnContext")); + authnContext.getParentNode().removeChild(authnContext); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck92() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_92"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element conditions = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + conditions.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element authnStatement = DocumentUtil.getChildElement(samlAssertion, new QName("AuthnStatement")); + Element authnContext = DocumentUtil.getChildElement(authnStatement, new QName("AuthnContext")); + Element authnContextClassRef = DocumentUtil.getChildElement(authnContext, new QName("AuthnContextClassRef")); + authnContextClassRef.getFirstChild().setNodeValue(""); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck93() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_93"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element conditions = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + conditions.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element authnStatement = DocumentUtil.getChildElement(samlAssertion, new QName("AuthnStatement")); + Element authnContext = DocumentUtil.getChildElement(authnStatement, new QName("AuthnContext")); + Element authnContextClassRef = DocumentUtil.getChildElement(authnContext, new QName("AuthnContextClassRef")); + authnContextClassRef.getParentNode().removeChild(authnContextClassRef); + authnContext.appendChild(samlAssertion.getOwnerDocument().createElement("DummyElement")); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @ParameterizedTest + @CsvSource({ + "https://www.spid.gov.it/SpidL1,https://www.spid.gov.it/SpidL2,EXACT,SpidSamlCheck_94", + "https://www.spid.gov.it/SpidL2,https://www.spid.gov.it/SpidL1,EXACT,SpidSamlCheck_95", + "https://www.spid.gov.it/SpidL3,https://www.spid.gov.it/SpidL1,EXACT,SpidSamlCheck_96", + "https://www.spid.gov.it/SpidL1,https://www.spid.gov.it/SpidL2,MINIMUM,SpidSamlCheck_94", + "https://www.spid.gov.it/SpidL2,https://www.spid.gov.it/SpidL3,MINIMUM,SpidSamlCheck_95", + "https://www.spid.gov.it/SpidL2,https://www.spid.gov.it/SpidL1,MAXIMUM,SpidSamlCheck_95", + "https://www.spid.gov.it/SpidL3,https://www.spid.gov.it/SpidL1,MAXIMUM,SpidSamlCheck_96", + "https://www.spid.gov.it/SpidL1,https://www.spid.gov.it/SpidL2,BETTER,SpidSamlCheck_94", + "https://www.spid.gov.it/SpidL2,https://www.spid.gov.it/SpidL1,BETTER,SpidSamlCheck_95", + "https://www.spid.gov.it/SpidL3,https://www.spid.gov.it/SpidL2,BETTER,SpidSamlCheck_96" + }) + void raiseSpidSamlCheck94_95_96(String authnClassRefResponse, String authnClassRef, String comparisonType, String expectedError) throws DatatypeConfigurationException { + // Given + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element conditions = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + conditions.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element authnStatement = DocumentUtil.getChildElement(samlAssertion, new QName("AuthnStatement")); + Element authnContext = DocumentUtil.getChildElement(authnStatement, new QName("AuthnContext")); + Element authnContextClassRef = DocumentUtil.getChildElement(authnContext, new QName("AuthnContextClassRef")); + authnContextClassRef.getFirstChild().setNodeValue(authnClassRefResponse); + + SpidSAMLEndpointHelper customHelper = CustomHelper(authnClassRef, AuthnContextComparisonType.valueOf(comparisonType)); + + // When + String actualError = customHelper.verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck97() throws DatatypeConfigurationException { + // Given + String expectedError = "SpidSamlCheck_97"; + + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element conditions = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + conditions.setAttribute("NotOnOrAfter", someMinutesFromNow); + + Element authnStatement = DocumentUtil.getChildElement(samlAssertion, new QName("AuthnStatement")); + Element authnContext = DocumentUtil.getChildElement(authnStatement, new QName("AuthnContext")); + Element authnContextClassRef = DocumentUtil.getChildElement(authnContext, new QName("AuthnContextClassRef")); + authnContextClassRef.getFirstChild().setNodeValue("wrong-value"); + + // When + String actualError = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertEquals(expectedError, actualError); + } + + + @Test + void raiseNoError() throws DatatypeConfigurationException { + // Given + Element samlAssertion = samlAssertion(); + + Element subject = DocumentUtil.getChildElement(samlAssertion, new QName("Subject")); + Element subjectConfirmation = DocumentUtil.getChildElement(subject, new QName("SubjectConfirmation")); + Element subjectConfirmationData = DocumentUtil.getChildElement(subjectConfirmation, new QName("SubjectConfirmationData")); + ZonedDateTime to = ZonedDateTime.now().plus(Duration.of(3, TimeUnit.MINUTES.toChronoUnit())); + String someMinutesFromNow = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(to); + subjectConfirmationData.setAttribute("NotOnOrAfter", someMinutesFromNow); + // Confirmation NotOnOrAfter is checked with "now" + Element conditions = DocumentUtil.getChildElement(samlAssertion, new QName("Conditions")); + conditions.setAttribute("NotOnOrAfter", someMinutesFromNow); + + // When + String actualResult = DefaultHelper().verifySpidResponse(samlResponseElement(), samlAssertion, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21", CompleteResponseType(), "2024-04-10T09:22:28.000Z", "https://login.agid.gov.it/saml/module.php/saml/sp/saml2-acs.php/service"); + + // Then + assertNull(actualResult); + + } + + private void printDoc(Document document) { + try { + System.out.println("Document:\n" + DocumentUtil.getDocumentAsString(document)); + } catch (ProcessingException | ConfigurationException e) { + throw new RuntimeException(e); + } + } + + private void printDoc(Element element) { + printDoc(element.getOwnerDocument()); + } + + + @Test + void raiseSpidStatusError104() { + // Given + String expectedError = "SpidFault_ErrorCode_nr19"; + // When + String actualError = DefaultHelper().checkSpidStatus(UserAnomalies("ErrorCode nr19")); + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidStatusError105() { + // Given + String expectedError = "SpidFault_ErrorCode_nr20"; + // When + String actualError = DefaultHelper().checkSpidStatus(UserAnomalies("ErrorCode nr20")); + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidStatusError106() { + // Given + String expectedError = "SpidFault_ErrorCode_nr21"; + // When + String actualError = DefaultHelper().checkSpidStatus(UserAnomalies("ErrorCode nr21")); + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidStatusError107() { + // Given + String expectedError = "SpidFault_ErrorCode_nr22"; + // When + String actualError = DefaultHelper().checkSpidStatus(UserAnomalies("ErrorCode nr22")); + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidStatusError108() { + // Given + String expectedError = "SpidFault_ErrorCode_nr23"; + // When + String actualError = DefaultHelper().checkSpidStatus(UserAnomalies("ErrorCode nr23")); + // Then + assertEquals(expectedError, actualError); + } + + //validateInResponseToAttribute + + @Test + void successExpectedRequestIdIsEmpty() { + // Given + String expectedRequestId = null; + + // When + boolean actual = DefaultHelper().validateInResponseToAttribute(CompleteResponseType(), null); + + // Then + assertTrue(actual); + } + + + @Test + void failForResponseNull() { + + // Given + ResponseType responseType = CompleteResponseType(); + responseType.setInResponseTo(null); + + // When + boolean actual = DefaultHelper().validateInResponseToAttribute(responseType, "something"); + + // Then + assertAll("Validation error", () -> assertFalse(actual), () -> assertThat(logCaptor.getErrorLogs()).containsExactly("Response Validation Error: InResponseTo attribute was expected but not present in received response")); + + } + + @Test + void failForResponseEmpty() { + + // Given + ResponseType responseType = CompleteResponseType(); + responseType.setInResponseTo(""); + + // When + boolean actual = DefaultHelper().validateInResponseToAttribute(responseType, "something"); + + // Then + assertAll("Validation error", () -> assertFalse(actual), () -> assertThat(logCaptor.getErrorLogs()).containsExactly("Response Validation Error: InResponseTo attribute was expected but it is empty in received response")); + + } + + @Test + void failForResponseDontMatch() { + + // Given + ResponseType responseType = CompleteResponseType(); + responseType.setInResponseTo("something"); + + // When + boolean actual = DefaultHelper().validateInResponseToAttribute(responseType, "something else"); + + // Then + assertAll("Validation error", () -> assertFalse(actual), () -> assertThat(logCaptor.getErrorLogs()).containsExactly("Response Validation Error: received InResponseTo attribute does not match the expected request ID")); + + } + + + @Test + void successAssertionNotPresent() { + // Given + ResponseType responseType = CompleteResponseType(); + ResponseType.RTChoiceType rtChoiceType = responseType.getAssertions().get(0); + responseType.removeAssertion(rtChoiceType); + + // When + boolean actual = DefaultHelper().validateInResponseToAttribute(responseType, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21"); + + // Then + assertAll("Validation OK", () -> assertTrue(actual), () -> assertThat(logCaptor.getErrorLogs()).isEmpty()); + } + + @Test + void failForEmptySubjectConfirmationData() { + + // Given + ResponseType responseType = CompleteResponseType(); + responseType.getAssertions().get(0).getAssertion().getSubject().getConfirmation().get(0).getSubjectConfirmationData().setInResponseTo(""); + + // When + boolean actual = DefaultHelper().validateInResponseToAttribute(responseType, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21"); + + // Then + assertAll("Validation error", () -> assertFalse(actual), () -> assertThat(logCaptor.getErrorLogs()).containsExactly("Response Validation Error: SubjectConfirmationData InResponseTo attribute was expected but it is empty in received response")); + + } + + @Test + void failForWrongSubjectConfirmationData() { + + // Given + ResponseType responseType = CompleteResponseType(); + responseType.getAssertions().get(0).getAssertion().getSubject().getConfirmation().get(0).getSubjectConfirmationData().setInResponseTo("wrong"); + + // When + boolean actual = DefaultHelper().validateInResponseToAttribute(responseType, "spid-php_4be997744d3fde7b019fcfcae44d295ab3adb82c21"); + + // Then + assertAll("Validation error", () -> assertFalse(actual), () -> assertThat(logCaptor.getErrorLogs()).containsExactly("Response Validation Error: received SubjectConfirmationData InResponseTo attribute does not match the expected request ID")); + + } + + @Test + void raiseSpidSamlCheck23NoStatus() { + //Given + String expectedError = "SpidSamlCheck_23"; + // When + String actualError = DefaultHelper().checkSpidStatus(EmptyResponseType()); + // Then + assertEquals(expectedError, actualError); + } + + @Test + void raiseSpidSamlCheck32() { + //Given + String expectedError = "SpidSamlCheck_32"; + + // When + String actualError = DefaultHelper().checkAssertions(EmptyResponseType()); + // Then + assertEquals(expectedError, actualError); + } + +} \ No newline at end of file diff --git a/src/test/java/org/keycloak/broker/spid/mappers/SpidUserAttributeMapperTest.java b/src/test/java/org/keycloak/broker/spid/mappers/SpidUserAttributeMapperTest.java new file mode 100644 index 0000000..11d70b5 --- /dev/null +++ b/src/test/java/org/keycloak/broker/spid/mappers/SpidUserAttributeMapperTest.java @@ -0,0 +1,30 @@ +package org.keycloak.broker.spid.mappers; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class SpidUserAttributeMapperTest { + + + SpidUserAttributeMapper mapper = new SpidUserAttributeMapper(); + + @Test + void testGetCompatibleProviders() { + String[] actualCompatibleProviders = mapper.getCompatibleProviders(); + assertArrayEquals(SpidUserAttributeMapper.COMPATIBLE_PROVIDERS, actualCompatibleProviders); + } + + @Test + void testGetDisplayType() { + String actualDisplayType = mapper.getDisplayType(); + assertEquals("SPID Attribute Importer", actualDisplayType); + } + + @Test + void testGetId() { + String actualId = mapper.getId(); + assertEquals(SpidUserAttributeMapper.PROVIDER_ID, actualId); + } +} diff --git a/src/test/java/org/keycloak/broker/spid/mappers/SpidUsernameTemplateMapperTest.java b/src/test/java/org/keycloak/broker/spid/mappers/SpidUsernameTemplateMapperTest.java new file mode 100644 index 0000000..44f703f --- /dev/null +++ b/src/test/java/org/keycloak/broker/spid/mappers/SpidUsernameTemplateMapperTest.java @@ -0,0 +1,91 @@ +package org.keycloak.broker.spid.mappers; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.keycloak.broker.provider.BrokeredIdentityContext; +import org.keycloak.broker.spid.SpidIdentityProviderFactory; +import org.keycloak.dom.saml.v2.assertion.AssertionType; +import org.keycloak.dom.saml.v2.assertion.AttributeStatementType; +import org.keycloak.dom.saml.v2.assertion.AttributeType; +import org.keycloak.dom.saml.v2.assertion.AttributeStatementType.ASTChoiceType; +import org.keycloak.models.IdentityProviderMapperModel; + +class SpidUsernameTemplateMapperTest { + + private SpidUsernameTemplateMapper mapper = new SpidUsernameTemplateMapper(); + + @Test + void testGetCompatibleProviders() { + String[] expected = { SpidIdentityProviderFactory.PROVIDER_ID }; + assertArrayEquals(expected, mapper.getCompatibleProviders()); + } + + @Test + void testGetDisplayType() { + String expected = "SPID Username Template Importer"; + assertEquals(expected, mapper.getDisplayType()); + } + + @Test + void testGetId() { + String expected = SpidUsernameTemplateMapper.PROVIDER_ID; + assertEquals(expected, mapper.getId()); + } + + @ParameterizedTest + @CsvSource({ + "tinit-drcgnn12a46a326k,drcgnn12a46a326k", + "MPRPSD70B15D971D,MPRPSD70B15D971D", + "RSSMRA89M01H501Y,RSSMRA89M01H501Y", + "TINIT-BLLMNO95M10A001X,BLLMNO95M10A001X", + }) + void testPreprocessFederatedIdentity(String actualFiscalCode, String expectedFiscalCode) + throws DatatypeConfigurationException { + + IdentityProviderMapperModel testMapperModel = buildMapperModel(); + BrokeredIdentityContext testContext = buildContext(actualFiscalCode); + mapper.preprocessFederatedIdentity(null, null, testMapperModel, testContext); + + assertEquals(expectedFiscalCode, testContext.getModelUsername()); + + } + + private static IdentityProviderMapperModel buildMapperModel() { + Map config = new HashMap<>(); + config.put("template", "${ATTRIBUTE.TEST_FISCAL_CODE}"); + + IdentityProviderMapperModel mapperModel = new IdentityProviderMapperModel(); + mapperModel.setConfig(config); + return mapperModel; + } + + private static BrokeredIdentityContext buildContext(String codiceFiscale) throws DatatypeConfigurationException { + XMLGregorianCalendar now = DatatypeFactory.newInstance().newXMLGregorianCalendar(); + AssertionType assertion = new AssertionType("my-spid", now); + AttributeStatementType attributeStatementType = new AttributeStatementType(); + AttributeType attributeType = new AttributeType("TEST_FISCAL_CODE"); + attributeType.addAttributeValue(codiceFiscale); + ASTChoiceType astChoiceType = new ASTChoiceType(attributeType); + + attributeStatementType.addAttribute(astChoiceType); + // assertion.getAttributeStatements().add(attributeStatementType); + + assertion.addStatement(attributeStatementType); + + BrokeredIdentityContext context = new BrokeredIdentityContext("my-spid"); + context.getContextData().put("SAML_ASSERTION", assertion); + + return context; + } +} diff --git a/src/test/java/org/keycloak/broker/spid/metadata/SpidSPMetadataResourceHelperTest.java b/src/test/java/org/keycloak/broker/spid/metadata/SpidSPMetadataResourceHelperTest.java index 3a0a7b0..3edceba 100644 --- a/src/test/java/org/keycloak/broker/spid/metadata/SpidSPMetadataResourceHelperTest.java +++ b/src/test/java/org/keycloak/broker/spid/metadata/SpidSPMetadataResourceHelperTest.java @@ -1,6 +1,8 @@ package org.keycloak.broker.spid.metadata; -import static org.junit.Assert.assertEquals; + + +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.io.StringWriter; @@ -17,23 +19,24 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.keycloak.broker.spid.SpidIdentityProviderConfig; import org.keycloak.dom.saml.v2.metadata.ContactType; import org.keycloak.dom.saml.v2.metadata.ContactTypeType; import org.keycloak.saml.common.exceptions.ConfigurationException; import org.w3c.dom.Element; -public class SpidSPMetadataResourceHelperTest { +class SpidSPMetadataResourceHelperTest { private SpidSPMetadataResourceHelper helper; private static SpidIdentityProviderConfig defaultConfig = new SpidIdentityProviderConfig(); private static SpidIdentityProviderConfig defaultConfigNomeCognome = new SpidIdentityProviderConfig(); - @BeforeClass + @BeforeAll public static void initClass() { //If the test will grow we will consider an object mother pattern defaultConfig.setBillingIdPaese("IT"); @@ -62,13 +65,13 @@ public static void initClass() { } - @Before - public void init() { + @BeforeEach + void init() { helper = new SpidSPMetadataResourceHelper(); } @Test - public void testCessionarioCommittenteBillingExtension() + void testCessionarioCommittenteBillingExtension() throws ConfigurationException, TransformerException, IOException { final String expected = Files.readString(Paths.get("src/test/resources/cessionarioCommittente.xml")); @@ -81,7 +84,7 @@ public void testCessionarioCommittenteBillingExtension() } @Test - public void testCessionarioCommittenteBillingExtensionNomeCognome() + void testCessionarioCommittenteBillingExtensionNomeCognome() throws ConfigurationException, TransformerException, IOException { final String expected = Files.readString(Paths.get("src/test/resources/cessionarioCommittenteNomeCognome.xml")); @@ -94,7 +97,7 @@ public void testCessionarioCommittenteBillingExtensionNomeCognome() } @Test - public void testTerzoIntermediarioSoggettoEmittenteBillingExtension() + void testTerzoIntermediarioSoggettoEmittenteBillingExtension() throws ConfigurationException, TransformerException, IOException { final String expected = "terzo_intermediario_soggetto_emittente\n"; diff --git a/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderFactoryTest.java b/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderFactoryTest.java new file mode 100644 index 0000000..0f31f81 --- /dev/null +++ b/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderFactoryTest.java @@ -0,0 +1,35 @@ +package org.keycloak.broker.spid.metadata; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import org.junit.jupiter.api.Test; +import org.keycloak.models.KeycloakSession; +import org.keycloak.services.DefaultKeycloakSession; +import org.keycloak.services.DefaultKeycloakSessionFactory; +import org.keycloak.services.resource.RealmResourceProvider; + +class SpidSpMetadataResourceProviderFactoryTest { + + private SpidSpMetadataResourceProviderFactory factory = new SpidSpMetadataResourceProviderFactory(); + + @Test + void testCreate() { + RealmResourceProvider actualRealmResourceProvider = factory.create(keycloakSession()); + assertInstanceOf(SpidSpMetadataResourceProvider.class, actualRealmResourceProvider); + } + + @Test + void testGetId() { + String actual = factory.getId(); + assertEquals(SpidSpMetadataResourceProviderFactory.ID, actual); + } + + private KeycloakSession keycloakSession() { + + KeycloakSession session = new DefaultKeycloakSession(new DefaultKeycloakSessionFactory()); + session.setAttribute("TestAttribute", "only4Testing"); + + return session; + } +} diff --git a/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java b/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java new file mode 100644 index 0000000..cf981af --- /dev/null +++ b/src/test/java/org/keycloak/broker/spid/metadata/SpidSpMetadataResourceProviderTest.java @@ -0,0 +1,35 @@ +package org.keycloak.broker.spid.metadata; + +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.DefaultKeycloakSession; +import org.keycloak.services.DefaultKeycloakSessionFactory; + +import jakarta.ws.rs.core.Response; + +class SpidSpMetadataResourceProviderTest { + + private SpidSpMetadataResourceProvider resourceProvider; + + @BeforeEach + void init() { + KeycloakSession session = new DefaultKeycloakSession(new DefaultKeycloakSessionFactory()); + session.setAttribute("TestAttribute", "only4Testing"); + //session.getContext().setRealm(); + resourceProvider = new SpidSpMetadataResourceProvider(session); + + } + + @Test + void testGetResource() { + Object actual = resourceProvider.getResource(); + assertInstanceOf(SpidSpMetadataResourceProvider.class, actual); + } + +} \ No newline at end of file diff --git a/src/test/java/org/keycloak/broker/spid/tests/SpidSAML2AuthnRequestBuilderTest.java b/src/test/java/org/keycloak/broker/spid/tests/SpidSAML2AuthnRequestBuilderTest.java deleted file mode 100644 index dd92a55..0000000 --- a/src/test/java/org/keycloak/broker/spid/tests/SpidSAML2AuthnRequestBuilderTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.keycloak.broker.spid.tests; - -import org.junit.Assert; -import org.junit.Test; - - -public class SpidSAML2AuthnRequestBuilderTest { - - @Test - public void fiscalNumberTrimming(){ - - String fiscalNumber = "tinit-drcgnn12a46a326k"; - String normalFiscalNumber = "MPRPSD70B15D971D"; - - - //l'uso di toUpperCase senza assegnazione è per lasciare inalterato il fiscalNumber originale - //e riassegnandogli solamente il valore non trimmato - String newFiscalNumber = manageFN(fiscalNumber); - String newNormalFiscalNumber = manageFN(normalFiscalNumber); - - Assert.assertTrue(!newFiscalNumber.toUpperCase().contains("TINIT-")); - - //normal fiscal number has not changed - Assert.assertTrue(newNormalFiscalNumber.equals(newNormalFiscalNumber)); - - - - } - - private String manageFN(String fiscalNumber) { - if(fiscalNumber.toUpperCase().startsWith("TINIT-")) - fiscalNumber = fiscalNumber.split("^(?i)TINIT-")[1]; - return fiscalNumber; - } - - -} diff --git a/src/test/resources/parsed-metadata.yaml b/src/test/resources/parsed-metadata.yaml new file mode 100644 index 0000000..09436c3 --- /dev/null +++ b/src/test/resources/parsed-metadata.yaml @@ -0,0 +1,56 @@ +postBindingLogout: true +singleLogoutServiceUrl: https://loginspid.aruba.it/ServiceLogoutRequest +postBindingResponse: true +nameIDPolicyFormat: urn:oasis:names:tc:SAML:2.0:nameid-format:transient +idpEntityId: https://loginspid.aruba.it +loginHint: false +enabledFromMetadata: true +postBindingAuthnRequest: true +singleSignOnServiceUrl: https://loginspid.aruba.it/ServiceLoginWelcome +wantAuthnRequestsSigned: true +addExtensionsElementWithKeyInfo: false +validateSignature: true +signingCertificate: | + MIIExTCCA62gAwIBAgIQH32A70kY92tuXB8AGi2DdDANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG + EwJJVDEYMBYGA1UECgwPQXJ1YmFQRUMgUy5wLkEuMSEwHwYDVQQLDBhDZXJ0aWZpY2F0aW9uIEF1 + dGhvcml0eUIxIDAeBgNVBAMMF0FydWJhUEVDIFMucC5BLiBORyBDQSAyMB4XDTIwMDEyMjAwMDAw + MFoXDTI1MDEyMTIzNTk1OVowgaAxCzAJBgNVBAYTAklUMRYwFAYDVQQKDA1BcnViYSBQRUMgc3Bh + MREwDwYDVQQLDAhQcm9kb3R0bzEWMBQGA1UEAwwNcGVjLml0IHBlYy5pdDEZMBcGA1UEBRMQWFhY + WFhYMDBYMDBYMDAwWDEPMA0GA1UEKgwGcGVjLml0MQ8wDQYDVQQEDAZwZWMuaXQxETAPBgNVBC4T + CDIwODc2Mzc5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqt2oHJhcp03l73p+QYpE + J+f3jYYj0W0gos0RItZx/w4vpsiKBygaqDNVWSwfo1aPdVDIX13f62O+lBki29KTt+QWv5K6SGHD + UXYPntRdEQlicIBh2Z0HfrM7fDl+xeJrMp1s4dsSQAuB5TJOlFZq7xCQuukytGWBTvjfcN/os5aE + sEg+RbtZHJR26SbbUcIqWb27Swgj/9jwK+tvzLnP4w8FNvEOrNfR0XwTMNDFrwbOCuWgthv5jNBs + VZaoqNwiA/MxYt+gTOMj/o5PWKk8Wpm6o/7/+lWAoxh0v8x9OkbIi+YaFpIxuCcUqsrJJk63x2gH + Cc2nr+yclYUhsKD/AwIDAQABo4IBLDCCASgwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBTKQ3+N + PGcXFk8nX994vMTVpba1EzBHBgNVHSAEQDA+MDwGCysGAQQBgegtAQEBMC0wKwYIKwYBBQUHAgEW + H2h0dHBzOi8vY2EuYXJ1YmFwZWMuaXQvY3BzLmh0bWwwWAYDVR0fBFEwTzBNoEugSYZHaHR0cDov + L2NybC5hcnViYXBlYy5pdC9BcnViYVBFQ1NwQUNlcnRpZmljYXRpb25BdXRob3JpdHlCL0xhdGVz + dENSTC5jcmwwHwYDVR0jBBgwFoAU8v9jQBwRQv3M3/FZ9m7omYcxR3kwMwYIKwYBBQUHAQEEJzAl + MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5hcnViYXBlYy5pdDANBgkqhkiG9w0BAQsFAAOCAQEA + ZKpor1MrrYwPw+IuPZElQAuNzXsaSWSnn/QQwJtW49c4rFM4mEud9c61p9XxIIbgQKmDmNbzC+Dm + wJSZ8ILdCAyBHmY3BehVRAy3KRA2KQhS9kd4vywf5KVYd1L5hQa9DBrusxF7i1X/SEeLQgoKkov0 + R8v43UncqXS/ql50ovJFxi938Rv4rVwa8o0hqqc6WUcjkidB6M9aNJLIbOZN3xNUgC28qIr8y7N8 + lbxWbwVrGxqKDtpaA9J0hOOXxwuTfSd1zOtT0KSSSUQ53QGOPnxyjxYDQbJu60/lBPuUV5wb/Z2r + gpeUH1/n7limHV5sVmOZgSnf18T+0STANCfkXg==,MIIExTCCA62gAwIBAgIQIHtEvEhGM77HwqsuvSbi9zANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG + EwJJVDEYMBYGA1UECgwPQXJ1YmFQRUMgUy5wLkEuMSEwHwYDVQQLDBhDZXJ0aWZpY2F0aW9uIEF1 + dGhvcml0eUIxIDAeBgNVBAMMF0FydWJhUEVDIFMucC5BLiBORyBDQSAyMB4XDTE3MDEyMzAwMDAw + MFoXDTIwMDEyMzIzNTk1OVowgaAxCzAJBgNVBAYTAklUMRYwFAYDVQQKDA1BcnViYSBQRUMgc3Bh + MREwDwYDVQQLDAhQcm9kb3R0bzEWMBQGA1UEAwwNcGVjLml0IHBlYy5pdDEZMBcGA1UEBRMQWFhY + WFhYMDBYMDBYMDAwWDEPMA0GA1UEKgwGcGVjLml0MQ8wDQYDVQQEDAZwZWMuaXQxETAPBgNVBC4T + CDE2MzQ1MzgzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqt2oHJhcp03l73p+QYpE + J+f3jYYj0W0gos0RItZx/w4vpsiKBygaqDNVWSwfo1aPdVDIX13f62O+lBki29KTt+QWv5K6SGHD + UXYPntRdEQlicIBh2Z0HfrM7fDl+xeJrMp1s4dsSQAuB5TJOlFZq7xCQuukytGWBTvjfcN/os5aE + sEg+RbtZHJR26SbbUcIqWb27Swgj/9jwK+tvzLnP4w8FNvEOrNfR0XwTMNDFrwbOCuWgthv5jNBs + VZaoqNwiA/MxYt+gTOMj/o5PWKk8Wpm6o/7/+lWAoxh0v8x9OkbIi+YaFpIxuCcUqsrJJk63x2gH + Cc2nr+yclYUhsKD/AwIDAQABo4IBLDCCASgwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBTKQ3+N + PGcXFk8nX994vMTVpba1EzBHBgNVHSAEQDA+MDwGCysGAQQBgegtAQEBMC0wKwYIKwYBBQUHAgEW + H2h0dHBzOi8vY2EuYXJ1YmFwZWMuaXQvY3BzLmh0bWwwWAYDVR0fBFEwTzBNoEugSYZHaHR0cDov + L2NybC5hcnViYXBlYy5pdC9BcnViYVBFQ1NwQUNlcnRpZmljYXRpb25BdXRob3JpdHlCL0xhdGVz + dENSTC5jcmwwHwYDVR0jBBgwFoAU8v9jQBwRQv3M3/FZ9m7omYcxR3kwMwYIKwYBBQUHAQEEJzAl + MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5hcnViYXBlYy5pdDANBgkqhkiG9w0BAQsFAAOCAQEA + nEw0NuaspbpDjA5wggwFtfQydU6b3Bw2/KXPRKS2JoqGmx0SYKj+L17A2KUBa2c7gDtKXYz0FLT6 + 0Bv0pmBN/oYCgVMEBJKqwRwdki9YjEBwyCZwNEx1kDAyyqFEVU9vw/OQfrAdp7MTbuZGFKknVt7b + 9wOYy/Op9FiUaTg6SuOy0ep+rqhihltYNAAl4L6fY45mHvqa5vvVG30OvLW/S4uvRYUXYwY6KhWv + NdDf5CnFugnuEZtHJrVe4wx9aO5GvFLFZ/mQ35C5mXPQ7nIb0CDdLBJdz82nUoLSA5BUbeXAUkfa + hW/hLxLdhks68/TK694xVIuiB40pvMmJwxIyDA== \ No newline at end of file diff --git a/src/test/resources/real-metadata.xml b/src/test/resources/real-metadata.xml new file mode 100644 index 0000000..daae5c5 --- /dev/null +++ b/src/test/resources/real-metadata.xml @@ -0,0 +1,65 @@ + +lEey40d2HutuRD0zUyTO31NAdM+rB7Zg6ioZavtdvJ8=HDvZyURr0FASd3BJGMzOLjEXaEf0PMVWfKfGlAAriBE6tWkVxNSkxPt9SHXB4jfmjwbtpZ+tEGtdCbuAIm/o9hbWA60TqN3hCNisIlRXtj9iSSa7CbO0mTAlxZlFCD4wWJ2sG8+Ets0T49NFC1nGU0yf55TmbSPWFffJZ3nB4oP9GdEcSDxEzfhtCAxCgHRboYvEmnXedZftljf5SYUZ//jsKTFr2uSwrc3FPib+yLQehWz3VosIj5adUzL/R+j+DSyaDdAuVZCPFIINIivngfHNqIizsTwe5LJ8iop3wBcaAENXlZyiycv3n3Ybr6uS+BqIviUL6C+H3CfGp3/LMA==MIIExTCCA62gAwIBAgIQH32A70kY92tuXB8AGi2DdDANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG +EwJJVDEYMBYGA1UECgwPQXJ1YmFQRUMgUy5wLkEuMSEwHwYDVQQLDBhDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eUIxIDAeBgNVBAMMF0FydWJhUEVDIFMucC5BLiBORyBDQSAyMB4XDTIwMDEyMjAwMDAw +MFoXDTI1MDEyMTIzNTk1OVowgaAxCzAJBgNVBAYTAklUMRYwFAYDVQQKDA1BcnViYSBQRUMgc3Bh +MREwDwYDVQQLDAhQcm9kb3R0bzEWMBQGA1UEAwwNcGVjLml0IHBlYy5pdDEZMBcGA1UEBRMQWFhY +WFhYMDBYMDBYMDAwWDEPMA0GA1UEKgwGcGVjLml0MQ8wDQYDVQQEDAZwZWMuaXQxETAPBgNVBC4T +CDIwODc2Mzc5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqt2oHJhcp03l73p+QYpE +J+f3jYYj0W0gos0RItZx/w4vpsiKBygaqDNVWSwfo1aPdVDIX13f62O+lBki29KTt+QWv5K6SGHD +UXYPntRdEQlicIBh2Z0HfrM7fDl+xeJrMp1s4dsSQAuB5TJOlFZq7xCQuukytGWBTvjfcN/os5aE +sEg+RbtZHJR26SbbUcIqWb27Swgj/9jwK+tvzLnP4w8FNvEOrNfR0XwTMNDFrwbOCuWgthv5jNBs +VZaoqNwiA/MxYt+gTOMj/o5PWKk8Wpm6o/7/+lWAoxh0v8x9OkbIi+YaFpIxuCcUqsrJJk63x2gH +Cc2nr+yclYUhsKD/AwIDAQABo4IBLDCCASgwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBTKQ3+N +PGcXFk8nX994vMTVpba1EzBHBgNVHSAEQDA+MDwGCysGAQQBgegtAQEBMC0wKwYIKwYBBQUHAgEW +H2h0dHBzOi8vY2EuYXJ1YmFwZWMuaXQvY3BzLmh0bWwwWAYDVR0fBFEwTzBNoEugSYZHaHR0cDov +L2NybC5hcnViYXBlYy5pdC9BcnViYVBFQ1NwQUNlcnRpZmljYXRpb25BdXRob3JpdHlCL0xhdGVz +dENSTC5jcmwwHwYDVR0jBBgwFoAU8v9jQBwRQv3M3/FZ9m7omYcxR3kwMwYIKwYBBQUHAQEEJzAl +MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5hcnViYXBlYy5pdDANBgkqhkiG9w0BAQsFAAOCAQEA +ZKpor1MrrYwPw+IuPZElQAuNzXsaSWSnn/QQwJtW49c4rFM4mEud9c61p9XxIIbgQKmDmNbzC+Dm +wJSZ8ILdCAyBHmY3BehVRAy3KRA2KQhS9kd4vywf5KVYd1L5hQa9DBrusxF7i1X/SEeLQgoKkov0 +R8v43UncqXS/ql50ovJFxi938Rv4rVwa8o0hqqc6WUcjkidB6M9aNJLIbOZN3xNUgC28qIr8y7N8 +lbxWbwVrGxqKDtpaA9J0hOOXxwuTfSd1zOtT0KSSSUQ53QGOPnxyjxYDQbJu60/lBPuUV5wb/Z2r +gpeUH1/n7limHV5sVmOZgSnf18T+0STANCfkXg==MIIExTCCA62gAwIBAgIQH32A70kY92tuXB8AGi2DdDANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG +EwJJVDEYMBYGA1UECgwPQXJ1YmFQRUMgUy5wLkEuMSEwHwYDVQQLDBhDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eUIxIDAeBgNVBAMMF0FydWJhUEVDIFMucC5BLiBORyBDQSAyMB4XDTIwMDEyMjAwMDAw +MFoXDTI1MDEyMTIzNTk1OVowgaAxCzAJBgNVBAYTAklUMRYwFAYDVQQKDA1BcnViYSBQRUMgc3Bh +MREwDwYDVQQLDAhQcm9kb3R0bzEWMBQGA1UEAwwNcGVjLml0IHBlYy5pdDEZMBcGA1UEBRMQWFhY +WFhYMDBYMDBYMDAwWDEPMA0GA1UEKgwGcGVjLml0MQ8wDQYDVQQEDAZwZWMuaXQxETAPBgNVBC4T +CDIwODc2Mzc5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqt2oHJhcp03l73p+QYpE +J+f3jYYj0W0gos0RItZx/w4vpsiKBygaqDNVWSwfo1aPdVDIX13f62O+lBki29KTt+QWv5K6SGHD +UXYPntRdEQlicIBh2Z0HfrM7fDl+xeJrMp1s4dsSQAuB5TJOlFZq7xCQuukytGWBTvjfcN/os5aE +sEg+RbtZHJR26SbbUcIqWb27Swgj/9jwK+tvzLnP4w8FNvEOrNfR0XwTMNDFrwbOCuWgthv5jNBs +VZaoqNwiA/MxYt+gTOMj/o5PWKk8Wpm6o/7/+lWAoxh0v8x9OkbIi+YaFpIxuCcUqsrJJk63x2gH +Cc2nr+yclYUhsKD/AwIDAQABo4IBLDCCASgwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBTKQ3+N +PGcXFk8nX994vMTVpba1EzBHBgNVHSAEQDA+MDwGCysGAQQBgegtAQEBMC0wKwYIKwYBBQUHAgEW +H2h0dHBzOi8vY2EuYXJ1YmFwZWMuaXQvY3BzLmh0bWwwWAYDVR0fBFEwTzBNoEugSYZHaHR0cDov +L2NybC5hcnViYXBlYy5pdC9BcnViYVBFQ1NwQUNlcnRpZmljYXRpb25BdXRob3JpdHlCL0xhdGVz +dENSTC5jcmwwHwYDVR0jBBgwFoAU8v9jQBwRQv3M3/FZ9m7omYcxR3kwMwYIKwYBBQUHAQEEJzAl +MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5hcnViYXBlYy5pdDANBgkqhkiG9w0BAQsFAAOCAQEA +ZKpor1MrrYwPw+IuPZElQAuNzXsaSWSnn/QQwJtW49c4rFM4mEud9c61p9XxIIbgQKmDmNbzC+Dm +wJSZ8ILdCAyBHmY3BehVRAy3KRA2KQhS9kd4vywf5KVYd1L5hQa9DBrusxF7i1X/SEeLQgoKkov0 +R8v43UncqXS/ql50ovJFxi938Rv4rVwa8o0hqqc6WUcjkidB6M9aNJLIbOZN3xNUgC28qIr8y7N8 +lbxWbwVrGxqKDtpaA9J0hOOXxwuTfSd1zOtT0KSSSUQ53QGOPnxyjxYDQbJu60/lBPuUV5wb/Z2r +gpeUH1/n7limHV5sVmOZgSnf18T+0STANCfkXg==MIIExTCCA62gAwIBAgIQIHtEvEhGM77HwqsuvSbi9zANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG +EwJJVDEYMBYGA1UECgwPQXJ1YmFQRUMgUy5wLkEuMSEwHwYDVQQLDBhDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eUIxIDAeBgNVBAMMF0FydWJhUEVDIFMucC5BLiBORyBDQSAyMB4XDTE3MDEyMzAwMDAw +MFoXDTIwMDEyMzIzNTk1OVowgaAxCzAJBgNVBAYTAklUMRYwFAYDVQQKDA1BcnViYSBQRUMgc3Bh +MREwDwYDVQQLDAhQcm9kb3R0bzEWMBQGA1UEAwwNcGVjLml0IHBlYy5pdDEZMBcGA1UEBRMQWFhY +WFhYMDBYMDBYMDAwWDEPMA0GA1UEKgwGcGVjLml0MQ8wDQYDVQQEDAZwZWMuaXQxETAPBgNVBC4T +CDE2MzQ1MzgzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqt2oHJhcp03l73p+QYpE +J+f3jYYj0W0gos0RItZx/w4vpsiKBygaqDNVWSwfo1aPdVDIX13f62O+lBki29KTt+QWv5K6SGHD +UXYPntRdEQlicIBh2Z0HfrM7fDl+xeJrMp1s4dsSQAuB5TJOlFZq7xCQuukytGWBTvjfcN/os5aE +sEg+RbtZHJR26SbbUcIqWb27Swgj/9jwK+tvzLnP4w8FNvEOrNfR0XwTMNDFrwbOCuWgthv5jNBs +VZaoqNwiA/MxYt+gTOMj/o5PWKk8Wpm6o/7/+lWAoxh0v8x9OkbIi+YaFpIxuCcUqsrJJk63x2gH +Cc2nr+yclYUhsKD/AwIDAQABo4IBLDCCASgwDgYDVR0PAQH/BAQDAgeAMB0GA1UdDgQWBBTKQ3+N +PGcXFk8nX994vMTVpba1EzBHBgNVHSAEQDA+MDwGCysGAQQBgegtAQEBMC0wKwYIKwYBBQUHAgEW +H2h0dHBzOi8vY2EuYXJ1YmFwZWMuaXQvY3BzLmh0bWwwWAYDVR0fBFEwTzBNoEugSYZHaHR0cDov +L2NybC5hcnViYXBlYy5pdC9BcnViYVBFQ1NwQUNlcnRpZmljYXRpb25BdXRob3JpdHlCL0xhdGVz +dENSTC5jcmwwHwYDVR0jBBgwFoAU8v9jQBwRQv3M3/FZ9m7omYcxR3kwMwYIKwYBBQUHAQEEJzAl +MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5hcnViYXBlYy5pdDANBgkqhkiG9w0BAQsFAAOCAQEA +nEw0NuaspbpDjA5wggwFtfQydU6b3Bw2/KXPRKS2JoqGmx0SYKj+L17A2KUBa2c7gDtKXYz0FLT6 +0Bv0pmBN/oYCgVMEBJKqwRwdki9YjEBwyCZwNEx1kDAyyqFEVU9vw/OQfrAdp7MTbuZGFKknVt7b +9wOYy/Op9FiUaTg6SuOy0ep+rqhihltYNAAl4L6fY45mHvqa5vvVG30OvLW/S4uvRYUXYwY6KhWv +NdDf5CnFugnuEZtHJrVe4wx9aO5GvFLFZ/mQ35C5mXPQ7nIb0CDdLBJdz82nUoLSA5BUbeXAUkfa +hW/hLxLdhks68/TK694xVIuiB40pvMmJwxIyDA==urn:oasis:names:tc:SAML:2.0:nameid-format:transientArubaPEC S.p.A.ArubaPEC S.p.A.https://www.pec.it/ \ No newline at end of file diff --git a/src/test/resources/saml_request.xml b/src/test/resources/saml_request.xml new file mode 100644 index 0000000..6792bfe --- /dev/null +++ b/src/test/resources/saml_request.xml @@ -0,0 +1,28 @@ + + + https://im.cdc.it + + + + + + + + + + + 1cu/vRFJt+NQPYsKZts/BxiGlLEHW3h62/X31TIdnAY= + + + PV0p/g8fnTjBrY3nhVZcpY3szJSmn5kgvt1WatYQye/aq+JMsuj6zhoSt5s5qgivZyeOYdbQUW7UlQyLf9N9e602fJJYEoDqkaffMwDi84a+yklaUxBsa4To79oiD+0bz8LNiS+haePU38H9QniSFXXYsnhFjJe0PhuylK4JCPbrmQUGU1/IHDKthKxfYZZaSObJKbhttqEHtYvrgGMESUldlDTsjzrLU/S9MOLcqTEEpO0FwfWacsVZ3K9dxbec59RvUwqWCCfyH6NzrezfXTpmDODHnD9iH//e25hUCa8EJWgep81MS67BJ8bub2J9eN+1mrwTchBPAQNRSmdLJL5gRYEgEsYzsE4RPAqekdSwzNkepyu2x47YR34WmGe9+TaUsw+hj94cLwV3xe9v+St1Q0bfzI44MbLq3o80dp6UM0jT0qY1wUWnPylync4ZidUYhOOYqhLJkEkefizsTYKgLiizcl4Q0a7XXUmpLPwkIsCZhcC1AfRLtaN5UO9Q + + + MIIGsDCCBRigAwIBAgIUMcVsHz4mB3z88NgQBaY/R5+/2GswDQYJKoZIhvcNAQELBQAwgbgxVzBVBgNVBAoMTkNhc3NhIE5hemlvbmFsZSBkaSBQcmV2aWRlbnphIGUgQXNzaXN0ZW56YSBhIGZhdm9yZSBEZWkgRG90dG9yaSBDb21tZXJjaWFsaXN0aTEPMA0GA1UEAwwGQ05QQURDMRowGAYDVQRTDBFodHRwczovL2ltLmNkYy5pdDEUMBIGA1UEYQwLUEE6SVQtY25wYWYxCzAJBgNVBAYTAklUMQ0wCwYDVQQHDARSb21hMB4XDTI0MDIyMjE1MjAxMVoXDTM0MDIxOTE1MjAxMVowgbgxVzBVBgNVBAoMTkNhc3NhIE5hemlvbmFsZSBkaSBQcmV2aWRlbnphIGUgQXNzaXN0ZW56YSBhIGZhdm9yZSBEZWkgRG90dG9yaSBDb21tZXJjaWFsaXN0aTEPMA0GA1UEAwwGQ05QQURDMRowGAYDVQRTDBFodHRwczovL2ltLmNkYy5pdDEUMBIGA1UEYQwLUEE6SVQtY25wYWYxCzAJBgNVBAYTAklUMQ0wCwYDVQQHDARSb21hMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA3IjlWR6i0Kruz+EBqBiHHqwg3GwmE3qI6VLETbbQZlrMyi8TwjAbPw8EkRK7r5FGuVvJOX0zcfhTJt1N2brRvPPyej+TlxFipNnSIv+NqecvwoD0StMMAHzb7t0shDfxDHvLoIWhFXBI6eO+TCBj59Rm/ZzM0HHDkjmhU41npz+TrapOInbKrCvlnPa0yqxmsnF6AHGXqysaVU7AGO6MfpJPRT5kTOZF6YngOGMVco8mEz1lbeIIjVJcT61IGxIKA3rd3X4QVV/SlrYJJZCNFz8inO7nJ0FWEeRnYSznDfwUwQOPeOJfdQO+CCqTrOYFNRkBBked3YjedECXnE83a+D19Mi2JYeviUEZ3EYrV3XMa/zlbOMM/7kKpNVXO8NAO1kRa8JsNEjgXMAB7sSSAYBoarF71Tb5LakduZZyGoNGmCCgbgwjMJ8J2GxkwhFbi1O042HcPDiMJ0a/jmCO0gKcu2Q5gdwG///CO6vzF0KYPXplxAsUApSclGrHqs67AgMBAAGjggGuMIIBqjAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIGwDBzBgNVHSAEbDBqMB8GAytMEDAYMBYGCCsGAQUFBwICMAoMCEFnSURyb290MCAGBCtMEAYwGDAWBggrBgEFBQcCAjAKDAhhZ0lEY2VydDAlBgYrTBAEAgEwGzAZBggrBgEFBQcCAjANDAtjZXJ0X1NQX1B1YjAdBgNVHQ4EFgQUGcqOgtTU1qDpi/y0rglQ4hsvMSwwgfgGA1UdIwSB8DCB7YAUGcqOgtTU1qDpi/y0rglQ4hsvMSyhgb6kgbswgbgxVzBVBgNVBAoMTkNhc3NhIE5hemlvbmFsZSBkaSBQcmV2aWRlbnphIGUgQXNzaXN0ZW56YSBhIGZhdm9yZSBEZWkgRG90dG9yaSBDb21tZXJjaWFsaXN0aTEPMA0GA1UEAwwGQ05QQURDMRowGAYDVQRTDBFodHRwczovL2ltLmNkYy5pdDEUMBIGA1UEYQwLUEE6SVQtY25wYWYxCzAJBgNVBAYTAklUMQ0wCwYDVQQHDARSb21hghQxxWwfPiYHfPzw2BAFpj9Hn7/YazANBgkqhkiG9w0BAQsFAAOCAYEAON+zZCsuz2MoWOt1H2+os4LCG84NCe/EiBWuuIlvGmXxa7QzrgE/+LsbJREQN5s3nQYL/XWFGN4zPEsVoZwvay84FLQn0SfnGEs50KXDHSlLdt9MRIalSuuiiZvDOhv96DN2sdICbE0nKbee0Hs52hPrl3oQxnmFQpmPDLD/G4+FdRppD2Ogzb3Z2wAn27Q0QPGdhEv6O8D2V9SRJ8keFcgG/5KGBy3WBjG0XZSrwIly7JdgajeLHzUN++0ntyxJL8ouX7Bz2+pN7EbnzwcG4lgQqT4nLG6dEX1VB8EBPdKIotNiWI1lheG323Q176EYLoXX9Z1+CQB7U0WS+ye61qtzV+f7XLlKwAByjAjEan1lMQbLn+yuili5zR8v5pozHAXz74aFy+/97VDxIT5K/aNxYlaYb/QPhl8qWy+q95CpDI3sSCnH/2aAq/xt6z/XkJ5E+B9ZaumHPJJ6RWRjPxgtwu/SYXn8R5BC1iZDT7Lh9mxbFa8AkoH/WRXMtNcN + + + + + + https://www.spid.gov.it/SpidL2 + + diff --git a/src/test/resources/saml_response.xml b/src/test/resources/saml_response.xml new file mode 100644 index 0000000..aff9333 --- /dev/null +++ b/src/test/resources/saml_response.xml @@ -0,0 +1,158 @@ + + + + https://id.lepida.it/idp/shibboleth + + + + + + + + + + + AASjTV+jtRdNe4BnvLQPqqLw3CTIvCUy7//vJ6isKWg= + + + +caKgJnBEjes2P53E/+z5XSZ2Aa3S3XxSqZ71/t4lKtvkklS2e7kVkUmS6TYjt3bO7pqTY7nHuU2O +3IcfL8P0srE0vyz8UunkmLalhHO5a8z989PNYjxVe8Y9e4NDTeywsIWfjyVCiCCF/S8mQNx45IPd +9jZJGzrQ08vGAwMLkow+orwWarNbCJvEvwr6PIHGpq+4QSEtJ6ZGg82OgGP8Uc2JLAiuwYAfJ+3d +/mKmVxl8p0+P4tdFYD5cUkO2JydDO4CH43jaPLwJ3+w4Im1K8gWToAmAmskqm9oXOT/YJx7u+Bn1 +gdZAMiTpAZAmTl2ZarT1qSFjl8IqXEpJu46NLg== + + + + MIIDHDCCAgSgAwIBAgIVALisbudTRxLy3vlMcEDfaqr3iW89MA0GCSqGSIb3DQEBCwUAMBcxFTAT +BgNVBAMMDGlkLmxlcGlkYS5pdDAeFw0xODA4MDgxMDIzMTJaFw0zODA4MDgxMDIzMTJaMBcxFTAT +BgNVBAMMDGlkLmxlcGlkYS5pdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMOFERgx +PEYPqAjN7oW6y8oSSY6tGm2OCIU+VyKhb2OqfNLpF8tPrytX17pgwVYHzjxRCNMTC83frbmtBapA +Btm9KuX7qaSPvaJx0+UqYk9FdKCKQOEkmWcNOJfwzNMP65B+cDxP3sa1JoAMeAO0x95bnYoX0ZHc +ssKkwpgMb8/JHZHzqu3odxADtO5PaT3xaCyMIcqIp1O2nVn7SizUE1gNucLAdaP4kh0o7nU61pz4 +pG3gQXK+uROteDD8cTU2Nxi7W1T73tQSuwst54BS2p9IBXzWrA9V0Ck10oiQTcIC8X9McepCrNzg +COBdap00Tifusb30t74BREARgwjp1N8CAwEAAaNfMF0wHQYDVR0OBBYEFL32/n7uf1Re14pW+gwG +xZQHUZBCMDwGA1UdEQQ1MDOCDGlkLmxlcGlkYS5pdIYjaHR0cHM6Ly9pZC5sZXBpZGEuaXQvaWRw +L3NoaWJib2xldGgwDQYJKoZIhvcNAQELBQADggEBAK80B1mEWKOTJkVJOJot2xU79Lhs1+domUSY +QiA+tlS46IAfWwDZqI1llIjgL85n7qMsKFvYTIskInoG51Iezv2dTxlB6IMI8NPRfiFXo2s8NYjb +zWyETbdXzCbDR0tKNke0TFE0oxunNfE5YRsmH4bPnjhPUjCSHX7wIhlNrLae3FjMQp1OLDs7HmJo +3AhuAVmHCoG7QV/ly4ZHcVYx4F7HUsFg5uxNYjZbo+XMutJz4nZFOFE+uRzTwwfdR2sxny+ppkru +TwIhEXyzknoiw1mGIEWZc6scnOAiwZeqTccUYVNHp+PSFs9SD8l+2PO4Oh8Y3dYT+5ojv+S6T7vy +5xE= + + + + + + + + https://id.lepida.it/idp/shibboleth + + + + + + + + + + + XQ0Zmxtg2d20cxljCJP+uJeZqyUZZQCE6mwlkFngxNY= + + + +sqfS8NKXnH8n8/YsbLeZziVx7jhKrveQ2OONCCzO+Cw4M0BRcIynPBXjpe/KoLdmxKS1aPn65+zs +C1NJEFsNki6uWSzuo7OR26CyqbJXkTC3LkO8ISEZ/0V+/26V8eEXV2i6vVJRubERjwlZgi3vPDQ1 +SoB7CxryA9rdTFi4qNescazeQOosW+sZVKiTWJsWVt/woK20Oej+88NM8J5NahSaARpN0/wtXigT +P/J2dpJ4VWHgeviRTbieVmdY0Uxxq90sWhnUqGw/Br03XT/YrTfDKRlidmSX5vGBjCAfhcVYj8iL +rh5HFb0FkOMh+MIGNDec1GXkbIoFgdOBLr6f6A== + + + + MIIDHDCCAgSgAwIBAgIVALisbudTRxLy3vlMcEDfaqr3iW89MA0GCSqGSIb3DQEBCwUAMBcxFTAT +BgNVBAMMDGlkLmxlcGlkYS5pdDAeFw0xODA4MDgxMDIzMTJaFw0zODA4MDgxMDIzMTJaMBcxFTAT +BgNVBAMMDGlkLmxlcGlkYS5pdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMOFERgx +PEYPqAjN7oW6y8oSSY6tGm2OCIU+VyKhb2OqfNLpF8tPrytX17pgwVYHzjxRCNMTC83frbmtBapA +Btm9KuX7qaSPvaJx0+UqYk9FdKCKQOEkmWcNOJfwzNMP65B+cDxP3sa1JoAMeAO0x95bnYoX0ZHc +ssKkwpgMb8/JHZHzqu3odxADtO5PaT3xaCyMIcqIp1O2nVn7SizUE1gNucLAdaP4kh0o7nU61pz4 +pG3gQXK+uROteDD8cTU2Nxi7W1T73tQSuwst54BS2p9IBXzWrA9V0Ck10oiQTcIC8X9McepCrNzg +COBdap00Tifusb30t74BREARgwjp1N8CAwEAAaNfMF0wHQYDVR0OBBYEFL32/n7uf1Re14pW+gwG +xZQHUZBCMDwGA1UdEQQ1MDOCDGlkLmxlcGlkYS5pdIYjaHR0cHM6Ly9pZC5sZXBpZGEuaXQvaWRw +L3NoaWJib2xldGgwDQYJKoZIhvcNAQELBQADggEBAK80B1mEWKOTJkVJOJot2xU79Lhs1+domUSY +QiA+tlS46IAfWwDZqI1llIjgL85n7qMsKFvYTIskInoG51Iezv2dTxlB6IMI8NPRfiFXo2s8NYjb +zWyETbdXzCbDR0tKNke0TFE0oxunNfE5YRsmH4bPnjhPUjCSHX7wIhlNrLae3FjMQp1OLDs7HmJo +3AhuAVmHCoG7QV/ly4ZHcVYx4F7HUsFg5uxNYjZbo+XMutJz4nZFOFE+uRzTwwfdR2sxny+ppkru +TwIhEXyzknoiw1mGIEWZc6scnOAiwZeqTccUYVNHp+PSFs9SD8l+2PO4Oh8Y3dYT+5ojv+S6T7vy +5xE= + + + + + AAdzZWNyZXQxFTZ4qumJz3d1mwNma3bvoo613YNJtmCxDLBXa2uv9zg82pL1BVVxItAfIViiRlsFrPSboRRtg/5rTfLSTeF8/Ud8D/uJ2P2RJzz+70C76YuRro5ZQXuCLSfWIpjjyYErhbvlBIJrC4g= + + + + + + + https://spid.agid.gov.it + + + + + + https://www.spid.gov.it/SpidL2 + + + + + + + + H501 + + + M + + + cartaIdentita AJ2356255 ComunediRoma(RM) 2017-04-15 2027-12-06 + + + + + + LEPI0000938721 + + + 1975-16-14 + + + 345678987 + + + + + + ROSSI + + + MARIO + + + RM + + + + + + TINIT-RSSMRA75H14H501X + + + mario@rossi.com + + + 2031-10-31 + + + + diff --git a/theme/keycloak-spid-only/admin/admin.ftl b/theme/keycloak-spid-only/admin/admin.ftl new file mode 100644 index 0000000..4f8e55d --- /dev/null +++ b/theme/keycloak-spid-only/admin/admin.ftl @@ -0,0 +1,751 @@ +<#import "template.ftl" as layout> +
+ + + + +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+ {{:: 'redirect-uri.tooltip' | translate}} +
+
+
+
+ +
+ +
+ {{:: 'identity-provider.alias.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.display-name.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.enabled.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.store-tokens.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.stored-tokens-readable.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'trust-email.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'link-only.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'hide-on-login-page.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'gui-order.tooltip' | translate}} +
+
+ +
+
+ +
+
+ {{:: 'first-broker-login-flow.tooltip' | translate}} +
+
+ +
+
+ +
+
+ {{:: 'post-broker-login-flow.tooltip' | translate}} +
+
+ +
+
+ +
+
+ {{:: 'sync-mode.tooltip' | translate}} +
+
+ + + {{:: 'identity-provider.saml.protocol-endpoints.saml.tooltip' | translate}} +
+
+
+ {{:: 'saml-config' | translate}} {{:: 'identity-provider.saml-config.tooltip' | translate}} + +
+ +
+ +
+ {{:: 'identity-provider.saml.entity-id.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'saml.single-signon-service-url.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'saml.single-logout-service-url.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'backchannel-logout.tooltip' | translate}} +
+
+ +
+ + +
+ {{:: 'nameid-policy-format.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'saml.principal-type.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'saml.principal-attribute.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'saml.allow-create.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'http-post-binding-response.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'http-post-binding-for-authn-request.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'http-post-binding-logout.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'want-authn-requests-signed.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'want-assertions-signed.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'want-assertions-encrypted.tooltip' | translate}} +
+
+ +
+
+ +
+
+ {{:: 'signature-algorithm.tooltip' | translate}} +
+
+ +
+
+ +
+
+ {{:: 'saml-signature-keyName-transformer.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.force-authentication.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'saml.validate-signature.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'validating-x509-certificate.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.saml.sign-sp-metadata.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'saml.loginHint.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.allowed-clock-skew.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.saml.attribute-consuming-service-index.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.saml.attribute-consuming-service-name.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.organization-names.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.organization-display-names.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.organization-urls.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.is-sp-private.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.ipaCode.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.vatNumber.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.fiscalCode.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.contactCompany.other.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.contactPhone.other.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.contactEmail.other.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.contactCompany.billing.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.contactPhone.billing.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'identity-provider.spid.contactEmail.billing.tooltip' | translate}} +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente' | translate}} + {{:: 'identity-provider.spid.cessionarioCommittente.tooltip' | translate}} + +
+ {{:: 'identity-provider.spid.cessionarioCommittente.datiAnagrafici' | + translate}} + {{::'identity-provider.spid.cessionarioCommittente.datiAnagrafici.tooltip' | + translate}} + +
+ {{:: + 'identity-provider.spid.cessionarioCommittente.datiAnagrafici.idFiscaleIva' | translate}} + {{::'identity-provider.spid.cessionarioCommittente.datiAnagrafici.idFiscaleIva.tooltip' | + translate}} + +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.idPaese.billing.tooltip' | + translate}} +
+ +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.idCodice.billing.tooltip' | + translate}} +
+
+
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.codiceFiscale.billing.tooltip' | + translate}} +
+
+ {{:: 'identity-provider.spid.cessionarioCommittente.anagrafica' | + translate}} + {{::'identity-provider.spid.cessionarioCommittente.anagrafica.tooltip' | + translate}} + +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.anagraficaDenominazione.billing.tooltip' + | + translate}} +
+ +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.anagraficaNome.billing.tooltip' | + translate}} +
+ +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.anagraficaCognome.billing.tooltip' | + translate}} +
+ +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.anagraficaTitolo.billing.tooltip' | + translate}} +
+ +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.anagraficaCodiceEORI.billing.tooltip' | + translate}} +
+
+
+
+ {{:: 'identity-provider.spid.cessionarioCommittente.sede' | + translate}} + {{::'identity-provider.spid.cessionarioCommittente.sede.tooltip' | translate}} + +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.sedeIndirizzo.billing.tooltip' | + translate}} +
+ +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.sedeNumeroCivico.billing.tooltip' | + translate}} +
+ +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.sedeCap.billing.tooltip' | + translate}} +
+ +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.sedeComune.billing.tooltip' | + translate}} +
+ +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.sedeProvincia.billing.tooltip' | + translate}} +
+ +
+ +
+ +
+ {{:: 'identity-provider.spid.cessionarioCommittente.sedeNazione.billing.tooltip' | + translate}} +
+
+ +
+ {{:: 'identity-provider.spid.terzoIntermediario.soggettoEmittente' | + translate}} {{:: + 'identity-provider.spid.terzoIntermediario.soggettoEmittente.tooltip' | translate}} + +
+ +
+ +
+ {{:: 'identity-provider.spid.terzoIntermediario.soggettoEmittente.billing.tooltip' | + translate}} +
+
+
+
+
+ {{:: 'identity-provider.saml.requested-authncontext' | translate}} {{:: 'identity-provider.saml.requested-authncontext.tooltip' | translate}} + +
+ +
+
+ +
+
+ {{:: 'identity-provider.saml.authncontext-comparison-type.tooltip' | translate}} +
+
+ +
+
+ +
+
+ {{:: 'identity-provider.saml.authncontext-class-ref.tooltip' | translate}} + + +
+
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ {{:: 'identity-provider.saml.authncontext-decl-ref.tooltip' | translate}} +
+
+
+ {{:: 'import-external-idp-config' | translate}} {{:: 'import-external-idp-config.tooltip' | translate}} +
+ +
+ +
+ {{:: 'saml.import-from-url.tooltip' | translate}} +
+
+ +
+ +
+
+
+ +
+
+ + +
+ + {{files[0].name}} + +
+
+ +
+ +
+
+
+
+ +
+
+ + +
+
+
+
+ + \ No newline at end of file diff --git a/theme/keycloak-spid-only/admin/theme.properties b/theme/keycloak-spid-only/admin/theme.properties new file mode 100755 index 0000000..a5e2321 --- /dev/null +++ b/theme/keycloak-spid-only/admin/theme.properties @@ -0,0 +1,3 @@ +parent=base +import=common/keycloak +locales=en,it diff --git a/theme/keycloak-spid-only/login/login.ftl b/theme/keycloak-spid-only/login/login.ftl index 8c7c121..f3bc3ea 100644 --- a/theme/keycloak-spid-only/login/login.ftl +++ b/theme/keycloak-spid-only/login/login.ftl @@ -6,23 +6,19 @@
<#if social.providers??> -
Accesso tramite SPID
+
${msg("login.spid.access")}
- SPID, il Sistema Pubblico di Identità Digitale, è il sistema di accesso che consente di utilizzare, con un'identità digitale unica, i servizi online della Pubblica Amministrazione e dei privati accreditati. -
- Se sei già in possesso di un'identità digitale, accedi con le credenziali del tuo gestore. -
- Se non hai ancora un'identità digitale, richiedila ad uno dei gestori. + ${msg("login.spid.info")?no_esc}
@@ -97,21 +99,22 @@ "${p.alias}": "${p.loginUrl?no_esc}", } - + // social provider is the key, dom id is the value window.spidprovidermatch = { "spidtestidp": "spidtestidp", - "Aruba ID": "arubaid", - "Etna ID": "etnaid", - "Infocert ID": "infocertid", - "Intesa ID": "intesaid", - "Inforcamere ID": "infocamereid", - "Lepida ID": "lepidaid", - "Namirial ID": "namirialid", - "Poste ID": "posteid", - "Sielte ID": "sielteid", - "SPIDItalia Register.it": "spiditaliaid", - "TeamSystem ID": "teamsystemid", - "Tim ID": "timid" + "privatespid": "privatespid", + "arubaid": "arubaid", + "etnaid": "etnaid", + "infocertid": "infocertid", + "infocamereid": "infocamereid", + "lepidaid": "lepidaid", + "namirialid": "namirialid", + "posteid": "posteid", + "sielteid": "sielteid", + "spiditaliaid": "spiditaliaid", + "teamsystemid": "teamsystemid", + "intesigroupid": "intesigroupid", + "timid": "timid" } $(document).ready(function(){ for (const [key, value] of Object.entries(spidprovidermatch)) { @@ -126,7 +129,7 @@ - +
diff --git a/theme/keycloak-spid-only/login/messages/messages_en.properties b/theme/keycloak-spid-only/login/messages/messages_en.properties index e69de29..4218305 100755 --- a/theme/keycloak-spid-only/login/messages/messages_en.properties +++ b/theme/keycloak-spid-only/login/messages/messages_en.properties @@ -0,0 +1,7 @@ +login.spid.info=SPID, Public Digital Identity System is the simple, fast and secure access key to digital services of local and central administrations.
If you already have a digital identity, log in with your service provider's credentials.
If you don't have a digital identity contact one of the accredited identity provider. +login.spid.more-info=More info +login.spid.dont-have=You don't have SPID? +login.spid.need-help=Need help? +login.spid.enter=Log in with SPID +login.spid.access=Log in using SPID +login.error-message=Unexpected error when authenticating with identity provider \ No newline at end of file diff --git a/theme/keycloak-spid-only/login/messages/messages_it.properties b/theme/keycloak-spid-only/login/messages/messages_it.properties index e69de29..8fbb1ea 100755 --- a/theme/keycloak-spid-only/login/messages/messages_it.properties +++ b/theme/keycloak-spid-only/login/messages/messages_it.properties @@ -0,0 +1,7 @@ +login.spid.info=SPID, il Sistema Pubblico di Identità Digitale, è il sistema di accesso che consente di utilizzare, con un'identità digitale unica, i servizi online della Pubblica Amministrazione e dei privati accreditati.
Se sei già in possesso di un'identità digitale, accedi con le credenziali del tuo gestore.
Se non hai ancora un'identità digitale, richiedila ad uno dei gestori. +login.spid.more-info=Maggiori informazioni +login.spid.dont-have=Non hai SPID? +login.spid.need-help=Serve aiuto? +login.spid.enter=Entra con SPID +login.spid.access=Accesso tramite SPID +login.error-message=Autenticazione fallita per ripetuta sottomissione di credenziali errate. \ No newline at end of file diff --git a/theme/keycloak-spid-only/login/resources/css/tile.css b/theme/keycloak-spid-only/login/resources/css/tile.css new file mode 100644 index 0000000..1b2e911 --- /dev/null +++ b/theme/keycloak-spid-only/login/resources/css/tile.css @@ -0,0 +1 @@ +/* Add custom css */ \ No newline at end of file diff --git a/theme/keycloak-spid-only/login/resources/img/spid-idp-infocamereid.png b/theme/keycloak-spid-only/login/resources/img/spid-idp-infocamereid.png new file mode 100644 index 0000000..536de71 Binary files /dev/null and b/theme/keycloak-spid-only/login/resources/img/spid-idp-infocamereid.png differ diff --git a/theme/keycloak-spid-only/login/resources/img/spid-idp-infocamereid.svg b/theme/keycloak-spid-only/login/resources/img/spid-idp-infocamereid.svg new file mode 100644 index 0000000..63e6a67 --- /dev/null +++ b/theme/keycloak-spid-only/login/resources/img/spid-idp-infocamereid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/theme/keycloak-spid-only/login/resources/img/spid-idp-infocertid.png b/theme/keycloak-spid-only/login/resources/img/spid-idp-infocertid.png index 06e634d..d43a7e2 100644 Binary files a/theme/keycloak-spid-only/login/resources/img/spid-idp-infocertid.png and b/theme/keycloak-spid-only/login/resources/img/spid-idp-infocertid.png differ diff --git a/theme/keycloak-spid-only/login/resources/img/spid-idp-infocertid.svg b/theme/keycloak-spid-only/login/resources/img/spid-idp-infocertid.svg index ad963a6..908e42c 100755 --- a/theme/keycloak-spid-only/login/resources/img/spid-idp-infocertid.svg +++ b/theme/keycloak-spid-only/login/resources/img/spid-idp-infocertid.svg @@ -1 +1,16 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + diff --git a/theme/keycloak-spid-only/login/resources/img/spid-idp-intesigroup.png b/theme/keycloak-spid-only/login/resources/img/spid-idp-intesigroup.png new file mode 100644 index 0000000..703504c Binary files /dev/null and b/theme/keycloak-spid-only/login/resources/img/spid-idp-intesigroup.png differ diff --git a/theme/keycloak-spid-only/login/resources/img/spid-idp-intesigroup.svg b/theme/keycloak-spid-only/login/resources/img/spid-idp-intesigroup.svg new file mode 100644 index 0000000..281e8de --- /dev/null +++ b/theme/keycloak-spid-only/login/resources/img/spid-idp-intesigroup.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/theme/keycloak-spid-only/login/resources/img/spid-idp-sielteid.png b/theme/keycloak-spid-only/login/resources/img/spid-idp-sielteid.png index 3b4abd9..6dd48f9 100644 Binary files a/theme/keycloak-spid-only/login/resources/img/spid-idp-sielteid.png and b/theme/keycloak-spid-only/login/resources/img/spid-idp-sielteid.png differ diff --git a/theme/keycloak-spid-only/login/resources/img/spid-idp-sielteid.svg b/theme/keycloak-spid-only/login/resources/img/spid-idp-sielteid.svg index 5213ddc..8615bfc 100644 --- a/theme/keycloak-spid-only/login/resources/img/spid-idp-sielteid.svg +++ b/theme/keycloak-spid-only/login/resources/img/spid-idp-sielteid.svg @@ -1,14 +1,76 @@ - - - - - - - - - - \ No newline at end of file + + + + + + + + + + diff --git a/theme/keycloak-spid-only/login/template.ftl b/theme/keycloak-spid-only/login/template.ftl index f92a4f2..e9a15cf 100644 --- a/theme/keycloak-spid-only/login/template.ftl +++ b/theme/keycloak-spid-only/login/template.ftl @@ -1,5 +1,5 @@ <#macro registrationLayout bodyClass="" displayInfo=false displayMessage=true displayRequiredFields=false showAnotherWayIfPresent=true> - +