Configuration avancée de attribute-resolver.xml

Documentation :

Le fichier de configuration attribute-resolver.xml définit les sources de données pour les attributs utilisateurs ainsi que les attributs qui vont en être extraits. Chaque attribut fait référence à une (ou plusieurs) source de données dont il dépend ; plusieurs types de sources de données sont disponibles.

Les sources de données :

  • Statique (type Static) : permet de définir un ensemble d'attributs ayant une valeur fixe. Ce type de source est utile lorsque tous les utilisateurs d'un organisme ont la même valeur pour un attribut. Exemple d'usage : identifiant de l'organisme, entitlement pour l'accès à une ressource.
  • Base de données relationnelle (type RelationalDatabase) : permet d'interroger une base de données SQL.
  • Annuaire LDAP (type LDAPDirectory) : interrogation d'un annuaire LDAP
  • Script (type Script) : construction d'attributs à partir de scripts JavaScript ;
  • Identifiant (type ComputedId ou StoredId) : permet de calculer/charger un identifiant opaque. Exemple d'utilisation : calcul de l'attribut targetedId/persistentId.

Par ailleurs, il existe différents types d'attributs qui permettent de calculer un attribut à partir de plusieurs sources ou de transformer un attribut avant de le transmettre en SAML.

Des exemples de types d'attributs :

  • Attribut simple (type Simple ) : le type standard d'attribut issu d'une source ou d'un autre attribut, sans transformation de la valeur de l'attribut.
  • Attribut qualifié (type Scoped ) : permet de définir un attribut + domaine. Exemple : eduPersonScopedAffiliation (student@univ-test.fr).
  • Attribut basé sur une expression régulière (type RegexSplit) : permet d'extraire une partie de la valeur d'un attribut, à partir d'une expression régulière.
  • Attribut avec table de correspondance (type Mapped ) : on transforme les valeurs issues d'un ou plusieurs sources.
  • Attribut basé sur le template Velocity (type Template ) : Velocity est un moteur de template ; vous pouvez utiliser ce format de template pour construire un nouvel attribut (http://velocity.apache.org/).
  • Attribut basé sur un script (type Script) : permet de construire à partir d'un script ECMAScript.

Nous vous invitons à expérimenter ces possibilités de configuration en adaptant votre fichier attribute-resolver.xml .

Documentation :

Différence avec la version 2 : notre configuration permet d'utiliser le persistentID à la fois comme format de NameID (alternatif au transcientID) et de générer un attribut eduPersonTargetedID via le connecteur StoreIdConnector. Voir cette documentation
PersistentID et version <3.2.0 : si vous utilisez une version <3.2.0 de l'IdP Shibboleth, vous êtes concernés par le bug IDP-829 et il est fortement recommandé d'installer une version plus récente de l'IdP Shibboleth.

Le persistentID est un format particulier d'identifiant de session. Il est composé d'un triplet concaténé (Identifiant du SP + identifiant de l'IdP + identifiant de l'utilisateur en hash). C'est donc un identifiant opaque et persistant dans le temps. Il est unique par utilisateur et par ressource. Cet attribut est également appelé le persistentID en SAML2.

L'IdP Shibboleth vous propose deux alternatives pour gérer les persistentID :

  • génération dynamique du persistentID : cette approche est simple à mettre en oeuvre. Elle présente cependant des limitations : il n'est pas possible de retrouver un utilisateur connaissant son persistentID, il n'est pas possible de révoquer ou modifier la valeur du persistentID d'un utilisateur ;
  • génération du persistentID puis stockage en base de données : cette approche est plus lourde à mettre en oeuvre puisqu'elle requiert une base de données.

Dans le chapitre suivant nous allons mettre en oeuvre des persistentID stockés en base de données.

Nous installons le serveur MariaDB (version open source de MySQL distribué avec CentOS 7) et créons dans la foulée la base de données :

$> sudo yum install mariadb-server

Une fois le serveur installé, on le démarre et on le configure :

$> sudo systemctl start mariadb
$> mysql -u root
mysql> SET NAMES 'utf8';
SET CHARACTER SET utf8;
CHARSET utf8;
CREATE DATABASE IF NOT EXISTS shibboleth CHARACTER SET=utf8;
USE shibboleth;

mysql> connect shibboleth
mysql> CREATE TABLE IF NOT EXISTS shibpid (
    localEntity VARCHAR(255) NOT NULL,
    peerEntity VARCHAR(255) NOT NULL,
    persistentId VARCHAR(255) NOT NULL,
    principalName VARCHAR(255) NOT NULL,
    localId VARCHAR(255) NOT NULL,
    peerProvidedId VARCHAR(255) NULL,
    creationDate TIMESTAMP NOT NULL,
    deactivationDate TIMESTAMP NULL,
    PRIMARY KEY (localEntity, peerEntity, persistentId)
) DEFAULT CHARSET=utf8;

On vérifie que la base est bien constituée :

mysql>  DESCRIBE shibpid;

+------------------+--------------+------+-----+-------------------+-----------------------------+
| Field            | Type         | Null | Key | Default           | Extra                       |
+------------------+--------------+------+-----+-------------------+-----------------------------+
| localEntity      | varchar(255) | NO   | PRI | NULL              |                             |
| peerEntity       | varchar(255) | NO   | PRI | NULL              |                             |
| persistentId     | varchar(255) | NO   | PRI | NULL              |                             |
| principalName    | varchar(255) | NO   |     | NULL              |                             |
| localId          | varchar(255) | NO   |     | NULL              |                             |
| peerProvidedId   | varchar(255) | YES  |     | NULL              |                             |
| creationDate     | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| deactivationDate | timestamp    | YES  |     | NULL              |                             |
+------------------+--------------+------+-----+-------------------+-----------------------------++
8 rows in set (0.00 sec)

Nous allons à présent créer l'utilisateur privilégié pour la connexion à cette table :

mysql> USE mysql;
  Reading table information for completion of table and column names
  You can turn off this feature to get a quicker startup with -A
  Database changed

mysql> INSERT INTO user (Host,User,Password,Select_priv,
Insert_priv,Update_priv,Delete_priv,Create_tmp_table_priv,
Lock_tables_priv,Execute_priv) VALUES
('localhost','shibboleth',PASSWORD('demo'),
'Y','Y','Y','Y','Y','Y','Y');
Query OK, 1 row affected, 3 warnings (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> GRANT ALL ON shibboleth.* TO 'shibboleth'@'localhost'IDENTIFIED BY 'demo';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)
mysql> exit

Vérifiez que l'utilisateur « shibboleth » a bien été créé en tentant une connexion (mot de passe 'demo') :

$> mysql -u shibboleth -p
Enter password: **demo**
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 4
Server version: 5.5.65-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>

Le fichier saml-nameid.properties contrôle le format des NameID ; vous devez l'éditer :

/opt/shibboleth-idp/conf/saml-nameid.properties
...
# For computed IDs, set a source attribute and a secret salt:
**idp.persistentId.sourceAttribute = eduPersonPrincipalName**
#idp.persistentId.useUnfilteredAttributes = true
# Do *NOT* share the salt with other people, it's like divulging your private key.
#idp.persistentId.algorithm = SHA
**idp.persistentId.salt = XXXXXXXXXX**
# BASE64 will match V2 values, we recommend BASE32 encoding for new installs.
idp.persistentId.encoding = BASE32

# To use a database, use shibboleth.StoredPersistentIdGenerator
**idp.persistentId.generator = shibboleth.StoredPersistentIdGenerator**
# For basic use, set this to a JDBC DataSource bean name:
**idp.persistentId.dataSource = MyDataSource**
# For advanced use, set to a bean inherited from shibboleth.JDBCPersistentIdStore
#idp.persistentId.store = MyPersistentIdStore
# Set to an empty property to skip hash-based generation of first stored ID
**idp.persistentId.computed = shibboleth.ComputedPersistentIdGenerator**
...
Longueur du salt : l'IdP impose que la valeur de idp.persistentId.salt comprenne au moins 16 caractères.

Vous devez également ajouter un certain nombre d'objets pour produire cet attribut:

  • une source de données (DataSource)
  • un connecteur (DataConnector) basé sur cette source
  • une définition d'attribut (AttributeDefinition) basée sur ce connecteur

Nous allons définir la source de données au sein du fichier /opt/shibboleth-idp/conf/global.xml:

/opt/shibboleth-idp/conf/global.xml
<!-- source de données MariaDB: accès bas niveau à la base -->
<bean id="myDataSource"
    class="org.apache.tomcat.dbcp.dbcp2.BasicDataSource"
    p:driverClassName="org.mariadb.jdbc.Driver"
    p:url="jdbc:mariadb://localhost:3306/shibboleth"
    p:username="shibboleth"
    p:password="demo"
    p:validationQuery="select 1"
    p:validationQueryTimeout="0" />

Et les autres objets au sein du fichier /opt/shibboleth-idp/conf/attribute-resolver-ldap.xml:

/opt/shibboleth-idp/conf/attribute-resolver-ldap.xml
<DataConnector id="myStoredId"
    xsi:type="StoredId"
    generatedAttributeID="persistentID"
    salt="%{idp.persistentId.salt}"
    queryTimeout="0">
  <InputAttributeDefinition ref="%{idp.persistentId.sourceAttribute}" />
  <BeanManagedConnection>myDataSource</BeanManagedConnection>
</DataConnector>
 
<!-- définition de l'attribut eduPersonTargetedID -->
<AttributeDefinition id="eduPersonTargetedID" xsi:type="SAML2NameID"
    nameIdFormat="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
  <InputDataConnector ref="myStoredId" attributeNames="persistentID"/>
  <DisplayName xml:lang="en">Targeted ID</DisplayName>
  <DisplayDescription xml:lang="en">Targeted ID: A unique identifier for a person, different for each service provider.</DisplayDescription>
  <AttributeEncoder xsi:type="SAML1XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" />
  <AttributeEncoder xsi:type="SAML2XMLObject" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.10" friendlyName="eduPersonTargetedID" />
</AttributeDefinition>

Adaptez également votre filtre d'attributs pour autoriser la diffusion de l'attribut eduPersonTargetedID aux SPs (à tous les SPs dans l'exemple ci-dessous) :

/opt/shibboleth-idp/conf/attribute-filter.xml
<AttributeFilterPolicyGroup id="ShibbolethFilterPolicy"
    xmlns="urn:mace:shibboleth:2.0:afp"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:mace:shibboleth:2.0:afp http://shibboleth.net/schema/idp/shibboleth-afp.xsd">
 
    <!-- Release an additional attribute to an SP. -->
    <AttributeFilterPolicy id="example1">
        <PolicyRequirementRule xsi:type="ANY" />
        <AttributeRule attributeID="uid" permitAny="true" />
        <AttributeRule attributeID="mail" permitAny="true" />
        <AttributeRule attributeID="eduPersonPrincipalName" permitAny="true" />
**        <AttributeRule attributeID="eduPersonTargetedID" permitAny="true" />**
    </AttributeFilterPolicy>
</AttributeFilterPolicyGroup>

Vous devez installer le pilote JDBC pour la connexion à une base MySQL (pour d'autre type de bases SQL, se référer à la documentation Internet2 ci-dessus). Aller sur le site de téléchargement http://www.mysql.com/downloads/connector/j/.

$> cd
$> wget https://downloads.mariadb.com/Connectors/java/connector-java-2.6.2/mariadb-java-client-2.6.2.jar
$> sudo cp mariadb-java-client-2.6.2.jar /opt/shibboleth-idp/edit-webapp/WEB-INF/lib/

Il est nécessaire de reconstruire le fichier idp.war pour prendre en compte l'ajout de la bibliothèque mysql :

$> cd /opt/shibboleth-idp
$> sudo -E ./bin/build.sh
Installation Directory: [/opt/shibboleth-idp/]

Rebuilding /opt/shibboleth-idp/war/idp.war ...
...done

BUILD SUCCESSFUL
Total time: 24 seconds

Pour vérifier la génération de ce nouvel attribut, vous devez :

  1. redémarrer le serveur Tomcat pour prise en compte des modifications de configuration,
  2. tester l'accès à la ressource de Test via votre IdP. L'intitulé de l'attribut présenté par la ressource de test est eduPersonTargetedID.
Module de consentement : la configuration par défaut du module de consentement exclut certains attributs, notamment eduPersonTargetedID. Cet attribut n'est donc pas présenté à l'utilisateur pour consentement. Ce comportement est modifiable via le fichier de configuration conf/intercept/consent-intercept-config.xml.

L'attribut eduPersonEntitlement est défini dans les recommandations eduPerson ; il n'est pas repris dans SupAnn car cet attribut est a priori spécifique à l'environnement Shibboleth et la valeur de l'attribut peut être calculée dynamiquement par une brique Shibboleth fournisseur d'identités. Cet attribut sera généré à l'aide d'un Attribut de type Script dans le resolver.

Cet attribut permet à un fournisseur d'identités de transmettre des privilèges spécifiques à l'utilisateur courant, en fonction du fournisseur de services courant. Sa valeur est donc positionnée selon le fournisseur de services et l'utilisateur au moment de la connexion.

Dans le cas d'un accès à de la documentation électronique, la valeur attendue est souvent urn:mace:dir:entitlement:common-lib-terms, la sémantique associée est « cet utilisateur est duement autorisé à accéder à la ressource, selon les termes du contrat entre l'éditeur et l'université. » Cette valeur est à positionner pour toute personne étant référencée member par exemple dans l'annuaire de l'établissement. Commentez l' eduPersonEntitlement qui serait déjà présent dans le fichier attribute-resolver-ldap.xml et ajoutez cette nouvelle déclaration comme suit :

/opt/shibboleth-idp/conf/attribute-resolver-ldap.xml
<AttributeDefinition id="eduPersonEntitlement" xsi:type="ScriptedAttribute">
  <InputAttributeDefinition ref="eduPersonAffiliation" />
  <AttributeEncoder xsi:type="SAML1String" name="urn:mace:dir:attribute-def:eduPersonEntitlement" />
  <AttributeEncoder xsi:type="SAML2String" name="urn:oid:1.3.6.1.4.1.5923.1.1.1.7" friendlyName="eduPersonEntitlement" />
  <Script>
    <![CDATA[
    if (eduPersonAffiliation.getValues().contains("member")) {
      eduPersonEntitlement.getValues().add("urn:mace:dir:entitlement:common-lib-terms");
    }
    ]]>
  </Script>
</AttributeDefinition>

Une fois la modification effectuée, rechargez la configuration de résolution d'attribut et vérifiez le résultat sur le SP de test.

Dans quels cas utiliser cet attribut ?

Cet attribut est adapté lorsque la décision d'autorisation d'accès à une ressource ne peut pas être réalisée du côté du fournisseur de services (car les attributs ne sont pas standards ou pas divulguables). Dans ce cas, le fournisseur d'identités prépare l'attribut eduPersonEntitlement pour exprimer les droits de l'utilisateur vis à vis de ce fournisseur de services, en fonction des autres attributs de l'utilisateur.

Cet exemple ne montre pas comment générer un entitlement en fonction du fournisseur de services. Voici une fiche technique que vous pouvez consulter pour plus d'options : voir la documentation

Fichiers de configuration édités dans ce chapitre :
  • /opt/shibboleth-idp/conf/saml-nameid.properties
  • /opt/shibboleth-idp/conf/global.xml
  • /opt/shibboleth-idp/conf/attribute-resolver-ldap.xml
  • /opt/shibboleth/conf/attribute-filter.xml
  • federation/documentation/guides-installation/idp3.4/chap12.txt
  • Dernière modification : 2020/12/09 16:58
  • de geoffroy.arnoud@renater.fr