Gestion des accès aux APIs REST de WSO2 IS — Technique [1/2]

Gregory EVE
Smile with WSO2
Published in
4 min readOct 7, 2020

WSO2 migre progressivement l’ensemble des web services SOAP d’administration de ses produits vers des APIs REST JSON. Nous allons nous pencher sur comment le contrôle d’accès à ces APIs est réalisée.

Cette série, “Gestion des accès aux APIs REST de WSO2 IS” est en 2 parties :

1. Technique

2. Opérationnelle

Authentification

Méthodes d’authentification

Les produits WSO2 supportent nativement 3 modes d’accès aux APIs:

  • Authentification Basique: L’utilisateur utilise ses identifiants et mots de passe directement dans chacune des requêtes d’API
curl -u <username>:<password> -X GET "https://localhost:9443/scim2/Me" -H "accept: application/scim+json"
  • Authorisation OAuth 2: Obtention d’un access token OAuth 2, et insertion de celui-ci dans l’entête de chacune des requêtes d’API
#Obtention d'un access token OAuth2
curl -u <client id>:<client secret> -k -d "grant_type=password&username=<username>&password=<password>" -H "Content-Type:application/x-www-form-urlencoded" https://localhost:9443/oauth2/token
#Appel d'API avec l'access token
curl -X GET "https://localhost:9443/scim2/Me" -H "accept: application/scim+json" -H "authorization: bearer <access-token>
  • Authentification Mutuelle SSL: Signature envoyée en entête de chacune des requêtes d’API avec un certificat autorisé par le produit WSO2
#importation de la clé publique client dans le produit wso2
keytool -import -trustcacerts -alias carbon -file <public-key> -keystore client-truststore.jks -storepass wso2carbon
#Appel d'API avec certificat
curl --cert <public-key> --key <private-key> --cacert <server-public-key> -X GET "https://localhost:9443/scim2/Me" -H "accept: application/scim+json"

Il en existe en réalité 2 autres, non documentés, dont les produits WSO2 se servent en interne pour fonctionner :

  • Authentification Client par secret : Secret envoyé en entête de chacune des requêtes d’API (ex: application Dashboard)
#Editer le fichier <WSO2_HOME>/repository/conf/identity/identity.xml
<ClientAppAuthentication>
<Application name="<APP_NAME>" hash="{sha256(<APP_SECRET>)}"/>
</ClientAppAuthentication>
#Appel d'API avec secret client
curl -X GET "https://localhost:9443/scim2/Users" -H "accept: application/scim+json" -H "Authorization: Client {base64("<APP_NAME>" . ":" . sha256(<APP_SECRET>))}"
  • Authentification par cookie: Identifiant de session Tomcat envoyé par cookie en entête de chaque requête (ex: service soap)
#connexion via soap
curl -k -v -H “SOAPAction: urn:login” -H “Content-Type: text/xml” -d @auth.xml https://localhost:9443/services/AuthenticationAdmin
#avec auth.xml:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://org.apache.synapse/xsd">
<soapenv:Header/>
<soapenv:Body>
<aut:login xmlns:aut="http://authentication.services.core.carbon.wso2.org">
<aut:username>admin</aut:username>
<aut:password>admin</aut:password>
<aut:remoteAddress>localhost</aut:remoteAddress>
</aut:login>
</soapenv:Body>
</soapenv:Envelope>
#récupérez le cookie JSESSIONID#appel d'API avec cookie de session
curl -k -v -X GET "https://localhost:9443/t/carbon.super/api/server/v1/applications" -H "accept: application/json" -H "Cookie: JSESSIONID=<SESSION_ID>"

Implémentation

Cette fonctionnalité est implémentée au sein de l’extension https://github.com/wso2-extensions/identity-carbon-auth-rest

On y retrouve:

  • la valve Tomcat org.wso2.carbon.identity.auth.valve.AuthenticationValve qui est responsable d’intercepter toutes les requêtes et lancer, si nécessaire le mode d’authentification demandé via un gestionnaire d’authentification. Elle est chargée via le fichier <WSO2_HOME>/repository/conf/tomcat/catalina-server.xml :
<Host name="localhost" unpackWARs="true" deployOnStartup="false" autoDeploy="false" appBase="${carbon.home}/repository/deployment/server/webapps/">
[...]
<Valve className="org.wso2.carbon.identity.auth.valve.AuthenticationValve"/>
[...]
</Host>

