Introduction
Active Directory Federation Services (ADFS) permet de transmettre n'importe quel attribut d'un objet Active Directory à un partenaire par le biais d'une fédération. Cela reste facilement paramétrable via l'interface de gestion ADFS Management (ce paramétrage sera d'ailleurs détaillé dans la suite de cet article).
Cependant, il peut arriver que l'on souhaite transformer un attribut Active Directory en réalisant notamment des manipulations de chaines de caractères. Dans cet article nous étudierons le cas de l'attribut ObjectGuid qui est envoyé de manière encodée (en base 64) et dont le but sera de le transmettre sous la forme standard (exemple : {3F2704E0-4289-11D3-9B0C-0305E92C1301}). Il est aussi possible d'imaginer d'autres cas d'usage comme : mettre en majuscule un attribut, tronquer une chaîne de caractère...
Cette opération se réalise en deux étapes :
-
Le développement d'une DLL contenant l'opération de transformation de l'attribut.
-
Le déploiement de cette DLL sur un environnement ADFS. Cette dernière sera utilisé au travers d'un "custom attribute store" ADFS en conjonction avec une règle personnalisée de transformation de revendications lors de la configuration de la fédération.
Pour réaliser toutes ces manipulations, il est nécessaire d'avoir un environnement ADFS déjà opérationnel ainsi que Visual Studio (en version 2013 ou supérieure).
Les notions de développements abordées pendant cet article sont très accessibles y compris pour quelqu'un ne développant pas. Le langage utilisé est le C#.
En bonus, vous trouverez une petite application console permettant de tester les attributs retournés par vos fédérations.
NB : Cette article a été rédigé à partir d'une infrastructure ADFS 3.0 (Windows 2012 R2) et de Visual Studio 2015. Il est inspiré d'un article de Tino Donderwinkel : ici.
Fonctionnement
Tout d'abord, voici le principe de fonctionnement lorsqu'une nouvelle revendication doit être transmise à une fédération :
-
Une règle basée sur le template LDAP récupère l'attribut AD que l'on souhaite transmettre.
-
Une règle personnalisée traite la première revendication et la retourne dans une deuxième revendication après qu'elle ait subit sa transformation via une méthode appelée dans un custom attribute store
Le custom attribute store est composé d'une DLL qui contiendra toutes nos méthodes permettant de transformer des revendications. En effet, dans cet article nous modifions une revendication provenant d'un annuaire Active Directory mais celle-ci peut très bien être issue d'un autre référentiel puisque notre custom attribute store traite une revendication et non un attribut LDAP.
Le développement du custom attribute store nécessite d'implémenter des références à une DLL ADFS. Cela passe par l'ajout d'une DLL contenu dans le répertoire d'ADFS. Cette dernière contient 3 méthodes qu'il faut implémenter :
-
Initialize : Elle permet de fournir des paramètres de configuration à l'attribute store. Elle ne sera pas utiliser dans notre cas.
-
BeginExecuteQuery : Cette méthode contient une reqête sur l'attribute store. Cela sera modélisé par une méthode de transformation de notre attribut.
-
EndExecuteQuery : Elle est appelé lorsque la méthode EndExecuteQuery a terminé son exécution. Elle prend en paramètre le résultat retourné par la méthode précédente.
Prérequis
Pour développer un nouvel attribute store, il faut copier la DLL nommée “Microsoft.IdentityServer.ClaimsPolicy.dll” depuis un serveur ADFS sur l'ordinateur où Visual Studio est installé (celle-ci est située dans “C:\Windows\ADFS”).
Développement
Lorsque le prérequis est rempli, nous pouvons lancer Visual Studio est créer un nouveau projet via le bouton “File” puis “New” et enfin “Project”.
Dans la section “template”, on choisit la catégorie Visual C# et le modèle bibliothèque de classes (“Class Library”). La version 4.5 du Framework .Net ainsi qu'un chemin pour stocker les fichiers doivent être définies.
Le fichier qui nous intéresse le plus et le fichier “Class1.cs” qu'il est possible de renommer via un clic droit puis “Rename” en oubliant pas de valider la popup suivante permettant de modifier toutes les références à ce fichier dans notre projet.
Nous devons ensuite ajouter des références à notre projet. Pour rappel, ces dernières vont nous permettre de communiquer avec l'infrastructure ADFS en joignant entre autre la DLL précédemment copiée (“Microsoft.IdentityServer.ClaimsPolicy.dll”).
Cette première dépendance s'ajoute en effectuant un clic droit sur le champ “References” dans le menu de droite (Solution Explorer) puis en cliquant sur “Add Reference”.
Il faut choisir l'onglet “Browse” et enfin se rendre à l'emplacement de la DLL et la sélectionner
La seconde dépendance s'ajoute de la même manière Il s'agit de “System.IdentityModel”. Il est seulement nécessaire de se rendre dans le menu “Assemblies” au lieu de “Browse”.
Enfin, on ajoute les lignes de codes ci-dessous dans le fichier de classe.
Cela permet d'indiquer les références utilisées dans la classe.
Une fois les références ajoutées, il faut implémenter les méthodes explicitées dans le paragraphe précédent. Pour se faire, on doit ajouter ce qu'on appelle une interface à notre classe. Cela nous donnera accès aux méthodes permettant d'interagir avec ADFS. Notre classe représente l'attribute store que l'on souhaite créé.
Pour ajouter l'interface, il suffit d'ajouter “ : IAttributeStore” après “public class CustomStringAttributeStore”. Un avertissement est levé car Visual Studio ne trouve pas le code permettant de dire que notre classe est un attribute store (il cherche les 3 méthodes évoquées précédemment). Pour corriger cela, il suffit de faire un clic droit sur “IattributeStore” puis de cliquer sur “Implement Interface”. Le code de base pour les 3 méthodes est automatiquement généré.
Ci-dessous, vous trouverez le code pour chacune d'entre elles.
Initialize :
BeginExecuteQuery :
Il s'agit de la méthode où se déroule toute la logique de transformation. La section la plus importante est contenue à l'intérieure de la structure logique Switch. En fonction de la requête demandée, une transformation sera appliquée. Dans notre exemple, je n'ai qu'un seul cas possible, mais on peut améliorer cet attribute store afin qu'il contienne toutes les règles de transformations dont nous avons besoin.
NB : Une méthode Base64ToGuid est appelée dans le case Base64ToGuid. Cette méthode permet de convertir en chaîne lisible, le Guid d'un objet AD qui est normalement encodé en base 64.
EndExecuteQuery :
Le lien suivant mène vers le code complet de la classe : ici
Il suffit ensuite de générer la DLL en passant par le menu “Build” puis en cliquant sur “Build Solution”. La DLL est générée dans le sous répertoire “bin\Debug” du projet défini lors de la création. Par défaut il s'agit du chemin “C:\Users\nom_d'utilisateur\Documents\Visual Studio 2015\Projects”.
Déploiement
Une fois la DLL copiée, il faut la copier sur tous les serveurs ADFS dans le répertoire “C:\Windows\ADFS” (Attention : La DLL ne doit pas être déployée sur les serveurs Web Application Proxy). On peut ensuite passer à la configuration qui se fait via la console ADFS Management.
Configuration du Custom Attribute Store
Dans la section "Trust Relationship", effectuer un clic droit sur “AttributeStore” et cliquer sur “Add Custom Attribute Store”.
Une popup s'ouvre. Il faut renseigner le champ “Custom attribute store class name”. Ce champ doit être rempli avec attention et nécessite une syntaxe particulière :
Namespace.Classe,Nom_du_fichier_dll
Namespace : il s'agit du nom indiquer à côté de ce mot clé dans notre fichier de classe (Ici : CustomStringTransformAttributeStore).
Classe : Le nom de la classe (Ici : StringTransform)
Nom_du_fichier_dll : le nom de la DLL sans son extension (ici : CustomStringTransformAttributeStore)
Lorsque la configuration est validée, un évènement 251 apparait dans le journal Admin d'ADFS (dans la catégorie Applications and Services de l'observateur d'évènements). Lorsqu'il y a une erreur de configuration, un évènement avec l'ID 159 est présent.
Configuration de la fédération de test
Nous allons maintenant créer une fédération de test. Celle-ci ne doit pas forcément pointer vers un service réel. L'outil fourni dans le chapitre suivant permettra de générer des tokens. Dans la section Trust Relationship, il faut se rendre dans la section “Relying Party Trusts” puis choisir l'option “Add Relying Party Trust”.
Cliquez sur le bouton “Start” après l'ouverture de l'assistant. Choisir l'option “Enter data about the relying party manually”.
Insérez un nom pour la fédération puis cliquez sur “Next”.
Cliquez sur “Next” sur les onglets suivants jusqu'à “Configure Identifiers”. Entrez une url dans le champ “Relying party trust identifier” puis cliquez sur “Add”. Cette url n'a pas besoin de répondre.
Il faut ensuite cliquez sur suivant jusqu'à la fin de l'assistant en laissant cocher la case “Open the Edit Claim Rules dialog for this relying party trust when the wizard closes”. Cela nous permettra d'ajouter nos règles sur les revendications dès la création de la fédération (sinon, il vous faudra faire un clic droit sur la fédération et choisir “Edit Claim Rules”).
Dans l'assistant d'édition des règles de revendications, nous allons ajouter deux règles dans l'onglet “Issuance Transform Rules”. Pour se faire cliquez sur “Add Rule”.
Dans l'assistant, nous ajoutons une première règle qui va récupérer un attribut LDAP. On sélectionne donc le modèle “Send LDAP Attributes as Claims”.
On peut ensuite définir un nom puis choisir “Active Directory” en tant qu'attribute store. Enfin, on entre l'attribut LDAP qui nous intéresse pour la colonne “LDAP Attribute” (il peut être inséré à la main s'il n'est pas présent dans la liste comme c'est le cas pour “objectGUID”) puis on choisit une valeur pour “Outgoing Claim Type” (ici, j'ai choisi “Common Name”).
On ajoute ensuite une seconde règle qui va utiliser notre custom attribute store. Cette fois-ci le template à utiliser est “Send Claims Using a Custom Rule”. On défini ensuite un nom et on peut insérer le code suivant dans le champs “Custom Rule”.
Cette règle récupère la revendication “CommonName” que nous avons défini précédemment en tant que revendication de sortie de notre annuaire Active Directory et la transmet à notre custom attribute store (elle est retournée en tant que revendication de type “nameidentifier” en lui appliquant la méthode Base64ToGuid qui correspond au cas que l'on a défini dans notre classe “Transform.cs”. Il est bien entendu possible de changer les revendications, la requête ou le nom du custom attribute store en fonction de vos valeurs.
Test
Afin de tester vos fédérations, je met un outil console à disposition que j'ai développé et que vous pouvez récupérer via le lien suivant : ici. Il sera utile pour valider le bon fonctionnement de notre custom attribute store mais il vous permettra aussi de voir les revendications envoyées à vos partenaires pour n'importe laquelle de vos fédérations. En plus de ce dernier vous devez avoir dans le magasin racine de confiance, le certificat permettant de signé les tokens d'authentification (Token-Signing). Dans un déploiement par défaut, il est auto-signé.
L'outil est composé d'un .exe et d'un fichier .config. Ce dernier doit être rempli. Les paramètres à changer sont :
-
relyingPartyId : il s'agit de l'url d'accès à la fédération (le champ "identifier") : https://adfstest.myenterprise.com/adfs/services/trust.
-
adfsEndpoint : il s'agit du point d'accès pour effectuer l'authentification (exemple : https://fs.myenterprise.com/adfs/services/trust/13/windowsmixed). Le programme se base sur l'authentification windows. L'url “/trust/13/windowsmixed” doit être active sur l'infrastructure ADFS. Pour se faire, il faut se rendre dans la catégorie “Endpoints” de la console de gestion ADFS, puis effectuer un clic droit et choisir “Enable” sur cette url.
-
certSubject : La valeur doit être celle du champ objet du certificat de type Token-Signing. Exemple : CN = ADFS Signing - fs.myenterprise.com.
Une fois l'outil paramétré, il suffit de le lancer l'exécutable. Ce dernier affiche toutes les revendications par un couple "Nom de la revendication : valeur". Voici un exemple de résultat obtenu :
On remarque qu'on obtient bien les deux revendications représentant le GUID, l'une le représentant sous forme d'une chaîne de caractère et l'autre encodé en base 64 (la revendication originale). Notre custom attribute store est donc bien opérationnel.
Si l'on souhaite ajouter de nouvelles transformations de revendications, il suffit maintenant d'ajouter des cas supplémentaires dans la classe et de la redéployer.