Single Sign On i Single Sign Out a les aplicacions de la UPC. Cas particular amb Tomcat

Enviat per Jaume Moral el Dv, 23/11/2018 - 12:22

Els sistemes de Single Sign On ens permeten introduir les nostres credencials només una única vegada i tenir accés a moltes aplicacions. Aquest tipus de sistemes són essencials en organitzacions grans amb múltiples aplicacions, ja que ens permeten no només facilitar la vida a l’usuari, sinó també tenir molt més controlats els accessos i millorar la seguretat.

La UPC està renovant el seu Single Sign On, passant d’una solució basada en CAS a una solució multi protocol, que permetrà una autenticació federada, amb OAuth2 i, per no perdre la compatibilitat, amb CAS, que és el sistema utilitzat actualment.

Autenticació federada

L'autenticació federada és un concepte amb el qual segur que ja hem tingut un contacte previ sense saber-ho: la xarxa eduroam. Eduroam ens permet accedir a la WIFI no només de la UPC, sinó de qualsevol universitat o institució adherida. Simplement hem de posar un identificador que inclogui d'on venim (per exemple, jaume.moral@upc.edu) i el mateix sistema s'encarregarà d'autenticar-nos al servidor corresponent a la nostra institució. Es tracta, per tant, d'un sistema descentralitzat.

Traslladant aquest sistema al món web, tenim un parell de federacions a les quals tindrem accés amb el nou Single Sign On de la UPC: SIR2 (universitats espanyoles), UNIFICAT (universitats catalanes) i a través d'elles, la federació europea eduGain. Formar part de la federació ens pot permetre fer aplicacions per exemple que autentiquin usuaris amb les credencials de la seva universitat d'origen i estalviar-nos el problema d'haver-los de donar d'alta a la UPC. Això ens obre un nou món ple de possibilitats.

Si voleu saber quina pinta té un servei federat, podeu fer provar el servei filesender que proporciona rediris a través de la federació SIR.

Autenticació amb CAS

CAS és el sistema que utilitza actualment la UPC per gestionar el login únic. Es tracta d'un sistema molt simple, que podem resumir en aquest diagrama de seqüència:

 Scuency diagram

El problema del Logout

CAS, i en general tots els sistemes de SSO, té un problema amb el Logout. Quan nosaltres fem logout d’una de les aplicacions, s’ha de fer també un logout del servidor CAS. Fins aquí cap problema. Ara bé, si hem accedit prèviament a una segona aplicació… com se n’entera aquesta aplicació de que ha de fer logout? És més… s’ha de fer logout d’aquesta aplicació o no?

Per exemple, si hem entrat al Racó i després a l'API de la FIB (que clarament són 2 aplicacions diferents)... si sortim del Racó, hem de sortir de la API?

La veritat és que sobre aquest tema es podria estar hores discutint, ja que tenim per un costat un problema purament tècnic i per l’altre un tema d’experiència d’usuari.

A nivell tècnic, el fet de tancar una sessió al Racó i avisar al CAS que hem sortit és fàcil, però a l'API hi ha una sessió oberta i hem de buscar una forma de que també li arribi un missatge de que volem que s’invalidi.

A nivell d’experiència d’usuari, podríem suposar que es tracten d’aplicacions independents i que no caldria fer aquest logout, però hem d’assegurar-nos que l’usuari ho percep de la mateixa forma o bé informar que no s’ha fet logout a totes les aplicacions.

En el cas del Racó a més, tenim un segon problema: el mateix Racó està compost d’aplicacions independents entre ells que es veuen amb una interfície comuna. Seria un problema fer que el logout fos només d’una part del Racó, ja que no tindria cap sentit com a experiència d’usuari.

Solucionant el problema del Logout a Tomcat

A partir d’aquí veurem com podem atacar aquest problema en concret utilitzant Tomcat, que és el servidor que utilitzem pel Racó a la FIB. Si no us espanta l'XML, podeu continuar llegint. Si no, doncs crec és un bon moment per abandonar. De veritat que no us ho tindré en compte.

Tradicionalment, una aplicació de Tomcat a la que li hem volgut afegir suport per CAS el que fa és configurar uns filtres Java al web.xml. Algo aixi:

<filter>

 <filter-name>CAS Authentication Filter</filter-name>

 <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>

 <init-param>

   <param-name>casServerLoginUrl</param-name>

   <param-value>https://servidorcas.upc.edu/cas/login</param-value>

 </init-param>

</filter>

<filter>

 <filter-name>CAS Validation Filter</filter-name> <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>

 <init-param>

   <param-name>casServerUrlPrefix</param-name>

   <param-value>https://servidorcas.upc.edu/cas/</param-value>

 </init-param>

</filter>

<filter>

 <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>  <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>

</filter>