Extension

Avec les éléments précédents, vous vous en doutez, il est possible d’étendre le système d’authentification en ajoutant votre propre handler.

Pour cela il suffit d’étendre la classe abstraite org.wso2.carbon.identity.auth.service.handler.AuthenticationHandler en implémentant les deux méthodes :
-
AuthenticationResult doAuthenticate(MessageContext messageContext) [logique d’authentification à implémenter]
- void postAuthenticate(MessageContext messageContext, AuthenticationResult authenticationResult) [optionnel, par défaut défini le user authentifié dans le contexte Carbon]

Exemple pour une API Key hard-codée:

curl -X GET "https://localhost:9443/scim2/Users" -H "accept: application/scim+json" -H "Authorization: ApiKey GQP0EQV2354V263VGVTKKL2AZXC"

Autorisation

Règles d'accès aux ressources

Par défaut les produits WSO2 sont configurés pour rejeter toute requête entrante non authentifiée sauf pour les ressources spécifiquement définies.

Les ressources sont protégées historiquement par permission, mais les nouvelles APIs sont-elles protégées en plus par scope OAuth 2.

Une ressource est définie comme suit:

  • Context : path de la ressources sous forme d’une regex
  • Secured : booléen indiquant si on doit contrôler l’accès ou si l’accès est libre
  • Http-method : verbe HTTP applicable ou “all” sur la ressource
  • Permissions : liste des permissions nécessaires ou “none” si aucune (séparés par une virgule si plusieurs sont obligatoires)
  • Scopes : liste des scopes nécessaires

Ces règles d'accès sont référencées dans le fichier <WSO2_HOME>/repository/conf/identity.xml

<ResourceAccessControl default-access="deny">
#l'accès anonyme est autorisé
<Resource context="/.well-known/webfinger(.*)" secured="false" http-method="all"/>
#le client doit être simplement authentifié
<Resource context="(.*)/.well-known(.*)" secured="true" http-method="all"/>
#le client doit avoir la permission et faire l'appel avec le scope demandé
<Resource context="(.*)/scim2/Users(.*)" secured="true" http-method="POST">
<Permissions>/permission/admin/manage/identity/usermgt/create</Permissions>
<Scopes>internal_user_mgt_create</Scopes>
</Resource>
#le client doit faire l'appel avec le scope demandé mais sans avoir de permissions particulières
<Resource context="(.*)/scim2/Me" secured="true" http-method="GET">
<Permissions>none</Permissions>
<Scopes>internal_login</Scopes>
</Resource>
[...]
</ResourceAccessControl>

Et pour plus de facilité vous retrouvez la liste des permissions et des scopes nécessaires selon ce que vous souhaitez faire dans la documentation :
- https://is.docs.wso2.com/en/latest/develop/rest-apis/
- https://is.docs.wso2.com/en/latest/references/scopes-corresponding-to-api-permissions/

Implémentation

De la même manière que la partie authentification la fonctionnalité d’autorisation est implémentée au sein de l’extension https://github.com/wso2-extensions/identity-carbon-auth-rest

On y retrouve:

  • la valve Tomcat org.wso2.carbon.identity.authz.valve.AuthorizationValve qui est responsable d’intercepter toutes les requêtes et contrôler les accès demandés via un gestionnaire d’autorisation. Elle est chargée via le fichier <WSO2_HOME>/repository/conf/tomcat/catalina-server.xml :
<Host name="localhost" unpackWARs="true" deployOnStartup="false" autoDeploy="false" appBase="${carbon.home}/repository/deployment/server/webapps/">
[...]
<Valve className="org.wso2.carbon.identity.authz.valve.AuthorizationValve"/>
[...]
</Host>
  • le Handler d’autorisations chargés par OSGI au sein du gestionnaire d’autorisation.
    - AuthorizationHandler

Extension

Étendre la partie autorisation est plus complexe, car elle dépend des informations mises à disposition.

Vous pouvez étendre la classe org.wso2.carbon.identity.authz.service.handler.ResourceHandler pour définir les permissions nécessaires à l'accès à la ressource.

Et étendre la classe org.wso2.carbon.identity.authz.service.handler.AuthorizationHandler pour implémenter le contrôle d’accès.

--

--

Gregory EVE
Smile with WSO2

Solution Architect at Smile, french lover and open source supporter. Integrate, Search, Leverage and Secure your data what else?