...
Problème rencontré avec la persistance du cookie TGC (remember-me)
Nous avons constaté un changement de comportement concernant le cookie TGC (Ticket Granting Cookie), responsable du maintien de la session CAS entre le navigateur et le serveur.
En version 7.0, l’interface HTML proposait une case à cocher (<input type="checkbox">) pour l’option "Se souvenir de moi" (remember-me). Cela avait pour effet que, si l’utilisateur ne cochait pas cette case, aucun paramètre rememberme n’était transmis à CAS, et donc la session CAS était associée à un cookie de session navigateur (supprimé à la fermeture du navigateur).
À partir de la version 7.1, l’UI CAS a été modifiée : la case à cocher a été remplacée par un bouton de type "switch" (toggle), qui agit en réalité sur un champ caché (<input type="hidden" name="rememberme">). Résultat : le paramètre rememberme est toujours transmis côté serveur, même lorsqu’il est désactivé.
Or, dans le code de CAS (de la 7.1 à 7.2.2 comprise), la décision de rendre le TGC persistant repose uniquement sur la présence du paramètre rememberme, sans considération de sa valeur. Cela a pour effet de rendre le TGC persistant (cookie à durée de vie fixée), même lorsque l’utilisateur n’a pas demandé à rester connecté. Ce comportement peut poser problème dans des environnements où le navigateur est partagé (ex. : tablettes pour examens), car la session persiste même après fermeture/reprise du navigateur.
Nous avons corrigé ce comportement localement en surchargeant la méthode concernée pour que la persistance du TGC soit conditionnée à la valeur du paramètre rememberme, et non à sa seule présence.
Pull Request
Un pull request a été proposée à Apereo pour corriger cela de manière générique ici : https://github.com/apereo/cas/pull/6872
Modification dans le cas-overlay
...
Exceptionnellement et dans l'attente (et l'espoir) que le problème soit prise pris en compte et corrigé dans la version officielle d'Apereo CAS, nous avons donc patché localement la classe java.
- copie de classe java modifiée dans l'overlay dans src/main/java/org/apereo/cas/web/support/gen/CookieRetrievingCookieGenerator.java
- ajout des dépendances dans l'overlay pour la compilation :
Bloc de code language text /* pour override de CookieRetrievingCookieGenerator */ implementation "org.apereo.cas:cas-server-core-cookie-api" implementation "org.apereo.cas:cas-server-core-authentication-api" implementation "org.apereo.cas:cas-server-core-util-api" implementation "org.apereo.cas:cas-server-core-web-api"
- puis redéploiement et redémarrage pour prise en compte.
Attributs LDAP persistants sur l'ensemble de la session CAS
Depuis un certain nombre de temps (cf entre autre https://groups.google.com/a/apereo.org/g/cas-user/c/0HjUqWsM0oE/m/9mq9VPxaAwAJ ) , tout le long de la session CAS, les attributs calculés lors de l'authentification sont conservés et renvoyés dans les tickets CAS.
Ces attributs sont en effet stockés en base (dans mongidb chez nous) dans l'objet qui correspond au TGT et qui suit l'utilisateur tout le long de la sessions.
Le processus d'authentification standard fonctionne ainsi :
Lors de la création du TGT, les attributs LDAP sont récupérés et persistés avec le TGT (notamment dans MongoDB, Hazelcast, etc.).
Tous les Service Tickets (ST) générés pendant la session utiliseront par défaut ces attributs "figés" du TGT, sauf configuration contraire.
- Rappel : la durée de vie du TGT peut-être très longue, notamment si vous proposez un rememberMe de quelques jours, semaines, voire mois !!
Cela signifie que :
Les attributs ne sont pas rafraîchis à chaque identification sur un service ;
Cela peut poser problème dans les scénarios où des attributs sont utilisés dynamiquemen par des applicatifs cassifiés utilisant directement les attributs renvoyés par CAS (comme
memberOfpour un calcul de rôles par exemple).
La configuration suivante devrait (ou a du dans une version antérieure) faire en sorte que les attributs soient rafraichis à chaque fois que CAS en a besoin, mais ce n'est pas (plus ?) le cas, notamment en 7.2.2
| Bloc de code | ||
|---|---|---|
| ||
cas.authn.attribute-repository.core.expiration-time = 0 |
Ce problème n'est donc pas nouveau mais nous avons profité de la mise à jour de notre CAS pour le traiter.
Récupération des attributs lors de la récupération/calcul d'un service ticket
Cf https://groups.google.com/a/apereo.org/g/cas-user/c/0HjUqWsM0oE/m/9mq9VPxaAwAJ une solution est de passer par une configuration JSON sur chaque service, via un attributeReleasePolicy avec un CachingPrincipalAttributesRepository dont l'expiration est à 0 et avec ignoreResolvedAttributes=true (ignoreResolvedAttributes à true signifie "ne pas utiliser les attributs du TGT", mais interroger directement le repository).
| Bloc de code | ||
|---|---|---|
| ||
"attributeReleasePolicy" : {
"@class" : "org.apereo.cas.services.ReturnAllowedAttributeReleasePolicy",
"allowedAttributes" : [ "java.util.ArrayList", [ "uid", "mail", "displayName", "eduPersonPrincipalName", "eduPersonAffiliation", "sn", "givenName", "radiusFilterId", "memberOf" ] ],
"principalAttributesRepository" : {
"@class" : "org.apereo.cas.authentication.principal.cache.CachingPrincipalAttributesRepository",
"timeUnit" : "HOURS",
"expiration" : 0,
"attributeRepositoryIds": ["java.util.HashSet", [ "LdaptivePersonAttributeDao" ]],
"ignoreResolvedAttributes": true,
}
} |
Récupération des attributs lors de l'exécution de scripts groovy pour l'interrupt ou/et le mfa
Non documenté dans Apereo CAS, c'est une facilité que nous permet (actuellement en tout cas) spring depuis un script groovy opéré par CAS (le risque étant ici qu'une nouvelle version de CAS n'autorise plus cette possibilité un jour) : on récupère le bean spring qui permet d'interroger le ldap tel que configuré dans CAS, et on calcule des memberOf à jour.
Petite astuce supplémentaire ici, si cette récupération échoue, on fait juste un log et on utilise l'attribut memberOf issu du TGT :
| Bloc de code | ||
|---|---|---|
| ||
class SampleGroovyEventResolver {
def String run(service, registeredService, authentication, httpRequest, logger, ... args) {
def memberOf = authentication.principal.attributes.memberOf
// memberOf calculé via le TGT éventuellement ancien, on refait une requête ldap ici...
try {
def ctx = org.apereo.cas.util.spring.ApplicationContextProvider.getApplicationContext()
def dao = ctx.getBeansOfType(org.apereo.cas.authentication.principal.attribute.PersonAttributeDao.class)
def attrRepo = dao.get("cachingAttributeRepository")
memberOf = attrRepo.getPerson(authentication.principal.id).getAttributeValues("memberOf")
} catch(Exception ex) {
logger.error("pb retrieving memberof from ldap, use memberof from cas attributes : " + ex)
}
if ('cn=from.grouper.admin,ou=groups,dc=univ-rouen,dc=fr' in memberOf) {
return "mfa-esupotp"
}
... |
Via ces configurations, notez qu'il est évident que le ldap sera sollicité plus régulièrement qu'il ne l'était auparavant.