r1353 - in trunk/wikitty-api/src: main/java/org/nuiton/wikitty main/java/org/nuiton/wikitty/services test/java/org/nuiton/wikitty/services
Author: bpoussin Date: 2012-01-23 19:51:14 +0100 (Mon, 23 Jan 2012) New Revision: 1353 Url: http://nuiton.org/repositories/revision/wikitty/1353 Log: Evolution #1916: Split WikittyServiceSecurity in two service Authentication and Authorization Added: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthentication.java trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationAbstract.java trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationLDAP.java trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthorization.java trunk/wikitty-api/src/test/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationLDAPTest.java Modified: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/WikittyConfigOption.java trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittySecurityUtil.java trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurity.java trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurityExternalAuthentication.java trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurityExternalAuthenticationLDAP.java Modified: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/WikittyConfigOption.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/WikittyConfigOption.java 2012-01-20 17:11:12 UTC (rev 1352) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/WikittyConfigOption.java 2012-01-23 18:51:14 UTC (rev 1353) @@ -197,6 +197,7 @@ + " ex:WikittyServiceSecurityExternalAuthenticationLDAP"), null, String.class, false, false), + @Deprecated // not used any more with new split of authentification and authorization WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_ONLY( "wikitty.security.externalAuthenticationOnly", n_("Indique si on doit utiliser que le composant d'authentification externe" @@ -204,6 +205,7 @@ + " etre configurer, sinon aucun utilisateur ne pourra se logguer"), "false", Boolean.class, false, false), + @Deprecated // not used any more with new split of authentification and authorization WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_LDAP_JNDI( "wikitty.security.externalAuthentication.ldap.jndi.", n_("Prefix a utiliser pour ajouter n'importe quelle option supporte par le" @@ -211,6 +213,7 @@ + " se prefixe la cle jndi ex: '<prefix>java.naming.security.protocol"), null, String.class, false, false), + @Deprecated // not used any more with new split of authentification and authorization WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_LDAP_JNDI_INITIAL_CONTEXT_FACTORY( WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_LDAP_JNDI.getKey() + "java.naming.factory.initial", n_("Indique l'initial context factory a utiliser par defaut 'com.sun.jndi.ldap.LdapCtxFactory'"), @@ -223,12 +226,14 @@ "simple", String.class, false, false), // LDAP serveur n'est pas en prefix JNDI, car pourrait servir ailleur que pour JNDI + @Deprecated // not used any more with new split of authentification and authorization WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_LDAP_SERVER( "wikitty.security.externalAuthentication.ldap.server", n_("Indique l'url du serveur LDAP (obligatoire)"), null, String.class, false, false), // LDAP login pattern n'est pas en prefix JNDI, car pourrait servir ailleur que pour JNDI + @Deprecated // not used any more with new split of authentification and authorization WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_LDAP_LOGIN_PATTERN( "wikitty.security.externalAuthentication.ldap.loginPattern", n_("Le pattern utilise pour generer le DN de l'utilisateur. le '%s' est" @@ -340,16 +345,75 @@ + "wikitty.migration.class.WikittyUser=org.nuiton.wikitty.entities.MigrationUser"), "", Class.class, false, false), + @Deprecated // use WIKITTY_SERVICE_TIME_TO_LOG_INFO WIKITTY_SECURITY_TIME_TO_LOG_INFO( "wikitty.security.timeToLog.info", n_("maximum time before send log info with time consumed"), "100", Integer.class, false, false), + @Deprecated // use WIKITTY_SERVICE_TIME_TO_LOG_WARN WIKITTY_SECURITY_TIME_TO_LOG_WARN( "wikitty.security.timeToLog.warn", n_("maximum time before send log warn with time consumed"), "500", Integer.class, false, false), + WIKITTY_SERVICE_TIME_TO_LOG_INFO( + "wikitty.service.timeToLog.info", + n_("maximum time before send log info with time consumed"), + "100", + Integer.class, false, false), + WIKITTY_SERVICE_TIME_TO_LOG_WARN( + "wikitty.service.timeToLog.warn", + n_("maximum time before send log warn with time consumed"), + "500", + Integer.class, false, false), + WIKITTY_SERVICE_AUTHENTICATION_TIMEOUT( + "wikitty.WikittyServiceAuthentication.timeout", + n_("maximum time in seconde before remove authentication token in database," + + " by default never timeout"), + "0", + Integer.class, false, false), + WIKITTY_SERVICE_AUTHENTICATION_LDAP_AUTOCREATE_USER( + "wikitty.WikittyServiceAuthenticationLDAP.autoCreateUser", + n_("Si vrai et que l'utilisateur n'est que dans le LDAP, lors de sa premiere" + + " authentification il sera automatiquement creer dans la base" + + " Wikitty. Si faux et que l'utilisateur n'existe pas," + + " l'authentification echouera toujours tant que l'utilisateur ne" + + " sera pas cree manuellement dans la base Wikitty"), + "true", + Boolean.class, false, false), + WIKITTY_SERVICE_AUTHENTICATION_LDAP_JNDI( + "wikitty.WikittyServiceAuthenticationLDAP.jndi.", + n_("Prefix a utiliser pour ajouter n'importe quelle option supporte par le" + + " jndi pour la creation du initialContext. Vous ajoutez derriere" + + " se prefixe la cle jndi ex: '<prefix>java.naming.security.protocol"), + null, + String.class, false, false), + WIKITTY_SERVICE_AUTHENTICATION_LDAP_JNDI_INITIAL_CONTEXT_FACTORY( + WIKITTY_SERVICE_AUTHENTICATION_LDAP_JNDI.getKey() + "java.naming.factory.initial", + n_("Indique l'initial context factory a utiliser par defaut 'com.sun.jndi.ldap.LdapCtxFactory'"), + "com.sun.jndi.ldap.LdapCtxFactory", + String.class, false, false), + WIKITTY_SERVICE_AUTHENTICATION_LDAP_JNDI_SECURITY_AUTHENTICATION( + WIKITTY_SERVICE_AUTHENTICATION_LDAP_JNDI.getKey() + "java.naming.security.authentication", + n_("Indique le mode d'authentification a utiliser par defaut 'simple'." + + " Les modes possibles sont 'simple', 'SSL', 'SASL'"), + "simple", + String.class, false, false), + // LDAP serveur n'est pas en prefix JNDI, car pourrait servir ailleur que pour JNDI + WIKITTY_SERVICE_AUTHENTICATION_LDAP_SERVER( + "wikitty.WikittyServiceAuthenticationLDAP.server", + n_("Indique l'url du serveur LDAP (obligatoire)"), + null, + String.class, false, false), + // LDAP login pattern n'est pas en prefix JNDI, car pourrait servir ailleur que pour JNDI + WIKITTY_SERVICE_AUTHENTICATION_LDAP_LOGIN_PATTERN( + "wikitty.WikittyServiceAuthenticationLDAP.loginPattern", + n_("Le pattern utilise pour generer le DN de l'utilisateur. le '%s' est" + + " remplace par le login (obligatoire)." + + " Par exemple: 'uid=%s,ou=People,dc=codelutin,dc=com'"), + null, + String.class, false, false), JCS_DEFAULT("jcs.default", n_("jcs auxiliares to use"), "", Modified: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittySecurityUtil.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittySecurityUtil.java 2012-01-20 17:11:12 UTC (rev 1352) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittySecurityUtil.java 2012-01-23 18:51:14 UTC (rev 1353) @@ -24,7 +24,11 @@ */ package org.nuiton.wikitty.services; +import java.util.Collections; +import java.util.Date; import java.util.Set; +import org.nuiton.util.ApplicationConfig; +import org.nuiton.wikitty.WikittyConfigOption; import org.nuiton.wikitty.WikittyService; import org.nuiton.wikitty.entities.Wikitty; import org.nuiton.wikitty.entities.WikittyGroup; @@ -48,6 +52,40 @@ static final public String WIKITTY_APPADMIN_GROUP_NAME = "WikittyAppAdmin"; /** + * Verifie si la date du token depasse le temps authorise pour un token + * Si l'option n'est pas presente dans la config retourne toujours false + * (donc le token a une validite permanente) + * + * @param date la date a verifier + * @return vrai si la date est depassee + */ + public static boolean isOutdated( + ApplicationConfig config, WikittyService ws, String tokenId) { + boolean result = false; + + if (config != null) { + // TODO poussin 20120123 prevoir un timeout dans la config + // qui invaliderait le vieux token et en recreerait un nouveau + long timeout = 1000 * config.getOptionAsLong( + WikittyConfigOption.WIKITTY_SERVICE_AUTHENTICATION_TIMEOUT.getKey()); + if (timeout > 0) { + Wikitty token = ws.restore( + null, Collections.singletonList(tokenId)).get(0); + Date date = WikittyTokenHelper.getDate(token); + + Date now = new Date(); + long nowMillis = now.getTime(); + long dateMillis = date.getTime(); + long delta = nowMillis - dateMillis; + + result = delta > timeout; + } + } + + return result; + } + + /** * Check on a WikittyService if a user is member of a group. A * SecurityException might be thrown at runtime if the securityToken has * expired. Added: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthentication.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthentication.java (rev 0) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthentication.java 2012-01-23 18:51:14 UTC (rev 1353) @@ -0,0 +1,83 @@ +package org.nuiton.wikitty.services; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.ApplicationConfig; +import org.nuiton.util.TimeLog; +import org.nuiton.wikitty.WikittyConfigOption; +import org.nuiton.wikitty.WikittyService; +import org.nuiton.wikitty.WikittyUtil; +import org.nuiton.wikitty.entities.Wikitty; +import org.nuiton.wikitty.entities.WikittyImpl; +import org.nuiton.wikitty.entities.WikittyTokenHelper; +import org.nuiton.wikitty.entities.WikittyUser; +import org.nuiton.wikitty.entities.WikittyUserHelper; +import org.nuiton.wikitty.query.WikittyQuery; +import org.nuiton.wikitty.query.WikittyQueryMaker; + +/** + * Implantation de base de l'authentification sur Wikitty lui même. + * Cherche a authentifier l'utilisateur sur un objet WikittyUser via son + * login et mot de passe. Ce service doit retourne un token valide si + * l'authorisation a fonctionne. + * + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public class WikittyServiceAuthentication extends WikittyServiceAuthenticationAbstract { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(WikittyServiceAuthentication.class); + + /** use to trace time of security code, timelog must not include delegator + * time in this class */ + final static private TimeLog timeLog = new TimeLog(WikittyServiceAuthentication.class); + + public WikittyServiceAuthentication(ApplicationConfig config, WikittyService ws) { + super(config, ws); + if (config != null) { + long timeToLogInfo = config.getOptionAsInt(WikittyConfigOption. + WIKITTY_SECURITY_TIME_TO_LOG_INFO.getKey()); + long timeToLogWarn = config.getOptionAsInt(WikittyConfigOption. + WIKITTY_SECURITY_TIME_TO_LOG_WARN.getKey()); + timeLog.setTimeToLogInfo(timeToLogInfo); + timeLog.setTimeToLogWarn(timeToLogWarn); + } + } + + @Override + public String login(String login, String password) { + long start = TimeLog.getTime(); + + String tokenId; + + WikittyQuery criteria = new WikittyQueryMaker() + .eq(WikittyUser.FQ_FIELD_WIKITTYUSER_LOGIN, login).end(); + String userId = getDelegate().findByQuery(null, + Collections.singletonList(criteria)).get(0); + + if (userId == null) { + throw new SecurityException(String.format("no such account '%s'", login)); + } else { + Wikitty user = WikittyServiceEnhanced.restore( + getDelegate(), null, userId); + // check password is valid + if (WikittyUserHelper.getPassword(user).equals(password)) { + tokenId = getToken(user); + } else { + throw new SecurityException("bad login or password"); + } + } + + timeLog.log(start, "login"); + return tokenId; + } + +} Added: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationAbstract.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationAbstract.java (rev 0) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationAbstract.java 2012-01-23 18:51:14 UTC (rev 1353) @@ -0,0 +1,122 @@ +package org.nuiton.wikitty.services; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.ApplicationConfig; +import org.nuiton.util.TimeLog; +import org.nuiton.wikitty.WikittyConfigOption; +import org.nuiton.wikitty.WikittyService; +import org.nuiton.wikitty.WikittyUtil; +import org.nuiton.wikitty.entities.Wikitty; +import org.nuiton.wikitty.entities.WikittyImpl; +import org.nuiton.wikitty.entities.WikittyToken; +import org.nuiton.wikitty.entities.WikittyTokenHelper; +import org.nuiton.wikitty.entities.WikittyUserHelper; +import org.nuiton.wikitty.query.WikittyQuery; +import org.nuiton.wikitty.query.WikittyQueryMaker; + +/** + * Classe abstraite pour simplifier l'implantation d'autre methode + * d'authentification. Ce serivce surcharge logout qui supprime le token + * et la methode getToken qui recupere ou genere un token. + * + * Pour implanter une nouveau service, il faut faire l'authentification de la + * personne si elle reussi, on retourne l'id du token retourne par getToken + * si l'authentification echoue il faut lever une exception. + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public abstract class WikittyServiceAuthenticationAbstract extends WikittyServiceDelegator { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(WikittyServiceAuthenticationAbstract.class); + + /** use to trace time of security code, timelog must not include delegator + * time in this class */ + final static private TimeLog timeLog = new TimeLog(WikittyServiceAuthenticationAbstract.class); + + protected ApplicationConfig config; + + public WikittyServiceAuthenticationAbstract(WikittyService delegate) { + this(null, delegate); + } + + public WikittyServiceAuthenticationAbstract( + ApplicationConfig config, WikittyService delegate) { + super(delegate); + this.config = config; + + if (config != null) { + long timeToLogInfo = config.getOptionAsInt(WikittyConfigOption. + WIKITTY_SERVICE_TIME_TO_LOG_INFO.getKey()); + long timeToLogWarn = config.getOptionAsInt(WikittyConfigOption. + WIKITTY_SERVICE_TIME_TO_LOG_WARN.getKey()); + timeLog.setTimeToLogInfo(timeToLogInfo); + timeLog.setTimeToLogWarn(timeToLogWarn); + } + } + + @Override + abstract public String login(String login, String password); + + /** + * Recherche si l'utilisateur n'a pas deja un token, et dans ce cas on + * retourne le meme token. Sinon on en cree un nouveau + * + * @param user l'utilisateur pour l'equel il faut recherche/creer le token + * @return le token de l'utilisateur + */ + protected String getToken(Wikitty user) { + // on recherche si l'utilisateur n'est pas deja authentifier. + // s'il l'est on lui retourne le meme token. + WikittyQuery query = new WikittyQueryMaker() + .eq(WikittyToken.ELEMENT_FIELD_WIKITTYTOKEN_USER, user.getId()) + .end() + .setLimit(1); + + String tokenId = getDelegate().findByQuery( + null, Collections.singletonList(query)).get(0); + + // on a retrouve un ancien token, on le reutilise peut-etre + if (tokenId != null) { + if (WikittySecurityUtil.isOutdated(config, getDelegate(), tokenId)) { + getDelegate().delete(tokenId, Arrays.asList(tokenId)); + tokenId = null; + } + } + + if (tokenId == null) { + // generation d'un nouveau token + tokenId = WikittyUtil.genSecurityTokenId(); + Wikitty wikittyToken = new WikittyImpl(tokenId); + // force add extension to wikitty + WikittyTokenHelper.addExtension(wikittyToken); + WikittyTokenHelper.setUser(wikittyToken, user.getId()); + WikittyTokenHelper.setDate(wikittyToken, new Date()); + getDelegate().store(null, Arrays.asList(wikittyToken), false); + + if (log.isDebugEnabled()) { + log.debug(String.format("token '%s' is for login '%s'", + tokenId, WikittyUserHelper.getLogin(user))); + } + } + return tokenId; + } + + @Override + public void logout(String securityToken) { + long start = TimeLog.getTime(); + if (securityToken != null) { + getDelegate().delete(securityToken, Arrays.asList(securityToken)); + } + timeLog.log(start, "logout"); + } + +} Added: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationLDAP.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationLDAP.java (rev 0) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationLDAP.java 2012-01-23 18:51:14 UTC (rev 1353) @@ -0,0 +1,163 @@ +package org.nuiton.wikitty.services; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; +import java.util.UUID; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.ApplicationConfig; +import org.nuiton.util.TimeLog; +import org.nuiton.wikitty.WikittyConfigOption; +import org.nuiton.wikitty.WikittyService; +import org.nuiton.wikitty.entities.Wikitty; +import org.nuiton.wikitty.entities.WikittyImpl; +import org.nuiton.wikitty.entities.WikittyUser; +import org.nuiton.wikitty.entities.WikittyUserHelper; +import org.nuiton.wikitty.query.WikittyQuery; +import org.nuiton.wikitty.query.WikittyQueryMaker; + +/** + * Permet de faire une authentification sur un serveur LDAP. Il faut pour cela + * fournir les informations pour se connecter au LDAP (url du serveur, pattern + * des DN des utilisateurs et toutes autres informations utiles). + * + * Il est possible d'indiquer de creer automatiquement l'objet WikittyUser + * associe au compte LDAP si celui-ci n'existe pas. Si le WikittyUser n'existe + * pas et qu'on a pas le droit de le creer, l'authentification echoue forcement + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public class WikittyServiceAuthenticationLDAP extends WikittyServiceAuthenticationAbstract { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(WikittyServiceAuthenticationLDAP.class); + /** use to trace time of security code, timelog must not include delegator + * time in this class */ + final static private TimeLog timeLog = new TimeLog(WikittyServiceAuthenticationLDAP.class); + + protected Properties jndiProp; + protected String ldapLoginPattern; + + public WikittyServiceAuthenticationLDAP(ApplicationConfig config, WikittyService ws) { + super(config, ws); + + long timeToLogInfo = config.getOptionAsInt(WikittyConfigOption. + WIKITTY_SERVICE_TIME_TO_LOG_INFO.getKey()); + long timeToLogWarn = config.getOptionAsInt(WikittyConfigOption. + WIKITTY_SERVICE_TIME_TO_LOG_WARN.getKey()); + timeLog.setTimeToLogInfo(timeToLogInfo); + timeLog.setTimeToLogWarn(timeToLogWarn); + + // on charge toutes les options jndi + Properties jndiPropTmp = config.getOptionStartsWith(WikittyConfigOption + .WIKITTY_SERVICE_AUTHENTICATION_LDAP_JNDI.getKey()); + + jndiProp = new Properties(); + for (Enumeration<String> e=(Enumeration<String>)jndiPropTmp.propertyNames(); e.hasMoreElements();) { + String key = e.nextElement(); + String value = jndiPropTmp.getProperty(key); + int debut = WikittyConfigOption.WIKITTY_SERVICE_AUTHENTICATION_LDAP_JNDI.getKey().length(); + key = key.substring(debut); + jndiProp.setProperty(key, value); + } + + // on charge l'url du serveur ldap + String serverUrl = config.getOption( + WikittyConfigOption.WIKITTY_SERVICE_AUTHENTICATION_LDAP_SERVER.getKey()); + jndiProp.put(Context.PROVIDER_URL, serverUrl); + + // on charge le pattern des logins (DN) + ldapLoginPattern = config.getOption( + WikittyConfigOption.WIKITTY_SERVICE_AUTHENTICATION_LDAP_LOGIN_PATTERN.getKey()); + } + + protected boolean canCreateUser() { + boolean result = config.getOptionAsBoolean( + WikittyConfigOption.WIKITTY_SERVICE_AUTHENTICATION_LDAP_AUTOCREATE_USER.getKey()); + return result; + } + + @Override + public String login(String login, String password) { + long start = TimeLog.getTime(); + + Wikitty user = null; + String tokenId; + + WikittyQuery criteria = new WikittyQueryMaker() + .eq(WikittyUser.FQ_FIELD_WIKITTYUSER_LOGIN, login).end(); + String userId = getDelegate().findByQuery(null, + Collections.singletonList(criteria)).get(0); + + boolean authenticated = false; + + // si l'utilisateur existe deja ou qu'on pourra le creer alors on va + // plus loin dans l'authentification + if (canCreateUser() || userId != null) { + + String ldapLogin = String.format(ldapLoginPattern, login); + + // on fait une copie pour etre thread safe (si plusieurs thread appele + // cette methode en meme temps + Hashtable<String, String> env = new Hashtable(jndiProp); + env.put(Context.SECURITY_PRINCIPAL, ldapLogin); + env.put(Context.SECURITY_CREDENTIALS, password); + + try { + DirContext dirContext = new InitialDirContext(env); + dirContext.close(); + authenticated = true; + } catch (NamingException eee) { + log.debug(String.format( + "Erreur lors de l'acces au serveur LDAP pour l'utilisateur %s -> %s", + login, ldapLogin), eee); + } + + if (authenticated) { + log.info(String.format( + "External authentication success for account '%s'", login)); + // authentification reussi + // si l'utilisateur n'existe pas encore on le cree si autorise par la config + if (userId == null) { + user = new WikittyImpl(); + WikittyUserHelper.addExtension(user); + WikittyUserHelper.setLogin(user, login); + // on met un mot de passe genere car l'authentification + // est faite par un moyen externe. Si pour une raison + // celui-ci n'est pas active, il ne faut pas que l'utilisateur + // puisse se loguer avec une mot de passe reel (trouvable facilement) + String generatedPassword = "external-" + UUID.randomUUID(); + WikittyUserHelper.setPassword(user, generatedPassword); + getDelegate().store(null, Collections.singletonList(user), false); + log.info(String.format( + "Automatic user creation for account '%s'", login)); + } else { + // sinon on le charge + user = WikittyServiceEnhanced.restore( + getDelegate(), null, userId); + } + } + } + + if (authenticated) { + tokenId = getToken(user); + } else { + throw new SecurityException("bad login or password"); + } + + timeLog.log(start, "login"); + return tokenId; + } + + +} Added: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthorization.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthorization.java (rev 0) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceAuthorization.java 2012-01-23 18:51:14 UTC (rev 1353) @@ -0,0 +1,903 @@ +/* + * #%L + * Wikitty :: api + * + * $Id: WikittyServiceSecurity.java 1311 2012-01-09 19:03:18Z bpoussin $ + * $HeadURL: http://svn.nuiton.org/svn/wikitty/trunk/wikitty-api/src/main/java/org/nuiton... $ + * %% + * Copyright (C) 2009 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.wikitty.services; + +import static org.nuiton.i18n.I18n._; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import org.apache.commons.lang.StringUtils; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.util.ApplicationConfig; +import org.nuiton.util.TimeLog; +import org.nuiton.wikitty.WikittyConfigOption; +import org.nuiton.wikitty.entities.Wikitty; +import org.nuiton.wikitty.entities.WikittyAuthorisation; +import org.nuiton.wikitty.entities.WikittyAuthorisationHelper; +import org.nuiton.wikitty.entities.WikittyExtension; +import org.nuiton.wikitty.entities.WikittyGroup; +import org.nuiton.wikitty.entities.WikittyGroupHelper; +import org.nuiton.wikitty.entities.WikittyImpl; +import org.nuiton.wikitty.entities.WikittyMetaExtensionUtil; +import org.nuiton.wikitty.WikittyService; +import org.nuiton.wikitty.entities.WikittyTokenHelper; +import org.nuiton.wikitty.entities.WikittyTreeNode; +import org.nuiton.wikitty.entities.WikittyUser; +import org.nuiton.wikitty.entities.WikittyUserHelper; +import org.nuiton.wikitty.WikittyUtil; +import org.nuiton.wikitty.query.WikittyQuery; +import org.nuiton.wikitty.query.WikittyQueryMaker; + +/** + * + * FIXME add security policy level two on wikittyAuthorisation to prevent writing + * + * @author poussin + * @version $Revision: 1311 $ + * + * Last update: $Date: 2012-01-09 20:03:18 +0100 (Mon, 09 Jan 2012) $ + * by : $Author: bpoussin $ + */ +public class WikittyServiceAuthorization extends WikittyServiceDelegator { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + final static private Log log = LogFactory.getLog(WikittyServiceAuthorization.class); + /** use to trace time of security code, timelog must not include delegator + * time in this class */ + final static private TimeLog timeLog = new TimeLog(WikittyServiceAuthorization.class); + + /** cache de l'id du groupe AppAdmin */ + transient protected String appAdminGroupId = null; + + /** si non null alors un mecanisme d'authentification externe est utilise */ + protected WikittyServiceSecurityExternalAuthentication auth; + protected boolean externalAuthOnly = false; + + /** + * + * @param config + * @param ws + * @param auth si non null alors un mecanisme d'authentification externe est + * utilise + */ + public WikittyServiceAuthorization(ApplicationConfig config, WikittyService ws, + WikittyServiceSecurityExternalAuthentication auth) { + super(ws); + this.auth = auth; + if (config != null) { + externalAuthOnly = config.getOptionAsBoolean(WikittyConfigOption. + WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_ONLY.getKey()); + long timeToLogInfo = config.getOptionAsInt(WikittyConfigOption. + WIKITTY_SECURITY_TIME_TO_LOG_INFO.getKey()); + long timeToLogWarn = config.getOptionAsInt(WikittyConfigOption. + WIKITTY_SECURITY_TIME_TO_LOG_WARN.getKey()); + timeLog.setTimeToLogInfo(timeToLogInfo); + timeLog.setTimeToLogWarn(timeToLogWarn); + } + } + + @Override + public void addWikittyServiceListener(WikittyListener listener, ServiceListenerType type) { + getDelegate().addWikittyServiceListener(listener, type); + } + + @Override + public void removeWikittyServiceListener(WikittyListener listener, ServiceListenerType type) { + getDelegate().addWikittyServiceListener(listener, type); + } + + @Override + public String login(String login, String password) { + long start = TimeLog.getTime(); + + Wikitty user = null; + String tokenId; + + WikittyQuery criteria = new WikittyQueryMaker() + .eq(WikittyUser.FQ_FIELD_WIKITTYUSER_LOGIN, login).end(); + String userId = getDelegate().findByQuery(null, + Collections.singletonList(criteria)).get(0); + + boolean authenticated = false; + if (auth != null) { + // on a un moyen externe d'authentification, on l'utilise en 1er + authenticated = auth.login(login, password); + if (authenticated) { + log.info(String.format( + "External authentication success for account '%s'", login)); + // authentification reussi + // si l'utilisateur n'existe pas encore on le cree + if (userId == null) { + user = new WikittyImpl(); + WikittyUserHelper.addExtension(user); + WikittyUserHelper.setLogin(user, login); + // on met un mot de passe genere car l'authentification + // est faite par un moyen externe. Si pour une raison + // celui-ci n'est pas active, il ne faut pas que l'utilisateur + // puisse se loguer avec une mot de passe reel (trouvable facilement) + String generatedPassword = "external-" + UUID.randomUUID(); + WikittyUserHelper.setPassword(user, generatedPassword); + getDelegate().store(null, Collections.singletonList(user), false); + log.info(String.format( + "Automatic user creation for account '%s'", login)); + } else { + // sinon on le charge + user = WikittyServiceEnhanced.restore( + getDelegate(), null, userId); + } + } + } + + // si pas authentification externe ou l'authentification n'a pas reussi + // c'est peut-etre un utilisateur local, et qu'on authorise l'authentification + // local + if (!authenticated && !externalAuthOnly) { + if (userId == null) { + log.info(String.format("no such account '%s'", login)); + } else { + user = WikittyServiceEnhanced.restore( + getDelegate(), null, userId); + // check password is valid + authenticated = WikittyUserHelper.getPassword(user).equals(password); + } + } + + if (authenticated) { + tokenId = WikittyUtil.genSecurityTokenId(); + Wikitty wikittyToken = new WikittyImpl(tokenId); + // force add extension to wikitty + WikittyTokenHelper.addExtension(wikittyToken); + WikittyTokenHelper.setUser(wikittyToken, user.getId()); + WikittyTokenHelper.setDate(wikittyToken, new Date()); + getDelegate().store(null, Arrays.asList(wikittyToken), false); + if (log.isDebugEnabled()) { + log.debug(String.format("token '%s' is for login '%s'", + tokenId, login)); + } + } else { + throw new SecurityException("bad login or password"); + } + + timeLog.log(start, "login"); + return tokenId; + } + + @Override + public void logout(String securityToken) { + long start = TimeLog.getTime(); + if (securityToken != null) { + getDelegate().delete(securityToken, Arrays.asList(securityToken)); + } + timeLog.log(start, "logout"); + } + + @Override + public WikittyEvent clear(String securityToken) { + String userId = getUserId(securityToken); + if (isAppAdmin(securityToken, userId)) { + // seul les AppAdmin on le droit a cette method + WikittyEvent result = getDelegate().clear(securityToken); + return result; + } else { + throw new SecurityException(_("user %s can't clear data", userId)); + } + } + + @Override + public WikittyEvent replay( + String securityToken, List<WikittyEvent> events, boolean force) { + long start = TimeLog.getTime(); + + String userId = getUserId(securityToken); + for (WikittyEvent e : events) { + if (e.getType().contains( + WikittyEvent.WikittyEventType.CLEAR_WIKITTY) + || e.getType().contains( + WikittyEvent.WikittyEventType.CLEAR_EXTENSION)) { + if (isAppAdmin(securityToken, userId)) { + // seul les AppAdmin on le droit a cette method + // les AppAdmin on meme le droit de tout faire, donc on + // peut sortir de la boucle + break; + } else { + throw new SecurityException(_("user %s can't clear data", userId)); + } + } + if (e.getType().contains(WikittyEvent.WikittyEventType.PUT_WIKITTY)) { + checkStore(securityToken, e.getWikitties().values()); + } + if (e.getType().contains(WikittyEvent.WikittyEventType.REMOVE_WIKITTY)) { + checkDelete(securityToken, e.getRemoveDate().keySet()); + } + if (e.getType().contains(WikittyEvent.WikittyEventType.PUT_EXTENSION)) { + checkStoreExtension(securityToken, e.getExtensions().values()); + } + if (e.getType().contains(WikittyEvent.WikittyEventType.REMOVE_EXTENSION)) { + checkDeleteExtension(securityToken, e.getDeletedExtensions()); + } + } + + timeLog.log(start, "replay"); + WikittyEvent result = getDelegate().replay(securityToken, events, force); + return result; + } + + + /** + * if app-admin group exists, return true if given userId is app-admin + * if app-admin group doesn't exists, return true if user is anonymous + */ + protected boolean userIsAnonymousOrAppAdmin(String securityToken, String userId) { + boolean userIsAnonymousOrAppAdmin = false; + + if (getAppAdminGroup(securityToken) == null) { + if (securityToken == null) { + // user is anonymous + userIsAnonymousOrAppAdmin = true; + } + } else { + if (isAppAdmin(securityToken, userId)) { + // user is appAdmin + userIsAnonymousOrAppAdmin = true; + } + } + + return userIsAnonymousOrAppAdmin; + } + + @Override + public WikittyEvent store(String securityToken, + Collection<Wikitty> wikitties, boolean force) { + long start = TimeLog.getTime(); + checkStore(securityToken, wikitties); + timeLog.log(start, "store"); + WikittyEvent result = getDelegate().store(securityToken, wikitties, force); + return result; + } + + /** + * Indique si on a bien le droit d'enregistrer tout les wikitties de la + * collection. Des que pour un wikitty on a pas les droits, une exception + * est levee. + * + * @param securityToken + * @param wikitties + * @return + */ + protected void checkStore(String securityToken, Collection<Wikitty> wikitties) { + String userId = getUserId(securityToken); + for (Wikitty wikitty : wikitties) { + if (wikitty == null) { + continue; + } + // usual case, a user want to store a wikitty + Wikitty oldVersion = WikittyServiceEnhanced.restore( + getDelegate(), securityToken, wikitty.getId()); + + Collection<String> newExtensions = new ArrayList<String>( + wikitty.getExtensionNames()); + if (oldVersion != null) { + // we already checked the rights for those extension + // re-do the check has too much cost, avoid it + newExtensions.removeAll(oldVersion.getExtensionNames()); + } + + // check that **reader** right on Security for all extension + for (String extensionName: newExtensions) { + + Wikitty extensionRights = restoreExtensionAuthorisation( + securityToken, extensionName); + boolean canCreate = extensionRights == null || + canRead(securityToken, userId, null, extensionRights); + if ( ! canCreate ) { + throw new SecurityException(_( + "user %s can't create instance of extension %s", + userId, extensionRights)); + } + } + + if (oldVersion != null) { // it's an update + + for (String fqFieldDirtyName : wikitty.getDirty()) { + + String concernedExtensionName = WikittyUtil + .getExtensionNameFromFQFieldName(fqFieldDirtyName); + + if (log.isTraceEnabled()) { + log.trace(String.format( + "will update field %s from extension %s", + fqFieldDirtyName, concernedExtensionName)); + } + + boolean fieldRequireAdminRights = // true if field is a field of WikittyAuthorisation + // concerned extension is "WikittyAuthorisation" + WikittyAuthorisation.EXT_WIKITTYAUTHORISATION.equals( + concernedExtensionName) + // or concerned extension is something like "AnyExtension:WikittyAuthorisation" + || WikittyAuthorisation.EXT_WIKITTYAUTHORISATION.equals( + WikittyUtil.getMetaExtensionNameFromFQMetaExtensionName( + concernedExtensionName)); + + boolean canChange; // will be true if user can modify the value of this field + // according to his level of rights + if (fieldRequireAdminRights) { + canChange = canAdmin(securityToken, + userId, concernedExtensionName, wikitty); + } else { + canChange = canWrite(securityToken, + userId, concernedExtensionName, wikitty); + } + + // TODO poussin 20101208 quel est l'interet de faire cette copie ? + // surtout quelle ne fonctionne pas car le oldVersion n'a pas + // forcement toutes les extensions du nouveau wikitty + // Code supprime et remplace +// if (canChange) { +// Object newValue = wikitty.getFqField(fqFieldDirtyName); +// oldVersion.setFqField(fqFieldDirtyName, newValue); +// } else { +// throw new SecurityException(_("user %s can't write field %s on wikitty %s", +// userId, fqFieldDirtyName, wikitty)); +// } + if (!canChange) { + throw new SecurityException(_("user %s can't write field %s on wikitty %s", + userId, fqFieldDirtyName, wikitty)); + } + } + } + } + } + + @Override + public List<Wikitty> restore(String securityToken, List<String> ids) { + String userId = getUserId(securityToken); + List<Wikitty> wikitties = getDelegate().restore(securityToken, ids); + + long start = TimeLog.getTime(); + for (Wikitty wikitty : wikitties) { + if (wikitty != null) { + refuseUnauthorizedRead(securityToken, userId, wikitty); + } + } + timeLog.log(start, "restore"); + return wikitties; + } + + /** throw an exception if read is not allowed */ + protected void refuseUnauthorizedRead( String securityToken, + String userId, + Wikitty wikitty) { + if (wikitty != null) { + for (String extensionName : wikitty.getExtensionNames()) { + if ( ! canRead(securityToken, userId, extensionName, wikitty)) { + throw new SecurityException(_( + "user %s can't read extension %s on wikitty %s," + + " it may be due to a global policy on the wikitty", + userId, extensionName, wikitty)); + } + } + } + } + + protected boolean canRead(String securityToken, String userId, + String extensionName, Wikitty wikitty) { + + boolean canRead = false; + + // first, check per-extension rights + if (wikitty.hasMetaExtension(WikittyAuthorisation.EXT_WIKITTYAUTHORISATION, + extensionName)) { + // there is a policy on the extension + canRead = isReader(securityToken, userId, wikitty, extensionName) + || canWrite(securityToken, userId, extensionName, wikitty); + } + + if ( ! canRead && + wikitty.hasExtension(WikittyAuthorisation.EXT_WIKITTYAUTHORISATION) ) { + // there is no policy for this extension + // but there is a policy for all extension of wikitty + canRead = isReader(securityToken, userId, wikitty, null) + || canWrite(securityToken, userId, extensionName, wikitty); + } else { + // no security policy, everything is allowed + canRead = true; + } + + return canRead; + } + + protected boolean canWrite(String securityToken, String userId, + String extensionName, Wikitty wikitty) { + boolean canWrite = false; + + // first, check per-extension rights + if (wikitty.hasMetaExtension(WikittyAuthorisation.EXT_WIKITTYAUTHORISATION, + extensionName)) { + // there is a policy on the extension of fqFieldDirtyName + canWrite = isWriter(securityToken, userId, wikitty, extensionName) + || canAdmin(securityToken, userId, extensionName, wikitty); + } + + if ( ! canWrite && + wikitty.hasExtension(WikittyAuthorisation.EXT_WIKITTYAUTHORISATION) ) { + // there is no policy for this extension + // but there is a policy for all extension of wikitty + canWrite = isWriter(securityToken, userId, wikitty, null) + || canAdmin(securityToken, userId, extensionName, wikitty); + } else { + // no security policy, everything is allowed + canWrite = true; + } + + return canWrite; + } + + protected boolean canAdmin(String securityToken, String userId, + String extensionName, Wikitty wikitty) { + + boolean canAdmin = false; + + // first, check per-extension rights + if (wikitty.hasMetaExtension(WikittyAuthorisation.EXT_WIKITTYAUTHORISATION, + extensionName)) { + // there is a policy on the extension of fqFieldDirtyName + canAdmin = isAdmin(securityToken, userId, wikitty, extensionName) + || isOwner(securityToken, userId, wikitty, extensionName); + } + if ( ! canAdmin && + wikitty.hasExtension(WikittyAuthorisation.EXT_WIKITTYAUTHORISATION) ) { + // there is no policy for this extension + // but there is a policy for all extension of wikitty + canAdmin = isAdmin(securityToken, userId, wikitty, null) + || isOwner(securityToken, userId, wikitty, null); + } + if ( ! canAdmin ) { + // still not admin, check appAdmin + canAdmin = isAppAdmin(securityToken, userId); + } + + return canAdmin; + } + + @Override + public WikittyEvent delete(String securityToken, Collection<String> ids) { + long start = TimeLog.getTime(); + checkDelete(securityToken, ids); + timeLog.log(start, "delete"); + WikittyEvent result = getDelegate().delete(securityToken, ids); + return result; + } + + /** + * Check if we can delete all id passed in argument + * @param securityToken + * @param ids + */ + public void checkDelete(String securityToken, Collection<String> ids) { + String userId = getUserId(securityToken); + List<String> idsAsList = new ArrayList<String>(ids); + List<Wikitty> wikitties = getDelegate().restore(securityToken, idsAsList); + for (Wikitty wikitty : wikitties) { + if (wikitty != null) { + for (String extensionName : wikitty.getExtensionNames()) { + if ( ! canWrite(securityToken, userId, extensionName, wikitty)) { + throw new SecurityException(_( + "user %s doesn't have rights on extension %s on wikitty %s", + userId, extensionName, wikitty)); + } + } + } + } + } + + @Override + public boolean canWrite(String securityToken, Wikitty wikitty) { + boolean result = true; + String userId = getUserId(securityToken); + for (String extName : wikitty.getExtensionNames()) { + result = result && isWriter(securityToken, userId, wikitty, extName); + if (!result) { + break; + } + } + return result; + } + + @Override + public boolean canDelete(String securityToken, String wikittyId) { + boolean result = true; + Wikitty wikitty = WikittyServiceEnhanced.restore( + getDelegate(), securityToken, wikittyId); + if (wikitty != null) { + String userId = getUserId(securityToken); + for (String extName : wikitty.getExtensionNames()) { + result = result && isWriter(securityToken, userId, wikitty, extName); + if (!result) { + break; + } + } + } + return result; + } + + @Override + public boolean canRead(String securityToken, String wikittyId) { + boolean result = true; + String userId = getUserId(securityToken); + Wikitty wikitty = WikittyServiceEnhanced.restore(getDelegate(), securityToken, wikittyId); + if (wikitty == null) { + result = false; + } else { + for (String extName : wikitty.getExtensionNames()) { + result = result && isReader(securityToken, userId, wikitty, extName); + if (!result) { + break; + } + } + } + return result; + } + + /* *** storing and restoring extensions ***/ + + protected void checkStoreExtension(String securityToken, + Collection<WikittyExtension> exts) { + String userId = getUserId(securityToken); + if ( ! isAppAdmin(securityToken, userId)) { + for (WikittyExtension extension : exts) { + Wikitty extensionAuthorisation = restoreExtensionAuthorisation(securityToken, extension.getName()); + if (extensionAuthorisation != null) { + // canWrite is true if this user can modify the field for this extension + boolean canWrite = canWrite(securityToken, userId, null, extensionAuthorisation); + if ( ! canWrite) { + throw new SecurityException(_("user %s don't have write right for extension %s", userId, extension)); + } + } + } + } + } + + protected void checkDeleteExtension (String securityToken, Collection<String> extNames) { + // FIXME 20101115 poussin check security for extension deletion + } + + @Override + public WikittyEvent storeExtension(String securityToken, + Collection<WikittyExtension> exts) { + long start = TimeLog.getTime(); + checkStoreExtension(securityToken, exts); + timeLog.log(start, "storeExtension"); + return getDelegate().storeExtension(securityToken, exts); + } + + @Override + public WikittyEvent deleteExtension( + String securityToken, Collection<String> extNames) { + long start = TimeLog.getTime(); + checkDeleteExtension(securityToken, extNames); + timeLog.log(start, "deleteExtension"); + return getDelegate().deleteExtension(securityToken, extNames); + } + + private void checkRestoreTreeNode(String securityToken, String userId, WikittyTreeNode treeNode) { + Wikitty treeNodeWikitty = WikittyUtil.getWikitty(getDelegate(), securityToken, treeNode); + refuseUnauthorizedRead(securityToken, userId, treeNodeWikitty); + } + + @Override + public WikittyEvent deleteTree(String securityToken, String treeNodeId) { + Wikitty treeNodeWikitty = WikittyServiceEnhanced.restore( + getDelegate(), securityToken, treeNodeId); + + long start = TimeLog.getTime(); + Collection<Wikitty> wikitties = Collections.singletonList(treeNodeWikitty); + // TODO poussin 20101222 perhaps we must check deletion authorization + // for all children ? + checkStore(securityToken, wikitties); + timeLog.log(start, "deleteTree"); + return getDelegate().deleteTree(securityToken, treeNodeId); + } + + @Override + public Wikitty restoreVersion(String securityToken, String wikittyId, String version) { + Wikitty wikitty = getDelegate().restoreVersion(securityToken, wikittyId, version); + long start = TimeLog.getTime(); + String userId = getUserId(securityToken); + refuseUnauthorizedRead(securityToken, userId, wikitty); + timeLog.log(start, "restoreVersion"); + return wikitty; + } + + @Override + public void syncSearchEngine(String securityToken) { + long start = TimeLog.getTime(); + String userId = getUserId(securityToken); + if (isAppAdmin(securityToken, userId)) { + timeLog.log(start, "syncSearchEngine"); + // seul les AppAdmin on le droit a cette method + getDelegate().syncSearchEngine(securityToken); + } else { + throw new SecurityException(_("user %s can't sync search engine", + getUserId(securityToken))); + } + } + + // + // Method helper to check right + // + + /** tell who own a token (who got this token after login). + * @param securityToken the token whose owner will be returned + * @return a wikitty Id (wikitty has extension WikittyUser) + */ + protected String getUserId(String securityToken) { + String result = null; + // recuperation de l'utilisateur associe au securityToken + // le securityToken est aussi l'id de l'objet + if (securityToken != null) { + Wikitty securityTokenWikitty = WikittyServiceEnhanced.restore( + getDelegate(), securityToken, securityToken); + if (securityTokenWikitty == null) { + throw new SecurityException("bad (obsolete ?) token"); + } else { + result = WikittyTokenHelper.getUser(securityTokenWikitty); + } + } + return result; + } + + /** + * + * @param securityToken + * @param userId + * @param wikitty + * @param extensionName may be null + * @return + */ + protected boolean isReader(String securityToken, String userId, Wikitty wikitty, String extensionName) { + boolean result; + String metaFieldName = WikittyUtil.getMetaFieldName( + WikittyAuthorisation.EXT_WIKITTYAUTHORISATION, extensionName, + WikittyAuthorisation.FIELD_WIKITTYAUTHORISATION_READER); + result = isMember(securityToken, userId, wikitty, metaFieldName, true); + return result; + } + + /** + * + * @param securityToken + * @param userId + * @param wikitty + * @param extensionName may be null + * @return + */ + protected boolean isWriter(String securityToken, String userId, Wikitty wikitty, String extensionName) { + boolean result; + String metaFieldName = WikittyUtil.getMetaFieldName( + WikittyAuthorisation.EXT_WIKITTYAUTHORISATION, extensionName, + WikittyAuthorisation.FIELD_WIKITTYAUTHORISATION_WRITER); + log.trace("meta field name " + metaFieldName); + result = isMember(securityToken, userId, wikitty, metaFieldName); + return result; + } + + /** + * + * @param securityToken + * @param userId + * @param wikitty + * @param extensionName may be null + * @return + */ + protected boolean isAdmin(String securityToken, String userId, Wikitty wikitty, String extensionName) { + boolean result; + String metaFieldName = WikittyUtil.getMetaFieldName( + WikittyAuthorisation.EXT_WIKITTYAUTHORISATION, extensionName, + WikittyAuthorisation.FIELD_WIKITTYAUTHORISATION_ADMIN); + result = isMember(securityToken, userId, wikitty, metaFieldName); + return result; + } + + /** true if given user is owner + * + * @param securityToken + * @param userId + * @param wikitty + * @param extensionName may be null + * @return + */ + protected boolean isOwner(String securityToken, String userId, Wikitty wikitty, String extensionName) { + + String metaFieldName = WikittyUtil.getMetaFieldName( + WikittyAuthorisation.EXT_WIKITTYAUTHORISATION, extensionName, + WikittyAuthorisation.FIELD_WIKITTYAUTHORISATION_OWNER); + + String actualExtensionName = WikittyUtil.getExtensionNameFromFQFieldName(metaFieldName); + String fieldName = WikittyUtil.getFieldNameFromFQFieldName(metaFieldName); + + String owner = wikitty.getFieldAsString(actualExtensionName, fieldName); + + boolean isOwner; + if (owner == null) { + isOwner = false; + } else { + isOwner = owner.equals(userId); + } + return isOwner; + } + + /** {@link #isMember(String, String, Wikitty, String, boolean)} with default value */ + protected boolean isMember(String securityToken, String userId, Wikitty extensionRights, String fqFieldName) { + // by default, user is considered not member if he is not in the group, so passing "false" + return isMember(securityToken, userId, extensionRights, fqFieldName, false); + } + + /** check if a user is listed in a level of rights + * + * @param securityToken + * @param userId the userId to look for + * @param extensionRights a wikitty with WikittyAuthorisation as extension <strong>OR</strong> meta-extension + * @param fqFieldName the field to look into, it should be one of the field of extension WikittyAuthorisation + * it has to be a FQN and may contain an extension-name if using meta-extension + * @param considerEmptyGroupAsMembership if true, an empty field value will be considered as + * "every-one is in the group". Most of the time, it will be false but true should be + * passed for "reader" level because user has right to read if he belongs to "reader" OR + * if reader is empty + * @return true if userId appear in the single/list of group/user of given field + */ + protected boolean isMember(String securityToken, String userId, + Wikitty extensionRights, String fqFieldName, boolean considerEmptyGroupAsMembership) { + + String extensionName = WikittyUtil.getExtensionNameFromFQFieldName(fqFieldName); + String fieldName = WikittyUtil.getFieldNameFromFQFieldName(fqFieldName); + + Set<String> groupOrUser = extensionRights.getFieldAsSet(extensionName, + fieldName, + String.class); + + boolean isMember; + if (groupOrUser == null || groupOrUser.isEmpty()) { + isMember = considerEmptyGroupAsMembership; + } else { + isMember = isMember(securityToken, userId, groupOrUser); + } + + if ( ! isMember) { + // user don't have right on current object, check parent right + String parentId = WikittyAuthorisationHelper.getParent(extensionRights); + if (parentId != null) { + Wikitty parent = WikittyServiceEnhanced.restore( + getDelegate(), securityToken, parentId); + if (parent != null) { + isMember = isMember(securityToken, userId, parent, fqFieldName); + } + } + } + return isMember; + } + + /** check if a given user belong to the group of app-admins. */ + protected boolean isAppAdmin(String securityToken, String userId) { + // si le group n'existe pas alors tout le monde est admin + boolean result = true; + Wikitty group = getAppAdminGroup(securityToken); + if (group != null) { + Set<String> ids = WikittyGroupHelper.getMembers(group); + result = isMember(securityToken, userId, ids); + } + return result; + } + + /** get the wikitty with extension WikittyGroup that contains all app-admin. */ + protected Wikitty getAppAdminGroup(String securityToken) { + // on a deja fait la recherche precedement, on essaie de reutilise + // le meme id + Wikitty group = WikittyServiceEnhanced.restore( + getDelegate(), securityToken, appAdminGroupId); + if (group == null) { + // 1er fois, on le recherche + WikittyQuery criteria = new WikittyQueryMaker() + .eq(WikittyGroup.FQ_FIELD_WIKITTYGROUP_NAME, + WikittySecurityHelper.WIKITTY_APPADMIN_GROUP_NAME).end(); + String groupId = getDelegate().findByQuery( + securityToken, Collections.singletonList(criteria)).get(0); + appAdminGroupId = groupId; + group = WikittyServiceEnhanced.restore( + getDelegate(), securityToken, appAdminGroupId); + } + + return group; + } + + /** + * Verifie recursivement si un utilisateur est dans un groupe qui peut etre + * constitue d'autre groupe ou d'utilisateur + * + * @param userId l'utilisateur recherche + * @param groupOrUser la liste des id d'utilisateurs ou d'autres groupes + * @return vrai si userId est retrouve, false sinon + */ + protected boolean isMember( + String securityToken, String userId, Set<String> groupOrUser) { + if (groupOrUser != null) { + for (String id : groupOrUser) { + if (StringUtils.equals(id, userId)) { + // on a l'id du user, on l'autorise + return true; + } else { + // sinon, on charge l'objet car ca pourrait etre un groupe + // dans lequel il faut cherche le user + Wikitty groupWikitty = WikittyServiceEnhanced.restore( + getDelegate(), securityToken, id); + if (groupWikitty != null && + WikittyGroupHelper.hasExtension(groupWikitty)) { + Set<String> members = + WikittyGroupHelper.getMembers(groupWikitty); + return isMember(securityToken, userId, members); + } + } + } + } + return false; // not found in groupOrUser + } + + /** + * restore the wikitty authorisation attached to given extension. + * + * @return a wikitty with WikittyAuthorisation extension, or null if given + * extension has no security policy attached + */ + protected Wikitty restoreExtensionAuthorisation(String securityToken, + WikittyExtension extension) { + return restoreExtensionAuthorisation(securityToken, extension.getName()); + } + + /** + * restore the wikitty authorisation attached to given extension. + * + * @return a wikitty with WikittyAuthorisation extension, or null if given + * extension has no security policy attached + */ + protected Wikitty restoreExtensionAuthorisation(String securityToken, + String extensionName) { + String wikittyAuthorisationId = WikittyMetaExtensionUtil.generateId( + WikittyAuthorisation.EXT_WIKITTYAUTHORISATION, extensionName); + Wikitty wikittyAuthorisation = WikittyServiceEnhanced.restore( + getDelegate(), securityToken, wikittyAuthorisationId); + if (wikittyAuthorisation == null) { + log.debug(extensionName + " has no authorization attached"); + } + return wikittyAuthorisation; + } + +} Modified: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurity.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurity.java 2012-01-20 17:11:12 UTC (rev 1352) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurity.java 2012-01-23 18:51:14 UTC (rev 1353) @@ -67,7 +67,9 @@ * * Last update: $Date$ * by : $Author$ + * @deprecated since 3.4 use WikittyServiceAuthentication and WikittyServiceAuthorization */ +@Deprecated public class WikittyServiceSecurity extends WikittyServiceDelegator { /** to use log facility, just put in your code: log.info(\"...\"); */ @@ -98,9 +100,9 @@ externalAuthOnly = config.getOptionAsBoolean(WikittyConfigOption. WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_ONLY.getKey()); long timeToLogInfo = config.getOptionAsInt(WikittyConfigOption. - WIKITTY_SECURITY_TIME_TO_LOG_INFO.getKey()); + WIKITTY_SERVICE_TIME_TO_LOG_INFO.getKey()); long timeToLogWarn = config.getOptionAsInt(WikittyConfigOption. - WIKITTY_SECURITY_TIME_TO_LOG_WARN.getKey()); + WIKITTY_SERVICE_TIME_TO_LOG_WARN.getKey()); timeLog.setTimeToLogInfo(timeToLogInfo); timeLog.setTimeToLogWarn(timeToLogWarn); } Modified: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurityExternalAuthentication.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurityExternalAuthentication.java 2012-01-20 17:11:12 UTC (rev 1352) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurityExternalAuthentication.java 2012-01-23 18:51:14 UTC (rev 1353) @@ -10,7 +10,9 @@ * * Last update: $Date$ * by : $Author$ + * @deprecated since 3.4 use WikittyServiceAuthentication and WikittyServiceAuthorization */ +@Deprecated public interface WikittyServiceSecurityExternalAuthentication { /** Modified: trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurityExternalAuthenticationLDAP.java =================================================================== --- trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurityExternalAuthenticationLDAP.java 2012-01-20 17:11:12 UTC (rev 1352) +++ trunk/wikitty-api/src/main/java/org/nuiton/wikitty/services/WikittyServiceSecurityExternalAuthenticationLDAP.java 2012-01-23 18:51:14 UTC (rev 1353) @@ -19,7 +19,9 @@ * * Last update: $Date$ * by : $Author$ + * @deprecated since 3.4 use WikittyServiceAuthenticationLDAP and WikittyServiceAuthorization */ +@Deprecated public class WikittyServiceSecurityExternalAuthenticationLDAP implements WikittyServiceSecurityExternalAuthentication{ @@ -35,25 +37,25 @@ // on charge toutes les options jndi Properties jndiPropTmp = config.getOptionStartsWith(WikittyConfigOption - .WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_LDAP_JNDI.getKey()); + .WIKITTY_SERVICE_AUTHENTICATION_LDAP_JNDI.getKey()); jndiProp = new Properties(); for (Enumeration<String> e=(Enumeration<String>)jndiPropTmp.propertyNames(); e.hasMoreElements();) { String key = e.nextElement(); String value = jndiPropTmp.getProperty(key); - int debut = WikittyConfigOption.WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_LDAP_JNDI.getKey().length(); + int debut = WikittyConfigOption.WIKITTY_SERVICE_AUTHENTICATION_LDAP_JNDI.getKey().length(); key = key.substring(debut); jndiProp.setProperty(key, value); } // on charge l'url du serveur ldap String serverUrl = config.getOption( - WikittyConfigOption.WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_LDAP_SERVER.getKey()); + WikittyConfigOption.WIKITTY_SERVICE_AUTHENTICATION_LDAP_SERVER.getKey()); jndiProp.put(Context.PROVIDER_URL, serverUrl); // on charge le pattern des logins (DN) ldapLoginPattern = config.getOption( - WikittyConfigOption.WIKITTY_SECURITY_EXTERNAL_AUTHENTICATION_LDAP_LOGIN_PATTERN.getKey()); + WikittyConfigOption.WIKITTY_SERVICE_AUTHENTICATION_LDAP_LOGIN_PATTERN.getKey()); } public boolean login(String login, String password) { Added: trunk/wikitty-api/src/test/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationLDAPTest.java =================================================================== --- trunk/wikitty-api/src/test/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationLDAPTest.java (rev 0) +++ trunk/wikitty-api/src/test/java/org/nuiton/wikitty/services/WikittyServiceAuthenticationLDAPTest.java 2012-01-23 18:51:14 UTC (rev 1353) @@ -0,0 +1,49 @@ +package org.nuiton.wikitty.services; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Ignore; +import org.junit.Test; +import org.nuiton.util.ApplicationConfig; +import org.nuiton.wikitty.WikittyClient; +import org.nuiton.wikitty.WikittyConfig; +import org.nuiton.wikitty.WikittyConfigOption; + +/** + * + * @author poussin + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +public class WikittyServiceAuthenticationLDAPTest { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(WikittyServiceAuthenticationLDAPTest.class); + + @Test + @Ignore // on l'ignore car il faut avoir accept a un LDAP et mettre en clair un mot de passe :( + public void testLDAP() throws Exception { + ApplicationConfig config = WikittyConfig.getConfig(); + + String services = config.getOption(WikittyConfigOption.WIKITTY_WIKITTYSERVICE_COMPONENTS.getKey()); + services += "," + WikittyServiceAuthenticationLDAP.class.getName(); + config.setOption(WikittyConfigOption.WIKITTY_WIKITTYSERVICE_COMPONENTS.getKey(), + services); + + config.setOption(WikittyConfigOption + .WIKITTY_SERVICE_AUTHENTICATION_LDAP_SERVER.getKey(), + "ldap://intranet:389"); + config.setOption(WikittyConfigOption + .WIKITTY_SERVICE_AUTHENTICATION_LDAP_LOGIN_PATTERN.getKey(), + "uid=%s,ou=People,dc=codelutin,dc=home"); + WikittyClient client = new WikittyClient(config); + client.login("poussin", "xxxxxxxx"); + System.out.println("token:" + client.getSecurityToken()); +// System.out.println("user: " + client.getUser()); + + System.out.println("config: " + config.getFlatOptions()); + + } +}
participants (1)
-
bpoussin@users.nuiton.org