Index: topia/src/java/org/codelutin/topia/security/TopiaGroupPrincipal.java diff -u topia/src/java/org/codelutin/topia/security/TopiaGroupPrincipal.java:1.1 topia/src/java/org/codelutin/topia/security/TopiaGroupPrincipal.java:1.2 --- topia/src/java/org/codelutin/topia/security/TopiaGroupPrincipal.java:1.1 Fri Apr 29 16:00:39 2005 +++ topia/src/java/org/codelutin/topia/security/TopiaGroupPrincipal.java Wed May 4 16:09:41 2005 @@ -24,7 +24,7 @@ * Created: 29 avr. 2005 * * @author Arnaud Thimel -* @version $Revision: 1.1 $ +* @version $Revision: 1.2 $ */ @@ -33,6 +33,7 @@ public class TopiaGroupPrincipal extends TopiaPrincipal { public TopiaGroupPrincipal(String name) { + //super(name); this.name = "group." + name; } } Index: topia/src/java/org/codelutin/topia/security/TopiaLoginModule.java diff -u topia/src/java/org/codelutin/topia/security/TopiaLoginModule.java:1.1 topia/src/java/org/codelutin/topia/security/TopiaLoginModule.java:1.2 --- topia/src/java/org/codelutin/topia/security/TopiaLoginModule.java:1.1 Fri Apr 29 16:00:39 2005 +++ topia/src/java/org/codelutin/topia/security/TopiaLoginModule.java Wed May 4 16:09:41 2005 @@ -24,7 +24,7 @@ * Created: 29 avr. 2005 * * @author Arnaud Thimel - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ */ package org.codelutin.topia.security; @@ -145,7 +145,10 @@ */ public boolean logout() throws LoginException { System.out.println("org.codelutin.topia.security.TopiaLoginModule.logout"); - return false; + subject = null; + principals = null; + callbackHandler = null; + return true; } } Index: topia/src/java/org/codelutin/topia/security/TopiaPrincipal.java diff -u topia/src/java/org/codelutin/topia/security/TopiaPrincipal.java:1.1 topia/src/java/org/codelutin/topia/security/TopiaPrincipal.java:1.2 --- topia/src/java/org/codelutin/topia/security/TopiaPrincipal.java:1.1 Fri Apr 29 16:00:39 2005 +++ topia/src/java/org/codelutin/topia/security/TopiaPrincipal.java Wed May 4 16:09:41 2005 @@ -24,7 +24,7 @@ * Created: 29 avr. 2005 * * @author Arnaud Thimel -* @version $Revision: 1.1 $ +* @version $Revision: 1.2 $ */ @@ -36,6 +36,12 @@ public class TopiaPrincipal implements Principal { protected String name; + + /* + public TopiaPrincipal(String name) { + this.name = name; + } + */ /* (non-Javadoc) * @see java.security.Principal#getName() Index: topia/src/java/org/codelutin/topia/security/TopiaSimpleCallbackHandler.java diff -u topia/src/java/org/codelutin/topia/security/TopiaSimpleCallbackHandler.java:1.1 topia/src/java/org/codelutin/topia/security/TopiaSimpleCallbackHandler.java:1.2 --- topia/src/java/org/codelutin/topia/security/TopiaSimpleCallbackHandler.java:1.1 Fri Apr 29 16:00:39 2005 +++ topia/src/java/org/codelutin/topia/security/TopiaSimpleCallbackHandler.java Wed May 4 16:09:41 2005 @@ -24,7 +24,7 @@ * Created: 29 avr. 2005 * * @author Arnaud Thimel -* @version $Revision: 1.1 $ +* @version $Revision: 1.2 $ */ @@ -53,7 +53,7 @@ this.username = username; this.password = password; } - + /* (non-Javadoc) * @see javax.security.auth.callback.CallbackHandler#handle(javax.security.auth.callback.Callback[]) */ @@ -68,7 +68,6 @@ pc.setPassword(password.toCharArray()); } else throw new UnsupportedCallbackException(callbacks[i]); } - } } Index: topia/src/java/org/codelutin/topia/security/TopiaUserPrincipal.java diff -u topia/src/java/org/codelutin/topia/security/TopiaUserPrincipal.java:1.1 topia/src/java/org/codelutin/topia/security/TopiaUserPrincipal.java:1.2 --- topia/src/java/org/codelutin/topia/security/TopiaUserPrincipal.java:1.1 Fri Apr 29 16:00:39 2005 +++ topia/src/java/org/codelutin/topia/security/TopiaUserPrincipal.java Wed May 4 16:09:41 2005 @@ -24,7 +24,7 @@ * Created: 29 avr. 2005 * * @author Arnaud Thimel -* @version $Revision: 1.1 $ +* @version $Revision: 1.2 $ */ @@ -33,6 +33,7 @@ public class TopiaUserPrincipal extends TopiaPrincipal { public TopiaUserPrincipal(String name) { + //super(name); this.name = "user." + name; } } Index: topia/src/java/org/codelutin/topia/security/TopiaAccessController.java diff -u /dev/null topia/src/java/org/codelutin/topia/security/TopiaAccessController.java:1.1 --- /dev/null Wed May 4 16:09:46 2005 +++ topia/src/java/org/codelutin/topia/security/TopiaAccessController.java Wed May 4 16:09:41 2005 @@ -0,0 +1,70 @@ +/* *##% +* Copyright (C) 2002, 2003, 2004, 2005 Code Lutin, +* Cédric Pineau, Benjamin Poussin, +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* 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 Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*##%*/ + +/* * +* TopiaAccessController.java +* +* Created: 4 mai 2005 +* +* @author Arnaud Thimel +* @version $Revision: 1.1 $ +*/ + + +package org.codelutin.topia.security; + +import java.security.AccessControlException; +import java.security.AccessController; +import java.util.logging.Logger; + +import javax.security.auth.Subject; + +import org.codelutin.topia.TopiaEntity; +import org.codelutin.topia.TopiaException; + + +public class TopiaAccessController { + + static private Logger log = Logger.getLogger("org.codelutin.topia.security.TopiaAccessController"); + + public static void checkPermission(TopiaEntity entity, String actions) throws TopiaSecurityException { + log.finest("Checking permissions for entity : " + entity); + if (entity == null) + throw new TopiaSecurityException("TopiaEntity cannot be null"); + if (actions == null) + actions = "read"; //TODO Arno : DEFAULT_ACTIONS ??? + Subject subj =Subject.getSubject(AccessController.getContext()); + if (subj == null) + throw new TopiaSecurityException("Use doAs() and login first"); + String topiaId; + try { + topiaId = entity.get_topiaId_(); + } catch (TopiaException e) { + throw new TopiaSecurityException("Invalid TopiaEntity"); + } + try { + AccessController.checkPermission(new TopiaPermission(topiaId, subj.getPrincipals(), actions)); + } catch (AccessControlException e) { + throw new TopiaSecurityException("access denied to \"" + topiaId + "\""); + } + log.finest("Permission granted for entity : " + entity); + } + +} Index: topia/src/java/org/codelutin/topia/security/TopiaPermission.java diff -u /dev/null topia/src/java/org/codelutin/topia/security/TopiaPermission.java:1.1 --- /dev/null Wed May 4 16:09:46 2005 +++ topia/src/java/org/codelutin/topia/security/TopiaPermission.java Wed May 4 16:09:41 2005 @@ -0,0 +1,351 @@ +/* *##% + * Copyright (C) 2002, 2003, 2004, 2005 Code Lutin, + * Cédric Pineau, Benjamin Poussin, + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * TopiaPermission.java + * + * Created: 2 mai 2005 + * + * @author Benjamin Poussin + * @version $Revision: 1.1 $ + */ + +package org.codelutin.topia.security; + +import java.security.Permission; +import java.security.Principal; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.logging.Logger; + +public class TopiaPermission extends Permission { + + /** to use log facility, just put in your code: log.info("..."); */ + static private Logger log = Logger.getLogger("org.codelutin.topia.security.TopiaPermission"); + + static public int READ = 0x1; + + static public int WRITE = 0x2; + + static public int ADMIN = 0x4; + + protected String id = null; + + protected Map principals = null; + + /** les actions autorisees pour cette permission READ, WRITE ou ADMIN */ + protected int actions = 0x0; + + /** Représente l'attribut actions sous forme de chaine */ + protected String actionsAsString = null; + + /** + * identifiant de l'objet sur le quel porte cette permission. + * @return String + */ + public String getId() { + return id; + } + + + /** + * Pour chaque entrée de cette map, la clé est un nom de classe + * qui implémente l'interface Principal, la valeur est un Set qui + * contient tous les noms sur les quels porte cette permission. + * + * @return Map + */ + public Map getPrincipals() { + return principals; + } + + /** + * Transforme actionsAsString en un entier. + * @param actionsAsString - combinaison de mots cles "read" "write" "admin" separe par des virgules. Ex : "read,write" + * @return 0 si aucune permission. Une combinaison de TopiaPermission.READ, TopiaPermission.WRITE et TopiaPermission.ADMIN + * s'il y a une erreur, emet IllegalArgumentException. + */ + protected static int actionsAsStringToInt(String actionsAsString) { + int result = 0x0; + String[] actions = actionsAsString.split(","); + for (int i = 0; i < actions.length; i++) { + if (actions[i].trim().equalsIgnoreCase("READ")) { + result |= READ; + } else if (actions[i].trim().equalsIgnoreCase("WRITE")) { + result |= WRITE; + } else if (actions[i].trim().equalsIgnoreCase("ADMIN")) { + result |= ADMIN; + } else { + throw new IllegalArgumentException("actions not supported: " + + actions[i]); + } + } + return result; + } + + /* Constructeurs */ + /** + * Permet de construire une Permission avec un id, un set de principals et des actions + * @param id l'id de l'objet cible + * @param principals la liste des principals disponible dans le subject + * @param actionsAsString les actions possibles sur l'objet sous forme de chaine. Voir actionsAsStringToInt. + * @see #actionsAsStringToInt(String) + */ + public TopiaPermission(String id, Set principals, String actionsAsString) { + this(id, principals, actionsAsStringToInt(actionsAsString)); + } + + /** + * Permet de construire une Permission avec un id, un set de principals et des actions + * @param id l'id de l'objet cible + * @param principals la liste des principals disponible dans le subject + * @param actions les actions possibles sur l'objet + */ + public TopiaPermission(String id, Set principals, int actions) { + super(id); + this.id = id; + this.actions = actions; + this.principals = new HashMap(); + for (Iterator i = principals.iterator(); i.hasNext();) { + Principal p = (Principal) i.next(); + addPrincipal(p.getClass().getName(), p.getName()); + } + } + + /** + * Permet de construire une Permission. + * L'id et les principaux sont lu depuis la chaine 's' (voir init). + * Les actions autorises sont READ. + * + * @param s l'id, suivi de n fois (la classe du principal suivi du nom du + * principal). Voir {@link #init}. + * @see #init(String, int) + */ + public TopiaPermission(String s) { + this(s, "read"); + } + + /** + * Permet de construire une Permission. + * L'id et les principaux sont lu depuis la chaine 's' (voir init). + * + * @param s la liste des principals disponible dans le subject + * @param actionsAsString les actions possibles sur l'objet, sous forme de chaine. Voir actionsAsStringToInt. + * @see #actionsAsStringToInt(String) + */ + public TopiaPermission(String s, String actionsAsString) { + this(s, actionsAsStringToInt(actionsAsString)); + } + + /** + * Permet de construire une Permission. + * L'id et les principaux sont lu depuis la chaine 's' (voir init). + * + * @param s la liste des principals disponible dans le subject + * @param actionsAsInt les actions possibles sur l'objet, sous forme d'entier (combinaison de TopiaPermission.READ WRITE et ADMIN). + * @see #init(String, int) + */ + public TopiaPermission(String s, int actionsAsInt) { + super(s); + init(s, actionsAsInt); + } + + /** + * Parse la chaine s pour en deduire l'id ainsi que le nom de la classe principal et le nom du principal. + * Le nom de la classe principal et le nom du principal peuvent etre repete n fois. + * Appelle {@link #addPrincipal(String, String)} pour ajouter chaque principal. + * @param s format : id espace (principalClass espace principalName)* + * @param actionsAsInt action1,action2 ... + */ + protected void init(String s, int actionsAsInt) { + // log.info(this.getClass().getName()+" name="+name+" actions="+actionsAsInt); + + actions = actionsAsInt; + + StringTokenizer tok = new StringTokenizer(s, " ", false); + if (tok.hasMoreTokens()) { + id = tok.nextToken(); + // log.info("id="+id); + } + + principals = new HashMap(); + + while (tok.hasMoreTokens()) { + String principalClass = tok.nextToken(); + String principalName = null; + if (tok.hasMoreTokens()) { + principalName = tok.nextToken(); + } else { + throw new IllegalArgumentException( + "Principal must be followed by name"); + } + addPrincipal(principalClass, principalName); + } + } + + /** + * principals est une Map. A chaque clé (principalClass) est associé une HashSet + * contenant la liste des principalName. + * Si la clé n'existe pas, elle est créée. + * @param principalClass Le nom de la sous-classe de Principal + * @param principalName Le nom a associe a principalClass + */ + protected void addPrincipal(String principalClass, String principalName) { + Set names = (Set) principals.get(principalClass); + if (names == null) { + principals.put(principalClass, names = new HashSet()); + } + names.add(principalName); + // log.info("principal added : "+principalClass+" "+principalName); + } + + /** + * @return une chaine représentant le contenu de la variable d'instance actions sous forme de chaine. + */ + public String getActions() { + StringBuffer result = new StringBuffer(); + if (actionsAsString == null) { + if ((actions & READ) == READ) { + result.append("read,"); + } + if ((actions & WRITE) == WRITE) { + result.append("write,"); + } + if ((actions & ADMIN) == ADMIN) { + result.append("admin,"); + } + if (result.length() > 0) { + actionsAsString = result.substring(0, result.length() - 1); + } else { + actionsAsString = ""; + } + } + return actionsAsString; + } + + /** @return le hashCode de l'id */ + public int hashCode() { + return id.hashCode(); + } + + /** @param o n'importe quel objet + * @return vrai si la cette permission est egale ou identique à o */ + public boolean equals(Object o) { + if (o == null) + return false; + if (o == this) + return true; + if (!(o instanceof TopiaPermission)) + return false; + + TopiaPermission that = (TopiaPermission) o; + return (this.implies(that) && that.implies(this)); + } + + /** @param p une permission + * @return Vrai si la permission p est incluse dans this */ + public boolean implies(Permission p) { + if (p == null) + return false; + if (!(p instanceof TopiaPermission)) + return false; + + TopiaPermission that = (TopiaPermission) p; + + if (!impliesId(getId(), that.getId())) + return false; + if (!impliesActions(actions, that.actions)) + return false; + boolean result = impliesPrincipal(this.principals, that.principals); + return result; + } + + /** + * @param thisId un id + * @param thatId un autre Id + * @return vrai si les ids sont egaux + */ + private boolean impliesId(String thisId, String thatId) { + return thisId.equals(thatId); + } + + /** + * @param thisActions + * @param thatActions + * @return vrai si thisActions implique thatActions. ADMIN => WRITE, WRITE => READ + */ + private boolean impliesActions(int thisActions, int thatActions) { + return thisActions >= thatActions; + } + + /** + * @param thisPrincipals + * @param thatPrincipals + * @return vrai si thisPrincipals implique thatPrincipals. + */ + private boolean impliesPrincipal(Map thisPrincipals, Map thatPrincipals) { + // this should never happen + if (thisPrincipals == null || thatPrincipals == null) + return false; + + if (thisPrincipals.size() == 0) { + return true; + } + + if (thatPrincipals.size() == 0) { + return false; + } + + boolean result = true; + for (Iterator it = thisPrincipals.keySet().iterator(); result + && it.hasNext();) { + String thisPrincipalClass = (String) it.next(); + if ("*".equals(thisPrincipalClass)) { + continue; + } + + Set thisPrincipalNames = (Set) thisPrincipals.get(thisPrincipalClass); + Set thatPrincipalNames = (Set) thatPrincipals.get(thisPrincipalClass); + + if (thatPrincipalNames != null //that contient bien le principal nécessaire ! + && (thisPrincipalNames.contains("*") || thatPrincipalNames.containsAll(thisPrincipalNames))) { + //(this contient une étoile (accepte tous)) ou (that contient tout ce que contient this) + continue; + } + result = false; + } + return result; + } + + /** + * Returns a string that displays and identifies this object's properties + * @return a String representation of this object + */ + public String toString() { + return "(" + this.getClass().getName() + " id=\"" + id + "\" principals=" + + principals + " actions=\"" + getActions() + "\")"; + } + +} // TopiaPermission + Index: topia/src/java/org/codelutin/topia/security/TopiaPolicy.java diff -u /dev/null topia/src/java/org/codelutin/topia/security/TopiaPolicy.java:1.1 --- /dev/null Wed May 4 16:09:46 2005 +++ topia/src/java/org/codelutin/topia/security/TopiaPolicy.java Wed May 4 16:09:41 2005 @@ -0,0 +1,152 @@ +/* *##% + * Copyright (C) 2002, 2003, 2004, 2005 Code Lutin, + * Cédric Pineau, Benjamin Poussin, + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * TopiaPolicy.java + * + * Created: 2 mai 2005 + * + * @author Benjamin Poussin + * @version $Revision: 1.1 $ + */ + +package org.codelutin.topia.security; + +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Logger; + +/** + * Cette policy etend par delegation la policy qui etait deja  en place. + * Cette classe permet d'ajouter des permissions a l'execution. + */ +public class TopiaPolicy extends Policy { + + /** to use log facility, just put in your code: log.info("..."); */ + static private Logger log = Logger.getLogger("org.codelutin.topia.security.TopiaPolicy"); + + protected Policy parentPolicy; + + /** + * Renvoie la policy installee + * @see #installPolicy() + * @return l'attribut protected parentPolicy + */ + public Policy getParentPolicy() { + return parentPolicy; + } + + /** + * Modifie l'attribut parentPolicy de la classe par la Policy passée en parametre + * @param parentPolicy + * + */ + + public void setParentPolicy(Policy parentPolicy) { + this.parentPolicy = parentPolicy; + } + + protected List permissions = new ArrayList(); + + /** + * Renvoie toutes les permissions de la policy + * @return liste des permissions + */ + public List getPermissions() { + return permissions; + } + + /** + * Constructeur de la classe qui fait appel au constructeur de la super classe {@link java.security.Policy} + * @see java.security.Policy#Policy() + */ + public TopiaPolicy() { + super(); + } + + /** + * Permet d'ajouter la permission p durant l'execution du programme + * @param p permission a ajouter + */ + public void addPermission(Permission p) { + permissions.add(p); + } + + /** + * Methode qui permet de recuperer l'ensemble des permissions du CodeSource. + * Appelle {@link java.security.Policy#getPermissions(java.security.CodeSource)} et ajoute toutes les permissions a la collection + * @param cs + * @return une collection des permissions de parentPolicy + */ + public PermissionCollection getPermissions(CodeSource cs) { + log.finest("getPermsCodeSource"); + PermissionCollection pc = parentPolicy.getPermissions(cs); + return pc; + } + + /** + * Methode qui permet de recuperer l'ensemble des permissions du ProtectionDomain. + * Appelle {@link java.security.Policy#getPermissions(java.security.ProtectionDomain)} et ajoute toutes les permissions a la collection + * @param domain domaine de protection + * @return une collection des permissions de parentPolicy + */ + public PermissionCollection getPermissions(ProtectionDomain domain) { + log.finest("getPermsDomain permissions: " +permissions); + PermissionCollection pc = parentPolicy.getPermissions(domain); + for (Iterator i = permissions.iterator(); i.hasNext();) { + pc.add((Permission) i.next()); + } + return pc; + } + + /** + * appel refresh() de l'attribut parentPolicy + * + * @see java.security.Policy#refresh() + */ + public void refresh() { + parentPolicy.refresh(); + } + + /** + * Installe cette TopiaPolicy.
+ * Si la Policy existante est déja cette TopiaPolicy alors la méthode n'a pas d'effet
+ * Si une autre Policy existe deja alors cette TopiaPolicy, elle conserve l'ancienne Policy dans parentPolicy et la remplace alors + */ + public void installPolicy() { + Policy policy = Policy.getPolicy(); + + if (policy == this) + return; + if (policy instanceof TopiaPolicy) { + log.info("Policy deja modifie en: " + policy); + } else { + log.info("l'ancienne Policy etait: " + policy); + setParentPolicy(policy); + Policy.setPolicy(this); + } + } +}