Index: topia/src/java/org/codelutin/topia/AbstractTopiaEntity.java diff -u topia/src/java/org/codelutin/topia/AbstractTopiaEntity.java:1.3 topia/src/java/org/codelutin/topia/AbstractTopiaEntity.java:1.4 --- topia/src/java/org/codelutin/topia/AbstractTopiaEntity.java:1.3 Fri Dec 17 16:13:51 2004 +++ topia/src/java/org/codelutin/topia/AbstractTopiaEntity.java Fri May 20 17:51:10 2005 @@ -23,10 +23,10 @@ * * @author Benjamin Poussin * Copyright Code Lutin - * @version $Revision: 1.3 $ + * @version $Revision: 1.4 $ * - * Mise a jour: $Date: 2004/12/17 16:13:51 $ - * par : $Author: pineau $ + * Mise a jour: $Date: 2005/05/20 17:51:10 $ + * par : $Author: thimel $ */ package org.codelutin.topia; @@ -41,6 +41,9 @@ protected Date _topiaCreationDate_; protected Date _topiaLastUpdateDate_; protected TopiaUser _topiaLastUpdateUser_; + + //TODO ??? Arno : Faut-il sécuriser ces méthodes ? + //Réponse : Après discussion avec et selon Benjamin : non ! public AbstractTopiaEntity() throws TopiaException { set_topiaId_(TopiaId.create(getEntityClass())); Index: topia/src/java/org/codelutin/topia/TopiaContext.java diff -u topia/src/java/org/codelutin/topia/TopiaContext.java:1.34 topia/src/java/org/codelutin/topia/TopiaContext.java:1.35 --- topia/src/java/org/codelutin/topia/TopiaContext.java:1.34 Wed May 4 16:09:40 2005 +++ topia/src/java/org/codelutin/topia/TopiaContext.java Fri May 20 17:51:10 2005 @@ -23,9 +23,9 @@ * * @author Benjamin Poussin * Copyright Code Lutin - * @version $Revision: 1.34 $ + * @version $Revision: 1.35 $ * - * Mise a jour: $Date: 2005/05/04 16:09:40 $ + * Mise a jour: $Date: 2005/05/20 17:51:10 $ * par : $Author: thimel $ */ @@ -33,102 +33,126 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Enumeration; +import java.util.Iterator; import java.util.List; import java.util.Properties; +import java.util.StringTokenizer; import java.util.Vector; import org.codelutin.topia.distribution.AbstractDistributionHelper; import org.codelutin.topia.hook.HookHelper; import org.codelutin.topia.persistence.PersistenceHelper; +import org.codelutin.topia.security.TopiaAccessController; import org.codelutin.topia.security.TopiaGroupPrincipal; +import org.codelutin.topia.security.TopiaPermission; +import org.codelutin.topia.security.TopiaPermissionEvent; +import org.codelutin.topia.security.TopiaPermissionListener; import org.codelutin.topia.security.TopiaSecurityException; import org.codelutin.topia.security.TopiaUserPrincipal; import org.codelutin.util.CategorisedListenerSet; +import org.codelutin.util.HashMapMultiKey; +import org.codelutin.util.ListenerSet; + +import java.util.Hashtable; public abstract class TopiaContext { // TopiaContext - protected PersistenceHelper persistenceHelper = null; - protected AbstractDistributionHelper distributionHelper = null; - protected HookHelper hookHelper = null; - protected ContextHelper contextHelper = null; + protected PersistenceHelper persistenceHelper = null; + + protected AbstractDistributionHelper distributionHelper = null; + + protected HookHelper hookHelper = null; - protected CategorisedListenerSet listeners = - new CategorisedListenerSet(TopiaEntityListener.class); + protected ContextHelper contextHelper = null; - /** Toutes les propriétés qui permette de paramètrer le context */ - protected Properties properties = null; + protected CategorisedListenerSet listeners = new CategorisedListenerSet( + TopiaEntityListener.class); - /** + protected ListenerSet permissionListeners = new ListenerSet( + TopiaPermissionListener.class); + protected HashMapMultiKey permissions; + + protected boolean permFileModified = false; + + /** Toutes les propriétés qui permette de paramètrer le context */ + protected Properties properties = null; + + /** * Constructeur du context. Il est protégé car les contexts doivent-être * construit par {@TopiaContextFactory} */ - protected TopiaContext(Properties properties) { - this.properties = properties; - contextHelper = new ContextHelper(this, properties); - } + protected TopiaContext(Properties properties) { + this.properties = properties; + contextHelper = new ContextHelper(this, properties); + permissions = new HashMapMultiKey(); + } - /** + /** * Retourne tous les listeners */ - public CategorisedListenerSet getListeners() { - return listeners; - } - - /** - * Adds a new TopiaEntityListener to the subscribers list. - * @param topiaEntityListener - the TopiaEntityListener to add to the subscribers list. - */ - public void addTopiaEntityListener(TopiaEntityListener l) { - getListeners().add(this, l); - } - - /** - * Removes a TopiaEntityListener from the subscribers list. - * @param topiaEntityListener - the TopiaEntityListener to remove from the subscribers. - */ - public void removeTopiaEntityListener(TopiaEntityListener l) { - getListeners().remove(this, l); - } + public CategorisedListenerSet getListeners() { + return listeners; + } + + /** + * Adds a new TopiaEntityListener to the subscribers list. + * @param topiaEntityListener - the TopiaEntityListener to add to the subscribers list. + */ + public void addTopiaEntityListener(TopiaEntityListener l) { + getListeners().add(this, l); + } + + /** + * Removes a TopiaEntityListener from the subscribers list. + * @param topiaEntityListener - the TopiaEntityListener to remove from the subscribers. + */ + public void removeTopiaEntityListener(TopiaEntityListener l) { + getListeners().remove(this, l); + } - /** + /** * Retourne le service de persistance de l'entity demandé. Par defaut * le service de persitence de l'entity se nome de la meme façon que * l'entity avec PersistenceServiceImpl en plus * @param entityClass la class de l'entité dont on souhaite le persistence * service */ - public TopiaPersistenceService getPersistenceService(Class entityClass) throws TopiaException { - String className = entityClass.getName() + "PersistenceService"; - Class interfacez = Util.getClazz(className); - Object result = getService(interfacez, true); - return (TopiaPersistenceService)result; - -// -// -// className = entityClass.getName() + "PersistenceServiceImpl"; -// Class clazz = Util.getClazz(className); -// -// Object result = contextHelper.getSingletonObject(clazz); -// result = getHookHelper().addHookSupport(result, new Class[]{interfacez}); -// listeners.addCategory(this, result); -// return (TopiaPersistenceService)result; - } + public TopiaPersistenceService getPersistenceService(Class entityClass) + throws TopiaException { + String className = entityClass.getName() + "PersistenceService"; + Class interfacez = Util.getClazz(className); + Object result = getService(interfacez, true); + return (TopiaPersistenceService) result; + + // + // + // className = entityClass.getName() + "PersistenceServiceImpl"; + // Class clazz = Util.getClazz(className); + // + // Object result = contextHelper.getSingletonObject(clazz); + // result = getHookHelper().addHookSupport(result, new Class[]{interfacez}); + // listeners.addCategory(this, result); + // return (TopiaPersistenceService)result; + } - /** + /** * Retourne l'objet implantant les operations de l'entity demandé. Par defaut * cette classe se nome de la meme façon que * l'entity avec Operation en plus * @param entityClass la class de l'entité dont on souhaite les operations * @see #getService(Class, boolean) */ - public TopiaEntityOperation getEntityOperation(Class entityClass) throws TopiaException { - return getEntityOperation(entityClass, false); - } + public TopiaEntityOperation getEntityOperation(Class entityClass) + throws TopiaException { + return getEntityOperation(entityClass, false); + } - /** + /** * Retourne l'objet implantant les operations de l'entity demandé. Par defaut * cette classe se nome de la meme façon que * l'entity avec Operation en plus @@ -137,152 +161,397 @@ * lieu du Dist * @see #getService(Class, boolean) */ - public TopiaEntityOperation getEntityOperation(Class entityClass, boolean local) throws TopiaException { - String className = entityClass.getName() + "Operation"; - Class interfacez = Util.getClazz(className); - return (TopiaEntityOperation)getService(interfacez, local); - } + public TopiaEntityOperation getEntityOperation(Class entityClass, + boolean local) throws TopiaException { + String className = entityClass.getName() + "Operation"; + Class interfacez = Util.getClazz(className); + return (TopiaEntityOperation) getService(interfacez, local); + } - /** + /** * Retourne le service demandé. * @param local si vrai alors donne l'implantation local du service au * lieu du Dist */ - public TopiaService getService(Class serviceInterfacez, boolean local) throws TopiaException { - Class clazz = null; - if(local){ - try{ - clazz = contextHelper.getImplementationClass(serviceInterfacez); - }catch(TopiaNotFoundException eee){ - // if can't find mapping, try with default dist class - clazz = Util.getClazz(serviceInterfacez.getName() + "Impl"); - } - }else{ - try{ - clazz = contextHelper.getDistributionClass(serviceInterfacez); - }catch(TopiaNotFoundException eee){ - // if can't find mapping, try with default dist class - clazz = Util.getClazz(serviceInterfacez.getName() + "Dist"); - } - } - Object result = contextHelper.getSingletonObject(clazz); - result = getHookHelper().addHookSupport(result, new Class[]{serviceInterfacez}); - - if(result instanceof TopiaPersistenceService){ - listeners.addCategory(this, result); - } + public TopiaService getService(Class serviceInterfacez, boolean local) + throws TopiaException { + Class clazz = null; + if (local) { + try { + clazz = contextHelper.getImplementationClass(serviceInterfacez); + } catch (TopiaNotFoundException eee) { + // if can't find mapping, try with default dist class + clazz = Util.getClazz(serviceInterfacez.getName() + "Impl"); + } + } else { + try { + clazz = contextHelper.getDistributionClass(serviceInterfacez); + } catch (TopiaNotFoundException eee) { + // if can't find mapping, try with default dist class + clazz = Util.getClazz(serviceInterfacez.getName() + "Dist"); + } + } + Object result = contextHelper.getSingletonObject(clazz); + result = getHookHelper().addHookSupport(result, + new Class[] { serviceInterfacez }); + + if (result instanceof TopiaPersistenceService) { + listeners.addCategory(this, result); + } - return (TopiaService)result; - } + return (TopiaService) result; + } - /** + /** * Retourne le service Dist demandé * @see #getService(Class, boolean) */ - public TopiaService getService(Class serviceInterfacez) throws TopiaException { - return getService(serviceInterfacez, false); - } - /** + public TopiaService getService(Class serviceInterfacez) + throws TopiaException { + return getService(serviceInterfacez, false); + } + + /** * Retourne l'objet responsable de la persistance dans l'application */ - public PersistenceHelper getPersistenceHelper() throws TopiaNotFoundException { - if(persistenceHelper == null){ - persistenceHelper = (PersistenceHelper) - contextHelper.getHelper(TopiaConst.HELPER_PERSISTENCE); - } - return persistenceHelper; - } + public PersistenceHelper getPersistenceHelper() + throws TopiaNotFoundException { + if (persistenceHelper == null) { + persistenceHelper = (PersistenceHelper) contextHelper.getHelper(TopiaConst.HELPER_PERSISTENCE); + } + return persistenceHelper; + } - /** + /** * Retourne l'objet permettant d'appeler des méthodes distantes */ - public AbstractDistributionHelper getDistributionHelper() throws TopiaNotFoundException { - if(distributionHelper == null){ - distributionHelper = (AbstractDistributionHelper) - contextHelper.getHelper(TopiaConst.HELPER_DISTRIBUTION); - } - return distributionHelper; + public AbstractDistributionHelper getDistributionHelper() + throws TopiaNotFoundException { + if (distributionHelper == null) { + distributionHelper = (AbstractDistributionHelper) contextHelper.getHelper(TopiaConst.HELPER_DISTRIBUTION); + } + return distributionHelper; - } + } - /** + /** * Retourne l'objet permettant d'ajouter des hooks aux objets */ - public HookHelper getHookHelper() throws TopiaNotFoundException { - if(hookHelper == null){ - hookHelper = (HookHelper) - contextHelper.getHelper(TopiaConst.HELPER_HOOK); - } - return hookHelper; + public HookHelper getHookHelper() throws TopiaNotFoundException { + if (hookHelper == null) { + hookHelper = (HookHelper) contextHelper.getHelper(TopiaConst.HELPER_HOOK); + } + return hookHelper; - } + } - /** + /** * Cette methode permet de creer une nouvelle entity d'un certain type. * La methode a une visibilite package car seul le framework doit l'utiliser * et ceci au travers de AbstractTopiaPersistenceService.create() */ - TopiaEntity createEntity(Class entityClass) throws TopiaException { - return (TopiaEntity)contextHelper.getInstance(entityClass); - } - - public List authenticate(String login, String password) - throws TopiaSecurityException { - String authType = properties.getProperty("topia.auth.type"); - if ("simple".equalsIgnoreCase(authType)) { - return simpleAuthentication(login, password); - } else if ("ldap".equalsIgnoreCase(authType)) { - return ldapAuthentication(login, password); - } else if ("xmi".equalsIgnoreCase(authType)) { - return xmiAuthentication(login, password); - } - throw new TopiaSecurityException("Invalid auth type : " + authType); - } - - protected List simpleAuthentication(String login, String password) - throws TopiaSecurityException { - Properties props = new Properties(); - String fileName = properties.getProperty("topia.auth.simple.file"); - if (fileName == null) - throw new TopiaSecurityException( - "Authentication filename must be specified"); - try { - props.load(new FileInputStream(fileName)); - } catch(FileNotFoundException e) { - throw new TopiaSecurityException( - "Invalid authentication file : " + fileName); - } catch(IOException ioe) { - throw new TopiaSecurityException( - "Unable to read authentication file : " + fileName); - } - String hashMode = properties.getProperty("topia.auth.simple.hash"); - if (hashMode != null) { - try { - password = new String(MessageDigest.getInstance(hashMode) - .digest(password.getBytes())); - } catch (NoSuchAlgorithmException nsaE) { - throw new TopiaSecurityException( - "Invalid hash algorithm : " +hashMode); - } - } - if (!password.equals(props.getProperty(login))) - throw new TopiaSecurityException("Wrong Login/Password"); - Vector principals = new Vector(); - principals.addElement(new TopiaUserPrincipal(login)); - principals.addElement(new TopiaGroupPrincipal("users")); -// TODO Arno : ajouter la liste des GroupPrincipals - return principals; - } - - protected List ldapAuthentication(String login, String password) - throws TopiaSecurityException { - throw new TopiaSecurityException("ldapAuthentication not supported"); - //TODO Arno ;) - } - - protected abstract List xmiAuthentication(String login, String password) - throws TopiaSecurityException; - - -} // TopiaContext + TopiaEntity createEntity(Class entityClass) throws TopiaException { + return (TopiaEntity) contextHelper.getInstance(entityClass); + } + + ///////////////////////////////////////////////////// + // Security // + ///////////////////////////////////////////////////// + + + /** + * Authentifie l'utilisateur en fonction des paramètres du fichier de propriétés + * @param login + * @param password + * @return la liste des TopiaPrincipal de l'utilisateur + * @throws TopiaSecurityException + */ + public List authenticate(String login, String password) + throws TopiaSecurityException { + String authType = properties.getProperty("topia.auth.type"); + if ("simple".equalsIgnoreCase(authType)) { + return simpleAuthentication(login, password); + } else if ("ldap".equalsIgnoreCase(authType)) { + return ldapAuthentication(login, password); + } else if ("xmi".equalsIgnoreCase(authType)) { + return xmiAuthentication(login, password); + } + throw new TopiaSecurityException("Invalid auth type : " + authType); + } + + /** + * Authentification basée sur les fichiers textes + * @param login + * @param password + * @return la liste des TopiaPrincipal de l'utilisateur + * @throws TopiaSecurityException + */ + protected List simpleAuthentication(String login, String password) + throws TopiaSecurityException { + Properties props = new Properties(); + String fileName = properties.getProperty("topia.auth.simple.file.login"); + if (fileName == null) + throw new TopiaSecurityException( + "Authentication filename must be specified"); + try { + props.load(new FileInputStream(fileName)); + } catch (FileNotFoundException e) { + throw new TopiaSecurityException("Invalid authentication file : " + + fileName); + } catch (IOException ioe) { + throw new TopiaSecurityException( + "Unable to read authentication file : " + fileName); + } + String hashMode = properties.getProperty("topia.auth.simple.hash"); + if (hashMode != null) { + try { + password = new String(MessageDigest.getInstance(hashMode).digest( + password.getBytes())); + } catch (NoSuchAlgorithmException nsaE) { + throw new TopiaSecurityException("Invalid hash algorithm : " + + hashMode); + } + } + if (!password.equals(props.getProperty(login))) + throw new TopiaSecurityException("Wrong Login/Password"); + Vector principals = new Vector(); + principals.addElement(new TopiaUserPrincipal(login)); + + String groupsFileName = properties.getProperty("topia.auth.simple.file.groups"); + props = new Properties(); + try { + props.load(new FileInputStream(groupsFileName)); + } catch (FileNotFoundException e1) { + e1.printStackTrace(); + } catch (IOException e1) { + e1.printStackTrace(); + } + for (Enumeration e = props.keys(); e.hasMoreElements();) { + String key = (String) e.nextElement(); + + for (StringTokenizer sTK = new StringTokenizer(props.getProperty(key), + ","); sTK.hasMoreTokens();) { + if (login.equals(sTK.nextToken().trim())) { + principals.addElement(new TopiaGroupPrincipal(key)); + break; + } + } + } + return principals; + } + + /** + * Authntification basée sur un ldap + * @param login + * @param password + * @return la liste des TopiaPrincipal de l'utilisateur + * @throws TopiaSecurityException + */ + protected List ldapAuthentication(String login, String password) + throws TopiaSecurityException { + throw new TopiaSecurityException("ldapAuthentication not supported"); + //TODO Arno ;) + } + + /** + * Authentification basée sur l'utilisation de la BD de l'application + * @param login + * @param password + * @return la liste des TopiaPrincipal de l'utilisateur + * @throws TopiaSecurityException + */ + protected abstract List xmiAuthentication(String login, String password) + throws TopiaSecurityException; + + /** + * Charge en mémoire les fichiers contenant les permissions + */ + public void loadPermissions() { + loadPermissions("readPerm.file", "read"); + loadPermissions("writePerm.file", "write"); + loadPermissions("adminPerm.file", "admin"); + permFileModified = false; + } + + /** + * Charge en mémoire le fichier (fileName) contenant les permissions + * associées à l'action action + * @param fileName + * @param action + */ + private void loadPermissions(String fileName, String action) { + Properties props = new Properties(); + try { + props.load(new FileInputStream(fileName)); + HashMapMultiKey.Key multiKey = new HashMapMultiKey.Key().add(action); + for (Enumeration en = props.keys(); en.hasMoreElements();) { + String key = (String) en.nextElement(); + Vector propValues = new Vector(); + String principals = props.getProperty(key).replaceAll(" +", " "); + StringTokenizer sTK = new StringTokenizer(principals, ";"); + while (sTK.hasMoreTokens()) + try { + principals = sTK.nextToken(); + TopiaPermission perm = + new TopiaPermission(key + " " + principals, action); + addNoSecurityPermission(perm, true); + propValues.addElement(perm); + } catch (TopiaSecurityException e1) { + e1.printStackTrace(); + } + permissions.put(multiKey.add(key), propValues); + } + } catch (FileNotFoundException e) { + System.out.println("Fichier : \"" + fileName + "\" inexistant..."); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Sauvegarde les permissions dans les fichiers + * + */ + public void storePermissions() { + if (permFileModified) { + storePermissions("readPerm.file", "read"); + storePermissions("writePerm.file", "write"); + storePermissions("adminPerm.file", "admin"); + permFileModified = false; + } + } + + /** + * Sauvegardes les permissions de type "action" dans le fichier fileName + * @param fileName + * @param action + */ + private void storePermissions(String fileName, String action) { + Properties props = new Properties(); + List list = permissions.getKeys(action); + for (Iterator it = list.iterator(); it.hasNext(); ) { + HashMapMultiKey.Key key = (HashMapMultiKey.Key)it.next(); + String propKey = (String)key.get(1); + String value = ""; + Vector values = (Vector)permissions.get(key); + if (values == null || values.size()==0) + continue; + for (Enumeration en = values.elements(); en.hasMoreElements(); ) { + TopiaPermission perm = (TopiaPermission)en.nextElement(); + value += perm.principalsToString() + ";"; + } + props.put(propKey, value.substring(0, value.lastIndexOf(";"))); + } + try { + props.store(new FileOutputStream(fileName), null); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + /** + * Ajoute à l'application la Permission perm + * @param perm + * @throws TopiaSecurityException + */ + public void addPermission(TopiaPermission perm) + throws TopiaSecurityException { + TopiaAccessController.checkPermission("perm", "admin"); + addNoSecurityPermission(perm, true); + } + + /** + * Ajoute à l'application la Permission perm sans control de sécurité + * @param perm + * @param informListeners si true, informe les listeners concernés + * @throws TopiaSecurityException + */ + private void addNoSecurityPermission(TopiaPermission perm, boolean informListeners) + throws TopiaSecurityException { + HashMapMultiKey.Key key; + key = new HashMapMultiKey.Key().add(perm.getActions()).add(perm.getId()); + Vector perms = (Vector)permissions.get(key); + if (perms == null) + perms = new Vector(); + if (!perms.contains(perm)) + perms.add(perm); + permissions.put(key, perms); + permFileModified = true; + if (informListeners) { + try { + permissionListeners.fire("permissionAdded", new TopiaPermissionEvent( + this, perm)); + } catch (Exception e) { + throw new TopiaSecurityException("Error while adding permission " + + perm, e); + } + } + } + + /** + * Modifie la Permission perm + * @param perm + * @throws TopiaSecurityException + */ + public void modifyPermission(TopiaPermission perm) + throws TopiaSecurityException { + TopiaAccessController.checkPermission("perm", "admin"); + List list = permissions.getKeys(perm.getId()); + boolean found = false; + for (Iterator it = list.iterator(); !found && it.hasNext(); ) { + HashMapMultiKey.Key key = (HashMapMultiKey.Key)it.next(); + Vector perms = (Vector)permissions.get(key); + for (Enumeration en = perms.elements(); !found && en.hasMoreElements(); ) { + TopiaPermission topiaPerm = (TopiaPermission)en.nextElement(); + if (topiaPerm.getPrincipals().equals(perm.getPrincipals())) { + found = true; + if (!topiaPerm.getActions().equals(perm.getActions())) { + perms.remove(topiaPerm); + if (perms.isEmpty()) + permissions.remove(key); + addNoSecurityPermission(perm, false); + } + } + } + } + + permFileModified = true; + try { + permissionListeners.fire("permissionModified", + new TopiaPermissionEvent(this, perm)); + } catch (Exception e) { + throw new TopiaSecurityException("Error while modifying permission " + + perm, e); + } + } + + /** + * Retire la permission perm + * @param perm + * @throws TopiaSecurityException + */ + public void removePermission(TopiaPermission perm) + throws TopiaSecurityException { + TopiaAccessController.checkPermission("perm", "admin"); + HashMapMultiKey.Key key; + key = new HashMapMultiKey.Key().add(perm.getActions()).add(perm.getId()); + Vector perms = (Vector)permissions.get(key); + if ((perms != null) && (perms.contains(perm))) { + permFileModified = true; + perms.remove(perm); + if (perms.isEmpty()) + permissions.remove(key); + } + try { + permissionListeners.fire("permissionRemoved", + new TopiaPermissionEvent(this, perm)); + } catch (Exception e) { + throw new TopiaSecurityException("Error while removing permission " + + perm, e); + } + } +} // TopiaContext