<filter-mapping>

 <filter-name>CAS Authentication Filter</filter-name>

 <url-pattern>/*</url-pattern>

</filter-mapping>

<filter-mapping>

 <filter-name>CAS Validation Filter</filter-name>

 <url-pattern>/*</url-pattern>

</filter-mapping>

 

<filter-mapping>

 <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>

 <url-pattern>/*</url-pattern>

</filter-mapping>

Aquest sistema fa que cada aplicació sigui totalment independent de les altres que hi ha al mateix servidor, al mateix nivell al què ho estarien 2 aplicacions en 2 servidors diferents que utilitzessin el mateix servidor CAS per gestionar el login. I per tant, el logout tindria el problema que hem explicat: seria possible haver sortit d’una part del Racó i no de l’altra.

Tomcat permet solucionar el problema del logout sincronitzant l’autorització de l’usuari en totes les aplicacions instal·lades en un mateix servidor. Així doncs, si hem entrat amb un usuari a una de les aplicacions, a la resta d’aplicacions també estarem amb aquest mateix usuari. I al revés, si fem logout d’una de les aplicacions, sortirem de totes. Amb això ens estalviem tot el problema d’haver d’enviar la petició de logout a totes les aplicacions

Com ho fem? Doncs utilitzant el que tomcat anomena la SingleSignOn Valve.

Aquesta funcionarà amb conjunció amb la Valve de CAS (que es configurarà a cada aplicació) i el Realm de CAS que permetrà obtenir la informació dels rols.

Així doncs, resumint, el que tenim és alguna cosa així.

Com podem veure, les aplicacions app1 i app2 no són ni conscients de que estan protegides amb CAS, com si passava amb el filtre. Efectivament, al gestionar-se l’autenticació de forma centralitzada des del mateix Tomcat, les aplicacions es configuren utilitzant <security-constraint>, la forma normal de protegir les aplicacions dintre de Tomcat.

L’únic punt fosc d’aquesta solució és que les security-constraint obligues a protegir en funció de rols i el CAS d’entrada no proporciona informació de rols. Per solucionar això, hem fet una implementació pròpia del Realm CAS que retorna un rol fixat. Amb això ja podem unir totes les peces:

server.xml

<Engine>

 <Host>

   <Realm className="fib.cas.realm.FixedRoleCasRealm" roleName="user"/>

   <Valve className="org.apache.catalina.authenticator.SingleSignOn" />

 </Host>

</Engine>

context.xml

<Context>

 <Valve

   className="org.jasig.cas.client.tomcat.v7.Cas20CasAuthenticator"

   encoding="UTF-8" casServerLoginUrl="${cas_server}/cas/login"

   casServerUrlPrefix="${cas_server}/cas/"

   serverName="${raco_server}"/>

</Context>

dintre de cada web.xml

<security-constraint>

 <web-resource-collection>

   <web-resource-name>Protected Area</web-resource-name>

   <url-pattern>/*</url-pattern>

 </web-resource-collection>

 <auth-constraint>

   <role-name>user</role-name>

 </auth-constraint>

</security-constraint>

<security-role>

 <role-name>user</role-name>

</security-role>

I per acabar, la implementació del FixedRoleCasRealm, que recordem hem de tenir perquè el servidor CAS no ens està passant actualment cap rol.

FixedRoleCasRealm.java

package fib.cas.realm;
import org.jasig.cas.client.tomcat.CasRealm;
import org.jasig.cas.client.tomcat.v7.AbstractCasRealm;

public class FixedRoleCasRealm extends AbstractCasRealm {

private final FixedRoleCasRealmDelegatdelegate = new FixedRoleCasRealmDelegate();

public void setRoleName(final String name) {
delegate.setRoleName(name);
}

protected CasRealm getDelegate() {
return delegate;
}
}

FixedRoleCasRealmDelegate.java

package fib.cas.realm;

import java.security.Principal;
import java.util.Arrays;

import org.apache.catalina.Wrapper;
import org.apache.catalina.realm.RealmBase;
import org.jasig.cas.client.tomcat.CasRealm;

public class FixedRoleCasRealmDelegate implements CasRealm {
 String[] roles=new String[] {"user"};

 public Principal authenticate(final Principal p) {
   return p;
 }

 public String[] getRoles(final Principal p) {
   return roles;
 }

 public boolean hasRole(final Principal principal, final String role) {
   return Arrays.asList(getRoles(principal)).contains(role);
 }

 public void setRoleName(String name) {
   this.roles=new String[] {name};        
 }
}

Conclusions

En aquest article hem vist com és el login amb CAS, quins problemes pot tenir amb el logout global i com podem atacar aquest problema a Tomcat, veient una possible solució quan tenim vàries aplicacions que funcionen com una de sola. De totes maneres, atacar el problema del logout pot ser un tema més d’experiència d’usuari que purament tècnic.

Més informació

Repositori de codi de GitHub d'Apereo Foundation java-cas-client.

Segueix-nos a

Els nostres articles del bloc d'inLab FIB

         
         

inLab FIB incorpora esCert

Icona ESCERT

First LogoCSIRT Logo

inLab és membre de

CIT UPC