merging in git branch 'methodSecurityExpressions' and resolving conflict in SecurityT...
authorAndreas Kohlbecker <a.kohlbecker@bgbm.org>
Fri, 7 Sep 2012 14:42:02 +0000 (14:42 +0000)
committerAndreas Kohlbecker <a.kohlbecker@bgbm.org>
Fri, 7 Sep 2012 14:42:02 +0000 (14:42 +0000)
40 files changed:
.gitattributes
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/common/GrantedAuthorityImpl.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/EvaluationFailedException.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/CdmSecurityHibernateInterceptor.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/AuthorityPermission.java [deleted file]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CRUD.java [new file with mode: 0644]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmAuthority.java [new file with mode: 0644]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermission.java [deleted file]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermissionClass.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermissionEvaluator.java
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermissionEvaluatorPermitAll.java [deleted file]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/DescriptionPermissionEvaluator.java [deleted file]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/Operation.java [new file with mode: 0644]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/CdmPermissionVoter.java [new file with mode: 0644]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/DescriptionBaseVoter.java [new file with mode: 0644]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/DescriptionElementVoter.java [new file with mode: 0644]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/GrantAlwaysVoter.java [new file with mode: 0644]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/TaxonBaseVoter.java [new file with mode: 0644]
cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/TaxonNodeVoter.java [new file with mode: 0644]
cdmlib-persistence/src/main/resources/eu/etaxonomy/cdm/persistence_security.xml
cdmlib-persistence/src/test/java/eu/etaxonomy/cdm/persistence/dao/hibernate/common/CdmEntityDaoBaseTest.java
cdmlib-persistence/src/test/java/eu/etaxonomy/cdm/test/integration/CdmIntegrationTest.java
cdmlib-persistence/src/test/java/eu/etaxonomy/cdm/test/integration/CdmTransactionalIntegrationTest.java
cdmlib-persistence/src/test/java/eu/etaxonomy/cdm/test/integration/CdmTransactionalIntegrationTestWithSecurity.java
cdmlib-persistence/src/test/resources/eu/etaxonomy/cdm/persistence/dao/hibernate/common/CdmEntityDaoBaseTest.testUpdateWithAuthentication-result.xml
cdmlib-persistence/src/test/resources/eu/etaxonomy/cdm/persistence/dao/hibernate/common/CdmEntityDaoBaseTest.xml
cdmlib-persistence/src/test/resources/log4j.properties
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/IService.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/LocationServiceImpl.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/ServiceBase.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/UserService.java
cdmlib-services/src/main/resources/eu/etaxonomy/cdm/defaultSecurityContext.xml
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/SecurityTest.java
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/SecurityTest.java.orig [new file with mode: 0644]
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/SecurityWithTransaction.java
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/TaxonServiceSearchTest.java
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/UserServiceImplTest.java
cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/SecurityTest.xml
cdmlib-services/src/test/resources/eu/etaxonomy/cdm/applicationContext-securityTest.xml
cdmlib-services/src/test/resources/log4j.properties

index 35bdb00ade3e0c01026f6776bc201cd53b2f144f..0346e0601f25cadd3d03e25e6bccf11f9466de49 100644 (file)
@@ -1309,12 +1309,17 @@ cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/SaveOrUp
 cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/TableGenerator.java -text
 cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/TableGeneratorGlobalOverride.java -text
 cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/UpdateEntityListener.java -text
-cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/AuthorityPermission.java -text
-cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermission.java -text
+cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CRUD.java -text
+cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmAuthority.java -text
 cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermissionClass.java -text
 cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermissionEvaluator.java -text
-cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermissionEvaluatorPermitAll.java -text
-cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/DescriptionPermissionEvaluator.java -text
+cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/Operation.java -text
+cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/CdmPermissionVoter.java -text
+cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/DescriptionBaseVoter.java -text
+cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/DescriptionElementVoter.java -text
+cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/GrantAlwaysVoter.java -text
+cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/TaxonBaseVoter.java -text
+cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/TaxonNodeVoter.java -text
 cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/replace/ReferringObjectMetadata.java -text
 cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/replace/ReferringObjectMetadataFactory.java -text
 cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/replace/impl/ListReferringObjectMetadata.java -text
@@ -1972,6 +1977,7 @@ cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/NameServiceImplTest.j
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/NaturalLanguageGeneratorTest.java -text
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/PolytomousKeyServiceImplTest.java -text
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/SecurityTest.java -text
+cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/SecurityTest.java.orig -text
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/SecurityWithTransaction.java -text
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/TaxonNodeServiceImplTest.java -text
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/TaxonServiceImplBusinessTest.java -text
index bb69bdb59b59a05f1e3ed3b1128760cc15456e31..3b4d1aa68c46050c9894bd7fe8b8d134ca35d0c6 100644 (file)
@@ -1,8 +1,8 @@
 /**\r
  * Copyright (C) 2007 EDIT\r
- * European Distributed Institute of Taxonomy \r
+ * European Distributed Institute of Taxonomy\r
  * http://www.e-taxonomy.eu\r
- * \r
+ *\r
  * The contents of this file are subject to the Mozilla Public License Version 1.1\r
  * See LICENSE.TXT at the top of this package for the full license terms.\r
  */\r
@@ -25,61 +25,69 @@ import org.springframework.security.core.GrantedAuthority;
 @XmlRootElement(name = "Group")\r
 @Entity\r
 public class GrantedAuthorityImpl extends CdmBase implements GrantedAuthority {\r
-       private static final long serialVersionUID = 2651969425860655040L;\r
-       private static final Logger logger = Logger\r
-                       .getLogger(GrantedAuthority.class);\r
+    private static final long serialVersionUID = 2651969425860655040L;\r
+    private static final Logger logger = Logger\r
+            .getLogger(GrantedAuthority.class);\r
 \r
-       @XmlElement(name = "Authority")\r
-       @NaturalId\r
-       private String authority;\r
+    @XmlElement(name = "Authority")\r
+    @NaturalId\r
+    private String authority;\r
 \r
-       protected GrantedAuthorityImpl() {\r
-               super();\r
-       }\r
+    protected GrantedAuthorityImpl() {\r
+        super();\r
+    }\r
 \r
-       public static GrantedAuthorityImpl NewInstance() {\r
-               return new GrantedAuthorityImpl();\r
-       }\r
+    public static GrantedAuthorityImpl NewInstance() {\r
+        return new GrantedAuthorityImpl();\r
+    }\r
 \r
-       public String getAuthority() {\r
-               return authority;\r
-       }\r
+    public String getAuthority() {\r
+        return authority;\r
+    }\r
 \r
-       public void setAuthority(String authority) {\r
-               this.authority = authority;\r
-       }\r
+    public void setAuthority(String authority) {\r
+        this.authority = authority;\r
+    }\r
 \r
-       public int compareTo(Object o) {\r
-               if (o instanceof GrantedAuthority) {\r
-                       return this.authority.compareTo(((GrantedAuthority) o)\r
-                                       .getAuthority());\r
-               }\r
-               return 0;\r
-       }\r
+    public int compareTo(Object o) {\r
+        if (o instanceof GrantedAuthority) {\r
+            return this.authority.compareTo(((GrantedAuthority) o)\r
+                    .getAuthority());\r
+        }\r
+        return 0;\r
+    }\r
 \r
-       // *********** CLONE **********************************/\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.model.common.CdmBase#toString()\r
+     */\r
+    @Override\r
+    public String toString() {\r
+        return getAuthority();\r
+    }\r
 \r
-       /**\r
-        * Clones <i>this</i> Granted Authority. This is a shortcut that enables to\r
-        * create a new instance that differs only slightly from <i>this</i> Granted\r
-        * Authority by modifying only some of the attributes.<BR>\r
-        * \r
-        * \r
-        * \r
-        * @see eu.etaxonomy.cdm.model.common.CdmBase#clone()\r
-        * @see java.lang.Object#clone()\r
-        */\r
-       @Override\r
-       public Object clone() {\r
-               GrantedAuthority result;\r
-               try {\r
-                       result = (GrantedAuthority) super.clone();\r
-                       // no changes to authority\r
-                       return result;\r
-               } catch (CloneNotSupportedException e) {\r
-                       logger.warn("Object does not implement cloneable");\r
-                       e.printStackTrace();\r
-                       return null;\r
-               }\r
-       }\r
+    // *********** CLONE **********************************/\r
+\r
+    /**\r
+     * Clones <i>this</i> Granted Authority. This is a shortcut that enables to\r
+     * create a new instance that differs only slightly from <i>this</i> Granted\r
+     * Authority by modifying only some of the attributes.<BR>\r
+     *\r
+     *\r
+     *\r
+     * @see eu.etaxonomy.cdm.model.common.CdmBase#clone()\r
+     * @see java.lang.Object#clone()\r
+     */\r
+    @Override\r
+    public Object clone() {\r
+        GrantedAuthority result;\r
+        try {\r
+            result = (GrantedAuthority) super.clone();\r
+            // no changes to authority\r
+            return result;\r
+        } catch (CloneNotSupportedException e) {\r
+            logger.warn("Object does not implement cloneable");\r
+            e.printStackTrace();\r
+            return null;\r
+        }\r
+    }\r
 }\r
index 9e41a5a2c6a20350615db6927d2ee24c6b942df7..65c425fa23ff42786bcc1b6373124c41f9a2becc 100644 (file)
@@ -1,13 +1,24 @@
 \r
 package eu.etaxonomy.cdm.database;\r
 \r
+import java.util.EnumSet;\r
+\r
 import org.apache.log4j.Logger;\r
 import org.hibernate.HibernateException;\r
 import org.springframework.security.core.Authentication;\r
 \r
 import eu.etaxonomy.cdm.model.common.CdmBase;\r
-import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermission;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.Operation;\r
+\r
 \r
+/**\r
+ * FIXME Rename to PermissionDeniedException ???\r
+ *\r
+ * @author andreas\r
+ * @date Sep 4, 2012\r
+ *\r
+ */\r
 public class EvaluationFailedException extends HibernateException {\r
     private static final Logger logger = Logger\r
             .getLogger(EvaluationFailedException.class);\r
@@ -19,13 +30,18 @@ public class EvaluationFailedException extends HibernateException {
         super(message);\r
     }\r
 \r
-    public EvaluationFailedException(Authentication autherntication, CdmBase entity, CdmPermission permission) {\r
-        super(permission.name() + " not permitted for '" + autherntication.getName()\r
+    public EvaluationFailedException(Authentication authentication, CdmBase entity, Operation requiredOperation) {\r
+        super(requiredOperation + " not permitted for '" + authentication.getName()\r
+                + "' on " + entity.getClass().getSimpleName() + "[uuid:" + entity.getUuid() + "', toString:'" + entity.toString() + "']");\r
+    }\r
+\r
+    public EvaluationFailedException(Authentication authentication, CdmBase entity, EnumSet<CRUD> requiredOperation) {\r
+        super(requiredOperation + " not permitted for '" + authentication.getName()\r
                 + "' on " + entity.getClass().getSimpleName() + "[uuid:" + entity.getUuid() + "', toString:'" + entity.toString() + "']");\r
     }\r
 \r
-    public EvaluationFailedException(Authentication autherntication, CdmBase entity, String permission) {\r
-        super(permission + " not permitted for '" + autherntication.getName()\r
+    public EvaluationFailedException(Authentication authentication, CdmBase entity, String requiredOperation) {\r
+        super(requiredOperation + " not permitted for '" + authentication.getName()\r
                 + "' on " + entity.getClass().getSimpleName() + "[uuid:" + entity.getUuid() + "', toString:'" + entity.toString() + "']");\r
     }\r
 \r
index f6a9c8bc2fb39c51c83c70bc95b6fded8cef35db..03165ed8a6f1c53f51b3109352bc0a136edc992a 100644 (file)
@@ -11,6 +11,7 @@ package eu.etaxonomy.cdm.persistence.hibernate;
 \r
 \r
 import java.io.Serializable;\r
+import java.util.EnumSet;\r
 \r
 import org.apache.log4j.Logger;\r
 import org.hibernate.EmptyInterceptor;\r
@@ -21,7 +22,8 @@ import org.springframework.stereotype.Component;
 \r
 import eu.etaxonomy.cdm.database.EvaluationFailedException;\r
 import eu.etaxonomy.cdm.model.common.CdmBase;\r
-import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermission;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.Operation;\r
 import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator;\r
 /**\r
  * @author k.luther\r
@@ -31,7 +33,19 @@ import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator;
 @Component\r
 public class CdmSecurityHibernateInterceptor extends EmptyInterceptor {\r
 \r
-    private static final Logger logger = Logger.getLogger(CdmSecurityHibernateInterceptor.class);\r
+    private static final long serialVersionUID = 8477758472369568074L;\r
+\r
+    public static final Logger logger = Logger.getLogger(CdmSecurityHibernateInterceptor.class);\r
+\r
+    private CdmPermissionEvaluator permissionEvaluator;\r
+\r
+    public CdmPermissionEvaluator getPermissionEvaluator() {\r
+        return permissionEvaluator;\r
+    }\r
+\r
+    public void setPermissionEvaluator(CdmPermissionEvaluator permissionEvaluator) {\r
+        this.permissionEvaluator = permissionEvaluator;\r
+    }\r
 \r
 \r
     /* (non-Javadoc)\r
@@ -43,15 +57,16 @@ public class CdmSecurityHibernateInterceptor extends EmptyInterceptor {
             String[] propertyNames,\r
             Type[] type) {\r
 \r
-        CdmPermissionEvaluator permissionEvaluator = new eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator();\r
-        if (SecurityContextHolder.getContext().getAuthentication() != null && entity instanceof CdmBase){\r
-            if (!permissionEvaluator.hasPermission(SecurityContextHolder.getContext().getAuthentication(), entity, CdmPermission.CREATE)){\r
-                throw new EvaluationFailedException(SecurityContextHolder.getContext().getAuthentication(), (CdmBase)entity, CdmPermission.CREATE);\r
-            }else return true;\r
+        if (SecurityContextHolder.getContext().getAuthentication() == null || !(entity instanceof CdmBase) ) {\r
+            return true;\r
         }\r
-        else return true;\r
-\r
+        // evaluate throws EvaluationFailedException\r
+        checkPermissions((CdmBase)entity, Operation.CREATE);\r
+        logger.debug("permission check suceeded - object creation granted");\r
+        return true;\r
     }\r
+\r
+\r
     /* (non-Javadoc)\r
      * @see org.hibernate.EmptyInterceptor#onFlushDirty(java.lang.Object, java.io.Serializable, java.lang.Object[], java.lang.Object[], java.lang.String[], org.hibernate.type.Type[])\r
      */\r
@@ -62,65 +77,66 @@ public class CdmSecurityHibernateInterceptor extends EmptyInterceptor {
             String[] propertyNames,\r
             Type[] types) {\r
 \r
-        if( !(entity instanceof CdmBase) ){\r
+        if (SecurityContextHolder.getContext().getAuthentication() == null || !(entity instanceof CdmBase) ) {\r
             return true;\r
         }\r
         CdmBase cdmEntity = (CdmBase)entity;\r
 \r
-        CdmPermissionEvaluator permissionEvaluator = new CdmPermissionEvaluator();\r
-        String permission = null;;\r
-        if (SecurityContextHolder.getContext().getAuthentication() != null){\r
-            boolean equals = true;\r
-            for (int i = 0; i<currentState.length; i++){\r
-                if (currentState[i]== null ) {\r
-                    if ( previousState[i]!= null) {\r
-                        equals = false;\r
-                        break;\r
-                    }\r
-                }\r
-                if (currentState[i]!= null ){\r
-                    if (previousState == null){\r
-                        equals = false;\r
-                        break;\r
-                    }\r
-                }\r
-                if (currentState[i]!= null && previousState[i] != null){\r
-                    Object a = currentState[i];\r
-                    Object b = previousState[i];\r
-                    if (!currentState[i].equals(previousState[i])) {\r
-                        if (propertyNames[i].equals("password")){\r
-                            permission = "changePassword";\r
-                        }\r
-                        equals = false;\r
-                        break;\r
-                    }\r
-                }\r
-            }\r
+        if (isModified(currentState, previousState)){\r
+            // evaluate throws EvaluationFailedException\r
+            checkPermissions(cdmEntity, Operation.UPDATE);\r
+            logger.debug("permission check suceeded - object update granted");\r
+        }\r
+        return true;\r
+    }\r
 \r
+    /**\r
+     * checks if the current authentication has the <code>expectedPermission</code> on the supplied <code>entity</code>.\r
+     * Throws an {@link EvaluationFailedException} if the evaluation fails.\r
+     *\r
+     * @param entity\r
+     * @param expectedOperation\r
+     */\r
+    private void checkPermissions(CdmBase entity, EnumSet<CRUD> expectedOperation) {\r
 \r
-            if (!equals){\r
-                if (permission != null){\r
-                    if (!permissionEvaluator.hasPermission(SecurityContextHolder.getContext().getAuthentication(), cdmEntity, permission)){\r
-                        throw new EvaluationFailedException(SecurityContextHolder.getContext().getAuthentication(), cdmEntity, permission);\r
-                    }else {\r
-                        return true;\r
-                    }\r
+        if (!permissionEvaluator.hasPermission(SecurityContextHolder.getContext().getAuthentication(), entity, expectedOperation)){\r
+            throw new EvaluationFailedException(SecurityContextHolder.getContext().getAuthentication(), entity, expectedOperation);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Checks if the CDM entity as been modified by comparing the current with the previous state.\r
+     *\r
+     * @param currentState\r
+     * @param previousState\r
+     * @return true if the currentState and previousState differ.\r
+     */\r
+    private boolean isModified(Object[] currentState, Object[] previousState) {\r
+        boolean equals = true;\r
+        for (int i = 0; i<currentState.length; i++){\r
+            if (currentState[i]== null ) {\r
+                if ( previousState[i]!= null) {\r
+                    equals = false;\r
+                    break;\r
                 }\r
-                if (!permissionEvaluator.hasPermission(SecurityContextHolder.getContext().getAuthentication(), cdmEntity, CdmPermission.UPDATE)){\r
-                    throw new EvaluationFailedException(SecurityContextHolder.getContext().getAuthentication(), cdmEntity, CdmPermission.UPDATE);\r
-                }else {\r
-                    return true;\r
+            }\r
+            if (currentState[i]!= null ){\r
+                if (previousState == null){\r
+                    equals = false;\r
+                    break;\r
+                }\r
+            }\r
+            if (currentState[i]!= null && previousState[i] != null){\r
+                Object a = currentState[i];\r
+                Object b = previousState[i];\r
+                if (!currentState[i].equals(previousState[i])) {\r
+                    equals = false;\r
+                    break;\r
                 }\r
-            }else {\r
-                return true;\r
             }\r
         }\r
-        else{\r
-            return true;\r
-        }\r
-\r
+        return equals;\r
     }\r
 \r
 \r
-\r
 }\r
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/AuthorityPermission.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/AuthorityPermission.java
deleted file mode 100644 (file)
index efd4b1f..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-\r
-package eu.etaxonomy.cdm.persistence.hibernate.permission;\r
-\r
-import java.util.UUID;\r
-\r
-import eu.etaxonomy.cdm.model.common.GrantedAuthorityImpl;\r
-\r
-/**\r
- * A <code>AuthorityPermission</code> consists of two parts which are separated\r
- * by a dot character '.' in the permissionString which can retrieved by\r
- * {@link #getPermissionString(String)}:\r
- *\r
- * <ul>\r
- * <li><code>className</code>: an {@link CdmPermissionClass} instance with represents a cdm\r
- * type or a part of the cdm type hierarchy. The className is always represented\r
- * as an upper case string.</li>\r
- * <li><code>permission</code>: a string which specifies a {@link CdmPermission} on that set of cdm\r
- * types</li>\r
- * <li><code>targetUuid</code>: The permission may be restricted to a specific cdm entity by adding\r
- * the entity uuid to the permission. The uuid string is enclosed in curly brackets '<code>{</code>'\r
- * , '<code>}</code>' and appended to the end of the permission.</li>\r
- * </ul>\r
- * The authority string syntax looks like:<br>\r
- * <pre>CLASSNAME.PERMISSION[{UUID}]</pre>\r
- * Whereas the square brackets are indicating an optional element.\r
- *\r
- * <h3>Examples for permissionStrings</h3>\r
- *\r
- * <pre>\r
- * TAXONBASE.CREATE\r
- * TAXONBASE.READ\r
- * TAXONBASE.UPDATE\r
- * TAXONBASE.DELETE\r
- * DESCRIPTIONBASE.UPDATE\r
- * TAXONNODE.UPDATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}\r
- * </pre>\r
- *\r
- * The method {@link #getPermissionString(String)} parses a full authority and returns  permissionString and\r
- * the {@link AuthorityPermission} from the <code>authority</code>.\r
- *\r
- *\r
- *\r
- * @author k.luther\r
- */\r
-public class AuthorityPermission{\r
-    CdmPermissionClass className;\r
-    CdmPermission permission;\r
-    UUID targetUuid;\r
-\r
-    public AuthorityPermission(Object targetDomainObject, CdmPermission permission, UUID uuid){\r
-        this.className = CdmPermissionClass.getValueOf(targetDomainObject);\r
-        this.permission = permission;\r
-        targetUuid = uuid;\r
-    }\r
-\r
-    public CdmPermissionClass getClassName(){\r
-        return className;\r
-    }\r
-\r
-    public CdmPermission getPermission(){\r
-        return permission;\r
-    }\r
-\r
-    public UUID getTargetUUID(){\r
-        return targetUuid;\r
-    }\r
-\r
-    /**\r
-     * Constructs a new AuthorityPermission by parsing the contents of an\r
-     * authority string. For details on the syntax please refer to the class\r
-     * documentation above.\r
-     *\r
-     * @param authority\r
-     */\r
-    public AuthorityPermission (String authority){\r
-        String permissionString;\r
-        int firstPoint = authority.indexOf(".");\r
-        if (firstPoint == -1){\r
-            // no dot: the authorityString only holds a CdmPermissionClass\r
-            className = CdmPermissionClass.valueOf(authority);\r
-        }else{\r
-            // has a dot: the authorityString only holds a CdmPermissionClass and a permissionString\r
-            className = CdmPermissionClass.valueOf((authority.substring(0, firstPoint)));\r
-            int bracket = authority.indexOf("{");\r
-            permissionString = getPermissionString(authority);\r
-            if (bracket != -1){\r
-                // having a bracket means the permissionString contains a uuid !!!\r
-                int secondBracket = authority.indexOf("}");\r
-                String uuid = authority.substring(bracket+1, secondBracket);\r
-                targetUuid = UUID.fromString(uuid);\r
-            }\r
-            permission = CdmPermission.valueOf(permissionString.toUpperCase());\r
-        }\r
-    }\r
-\r
-    /**\r
-     * The method {@link #getPermissionString(String)} parses a full authority\r
-     * string like\r
-     * "<code>TAXONNODE.READ{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}</code>"and\r
-     * returns the string representation of the CdmPermission "<code>READ</code>"\r
-     * contained in the authority string\r
-     *\r
-     * @param authority\r
-     * @return\r
-     */\r
-    private static String getPermissionString(String authority){\r
-        int lastPoint = authority.lastIndexOf(".");\r
-        int bracket = authority.indexOf("{");\r
-        if (bracket == -1){\r
-            return authority.substring(lastPoint+1);\r
-        }else{\r
-            return authority.substring(lastPoint+1, bracket);\r
-        }\r
-    }\r
-\r
-}
\ No newline at end of file
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CRUD.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CRUD.java
new file mode 100644 (file)
index 0000000..5ee5f2c
--- /dev/null
@@ -0,0 +1,5 @@
+package eu.etaxonomy.cdm.persistence.hibernate.permission;
+
+public enum CRUD {
+    CREATE, READ, UPDATE, DELETE;
+}
\ No newline at end of file
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmAuthority.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmAuthority.java
new file mode 100644 (file)
index 0000000..beda5dd
--- /dev/null
@@ -0,0 +1,201 @@
+\r
+package eu.etaxonomy.cdm.persistence.hibernate.permission;\r
+\r
+import java.util.EnumSet;\r
+import java.util.UUID;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.springframework.security.access.ConfigAttribute;\r
+\r
+import eu.etaxonomy.cdm.model.common.CdmBase;\r
+\r
+import sun.security.provider.PolicyParser.ParsingException;\r
+\r
+/**\r
+ * A <code>CdmAuthority</code> consists basically of two parts which are separated\r
+ * by a dot character '.'.\r
+ *\r
+ * <ul>\r
+ * <li><code>permissionClass</code>: an {@link CdmPermissionClass} instance with represents a cdm\r
+ * type or a part of the cdm type hierarchy. The className is always represented\r
+ * as an upper case string.</li>\r
+ * <li><code>property</code>: The <code>CdmAuthority</code> only applies to instances\r
+ * which satisfy the specified property. Interpretation is up to type specific voters.</li>\r
+ * <li><code>operation</code>: a string which specifies a {@link Operation} on that set of cdm\r
+ * types</li>\r
+ * <li><code>targetUuid</code>: The <code>operation</code> may be restricted to a specific cdm entity by adding\r
+ * the entity uuid to the <code>operation</code>. The uuid string is enclosed in curly brackets '<code>{</code>'\r
+ * , '<code>}</code>' and appended to the end of the <code>operation</code>.</li>\r
+ * </ul>\r
+ *\r
+ * <h3>Examples for permissionStrings</h3>\r
+ *\r
+ * <pre>\r
+ * TAXONBASE.CREATE\r
+ * TAXONBASE.READ\r
+ * TAXONBASE.UPDATE\r
+ * TAXONBASE.DELETE\r
+ * DESCRIPTIONBASE.UPDATE\r
+ * TAXONNODE.UPDATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}\r
+ * </pre>\r
+ *\r
+ * The method {@link #getPermissionString(String)} parses a full authority and returns  permissionString and\r
+ * the {@link CdmAuthority} from the <code>authority</code>.\r
+ *\r
+ *\r
+ * @author k.luther\r
+ */\r
+public class CdmAuthority implements ConfigAttribute {\r
+\r
+    private static final long serialVersionUID = 1L;\r
+\r
+    public static final Logger logger = Logger.getLogger(CdmAuthority.class);\r
+\r
+    CdmPermissionClass permissionClass;\r
+    String property;\r
+    EnumSet<CRUD> operation;\r
+     UUID targetUuid;\r
+\r
+    public CdmAuthority(CdmBase targetDomainObject, EnumSet<CRUD> operation, UUID uuid){\r
+        this.permissionClass = CdmPermissionClass.getValueOf(targetDomainObject);\r
+        this.property = null;\r
+        this.operation = operation;\r
+        this.targetUuid = uuid;\r
+    }\r
+\r
+     public CdmAuthority(CdmBase targetDomainObject, String property, EnumSet<CRUD> operation, UUID uuid){\r
+       this.permissionClass = CdmPermissionClass.getValueOf(targetDomainObject);\r
+        this.property = property;\r
+        this.operation = operation;\r
+        this.targetUuid = uuid;\r
+    }\r
+\r
+\r
+    public CdmAuthority(CdmPermissionClass permissionClass, String property, EnumSet<CRUD> operation, UUID uuid){\r
+        this.permissionClass = permissionClass;\r
+        this.property = property;\r
+        this.operation = operation;\r
+        this.targetUuid = uuid;\r
+    }\r
+\r
+    /**\r
+     * Constructs a new CdmAuthority by parsing the contents of an\r
+     * authority string. For details on the syntax please refer to the class\r
+     * documentation above.\r
+     *\r
+     * TODO usually one would not use a constructor but a valueOf(String) or fromSting() method for this\r
+     *\r
+     * @param authority\r
+     * @throws ParsingException\r
+     */\r
+    public CdmAuthority (String authority) throws ParsingException{\r
+\r
+        String[] tokens = parse(authority);\r
+        // className must never be null\r
+        permissionClass = CdmPermissionClass.valueOf(tokens[0]);\r
+        property = tokens[1];\r
+        if(tokens[2] != null){\r
+            try {\r
+                operation = Operation.fromString(tokens[2]);\r
+            } catch (IllegalArgumentException e) {\r
+                logger.warn("cannot parse Operation " + tokens[2]);\r
+                throw e;\r
+            }\r
+        }\r
+        if(tokens[3] != null){\r
+            targetUuid = UUID.fromString(tokens[3]);\r
+        }\r
+    }\r
+\r
+    public CdmPermissionClass getPermissionClass(){\r
+        return permissionClass;\r
+    }\r
+\r
+    public String getProperty(){\r
+        return property;\r
+    }\r
+\r
+    public EnumSet<CRUD> getOperation(){\r
+        return operation;\r
+    }\r
+\r
+    public UUID getTargetUUID(){\r
+        return targetUuid;\r
+    }\r
+\r
+    public boolean hasTargetUuid() {\r
+        return targetUuid != null;\r
+    }\r
+\r
+    public boolean hasProperty() {\r
+        return property != null;\r
+    }\r
+\r
+    /**\r
+     * Parses the given <code>authority</code> and returns an array of tokens.\r
+     * The array has a length of four elements whereas the elements can be null.\r
+     * The elements in the array correspond to the fields of {@link CdmAuthority}:\r
+     * <ol>\r
+     * <li>{@link CdmAuthority#permissionClass}</li>\r
+     * <li>{@link CdmAuthority#property}</li>\r
+     * <li>{@link CdmAuthority#operation}</li>\r
+     * <li>{@link CdmAuthority#targetUuid}</li>\r
+     * </ol>\r
+     * @param authority\r
+     * @return an array of tokens\r
+     * @throws ParsingException\r
+     */\r
+    protected String[] parse(String authority) throws ParsingException {\r
+        //\r
+        // regex pattern explained:\r
+        //  (\\w*)             -> classname\r
+        //  (?:\\((\\w*)\\))?  -> (property)\r
+        //  \\.?               -> .\r
+        //  (?:(\\w*))(?:\\{([\\da-z\\-]+)\\})? -> Permmission and targetUuid\r
+        //\r
+        String regex = "(\\w*)(?:\\((\\w*)\\))?\\.?(?:(\\w*))(?:\\{([\\da-z\\-]+)\\})?";\r
+        Pattern pattern = Pattern.compile(regex);\r
+        String[] tokens = new String[4];\r
+        logger.debug("parsing '" + authority + "'");\r
+        Matcher m = pattern.matcher(authority);\r
+\r
+        if (m.find() && m.groupCount() == 4 ) {\r
+            for (int i = 0; i < m.groupCount(); i++) {\r
+                tokens[i] = m.group(i+1);\r
+                // normalize empty strings to null\r
+                if(tokens[i] != null && tokens[i].length() == 0){\r
+                    tokens[i] = null;\r
+                }\r
+                logger.debug("[" + i + "]: " + tokens[i]+ "\n");\r
+            }\r
+        } else {\r
+            logger.debug("no match");\r
+            throw new ParsingException("Unsupported authority string: '" + authority + "'");\r
+        }\r
+\r
+        return tokens;\r
+    }\r
+\r
+\r
+    @Override\r
+    public String toString() {\r
+        StringBuilder sb = new StringBuilder();\r
+        sb.append(permissionClass.toString());\r
+        if(property != null){\r
+            sb.append('(').append(property).append(')');\r
+        }\r
+        sb.append('.').append(operation.toString());\r
+        if(targetUuid != null){\r
+            sb.append('{').append(targetUuid.toString()).append('}');\r
+        }\r
+        return sb.toString() ;\r
+    }\r
+\r
+    @Override\r
+    public String getAttribute() {\r
+        return toString();\r
+    }\r
+\r
+}
\ No newline at end of file
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermission.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermission.java
deleted file mode 100644 (file)
index 8731e6b..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/**\r
-* Copyright (C) 2009 EDIT\r
-* European Distributed Institute of Taxonomy\r
-* http://www.e-taxonomy.eu\r
-*\r
-* The contents of this file are subject to the Mozilla Public License Version 1.1\r
-* See LICENSE.TXT at the top of this package for the full license terms.\r
-*/ \r
-package eu.etaxonomy.cdm.persistence.hibernate.permission;\r
-\r
-\r
-/**\r
- * @author k.luther\r
- * @date 06.07.2011\r
- *\r
- */\r
-public enum CdmPermission {\r
-       CREATE, READ, UPDATE, DELETE, DENYALL, ADMIN;\r
-}\r
-\r
-\r
index 8995a2e5be800094b352807ddb706529015a818d..ac66ca740474388ef1ff42ca221d72e82adcf2f2 100644 (file)
@@ -5,7 +5,7 @@
 *\r
 * The contents of this file are subject to the Mozilla Public License Version 1.1\r
 * See LICENSE.TXT at the top of this package for the full license terms.\r
-*/ \r
+*/\r
 package eu.etaxonomy.cdm.persistence.hibernate.permission;\r
 \r
 import eu.etaxonomy.cdm.model.common.CdmBase;\r
@@ -15,31 +15,45 @@ import eu.etaxonomy.cdm.model.common.CdmBase;
  * @date 06.07.2011\r
  */\r
 public enum CdmPermissionClass {\r
-       USER, DESCRIPTIONBASE, DESCRIPTIONELEMENTBASE, TAXONBASE, ALL, TAXONNODE, CLASSIFICATION;\r
-       \r
-       \r
-       public static CdmPermissionClass getValueOf(Object s){\r
-               String permissionClassString ;\r
-               if (s instanceof String){\r
-                       permissionClassString = (String)s;\r
-               }else if (s instanceof CdmBase){\r
-                       permissionClassString = s.getClass().getSimpleName().toUpperCase(); \r
-               } else if(s instanceof Class){\r
-                       permissionClassString = ((Class) s).getSimpleName().toUpperCase();\r
-               }else{\r
-                       \r
-                       return null;\r
-               }\r
-               try{\r
-                       return CdmPermissionClass.valueOf(permissionClassString);\r
-               }catch(IllegalArgumentException e){\r
-                       if (s instanceof CdmBase){\r
-                               s = s.getClass().getSuperclass();\r
-                               \r
-                               return getValueOf(s);\r
-                       }\r
-                       \r
-               }\r
-               return null;\r
-       }\r
+    USER,\r
+    DESCRIPTIONBASE,\r
+    DESCRIPTIONELEMENTBASE,\r
+    TAXONBASE,\r
+    ALL,\r
+    TAXONNODE,\r
+    CLASSIFICATION;\r
+\r
+\r
+    /**\r
+     * return the appropriate CdmPermissionClass for the given Object. May return null if no matching CdmPermissionClass was found.\r
+     * @param s\r
+     * @return the CdmPermissionClass or null\r
+     */\r
+    public static CdmPermissionClass getValueOf(Object s){\r
+\r
+        String permissionClassString ;\r
+        if (s instanceof String){\r
+            permissionClassString = (String)s;\r
+        }else if (s instanceof CdmBase){\r
+            permissionClassString = s.getClass().getSimpleName().toUpperCase();\r
+        } else if(s instanceof Class){\r
+            permissionClassString = ((Class) s).getSimpleName().toUpperCase();\r
+        }else{\r
+\r
+            return null;\r
+        }\r
+\r
+        try{\r
+            return CdmPermissionClass.valueOf(permissionClassString);\r
+        }catch(IllegalArgumentException e){\r
+            if (s instanceof CdmBase){\r
+                s = s.getClass().getSuperclass();\r
+\r
+                return getValueOf(s);\r
+            }\r
+\r
+        }\r
+\r
+        return null;\r
+    }\r
 }\r
index b4222556142f791a5d8b2bc653b2dd2190feb55d..dceb28611ade5d569eb0b4ea4111f066eaec65f1 100644 (file)
@@ -10,15 +10,19 @@ package eu.etaxonomy.cdm.persistence.hibernate.permission;
 \r
 import java.io.Serializable;\r
 import java.util.Collection;\r
+import java.util.EnumSet;\r
+import java.util.HashSet;\r
 import java.util.UUID;\r
 \r
 import org.apache.log4j.Logger;\r
+import org.springframework.security.access.AccessDecisionManager;\r
+import org.springframework.security.access.ConfigAttribute;\r
 import org.springframework.security.access.PermissionEvaluator;\r
 import org.springframework.security.core.Authentication;\r
 import org.springframework.security.core.GrantedAuthority;\r
+import org.springframework.stereotype.Component;\r
 \r
 import eu.etaxonomy.cdm.model.common.CdmBase;\r
-import eu.etaxonomy.cdm.model.common.User;\r
 import eu.etaxonomy.cdm.model.description.DescriptionBase;\r
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonNode;\r
@@ -27,10 +31,21 @@ import eu.etaxonomy.cdm.model.taxon.TaxonNode;
  * @author k.luther\r
  * @date 06.07.2011\r
  */\r
+@Component\r
 public class CdmPermissionEvaluator implements PermissionEvaluator {\r
 \r
     protected static final Logger logger = Logger.getLogger(CdmPermissionEvaluator.class);\r
 \r
+    private AccessDecisionManager accessDecisionManager;\r
+\r
+    public AccessDecisionManager getAccessDecisionManager() {\r
+        return accessDecisionManager;\r
+    }\r
+\r
+    public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {\r
+        this.accessDecisionManager = accessDecisionManager;\r
+    }\r
+\r
     /* (non-Javadoc)\r
      * @see org.springframework.security.access.PermissionEvaluator#hasPermission(org.springframework.security.core.Authentication, java.io.Serializable, java.lang.String, java.lang.Object)\r
      */\r
@@ -45,58 +60,65 @@ public class CdmPermissionEvaluator implements PermissionEvaluator {
     /* (non-Javadoc)\r
      * @see org.springframework.security.access.PermissionEvaluator#hasPermission(org.springframework.security.core.Authentication, java.lang.Object, java.lang.Object)\r
      */\r
-    public boolean hasPermission(Authentication authentication,\r
-            Object targetDomainObject, Object permission) {\r
+    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {\r
 \r
 \r
-        AuthorityPermission evalPermission;\r
-        CdmPermission cdmPermission;\r
+        CdmAuthority evalPermission;\r
+        EnumSet<CRUD> requiredOperation;\r
+\r
         if(logger.isDebugEnabled()){\r
             StringBuilder grantedAuthoritiesTxt = new StringBuilder();\r
             for(GrantedAuthority ga : authentication.getAuthorities()){\r
                 grantedAuthoritiesTxt.append("    - ").append(ga.getAuthority()).append("\n");\r
-                logger.debug("evaluating:\n"\r
-                        + "  User '" + authentication.getName() + "':\n"\r
-                        + grantedAuthoritiesTxt\r
-                        + "  Object: " + ((CdmBase)targetDomainObject).instanceToString() + "\n"\r
-                        + "  Permission: " + permission);\r
             }\r
-        }\r
-        if (!(permission instanceof CdmPermission)){\r
-            String permissionString = (String)permission;\r
-            if (permissionString.equals("changePassword")){\r
-                if (targetDomainObject.equals(((User)authentication.getPrincipal()))){\r
-                    return true;\r
-                }else{\r
-                    cdmPermission = CdmPermission.ADMIN;\r
-                }\r
-            }else{\r
-                cdmPermission = CdmPermission.valueOf(permissionString);\r
+            if(grantedAuthoritiesTxt.length() == 0){\r
+                grantedAuthoritiesTxt.append("    - ").append("<No GrantedAuthority given>").append("\n");\r
             }\r
-        }else {\r
-            cdmPermission = (CdmPermission)permission;\r
+            logger.debug("hasPermission()\n"\r
+                    + "  User '" + authentication.getName() + "':\n"\r
+                    + grantedAuthoritiesTxt\r
+                    + "  Object: " + ((CdmBase)targetDomainObject).instanceToString() + "\n"\r
+                    + "  Permission: " + permission);\r
         }\r
+        try {\r
+            // FIXME refactor into Operation ======\r
+            if (Operation.isOperation(permission)){\r
+                requiredOperation = (EnumSet<CRUD>)permission;\r
+            } else {\r
+                // try to treat as string\r
+                requiredOperation = Operation.fromString(permission.toString());\r
+            }\r
+            // =======================================\r
 \r
-        Collection<GrantedAuthority> authorities = ((User)authentication.getPrincipal()).getAuthorities();\r
+        } catch (IllegalArgumentException e) {\r
+            logger.debug("permission string '"+ permission.toString() + "' not parsable => true");\r
+            return true; // it might be wrong to return true\r
+        }\r
 \r
         try{\r
-            //evalPermission = new AuthorityPermission(targetDomainObject.getClass().getSimpleName().toUpperCase(), cdmPermission, ((CdmBase)targetDomainObject).getUuid());\r
-            evalPermission = new AuthorityPermission(targetDomainObject, cdmPermission, ((CdmBase)targetDomainObject).getUuid());\r
+            //evalPermission = new CdmAuthority(targetDomainObject.getClass().getSimpleName().toUpperCase(), cdmPermission, ((CdmBase)targetDomainObject).getUuid());\r
+            evalPermission = new CdmAuthority((CdmBase)targetDomainObject, requiredOperation, ((CdmBase)targetDomainObject).getUuid());\r
         }catch(NullPointerException e){\r
-            //evalPermission = new AuthorityPermission(targetDomainObject.getClass().getSimpleName().toUpperCase(), cdmPermission, null);\r
-            evalPermission = new AuthorityPermission(targetDomainObject, cdmPermission, null);\r
+            //evalPermission = new CdmAuthority(targetDomainObject.getClass().getSimpleName().toUpperCase(), cdmPermission, null);\r
+            evalPermission = new CdmAuthority((CdmBase)targetDomainObject, requiredOperation, null);\r
         }\r
 \r
 \r
-        if (evalPermission.className != null) {\r
-            return evalPermission(authorities, evalPermission, (CdmBase) targetDomainObject);\r
-\r
+        if (evalPermission.permissionClass != null) {\r
+            logger.debug("starting evaluation => ...");\r
+            return evalPermission(authentication, evalPermission, (CdmBase) targetDomainObject);\r
         }else{\r
+            logger.debug("skipping evaluation => true");\r
             return true;\r
         }\r
 \r
     }\r
 \r
+    /**\r
+     * @param targetUuid\r
+     * @param node\r
+     * @return\r
+     */\r
     private TaxonNode findTargetUuidInTree(UUID targetUuid, TaxonNode node){\r
         if (targetUuid.equals(node.getUuid()))\r
             return node;\r
@@ -107,55 +129,31 @@ public class CdmPermissionEvaluator implements PermissionEvaluator {
     }\r
 \r
 \r
-    public boolean evalPermission(Collection<GrantedAuthority> authorities, AuthorityPermission evalPermission, CdmBase targetDomainObject){\r
+    /**\r
+     * @param authorities\r
+     * @param evalPermission\r
+     * @param targetDomainObject\r
+     * @return\r
+     */\r
+    private boolean evalPermission(Authentication authentication, CdmAuthority evalPermission, CdmBase targetDomainObject){\r
 \r
         //if user has administrator rights return true;\r
-         for (GrantedAuthority authority: authorities){\r
-             if (authority.getAuthority().equals("ALL.ADMIN"))return true;\r
+         for (GrantedAuthority authority: authentication.getAuthorities()){\r
+             if (authority.getAuthority().equals("ROLE_ADMIN")){\r
+                 logger.debug("ROLE_ADMIN found => true");\r
+                 return true;\r
+             }\r
          }\r
 \r
-        //if targetDomainObject is instance of DescriptionBase or DescriptionElementBase use the DescriptionPermissionEvaluator\r
-        if (targetDomainObject instanceof DescriptionElementBase || targetDomainObject instanceof DescriptionBase){\r
-            return DescriptionPermissionEvaluator.hasPermission(authorities, targetDomainObject, evalPermission);\r
-        }\r
-\r
-        for (GrantedAuthority authority: authorities){\r
-            AuthorityPermission authorityPermission= new AuthorityPermission(authority.getAuthority());\r
-            //evaluate authorities\r
-           //if classnames match or the authorityClassName is ALL, AND the permission matches or is ADMIN the evaluation is successful\r
-            if ((authorityPermission.className.equals(evalPermission.className) || authorityPermission.className.equals(CdmPermissionClass.ALL))\r
-                    && (authorityPermission.permission.equals(evalPermission.permission)|| authorityPermission.permission.equals(CdmPermission.ADMIN))){\r
-               /* if (authorityPermission.targetUuid != null){\r
-                    //TODO\r
+        // === run voters\r
+        Collection<ConfigAttribute> attributes = new HashSet<ConfigAttribute>();\r
+        attributes.add(evalPermission);\r
 \r
-                }else{*/\r
-                    return true;\r
-                //}\r
+        // decide() throws AccessDeniedException, InsufficientAuthenticationException\r
+        logger.debug("AccessDecisionManager will decide ...");\r
+        accessDecisionManager.decide(authentication, targetDomainObject, attributes);\r
 \r
-            }\r
-            //if authority is restricted to only one object (and the cascaded objects???)\r
-            if (authorityPermission.targetUuid != null){\r
-                if (authorityPermission.targetUuid.equals(((CdmBase)targetDomainObject).getUuid())){\r
-                    if (authorityPermission.permission.equals(evalPermission.permission)){\r
-                        return true;\r
-                    }\r
-                }\r
-            }\r
-            //if the user has the rights for a subtree\r
-            if (authorityPermission.className.equals(CdmPermissionClass.TAXONBASE) && targetDomainObject.getClass().getSimpleName().toUpperCase().equals("TaxonNode")){\r
-\r
-                TaxonNode node = (TaxonNode)targetDomainObject;\r
-                TaxonNode targetNode = findTargetUuidInTree(authorityPermission.targetUuid, node);\r
-                if (targetNode != null){\r
-                    if (evalPermission.permission.equals(authorityPermission.permission) ){\r
-                        return true;\r
-                    }\r
-                }\r
-            }\r
-\r
-\r
-        }\r
-        return false;\r
+        return true;\r
     }\r
 \r
 }\r
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermissionEvaluatorPermitAll.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/CdmPermissionEvaluatorPermitAll.java
deleted file mode 100644 (file)
index 9ab27ee..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-// $Id$\r
-/**\r
-* Copyright (C) 2007 EDIT\r
-* European Distributed Institute of Taxonomy \r
-* http://www.e-taxonomy.eu\r
-* \r
-* The contents of this file are subject to the Mozilla Public License Version 1.1\r
-* See LICENSE.TXT at the top of this package for the full license terms.\r
-*/\r
-\r
-package eu.etaxonomy.cdm.persistence.hibernate.permission;\r
-\r
-import java.io.Serializable;\r
-\r
-import org.apache.log4j.Logger;\r
-import org.springframework.security.access.PermissionEvaluator;\r
-import org.springframework.security.core.Authentication;\r
-\r
-/**\r
- * @author k.luther\r
- * @created 13.07.2011\r
- * @version 1.0\r
- */\r
-public class CdmPermissionEvaluatorPermitAll implements PermissionEvaluator {\r
-       private static final Logger logger = Logger.getLogger(CdmPermissionEvaluatorPermitAll.class);\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.springframework.security.access.PermissionEvaluator#hasPermission(org.springframework.security.core.Authentication, java.lang.Object, java.lang.Object)\r
-        */\r
-       \r
-       public boolean hasPermission(Authentication authentication,\r
-                       Object targetDomainObject, Object permission) {\r
-               //everybody has the permission\r
-               return true;\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see org.springframework.security.access.PermissionEvaluator#hasPermission(org.springframework.security.core.Authentication, java.io.Serializable, java.lang.String, java.lang.Object)\r
-        */\r
-       \r
-       public boolean hasPermission(Authentication authentication,\r
-                       Serializable targetId, String targetType, Object permission) {\r
-               // TODO Auto-generated method stub\r
-               return false;\r
-       }\r
-}\r
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/DescriptionPermissionEvaluator.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/DescriptionPermissionEvaluator.java
deleted file mode 100644 (file)
index ac625c6..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/**\r
-* Copyright (C) 2009 EDIT\r
-* European Distributed Institute of Taxonomy\r
-* http://www.e-taxonomy.eu\r
-*\r
-* The contents of this file are subject to the Mozilla Public License Version 1.1\r
-* See LICENSE.TXT at the top of this package for the full license terms.\r
-*/\r
-package eu.etaxonomy.cdm.persistence.hibernate.permission;\r
-\r
-import java.util.Collection;\r
-import java.util.Set;\r
-\r
-import org.springframework.security.core.Authentication;\r
-import org.springframework.security.core.GrantedAuthority;\r
-\r
-import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
-import eu.etaxonomy.cdm.model.common.User;\r
-import eu.etaxonomy.cdm.model.description.DescriptionBase;\r
-import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
-import eu.etaxonomy.cdm.model.description.Feature;\r
-\r
-/**\r
- * Evaluates permissions ...\r
- *\r
- * @author k.luther\r
- * @date 06.07.2011\r
- *\r
- */\r
-public class DescriptionPermissionEvaluator {\r
-\r
-    public static boolean hasPermission(Collection<GrantedAuthority> authorities,\r
-            Object targetDomainObject, AuthorityPermission evalPermission) {\r
-        Feature feature = null;\r
-        String authorityString;\r
-        AuthorityPermission authorityPermission;\r
-\r
-\r
-        if (targetDomainObject instanceof DescriptionElementBase){\r
-            feature = ((DescriptionElementBase)targetDomainObject).getFeature();\r
-        }\r
-\r
-        for (GrantedAuthority authority: authorities){\r
-\r
-            authorityString = authority.getAuthority();\r
-            authorityPermission = new AuthorityPermission(authorityString);\r
-\r
-            if (targetDomainObject instanceof DescriptionElementBase){\r
-                try{\r
-                    //check for a special feature\r
-                    if (feature != null){\r
-                        if (authorityString.contains(feature.getLabel()) && (evalPermission.permission.equals(authorityPermission.permission) || authorityPermission.equals(CdmPermission.ADMIN))){\r
-                            return true;\r
-                        } else if (authorityPermission.className.equals(CdmPermissionClass.DESCRIPTIONBASE)) {\r
-                            if (evalPermission.permission.equals(authorityPermission.permission) ){\r
-                                return true;\r
-                            } else if (authorityPermission.permission.equals(CdmPermission.ADMIN)){\r
-                                return true;\r
-                            }\r
-                        }\r
-                    }\r
-                }catch(Exception e){\r
-                    //in tests the initialisation of terms like features fails...\r
-                    if (org.hibernate.ObjectNotFoundException.class.isInstance(e)){\r
-                        if (evalPermission.permission.equals(authorityPermission.permission)|| authorityPermission.permission.equals(CdmPermission.ADMIN)){\r
-                            return true;\r
-                        }\r
-                    }else {\r
-                        return false;\r
-                    }\r
-\r
-                }\r
-                //the user has the general right for descriptions\r
-                if (authorityPermission.className.equals(CdmPermissionClass.DESCRIPTIONBASE)){\r
-                    //no special feature\r
-                    if (authority.getAuthority().lastIndexOf(".") == authority.getAuthority().indexOf(".") && (authorityPermission.className.equals(evalPermission.permission) || authorityPermission.equals(CdmPermission.ADMIN))){\r
-                        return true;\r
-                    }\r
-                }\r
-            } else{\r
-                if (authorityPermission.getClassName().equals(CdmPermissionClass.DESCRIPTIONBASE) && authorityPermission.permission.equals(evalPermission.permission)){\r
-                    return true;\r
-                }\r
-            }\r
-        }\r
-\r
-        return false;\r
-    }\r
-\r
-\r
-    /*public static boolean hasPermission (Collection<GrantedAuthority> authorities,\r
-            DescriptionBase targetDomainObject, AuthorityPermission evalPermission){\r
-        Set<DescriptionElementBase> elements = targetDomainObject.getElements();\r
-\r
-        for (GrantedAuthority authority :authorities){\r
-            if (authority.getAuthority().contains(CdmPermissionClass.DESCRIPTIONBASE.toString())){\r
-                if (authority.getAuthority().lastIndexOf(".") == authority.getAuthority().indexOf(".") && authority.getAuthority().contains(evalPermission.permission.toString())){\r
-                    return true;\r
-                }else{\r
-                    //TODO: das stimmt noch nicht so ganz!!!\r
-                    for (DescriptionElementBase element: elements){\r
-                        if (authority.getAuthority().contains(element.getFeature().getLabel()) && authority.getAuthority().contains(evalPermission.permission.toString())){\r
-                            return true;\r
-                        }\r
-                    }\r
-                }\r
-            }\r
-        }\r
-\r
-\r
-        return false;\r
-\r
-    }*/\r
-}\r
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/Operation.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/Operation.java
new file mode 100644 (file)
index 0000000..8ad2c49
--- /dev/null
@@ -0,0 +1,62 @@
+/**\r
+* Copyright (C) 2009 EDIT\r
+* European Distributed Institute of Taxonomy\r
+* http://www.e-taxonomy.eu\r
+*\r
+* The contents of this file are subject to the Mozilla Public License Version 1.1\r
+* See LICENSE.TXT at the top of this package for the full license terms.\r
+*/\r
+package eu.etaxonomy.cdm.persistence.hibernate.permission;\r
+\r
+import java.util.EnumSet;\r
+\r
+\r
+/**\r
+ * @author k.luther\r
+ * @date 06.07.2011\r
+ *\r
+ */\r
+public class Operation {\r
+\r
+    private Operation(){\r
+\r
+    }\r
+\r
+    final static public EnumSet<CRUD> CREATE = EnumSet.of(CRUD.CREATE);\r
+\r
+    final static public EnumSet<CRUD> READ = EnumSet.of(CRUD.READ);\r
+\r
+    final static public EnumSet<CRUD> UPDATE = EnumSet.of(CRUD.UPDATE);\r
+\r
+    final static public EnumSet<CRUD> DELETE = EnumSet.of(CRUD.DELETE);\r
+\r
+    final static public EnumSet<CRUD> ALL = EnumSet.allOf(CRUD.class);\r
+\r
+    final static public EnumSet<CRUD> ADMIN = ALL; // FIXME remove?\r
+\r
+    final static public EnumSet<CRUD> NONE = EnumSet.noneOf(CRUD.class);\r
+\r
+    public static EnumSet<CRUD> fromString(String string){\r
+        if(string.equals("ALL")){\r
+            return ALL;\r
+        }\r
+        if(string.equals("ADMIN")){\r
+            return ADMIN;\r
+        }\r
+        if(string.equals("NONE")){\r
+            return NONE;\r
+        }\r
+        return EnumSet.of(CRUD.valueOf(string));\r
+    }\r
+\r
+    public static boolean isOperation(Object o){\r
+        try {\r
+        return o instanceof EnumSet<?> && ALL.containsAll((EnumSet<?>)o);\r
+        } catch (Throwable e){\r
+            return false;\r
+        }\r
+    }\r
+\r
+}\r
+\r
+\r
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/CdmPermissionVoter.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/CdmPermissionVoter.java
new file mode 100644 (file)
index 0000000..5895149
--- /dev/null
@@ -0,0 +1,200 @@
+// $Id$
+/**
+* Copyright (C) 2012 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.cdm.persistence.hibernate.permission.voter;
+
+import java.util.Collection;
+
+import org.apache.log4j.Logger;
+import org.springframework.security.access.AccessDecisionVoter;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+
+import sun.security.provider.PolicyParser.ParsingException;
+
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority;
+import eu.etaxonomy.cdm.persistence.hibernate.permission.Operation;
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionClass;
+
+/**
+ * The <code>CdmPermissionVoter</code> provides access control votes for {@link CdmBase} objects.
+ *
+ * @author andreas kohlbecker
+ * @date Sep 4, 2012
+ *
+ */
+public abstract class CdmPermissionVoter implements AccessDecisionVoter {
+
+    public static final Logger logger = Logger.getLogger(CdmPermissionVoter.class);
+
+    /* (non-Javadoc)
+     * @see org.springframework.security.access.AccessDecisionVoter#supports(org.springframework.security.access.ConfigAttribute)
+     */
+    public boolean supports(ConfigAttribute attribute) {
+        // all CdmPermissionVoter support CdmAuthority
+        return attribute instanceof CdmAuthority;
+    }
+
+    /* (non-Javadoc)
+     * @see org.springframework.security.access.AccessDecisionVoter#supports(java.lang.Class)
+     */
+    public boolean supports(Class<?> clazz) {
+        /* NOTE!!!
+         * Do not change this, all CdmPermissionVoters must support CdmBase.class
+         */
+        return clazz.isInstance(CdmBase.class);
+    }
+
+    /**
+     * Sets the Cdm type, or super type this Voter is responsible for.
+     */
+    abstract public Class<? extends CdmBase> getResponsibilityClass();
+
+
+    protected boolean isResponsibleFor(Object securedObject) {
+        return getResponsibilityClass().isAssignableFrom(securedObject.getClass());
+    }
+
+    protected boolean isResponsibleFor(CdmPermissionClass permissionClass) {
+        return getResponsibility().equals(permissionClass);
+    }
+
+    /**
+     * Get the according CdmPermissionClass matching {@link #getResponsibilityClass()} the cdm class this voter is responsible for.
+     * @return
+     */
+    protected CdmPermissionClass getResponsibility() {
+        return CdmPermissionClass.getValueOf(getResponsibilityClass());
+    }
+
+    /* (non-Javadoc)
+     * @see org.springframework.security.access.AccessDecisionVoter#vote(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
+     */
+    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
+
+        if(!isResponsibleFor(object)){
+            logger.debug("class missmatch => ACCESS_ABSTAIN");
+            return ACCESS_ABSTAIN;
+        }
+
+        if (logger.isDebugEnabled()){
+            logger.debug("authentication: " + authentication.getName() + ", object : " + object.toString() + ", attribute[0]:" + ((CdmAuthority)attributes.iterator().next()).getAttribute());
+        }
+
+        int fallThroughVote = ACCESS_DENIED;
+
+        // loop over all attributes = permissions of which at least one must match
+        // usually there is only one element in the collection!
+        for(ConfigAttribute attribute : attributes){
+            if(!(attribute instanceof CdmAuthority)){
+                throw new RuntimeException("attributes must contain only CdmAuthority");
+            }
+            CdmAuthority evalPermission = (CdmAuthority)attribute;
+
+            for (GrantedAuthority authority: authentication.getAuthorities()){
+
+                CdmAuthority ap;
+                try {
+                    ap = new CdmAuthority(authority.getAuthority());
+                } catch (ParsingException e) {
+                    logger.debug("skipping " + authority.getAuthority() + " due to ParsingException");
+                    continue;
+                }
+
+                // check if the voter is responsible for the permission to be evaluated
+                if( ! isResponsibleFor(evalPermission.getPermissionClass())){
+                    logger.debug(getResponsibility() + " not responsible for " + evalPermission.getPermissionClass() + " -> skipping");
+                    continue;
+                }
+
+                ValidationResult vr = new ValidationResult();
+
+                boolean isALL = ap.getPermissionClass().equals(CdmPermissionClass.ALL);
+
+                vr.isClassMatch = isALL || ap.getPermissionClass().equals(evalPermission.getPermissionClass());
+                vr.isPermissionMatch = ap.getOperation().containsAll(evalPermission.getOperation());
+                vr.isUuidMatch = ap.hasTargetUuid() && ap.getTargetUUID().equals(((CdmBase)object).getUuid());
+
+                //
+                // only vote if no property is defined.
+                // Authorities with properties must be voted by type specific voters.
+                //
+                if(!ap.hasProperty()){
+                    if ( !ap.hasTargetUuid() && vr.isClassMatch && vr.isPermissionMatch){
+                        logger.debug("no tragetUuid, class & permission match => ACCESS_GRANTED");
+                        return ACCESS_GRANTED;
+                    }
+                    if ( vr.isUuidMatch  && vr.isClassMatch && vr.isPermissionMatch){
+                        logger.debug("permission, class and uuid are matching => ACCESS_GRANTED");
+                        return ACCESS_GRANTED;
+                    }
+                } else {
+                    //
+                    // If the authority contains a property AND the voter is responsible for this class
+                    // we must change the fallThroughVote
+                    // to ABSTAIN, since no decision can be made in this case at this point
+                    //
+                    if(vr.isClassMatch){
+                        fallThroughVote = ACCESS_ABSTAIN;
+                    }
+                }
+
+                //
+                // ask subclasses for further voting decisions
+                // subclasses will cast votes for specific Cdm Types
+                //
+                Integer furtherVotingResult = furtherVotingDescisions(ap, object, attributes, vr);
+                if(furtherVotingResult != null && furtherVotingResult != ACCESS_ABSTAIN){
+                    logger.debug("furtherVotingResult => " + furtherVotingResult);
+                    return furtherVotingResult;
+                }
+
+            } // END Authorities loop
+        } // END attributes loop
+
+        // the value of fallThroughVote depends on whether the authority had an property or not, see above
+        logger.debug("fallThroughVote => " + fallThroughVote);
+        return fallThroughVote;
+    }
+
+    /**
+     * Override this method to implement specific decisions.
+     * Implementations of this method will be executed in {@link #vote(Authentication, Object, Collection)}.
+     *
+     * @param CdmAuthority
+     * @param object
+     * @param attributes
+     * @param validationResult
+     * @return A return value of ACCESS_ABSTAIN or null will be ignored in {@link #vote(Authentication, Object, Collection)}
+     */
+    protected Integer furtherVotingDescisions(CdmAuthority CdmAuthority, Object object, Collection<ConfigAttribute> attributes,
+            ValidationResult validationResult) {
+        return null;
+    }
+
+    /**
+     * Holds various flags with validation results.
+     * Is used to pass this information from
+     * {@link CdmPermissionVoter#vote(Authentication, Object, Collection)}
+     * to {@link CdmPermissionVoter#furtherVotingDescisions(CdmAuthority, Object, Collection, ValidationResult)}
+     *
+     * @author andreas kohlbecker
+     * @date Sep 5, 2012
+     *
+     */
+    protected class ValidationResult {
+        boolean isPermissionMatch = false;
+        boolean isPropertyMatch = false;
+        boolean isUuidMatch = false;
+        boolean isClassMatch = false;
+    }
+
+}
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/DescriptionBaseVoter.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/DescriptionBaseVoter.java
new file mode 100644 (file)
index 0000000..b21af83
--- /dev/null
@@ -0,0 +1,13 @@
+package eu.etaxonomy.cdm.persistence.hibernate.permission.voter;
+
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.description.DescriptionBase;
+
+public class DescriptionBaseVoter extends CdmPermissionVoter {
+
+    @Override
+    public Class<? extends CdmBase> getResponsibilityClass() {
+        return DescriptionBase.class;
+    }
+
+}
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/DescriptionElementVoter.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/DescriptionElementVoter.java
new file mode 100644 (file)
index 0000000..5176429
--- /dev/null
@@ -0,0 +1,60 @@
+package eu.etaxonomy.cdm.persistence.hibernate.permission.voter;
+
+import java.util.Collection;
+import java.util.UUID;
+
+import org.springframework.security.access.ConfigAttribute;
+
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.description.DescriptionBase;
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+import eu.etaxonomy.cdm.model.description.Feature;
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority;
+
+public class DescriptionElementVoter extends CdmPermissionVoter {
+
+    @Override
+    public Class<? extends CdmBase> getResponsibilityClass() {
+        return DescriptionElementBase.class;
+    }
+
+    @Override
+    protected Integer furtherVotingDescisions(CdmAuthority ap, Object object, Collection<ConfigAttribute> attributes,
+            ValidationResult vr) {
+
+        // we only need to implement the case where a property is contained in the authority
+        // the other case is covered by the CdmPermissionVoter
+        if(ap.hasProperty() && object instanceof DescriptionElementBase){
+
+            Feature feature = ((DescriptionElementBase)object).getFeature();
+
+            if(feature == null){
+                // if the user is granted for a specific feature
+                // he should not be granted for DescriptoinElements without a feature
+                return ACCESS_DENIED;
+            }
+            boolean isPropertyMatch = false;
+
+            try {
+                UUID featureUUID = UUID.fromString(ap.getProperty());
+                isPropertyMatch = featureUUID.equals(feature.getUuid());
+                // FIXME uuids as property not yes supported in AuhorityPermission !!!!
+            } catch (IllegalArgumentException e) {
+                // Property is not a uuid, so treat is as Label:
+                isPropertyMatch = ap.getProperty().equals(feature.getLabel());
+            }
+
+            if ( !ap.hasTargetUuid() && vr.isClassMatch && vr.isPermissionMatch && isPropertyMatch){
+                logger.debug("no tragetUuid, class & permission match => ACCESS_GRANTED");
+                return ACCESS_GRANTED;
+            }
+            if ( vr.isUuidMatch  && vr.isClassMatch && vr.isPermissionMatch && isPropertyMatch){
+                logger.debug("permission, class and uuid are matching => ACCESS_GRANTED");
+                return ACCESS_GRANTED;
+            }
+        }
+
+        return ACCESS_DENIED;
+    }
+
+}
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/GrantAlwaysVoter.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/GrantAlwaysVoter.java
new file mode 100644 (file)
index 0000000..dd45332
--- /dev/null
@@ -0,0 +1,42 @@
+// $Id$
+/**
+* Copyright (C) 2012 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.cdm.persistence.hibernate.permission.voter;
+
+import java.util.Collection;
+
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.access.vote.UnanimousBased;
+import org.springframework.security.core.Authentication;
+
+import eu.etaxonomy.cdm.model.common.CdmBase;
+
+/**
+ * This voter always returns {@link #ACCESS_GRANTED}.
+ * It is needed as default voter when using the {@link UnanimousBased}
+ * @author andreas kohlbecker
+ * @date Sep 4, 2012
+ *
+ */
+public class GrantAlwaysVoter extends CdmPermissionVoter {
+
+    /* (non-Javadoc)
+     * @see org.springframework.security.access.AccessDecisionVoter#vote(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
+     */
+    @Override
+    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
+        return ACCESS_GRANTED;
+    }
+
+    @Override
+    public Class<? extends CdmBase> getResponsibilityClass() {
+        return CdmBase.class;
+    }
+
+}
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/TaxonBaseVoter.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/TaxonBaseVoter.java
new file mode 100644 (file)
index 0000000..a254a39
--- /dev/null
@@ -0,0 +1,31 @@
+// $Id$
+/**
+* Copyright (C) 2012 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.cdm.persistence.hibernate.permission.voter;
+
+import org.apache.log4j.Logger;
+
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.taxon.TaxonBase;
+
+/**
+ * @author andreas kohlbecker
+ * @date Sep 4, 2012
+ *
+ */
+public class TaxonBaseVoter extends CdmPermissionVoter {
+
+    public static final Logger logger = Logger.getLogger(TaxonBaseVoter.class);
+
+    @Override
+    public Class<? extends CdmBase> getResponsibilityClass() {
+        return TaxonBase.class;
+    }
+
+}
diff --git a/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/TaxonNodeVoter.java b/cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/persistence/hibernate/permission/voter/TaxonNodeVoter.java
new file mode 100644 (file)
index 0000000..5cb82de
--- /dev/null
@@ -0,0 +1,65 @@
+// $Id$
+/**
+* Copyright (C) 2012 EDIT
+* European Distributed Institute of Taxonomy
+* http://www.e-taxonomy.eu
+*
+* The contents of this file are subject to the Mozilla Public License Version 1.1
+* See LICENSE.TXT at the top of this package for the full license terms.
+*/
+package eu.etaxonomy.cdm.persistence.hibernate.permission.voter;
+
+import java.util.Collection;
+import java.util.UUID;
+
+import org.apache.log4j.Logger;
+import org.springframework.security.access.ConfigAttribute;
+
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.taxon.TaxonNode;
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority;
+
+/**
+ * @author andreas kohlbecker
+ * @date Sep 4, 2012
+ *
+ */
+public class TaxonNodeVoter extends CdmPermissionVoter {
+
+    public static final Logger logger = Logger.getLogger(TaxonNodeVoter.class);
+
+    @Override
+    public Class<? extends CdmBase> getResponsibilityClass() {
+        return TaxonNode.class;
+    }
+
+    /* (non-Javadoc)
+     * @see eu.etaxonomy.cdm.persistence.hibernate.permission.voter.CdmPermissionVoter#furtherVotingDescisions(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection, eu.etaxonomy.cdm.persistence.hibernate.permission.voter.TaxonBaseVoter.ValidationResult)
+     */
+    @Override
+    protected Integer furtherVotingDescisions(CdmAuthority CdmAuthority, Object object, Collection<ConfigAttribute> attributes,
+            ValidationResult validationResult) {
+
+        boolean isUuidMatchInParentNodes = CdmAuthority.hasTargetUuid() && findTargetUuidInParentNodes(CdmAuthority.getTargetUUID(), (TaxonNode)object);
+        if ( isUuidMatchInParentNodes  && validationResult.isClassMatch && validationResult.isPermissionMatch){
+            logger.debug("permission, class and uuid in parent nodes are matching => ACCESS_GRANTED");
+            return ACCESS_GRANTED;
+        }
+        return null;
+    }
+
+    /**
+     * @param targetUuid
+     * @param node
+     * @return
+     */
+    private boolean findTargetUuidInParentNodes(UUID targetUuid, TaxonNode node){
+        if (targetUuid.equals(node.getUuid()))
+            return true;
+        else if (node.getParent()!= null){
+             return findTargetUuidInParentNodes(targetUuid, node.getParent());
+        }
+        return false;
+    }
+
+}
index 21a8e27793858faef55afd6e1b95f9176d776407..f6a2980407eb134c7f20f0d794fd1afaa68e8017 100644 (file)
@@ -1,53 +1,76 @@
 <?xml version="1.0" encoding="UTF-8"?>\r
 <beans xmlns="http://www.springframework.org/schema/beans"\r
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"\r
-       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"\r
-       xsi:schemaLocation="http://www.springframework.org/schema/beans\r
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"\r
+  xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"\r
+  xsi:schemaLocation="http://www.springframework.org/schema/beans\r
     http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\r
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd\r
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd\r
     ">\r
 \r
-       <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">\r
-               <property name="sessionFactory" ref="sessionFactory" />\r
-       </bean>\r
-\r
-       <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" depends-on="tableGeneratorGlobalOverride">\r
-           <property name="namingStrategy">\r
-               <bean class="org.hibernate.cfg.DefaultComponentSafeNamingStrategy" />\r
-           </property>\r
-\r
-               <property name="entityInterceptor">\r
-                        <bean class="eu.etaxonomy.cdm.persistence.hibernate.CdmSecurityHibernateInterceptor"/>\r
-               </property>\r
\r
-           <property name="configLocation" value="classpath:eu/etaxonomy/cdm/hibernate.cfg.xml"/>\r
-           <property  name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/>\r
-           <!--\r
-            If dataSource is set, this will override corresponding settings in Hibernate properties.\r
-            If this is set, the Hibernate settings should not define a connection provider to\r
-            avoid meaningless double configuration.\r
-\r
-            see also org.springframework.orm.hibernate3.AbstractSessionFactoryBean.setDataSource(DataSource dataSource)\r
-         -->\r
-           <property name="dataSource" ref="dataSource"/>\r
-           <property name="hibernateProperties" ref="hibernateProperties"/>\r
-       </bean>\r
-\r
-       <!--\r
-          Configuration for the BeanInitialization\r
-        -->\r
-       <bean id="titleCacheAutoInitializer" class="eu.etaxonomy.cdm.persistence.dao.TitleCacheAutoInitializer"></bean>\r
+    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">\r
+      <property name="sessionFactory" ref="sessionFactory" />\r
+    </bean>\r
+\r
+    <bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">\r
+        <property name="decisionVoters">\r
+            <list>\r
+                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.GrantAlwaysVoter" />\r
+                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.TaxonNodeVoter" />\r
+                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.TaxonBaseVoter" />\r
+                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.DescriptionBaseVoter" />\r
+                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.DescriptionElementVoter" />\r
+            </list>\r
+        </property>\r
+    </bean>\r
+\r
+    <!--\r
+        CdmPermissionEvaluator.hasPermissions() evaluates the CdmPermissions like TAXONNODE.UPDATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}\r
+    -->\r
+    <bean id="cdmPermissionEvaluator" class="eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator">\r
+        <property name="accessDecisionManager" ref="accessDecisionManager" />\r
+    </bean>\r
+\r
+    <!-- The CdmSecurityHibernateInterceptor checks onSave() and on flushDirty() if the currently authenticated principal or token  has\r
+    sufficient permissions on the entity to be persisted -->\r
+    <bean id="securityHibernateInterceptor" class="eu.etaxonomy.cdm.persistence.hibernate.CdmSecurityHibernateInterceptor">\r
+        <property name="permissionEvaluator" ref="cdmPermissionEvaluator" />\r
+    </bean>\r
+\r
+    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" depends-on="tableGeneratorGlobalOverride">\r
+        <property name="namingStrategy">\r
+          <bean class="org.hibernate.cfg.DefaultComponentSafeNamingStrategy" />\r
+        </property>\r
+\r
+        <property name="entityInterceptor" ref="securityHibernateInterceptor" />\r
+\r
+        <property name="configLocation" value="classpath:eu/etaxonomy/cdm/hibernate.cfg.xml"/>\r
+        <property  name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/>\r
+        <!--\r
+              If dataSource is set, this will override corresponding settings in Hibernate properties.\r
+              If this is set, the Hibernate settings should not define a connection provider to\r
+              avoid meaningless double configuration.\r
+\r
+              see also org.springframework.orm.hibernate3.AbstractSessionFactoryBean.setDataSource(DataSource dataSource)\r
+           -->\r
+        <property name="dataSource" ref="dataSource"/>\r
+        <property name="hibernateProperties" ref="hibernateProperties"/>\r
+    </bean>\r
+\r
+  <!--\r
+     Configuration for the BeanInitialization\r
+   -->\r
+  <bean id="titleCacheAutoInitializer" class="eu.etaxonomy.cdm.persistence.dao.TitleCacheAutoInitializer"></bean>\r
     <bean id="annotationTypeAutoInitializer" class="eu.etaxonomy.cdm.persistence.dao.AnnotationTypeAutoInitializer"></bean>\r
-       <bean id="defaultBeanInitializer" class="eu.etaxonomy.cdm.persistence.dao.hibernate.HibernateBeanInitializer">\r
-          <property name="beanAutoInitializers">\r
-              <map>\r
-                  <entry key="eu.etaxonomy.cdm.model.common.IdentifiableEntity"  value-ref="titleCacheAutoInitializer" />\r
+  <bean id="defaultBeanInitializer" class="eu.etaxonomy.cdm.persistence.dao.hibernate.HibernateBeanInitializer">\r
+     <property name="beanAutoInitializers">\r
+         <map>\r
+             <entry key="eu.etaxonomy.cdm.model.common.IdentifiableEntity"  value-ref="titleCacheAutoInitializer" />\r
                <entry key="eu.etaxonomy.cdm.model.common.Annotation"  value-ref="annotationTypeAutoInitializer" />\r
-              </map>\r
-          </property>\r
-       </bean>\r
+         </map>\r
+     </property>\r
+  </bean>\r
 \r
 \r
 </beans>\r
index 353e9a7697badb9c0f633ad4fd1153ff9c7970c1..21e3c4edfe2d0d1f14e2d6c029d64bdbaae043b2 100644 (file)
@@ -25,6 +25,7 @@ import org.junit.Test;
 import org.springframework.security.authentication.AuthenticationManager;\r
 import org.springframework.security.authentication.TestingAuthenticationToken;\r
 import org.springframework.security.core.Authentication;\r
+import org.springframework.security.core.context.SecurityContext;\r
 import org.springframework.security.core.context.SecurityContextHolder;\r
 import org.springframework.security.core.context.SecurityContextImpl;\r
 import org.unitils.database.annotations.Transactional;\r
@@ -52,229 +53,294 @@ import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTestWithSecu
  */\r
 public class CdmEntityDaoBaseTest extends CdmTransactionalIntegrationTestWithSecurity {\r
 \r
-       private UUID uuid;\r
-       private TaxonBase cdmBase;\r
+    private static final UUID UUID_USER_BEN = UUID.fromString("dbac0f20-07f2-11de-8c30-0800200c9a66");\r
+    private UUID uuid;\r
+    private TaxonBase cdmBase;\r
 \r
-       @SpringBeanByType\r
-       private ITaxonDao cdmEntityDaoBase;\r
+    @SpringBeanByType\r
+    private ITaxonDao cdmEntityDaoBase;\r
 \r
-       @SpringBeanByType\r
+    @SpringBeanByType\r
     private AuthenticationManager authenticationManager;\r
 \r
-       @SpringBeanByType\r
-       private IUserDao userDao;\r
+    @SpringBeanByType\r
+    private IUserDao userDao;\r
 \r
-       /**\r
-        * @throws java.lang.Exception\r
-        */\r
-       @Before\r
-       public void setUp() throws Exception {\r
-               uuid = UUID.fromString("8d77c380-c76a-11dd-ad8b-0800200c9a66");\r
-               cdmBase = Taxon.NewInstance(null, null);\r
-               cdmBase.setUuid(UUID.fromString("e463b270-c76b-11dd-ad8b-0800200c9a66"));\r
-               \r
+    private TestingAuthenticationToken taxonEditorToken;\r
+    private TestingAuthenticationToken adminToken;\r
+    private TestingAuthenticationToken testerToken;\r
 \r
-               // Clear the context prior to each test\r
-               SecurityContextHolder.clearContext();\r
-       }\r
 \r
-       private void setAuthentication(User user) {\r
-               TestingAuthenticationToken token = new TestingAuthenticationToken(user, "password",  new GrantedAuthorityImpl[0]);\r
-           Authentication authentication = authenticationManager.authenticate(token);\r
 \r
-           SecurityContextImpl secureContext = new SecurityContextImpl();\r
-           secureContext.setAuthentication(authentication);\r
-           SecurityContextHolder.setContext(secureContext);\r
-       }\r
+    /**\r
+     * @throws java.lang.Exception\r
+     */\r
+    @Before\r
+    public void setUp() throws Exception {\r
+        uuid = UUID.fromString("8d77c380-c76a-11dd-ad8b-0800200c9a66");\r
+        cdmBase = Taxon.NewInstance(null, null);\r
+        cdmBase.setUuid(UUID.fromString("e463b270-c76b-11dd-ad8b-0800200c9a66"));\r
+\r
+        taxonEditorToken = new TestingAuthenticationToken("taxonEditor", "password",  "TAXONBASE.CREATE", "TAXONBASE.UPDATE");\r
+        adminToken = new TestingAuthenticationToken("admin", "password",  "ALL.ADMIN");\r
+        testerToken = new TestingAuthenticationToken("tester", "password");\r
+\r
+\r
+        // Clear the context prior to each test\r
+        SecurityContextHolder.clearContext();\r
+    }\r
+\r
+    private void setAuthentication(TestingAuthenticationToken token) {\r
+         Authentication authentication = authenticationManager.authenticate(token);\r
+\r
+        SecurityContextImpl secureContext = new SecurityContextImpl();\r
+        SecurityContextHolder.setContext(secureContext);\r
+        secureContext.setAuthentication(authentication);\r
+    }\r
 \r
 /************ TESTS ********************************/\r
 \r
 \r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#CdmEntityDaoBase(java.lang.Class)}.\r
-        * @throws Exception\r
-        */\r
-       @Test\r
-       public void testCdmEntityDaoBase() throws Exception {\r
-               assertNotNull("cdmEntityDaoBase should exist",cdmEntityDaoBase);\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#saveOrUpdate(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       @ExpectedDataSet\r
-       public void testSaveOrUpdate() {\r
-               TaxonBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
-               cdmBase.setDoubtful(true);\r
-               cdmEntityDaoBase.saveOrUpdate(cdmBase);\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#saveOrUpdate(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       @ExpectedDataSet\r
-       public void testSaveOrUpdateWithAuthentication() {\r
-               User user = userDao.findByUuid(UUID.fromString("dbac0f20-07f2-11de-8c30-0800200c9a66"));\r
-               assert user != null : "User cannot be null";\r
-               setAuthentication(user);\r
-               TaxonBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
-               cdmBase.setDoubtful(true);\r
-               cdmEntityDaoBase.saveOrUpdate(cdmBase);\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#saveOrUpdate(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#CdmEntityDaoBase(java.lang.Class)}.\r
+     * @throws Exception\r
+     */\r
+    @Test\r
+    public void testCdmEntityDaoBase() throws Exception {\r
+        assertNotNull("cdmEntityDaoBase should exist",cdmEntityDaoBase);\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#saveOrUpdate(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    @ExpectedDataSet\r
+    public void testSaveOrUpdate() {\r
+        TaxonBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
+        cdmBase.setDoubtful(true);\r
+        cdmEntityDaoBase.saveOrUpdate(cdmBase);\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#saveOrUpdate(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
     @ExpectedDataSet\r
-       public void testSaveOrUpdateNewObjectWithAuthentication() {\r
+    public void testSaveOrUpdateWithAuthentication() {\r
+\r
+        setAuthentication(taxonEditorToken);\r
+        TaxonBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
+        cdmBase.setDoubtful(true);\r
+        cdmEntityDaoBase.saveOrUpdate(cdmBase);\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#saveOrUpdate(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    @ExpectedDataSet\r
+    public void testSaveOrUpdateNewObjectWithAuthentication() {\r
 //             printDataSet(System.err, new String[]{"TAXONBASE", "HIBERNATE_SEQUENCES"});\r
-               User user = userDao.findByUuid(UUID.fromString("dbac0f20-07f2-11de-8c30-0800200c9a66"));\r
-               assert user != null : "User cannot be null";\r
-               setAuthentication(user);\r
-               cdmEntityDaoBase.saveOrUpdate(cdmBase);\r
-               cdmBase.setDoubtful(true);\r
-               cdmEntityDaoBase.saveOrUpdate(cdmBase);\r
-               \r
-       }\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#save(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       @ExpectedDataSet\r
-       public void testSave() throws Exception {\r
-               cdmEntityDaoBase.save(cdmBase);\r
-       }\r
-       \r
-       \r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#save(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       public void testSaveWithAuthenticationFailedPermissionEvaluation() throws Exception {\r
-               User user = userDao.findByUuid(UUID.fromString("04f43bec-ff0e-4263-b4f8-24d763e590eb"));\r
-               assert user != null : "User cannot be null";\r
-               setAuthentication(user);\r
-               \r
-               \r
-               try{\r
-                       cdmEntityDaoBase.save(cdmBase);\r
-                       Assert.fail();\r
-               }catch(EvaluationFailedException e){\r
-                       \r
-               }\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#save(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       @ExpectedDataSet\r
-       public void testSaveWithAuthentication() throws Exception {\r
-               User user = userDao.findByUuid(UUID.fromString("dbac0f20-07f2-11de-8c30-0800200c9a66"));\r
-               assert user != null : "User cannot be null";\r
-               setAuthentication(user);\r
-               cdmEntityDaoBase.save(cdmBase);\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#update(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       @ExpectedDataSet\r
-       public void testUpdate() {\r
-               TaxonBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
-               cdmBase.setDoubtful(true);\r
-               cdmEntityDaoBase.update(cdmBase);\r
-       }\r
-\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       @ExpectedDataSet\r
-       public void testUpdateWithAuthentication() {\r
-               User user = userDao.findByUuid(UUID.fromString("dbac0f20-07f2-11de-8c30-0800200c9a66"));\r
-               assert user != null : "User cannot be null";\r
-\r
-               setAuthentication(user);\r
-               TaxonBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
-               cdmBase.setDoubtful(true);\r
-               cdmEntityDaoBase.update(cdmBase);\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#findById(int)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       public void testFindById() {\r
-               CdmBase cdmBase = cdmEntityDaoBase.findById(1);\r
-               assertNotNull("There should be an entity with an id of 1",cdmBase);\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#findByUuid(java.util.UUID)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       public void testFindByUuid() {\r
-               CdmBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
-               assertNotNull("testFindByUuid() an entity with a uuid of " + uuid.toString(),cdmBase);\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#exists(java.util.UUID)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       public void testExists() {\r
-               boolean exists = cdmEntityDaoBase.exists(uuid);\r
-               assertTrue("exists() should return true for uuid " + uuid.toString(), exists);\r
-               boolean existsRandom = cdmEntityDaoBase.exists(UUID.randomUUID());\r
-               assertFalse("exists() should return false for any other uuid", existsRandom);\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#list(int, int)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       public void testList() {\r
-               List<TaxonBase> list = cdmEntityDaoBase.list(1000, 0);\r
-               assertNotNull("list() should not return null",list);\r
-               assertEquals("list() should return a list with two entities in it",list.size(),2);\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#list(int, int)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       public void testRandomOrder() {\r
-               List<OrderHint> orderHints = new ArrayList<OrderHint>();\r
-               orderHints.add(new RandomOrder());\r
-               List<TaxonBase> list = cdmEntityDaoBase.list(null,1000, 0,orderHints,null);\r
-               assertNotNull("list() should not return null",list);\r
-               assertEquals("list() should return a list with two entities in it",list.size(),2);\r
-       }\r
-\r
-       /**\r
-        * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#delete(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
-        */\r
-       @Test\r
-       @DataSet("CdmEntityDaoBaseTest.xml")\r
-       @ExpectedDataSet\r
-       public void testDelete() {\r
-               TaxonBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
-               assertNotNull(cdmBase);\r
-               cdmEntityDaoBase.delete(cdmBase);\r
-       }\r
+        setAuthentication(taxonEditorToken);\r
+        RuntimeException securityException = null;\r
+\r
+        // 1) test create\r
+        try{\r
+            cdmEntityDaoBase.saveOrUpdate(cdmBase);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            Assert.fail();\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        // 1) test update\r
+        cdmBase = cdmEntityDaoBase.findByUuid(cdmBase.getUuid());\r
+        cdmBase.setDoubtful(true);\r
+        try{\r
+            cdmEntityDaoBase.saveOrUpdate(cdmBase);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            Assert.fail();\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+    }\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#save(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    @ExpectedDataSet\r
+    public void testSave() throws Exception {\r
+        cdmEntityDaoBase.save(cdmBase);\r
+    }\r
+\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#save(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    public void testSaveWithAuthenticationFailedPermissionEvaluation() throws Exception {\r
+        setAuthentication(testerToken);\r
+        RuntimeException securityException = null;\r
+        try{\r
+            cdmEntityDaoBase.save(cdmBase);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.error("Unexpected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNotNull("evaluation must fail since the user has no permission", securityException);\r
+\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#save(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    @ExpectedDataSet\r
+    public void testSaveWithAuthentication() throws Exception {\r
+        setAuthentication(taxonEditorToken);\r
+        RuntimeException securityException = null;\r
+        try {\r
+            cdmEntityDaoBase.save(cdmBase);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException   = findSecurityRuntimeException(e);\r
+            logger.error("Unexpected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#update(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    @ExpectedDataSet\r
+    public void testUpdate() {\r
+        TaxonBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
+        cdmBase.setDoubtful(true);\r
+        cdmEntityDaoBase.update(cdmBase);\r
+    }\r
+\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    @ExpectedDataSet\r
+    public void testUpdateWithAuthentication() {\r
+\r
+        setAuthentication(taxonEditorToken);\r
+        TaxonBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
+        cdmBase.setDoubtful(true);\r
+        RuntimeException securityException = null;\r
+        try {\r
+        cdmEntityDaoBase.update(cdmBase);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException    = findSecurityRuntimeException(e);\r
+            logger.error("Unexpected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#findById(int)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    public void testFindById() {\r
+        CdmBase cdmBase = cdmEntityDaoBase.findById(1);\r
+        assertNotNull("There should be an entity with an id of 1",cdmBase);\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#findByUuid(java.util.UUID)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    public void testFindByUuid() {\r
+        CdmBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
+        assertNotNull("testFindByUuid() an entity with a uuid of " + uuid.toString(),cdmBase);\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#exists(java.util.UUID)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    public void testExists() {\r
+        boolean exists = cdmEntityDaoBase.exists(uuid);\r
+        assertTrue("exists() should return true for uuid " + uuid.toString(), exists);\r
+        boolean existsRandom = cdmEntityDaoBase.exists(UUID.randomUUID());\r
+        assertFalse("exists() should return false for any other uuid", existsRandom);\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#list(int, int)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    public void testList() {\r
+        List<TaxonBase> list = cdmEntityDaoBase.list(1000, 0);\r
+        assertNotNull("list() should not return null",list);\r
+        assertEquals("list() should return a list with two entities in it",list.size(),2);\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#list(int, int)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    public void testRandomOrder() {\r
+        List<OrderHint> orderHints = new ArrayList<OrderHint>();\r
+        orderHints.add(new RandomOrder());\r
+        List<TaxonBase> list = cdmEntityDaoBase.list(null,1000, 0,orderHints,null);\r
+        assertNotNull("list() should not return null",list);\r
+        assertEquals("list() should return a list with two entities in it",list.size(),2);\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.persistence.dao.hibernate.common.CdmEntityDaoBase#delete(eu.etaxonomy.cdm.model.common.CdmBase)}.\r
+     */\r
+    @Test\r
+    @DataSet("CdmEntityDaoBaseTest.xml")\r
+    @ExpectedDataSet\r
+    public void testDelete() {\r
+        TaxonBase cdmBase = cdmEntityDaoBase.findByUuid(uuid);\r
+        assertNotNull(cdmBase);\r
+        cdmEntityDaoBase.delete(cdmBase);\r
+    }\r
 }\r
index ea355aacb7b13366dc69cb2e9d9b270be72d9c9d..5e8c98aeab4cc6ac6319faeeb09bed266f6fef23 100644 (file)
@@ -280,12 +280,15 @@ public abstract class CdmIntegrationTest extends UnitilsJUnit4 {
     public void printDataSet(OutputStream out, String[] tableNames) {\r
         IDatabaseConnection connection = null;\r
 \r
+        if(tableNames == null){\r
+            return;\r
+        }\r
+\r
         try {\r
             connection = getConnection();\r
             IDataSet actualDataSet = connection.createDataSet(tableNames);\r
             FlatXmlDataSet.write(actualDataSet, out);\r
 \r
-\r
         } catch (Exception e) {\r
             logger.error(e);\r
         } finally {\r
index 10519ab25c7c28fdd963d63ee2972b7493960072..8c63dd441af0dc77cfa1ae9d67085c39c94aaa58 100644 (file)
@@ -25,6 +25,7 @@ import org.unitils.spring.annotation.SpringBeanByType;
 \r
 @Transactional(TransactionMode.DISABLED) // NOTE: we are handling transaction by ourself in this class, thus we prevent unitils from creating transactions\r
 public abstract class CdmTransactionalIntegrationTest extends CdmIntegrationTest {\r
+\r
     protected static final Logger logger = Logger.getLogger(CdmTransactionalIntegrationTest.class);\r
 \r
     /**\r
@@ -281,6 +282,7 @@ public abstract class CdmTransactionalIntegrationTest extends CdmIntegrationTest
 \r
         if (this.transactionStatus != null) {\r
             try {\r
+                logger.debug("Trying to commit or rollback");\r
                 if (commit) {\r
                     this.transactionManager.commit(this.transactionStatus);\r
                     logger.debug("Committed transaction after execution of test");\r
@@ -291,6 +293,7 @@ public abstract class CdmTransactionalIntegrationTest extends CdmIntegrationTest
                 }\r
             }\r
             finally {\r
+                logger.debug("Clearing transactionStatus");\r
                 this.transactionStatus = null;\r
             }\r
         }\r
@@ -300,10 +303,12 @@ public abstract class CdmTransactionalIntegrationTest extends CdmIntegrationTest
 \r
         if (this.transactionStatus != null) {\r
             try {\r
+                logger.debug("trying to rollback");\r
                 this.transactionManager.rollback(this.transactionStatus);\r
                 logger.debug("Rolled back transaction after execution of test.");\r
             }\r
             finally {\r
+                logger.debug("Clearing transactionStatus");\r
                 this.transactionStatus = null;\r
             }\r
         }\r
@@ -347,12 +352,19 @@ public abstract class CdmTransactionalIntegrationTest extends CdmIntegrationTest
      * </pre>\r
      */\r
     protected void commitAndStartNewTransaction(final String[] tableNames) {\r
-        setComplete();\r
-        endTransaction();\r
+        commit();\r
         if(logger.isDebugEnabled()){\r
             printDataSet(System.out, tableNames);\r
         }\r
         startNewTransaction();\r
     }\r
 \r
+    /**\r
+     *\r
+     */\r
+    protected void commit() {\r
+        setComplete();\r
+        endTransaction();\r
+    }\r
+\r
 }\r
index 78abb01baf86f3ab3d13b48f1f269850c16ebf3d..94845e2f10cb91b96abf058cf34bd07a682792f8 100644 (file)
@@ -1,7 +1,9 @@
 package eu.etaxonomy.cdm.test.integration;\r
 \r
+import org.apache.log4j.Logger;\r
 import org.junit.After;\r
 import org.junit.Before;\r
+import org.springframework.security.access.AccessDeniedException;\r
 import org.springframework.security.core.context.SecurityContextHolder;\r
 import org.unitils.spring.annotation.SpringApplicationContext;\r
 \r
@@ -10,20 +12,41 @@ import eu.etaxonomy.cdm.database.EvaluationFailedException;
 @SpringApplicationContext("file:./target/test-classes/eu/etaxonomy/cdm/applicationContext-securityTest.xml")\r
 public abstract class CdmTransactionalIntegrationTestWithSecurity extends  CdmTransactionalIntegrationTest {\r
 \r
-       /**\r
-        * Finds a nested {@link EvaluationFailedException} or returns <code>null</code> \r
-        * @param exception\r
-        * @return\r
-        */\r
-       public static EvaluationFailedException findEvaluationFailedExceptionIn(Throwable exception) {\r
-           if( EvaluationFailedException.class.isInstance(exception) ){\r
-               return (EvaluationFailedException)exception;\r
-           } else if(exception != null ){\r
-               return findEvaluationFailedExceptionIn(exception.getCause());\r
-           }\r
-           return null;\r
-       }\r
+    public static final Logger logger = Logger.getLogger(CdmTransactionalIntegrationTestWithSecurity.class);\r
 \r
+    /**\r
+     * Finds a nested RuntimeExceptions of the types {@link EvaluationFailedException}, {@link AccessDeniedException}\r
+     * or returns null.\r
+     * @param exception\r
+     * @return\r
+     */\r
+    public static RuntimeException findSecurityRuntimeException(Throwable exception) {\r
 \r
+        if( EvaluationFailedException.class.isInstance(exception) || AccessDeniedException.class.isInstance(exception) ){\r
+            return (RuntimeException) exception;\r
+        } else if(exception != null ){\r
+            return findSecurityRuntimeException(exception.getCause());\r
+        }\r
+        return null;\r
+\r
+    }\r
+\r
+\r
+    /**\r
+     * find in the nested <code>exception</code> the exception of type <code>clazz</code>\r
+     * or returns <code>null</code> if no such exception is found.\r
+     *\r
+     * @param clazz\r
+     * @param exception the nested <code>Throwable</code> to search in.\r
+     * @return\r
+     */\r
+    public <T extends Throwable> T  findThrowableOfTypeIn(Class<T> clazz, Throwable exception) {\r
+        if( EvaluationFailedException.class.isInstance(exception) ){\r
+            return (T)exception;\r
+        } else if(exception != null ){\r
+            return findThrowableOfTypeIn(clazz, exception.getCause());\r
+        }\r
+        return null;\r
+    }\r
 \r
 }\r
index d59b31ca25f73743e676ee3cf8403c376c3266bd..ba0a0b1bdb8bc5ba8143da079d0935e9467aa915 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version='1.0' encoding='UTF-8'?>\r
 <dataset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../dataset.xsd">\r
-  <TAXONBASE DTYPE="Taxon" ID="1" SEC_ID="1" UUID="8d77c380-c76a-11dd-ad8b-0800200c9a66" TITLECACHE=" sec. ???" PROTECTEDTITLECACHE="true" DOUBTFUL="true" TAXONOMICCHILDRENCOUNT="1" NAME_ID="1" UPDATEDBY_ID="5"/>\r
+  <TAXONBASE DTYPE="Taxon" ID="1" SEC_ID="1" UUID="8d77c380-c76a-11dd-ad8b-0800200c9a66" TITLECACHE=" sec. ???" PROTECTEDTITLECACHE="true" DOUBTFUL="true" TAXONOMICCHILDRENCOUNT="1" NAME_ID="1" UPDATEDBY_ID="1"/>\r
   <TAXONBASE DTYPE="Taxon" ID="2" SEC_ID="1" UUID="822d98dc-9ef7-44b7-a870-94573a3bcb46" TITLECACHE="  sec. ???" PROTECTEDTITLECACHE="true" DOUBTFUL="false" TAXONOMICCHILDRENCOUNT="0" NAME_ID="2" UPDATEDBY_ID="1" />\r
 </dataset>
\ No newline at end of file
index f7cf13960c749300a35727804f0304a703c21eef..447b97ede9e1c6abd9016a1ad5b110a405cdaa5a 100644 (file)
   <TAXONRELATIONSHIP ID="1" CREATED="2008-12-10 09:56:07.0" UUID="25064dff-f526-408e-b851-670d7770e337" UPDATED="2008-12-10 09:56:07.253" CITATIONMICROREFERENCE="Lorem ipsum dolor" TYPE_ID="889" RELATEDTO_ID="1" RELATEDFROM_ID="2" DOUBTFUL="false"/>
   <TAXONRELATIONSHIP_AUD ID="1" REV="1000" REVTYPE="0" CREATED="2008-12-10 09:56:07.0" UUID="25064dff-f526-408e-b851-670d7770e337" UPDATED="2008-12-10 09:56:07.253" CITATIONMICROREFERENCE="Lorem ipsum dolor" TYPE_ID="889" RELATEDTO_ID="1" RELATEDFROM_ID="2" DOUBTFUL="false"/>\r
 \r
-  <USERACCOUNT ID="5" UUID="dbac0f20-07f2-11de-8c30-0800200c9a66" USERNAME="ben" ACCOUNTNONEXPIRED="true" ACCOUNTNONLOCKED="true" CREATED="2008-12-10 09:56:07.0" CREATEDBY_ID="1" CREDENTIALSNONEXPIRED="true" EMAILADDRESS="b.clark@example.org" ENABLED="true" PASSWORD="xyz"/>\r
-  <USERACCOUNT_AUD ID="5" REV="1000" REVTYPE="0" UUID="dbac0f20-07f2-11de-8c30-0800200c9a66" USERNAME="ben" ACCOUNTNONEXPIRED="true" ACCOUNTNONLOCKED="true" CREATED="2008-12-10 09:56:07.0" CREATEDBY_ID="1" CREDENTIALSNONEXPIRED="true" EMAILADDRESS="b.clark@example.org" ENABLED="true"/>\r
   <USERACCOUNT ID="1" UUID="c026b289-1a36-4afc-8673-92ffe8ed05b6" USERNAME="admin" ACCOUNTNONEXPIRED="true" ACCOUNTNONLOCKED="true" CREATED="2008-12-10 09:56:07.0" CREATEDBY_ID="1" CREDENTIALSNONEXPIRED="true" EMAILADDRESS="admin@example.org" ENABLED="true" PASSWORD="xyz"/>\r
   <USERACCOUNT_AUD ID="1" REV="1000" REVTYPE="0" UUID="c026b289-1a36-4afc-8673-92ffe8ed05b6" USERNAME="admin" ACCOUNTNONEXPIRED="true" ACCOUNTNONLOCKED="true" CREATED="2008-12-10 09:56:07.0" CREATEDBY_ID="1" CREDENTIALSNONEXPIRED="true" EMAILADDRESS="admin@example.org" ENABLED="true"/>\r
+  <USERACCOUNT ID="5" UUID="dbac0f20-07f2-11de-8c30-0800200c9a66" USERNAME="taxoneditor" ACCOUNTNONEXPIRED="true" ACCOUNTNONLOCKED="true" CREATED="2008-12-10 09:56:07.0" CREATEDBY_ID="1" CREDENTIALSNONEXPIRED="true" EMAILADDRESS="b.clark@example.org" ENABLED="true" PASSWORD="xyz"/>\r
+  <USERACCOUNT_AUD ID="5" REV="1000" REVTYPE="0" UUID="dbac0f20-07f2-11de-8c30-0800200c9a66" USERNAME="taxoneditor" ACCOUNTNONEXPIRED="true" ACCOUNTNONLOCKED="true" CREATED="2008-12-10 09:56:07.0" CREATEDBY_ID="1" CREDENTIALSNONEXPIRED="true" EMAILADDRESS="taxoneditor@example.org" ENABLED="true"/>\r
   <USERACCOUNT ID="6" UUID="04f43bec-ff0e-4263-b4f8-24d763e590eb" USERNAME="tester" ACCOUNTNONEXPIRED="true" ACCOUNTNONLOCKED="true" CREATED="2008-12-10 09:56:07.0" CREATEDBY_ID="1" CREDENTIALSNONEXPIRED="true" EMAILADDRESS="admin@example.org" ENABLED="true" PASSWORD="xyz"/>\r
   <USERACCOUNT_AUD ID="6" REV="1000" REVTYPE="0" UUID="04f43bec-ff0e-4263-b4f8-24d763e590eb" USERNAME="tester" ACCOUNTNONEXPIRED="true" ACCOUNTNONLOCKED="true" CREATED="2008-12-10 09:56:07.0" CREATEDBY_ID="1" CREDENTIALSNONEXPIRED="true" EMAILADDRESS="admin@example.org" ENABLED="true"/>\r
   <GRANTEDAUTHORITYIMPL ID="1" UUID="9a37ef49-0c22-44f8-982f-24a8423269cd" CREATED="2009-02-03 17:52:26.0" AUTHORITY="ALL.ADMIN"/>\r
   <GRANTEDAUTHORITYIMPL ID="2" UUID="a3b3ebd0-2f88-4050-aaa9-2000cf7bc41d" CREATED="2009-02-03 17:52:26.0" AUTHORITY="TAXONBASE.UPDATE"/>\r
   <GRANTEDAUTHORITYIMPL ID="3" UUID="5fe558c2-a77c-4dfc-8a17-dfb220af597c" CREATED="2009-02-03 17:52:26.0" AUTHORITY="TAXONBASE.CREATE"/>\r
+  <USERACCOUNT_GRANTEDAUTHORITYIMPL USERACCOUNT_ID="1" GRANTEDAUTHORITIES_ID="1"/>\r
   <USERACCOUNT_GRANTEDAUTHORITYIMPL USERACCOUNT_ID="5" GRANTEDAUTHORITIES_ID="2"/>\r
   <USERACCOUNT_GRANTEDAUTHORITYIMPL USERACCOUNT_ID="5" GRANTEDAUTHORITIES_ID="3"/>\r
-  <USERACCOUNT_GRANTEDAUTHORITYIMPL USERACCOUNT_ID="1" GRANTEDAUTHORITIES_ID="1"/>\r
 </dataset>
\ No newline at end of file
index aede98dcc3b9b732af9bfe0b9b5690b1490187ad..5b4d669e9b17c6967491266ce00a17986aa29085 100644 (file)
@@ -41,6 +41,14 @@ log4j.logger.org.hibernate.search.impl.SearchFactoryImpl=error
 #log4j.logger.org.springframework.FileSystemXmlApplicationContext = warn
 #log4j.logger.org.springframework.core.io.support = info
 
+#### log spring security #####
+log4j.logger.eu.etaxonomy.cdm.persistence.hibernate.permission=debug
+log4j.logger.eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority=warn
+log4j.logger.eu.etaxonomy.cdm.persistence.hibernate.CdmSecurityHibernateInterceptor=debug
+log4j.logger.org.springframework.security.access.intercept=debug
+log4j.logger.org.springframework.security.access.vote=debug
+#log4j.logger.eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest=debug
+
 
   ### ***HIBERNATE ************ ###
 
index b1e4868dc8f35ee732014fc112a3cde125f08d5c..cb6de2e0d400608a5144f184d7d03b1c86a60588 100644 (file)
@@ -1,9 +1,9 @@
 // $Id$\r
 /**\r
 * Copyright (C) 2007 EDIT\r
-* European Distributed Institute of Taxonomy \r
+* European Distributed Institute of Taxonomy\r
 * http://www.e-taxonomy.eu\r
-* \r
+*\r
 * The contents of this file are subject to the Mozilla Public License Version 1.1\r
 * See LICENSE.TXT at the top of this package for the full license terms.\r
 */\r
@@ -12,6 +12,7 @@ package eu.etaxonomy.cdm.api.service;
 \r
 \r
 import java.util.Collection;\r
+import java.util.EnumSet;\r
 import java.util.List;\r
 import java.util.Map;\r
 import java.util.Set;\r
@@ -24,7 +25,8 @@ import org.springframework.security.core.Authentication;
 import eu.etaxonomy.cdm.api.service.pager.Pager;\r
 import eu.etaxonomy.cdm.model.common.ICdmBase;\r
 import eu.etaxonomy.cdm.persistence.dao.BeanInitializer;\r
-import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermission;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.Operation;\r
 import eu.etaxonomy.cdm.persistence.query.Grouping;\r
 import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
 \r
@@ -41,265 +43,266 @@ import eu.etaxonomy.cdm.persistence.query.OrderHint;
  */\r
 public interface IService<T extends ICdmBase>{\r
 \r
-       // FIXME what does this method do?\r
-       public void clear();\r
-       \r
-       /**\r
-        * Obtain the specified lock mode on the given object t\r
-        */\r
-       public void lock(T t, LockMode lockMode);\r
-       \r
-       /**\r
-        * Refreshes a given object t using the specified lockmode\r
-        * \r
+    // FIXME what does this method do?\r
+    public void clear();\r
+\r
+    /**\r
+     * Obtain the specified lock mode on the given object t\r
+     */\r
+    public void lock(T t, LockMode lockMode);\r
+\r
+    /**\r
+     * Refreshes a given object t using the specified lockmode\r
+     *\r
      * All bean properties given in the <code>propertyPaths</code> parameter are recursively initialized.\r
-        * <p>\r
-        * For detailed description and examples <b>please refer to:</b> \r
-        * {@link BeanInitializer#initialize(Object, List)}\r
-        * \r
-        * NOTE: in the case of lockmodes that hit the database (e.g. LockMode.READ), you will need to re-initialize\r
-        * child propertiesto avoid a HibernateLazyInitializationException (even if the properties of the child \r
-        * were initialized prior to the refresh).\r
-        * \r
-        * @param t\r
-        * @param lockMode\r
-        */\r
-       public void refresh(T t, LockMode lockMode, List<String> propertyPaths);\r
-       \r
-       /**\r
-        * Returns a count of all entities of type <T>  optionally restricted\r
-        * to objects belonging to a class that that extends <T>\r
-        * \r
-        * @param clazz the class of entities to be counted (can be null to count all entities of type <T>)\r
-        * @return a count of entities\r
-        */\r
-       public int count(Class<? extends T> clazz);\r
-       \r
-       /**\r
-        * Delete an existing persistent object\r
-        * \r
-        * @param persistentObject the object to be deleted\r
-        * @return the unique identifier of the deleted entity\r
-        */\r
-       public UUID delete(T persistentObject);\r
-       \r
-       /**\r
-        * Returns true if an entity of type <T> with a unique identifier matching the \r
-        * identifier supplied exists in the database, or false if no such entity can be \r
-        * found. \r
-        * @param uuid the unique identifier of the entity required\r
-        * @return an entity of type <T> matching the uuid, or null if that entity does not exist\r
-        */\r
-       public boolean exists(UUID uuid);\r
-       \r
-       /**\r
-        * Return a list of persisted entities that match the unique identifier\r
-        * set supplied as an argument\r
-        * \r
-        * @param uuidSet the set of unique identifiers of the entities required\r
-        * @return a list of entities of type <T>\r
-        */\r
-       public List<T> find(Set<UUID> uuidSet);\r
-       \r
-       /**\r
-        * Return a persisted entity that matches the unique identifier\r
-        * supplied as an argument, or null if the entity does not exist\r
-        * \r
-        * @param uuid the unique identifier of the entity required\r
-        * @return an entity of type <T>, or null if the entity does not exist\r
-        */\r
-       public T find(UUID uuid);\r
-       \r
-       /**\r
-        * Return a persisted entity that matches the database identifier\r
-        * supplied as an argument, or null if the entity does not exist\r
-        * \r
-        * @param id the database identifier of the entity required\r
-        * @return an entity of type <T>, or null if the entity does not exist\r
-        */\r
-       public T find(int id);\r
-       \r
-       /**\r
-        * Returns a set of persisted entities that match the database identifiers.\r
-        * Returns an empty list if no identifier matches.\r
-        * @param idSet\r
-        * @return\r
-        */\r
-       public List<T> findById(Set<Integer> idSet);  //can't be called find(Set<Integer>) as this conflicts with find(Set<UUID)\r
-\r
-       \r
-       // FIXME should we expose this method?\r
-       public Session getSession();\r
-       \r
-       /**\r
+     * <p>\r
+     * For detailed description and examples <b>please refer to:</b>\r
+     * {@link BeanInitializer#initialize(Object, List)}\r
+     *\r
+     * NOTE: in the case of lockmodes that hit the database (e.g. LockMode.READ), you will need to re-initialize\r
+     * child propertiesto avoid a HibernateLazyInitializationException (even if the properties of the child\r
+     * were initialized prior to the refresh).\r
+     *\r
+     * @param t\r
+     * @param lockMode\r
+     */\r
+    public void refresh(T t, LockMode lockMode, List<String> propertyPaths);\r
+\r
+    /**\r
+     * Returns a count of all entities of type <T>  optionally restricted\r
+     * to objects belonging to a class that that extends <T>\r
+     *\r
+     * @param clazz the class of entities to be counted (can be null to count all entities of type <T>)\r
+     * @return a count of entities\r
+     */\r
+    public int count(Class<? extends T> clazz);\r
+\r
+    /**\r
+     * Delete an existing persistent object\r
+     *\r
+     * @param persistentObject the object to be deleted\r
+     * @return the unique identifier of the deleted entity\r
+     */\r
+    public UUID delete(T persistentObject);\r
+\r
+    /**\r
+     * Returns true if an entity of type <T> with a unique identifier matching the\r
+     * identifier supplied exists in the database, or false if no such entity can be\r
+     * found.\r
+     * @param uuid the unique identifier of the entity required\r
+     * @return an entity of type <T> matching the uuid, or null if that entity does not exist\r
+     */\r
+    public boolean exists(UUID uuid);\r
+\r
+    /**\r
+     * Return a list of persisted entities that match the unique identifier\r
+     * set supplied as an argument\r
+     *\r
+     * @param uuidSet the set of unique identifiers of the entities required\r
+     * @return a list of entities of type <T>\r
+     */\r
+    public List<T> find(Set<UUID> uuidSet);\r
+\r
+    /**\r
+     * Return a persisted entity that matches the unique identifier\r
+     * supplied as an argument, or null if the entity does not exist\r
+     *\r
+     * @param uuid the unique identifier of the entity required\r
+     * @return an entity of type <T>, or null if the entity does not exist\r
+     */\r
+    public T find(UUID uuid);\r
+\r
+    /**\r
+     * Return a persisted entity that matches the database identifier\r
+     * supplied as an argument, or null if the entity does not exist\r
+     *\r
+     * @param id the database identifier of the entity required\r
+     * @return an entity of type <T>, or null if the entity does not exist\r
+     */\r
+    public T find(int id);\r
+\r
+    /**\r
+     * Returns a set of persisted entities that match the database identifiers.\r
+     * Returns an empty list if no identifier matches.\r
+     * @param idSet\r
+     * @return\r
+     */\r
+    public List<T> findById(Set<Integer> idSet);  //can't be called find(Set<Integer>) as this conflicts with find(Set<UUID)\r
+\r
+\r
+    // FIXME should we expose this method?\r
+    public Session getSession();\r
+\r
+    /**\r
      * Returns a sublist of objects matching the grouping projections supplied using the groups parameter\r
-     * \r
+     *\r
      * It would be nice to be able to return a pager, but for the moment hibernate doesn't\r
      * seem to support this (HHH-3238 - impossible to get the rowcount for a criteria that has projections)\r
-     * \r
+     *\r
      * @param clazz Restrict the query to objects of a certain class, or null for all objects of type T or subclasses\r
      * @param limit the maximum number of entities returned (can be null to return\r
-        *            all entities)\r
+     *            all entities)\r
      * @param start The (0-based) offset from the start of the recordset (can be null, equivalent of starting at the beginning of the recordset)\r
      * @param groups The grouping objects representing a projection, plus an optional ordering on that projected property\r
      * @param propertyPaths paths initialized on the returned objects - only applied to the objects returned from the first grouping\r
      * @return a list of arrays of objects, each matching the grouping objects supplied in the parameters.\r
      */\r
-       public List<Object[]> group(Class<? extends T> clazz,Integer limit, Integer start, List<Grouping> groups, List<String> propertyPaths);\r
-       \r
-       /**\r
-        * Returns a list of entities of type <T> optionally restricted\r
-        * to objects belonging to a class that that extends <T>\r
-        * \r
-        * @param type  The type of entities to return (can be null to count all entities of type <T>)\r
-        * @param limit The maximum number of objects returned (can be null for all matching objects)\r
-        * @param start The offset from the start of the result set (0 - based, can be null - equivalent of starting at the beginning of the recordset)\r
-        * @param orderHints\r
-        *            Supports path like <code>orderHints.propertyNames</code> which\r
-        *            include *-to-one properties like createdBy.username or\r
-        *            authorTeam.persistentTitleCache\r
-        * @param propertyPaths properties to be initialized\r
-        * @return\r
-        */\r
-       public List<T> list(Class<? extends T> type, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths);\r
-       \r
-       /**\r
-        * Finds the cdm entity specified by the <code>uuid</code> parameter and\r
-        * initializes all its *ToOne relations.\r
-        * \r
-        * @param uuid\r
-        * @return\r
-        */\r
-       public T load(UUID uuid);\r
-       \r
-       /**\r
-        * Finds the cdm entity specified by the <code>uuid</code> parameter and\r
-        * recursively initializes all bean properties given in the\r
-        * <code>propertyPaths</code> parameter.\r
-        * <p>\r
-        * For detailed description and examples <b>please refer to:</b> \r
-        * {@link BeanInitializer#initialize(Object, List)}\r
-        * \r
-        * @param uuid\r
-        * @return\r
-        */\r
-       public T load(UUID uuid, List<String> propertyPaths);\r
-       \r
-       /**\r
-        * Copy the state of the given object onto the persistent object with the same identifier.\r
-        * \r
-        * @param transientObject the entity to be merged\r
-        * @return The unique identifier of the persisted entity\r
-        */\r
-       public T merge(T transientObject);\r
-       \r
-       /**\r
-        * Returns a paged list of entities of type <T> optionally restricted\r
-        * to objects belonging to a class that that extends <T>\r
-        * \r
-        * @param type  The type of entities to return (can be null to count all entities of type <T>)\r
-        * @param pageSize The maximum number of objects returned (can be null for all matching objects)\r
-        * @param pageNumber The offset (in pageSize chunks) from the start of the result set (0 - based, \r
-        *                   can be null, equivalent of starting at the beginning of the recordset)\r
-        * @param orderHints\r
-        *            Supports path like <code>orderHints.propertyNames</code> which\r
-        *            include *-to-one properties like createdBy.username or\r
-        *            authorTeam.persistentTitleCache\r
-        * @param propertyPaths properties to be initialized\r
-        * @return a pager of objects of type <T>\r
-        */\r
-       public Pager<T> page(Class<? extends T> type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths);\r
-       \r
-       /**\r
-        * Re-read the state of the given instance from the underlying database.\r
-        * \r
-        * Hibernate claims that it is inadvisable to use refresh in long-running-sessions. \r
-        * I don't really see where we would get into a situation where problems as discussed\r
-        * this forum thread would apply for our scenario \r
-        * \r
-        * http://forum.hibernate.org/viewtopic.php?t=974544 \r
-        * \r
-        * @param persistentObject the object to be refreshed\r
-        * @return the unique identifier\r
-        */\r
-       public UUID refresh(T persistentObject);\r
-       \r
-       public List<T> rows(String tableName, int limit, int start);\r
-\r
-       /**\r
-        * Save a collection containing new entities (persists the entities)\r
-        * @param newInstances the new entities to be persisted\r
-        * @return A Map containing the new entities, keyed using the generated UUID's\r
-        *         of those entities\r
-        */\r
-       public Map<UUID,T> save(Collection<T> newInstances);    \r
-       \r
-       /**\r
-        * Save a new entity (persists the entity)\r
-        * @param newInstance the new entity to be persisted\r
-        * @return A generated UUID for the new persistent entity\r
-        */\r
-       public UUID save(T newInstance);\r
-       \r
-       /**\r
-        * Save a new entity or update the persistent state of an existing \r
-        * transient entity that has been persisted previously\r
-        * \r
-        * @param transientObject the entity to be persisted\r
-        * @return The unique identifier of the persisted entity\r
-        */\r
-       public UUID saveOrUpdate(T transientObject);\r
-       \r
-       /**\r
-        * Save new entities or update the persistent state of existing \r
-        * transient entities that have been persisted previously\r
-        * \r
-        * @param transientObjects the entities to be persisted\r
-        * @return The unique identifier of the persisted entity\r
-        */\r
-       public Map<UUID,T> saveOrUpdate(Collection<T> transientObjects);\r
-       \r
+    public List<Object[]> group(Class<? extends T> clazz,Integer limit, Integer start, List<Grouping> groups, List<String> propertyPaths);\r
+\r
+    /**\r
+     * Returns a list of entities of type <T> optionally restricted\r
+     * to objects belonging to a class that that extends <T>\r
+     *\r
+     * @param type  The type of entities to return (can be null to count all entities of type <T>)\r
+     * @param limit The maximum number of objects returned (can be null for all matching objects)\r
+     * @param start The offset from the start of the result set (0 - based, can be null - equivalent of starting at the beginning of the recordset)\r
+     * @param orderHints\r
+     *            Supports path like <code>orderHints.propertyNames</code> which\r
+     *            include *-to-one properties like createdBy.username or\r
+     *            authorTeam.persistentTitleCache\r
+     * @param propertyPaths properties to be initialized\r
+     * @return\r
+     */\r
+    public List<T> list(Class<? extends T> type, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths);\r
+\r
+    /**\r
+     * Finds the cdm entity specified by the <code>uuid</code> parameter and\r
+     * initializes all its *ToOne relations.\r
+     *\r
+     * @param uuid\r
+     * @return\r
+     */\r
+    public T load(UUID uuid);\r
+\r
+    /**\r
+     * Finds the cdm entity specified by the <code>uuid</code> parameter and\r
+     * recursively initializes all bean properties given in the\r
+     * <code>propertyPaths</code> parameter.\r
+     * <p>\r
+     * For detailed description and examples <b>please refer to:</b>\r
+     * {@link BeanInitializer#initialize(Object, List)}\r
+     *\r
+     * @param uuid\r
+     * @return\r
+     */\r
+    public T load(UUID uuid, List<String> propertyPaths);\r
+\r
+    /**\r
+     * Copy the state of the given object onto the persistent object with the same identifier.\r
+     *\r
+     * @param transientObject the entity to be merged\r
+     * @return The unique identifier of the persisted entity\r
+     */\r
+    public T merge(T transientObject);\r
+\r
+    /**\r
+     * Returns a paged list of entities of type <T> optionally restricted\r
+     * to objects belonging to a class that that extends <T>\r
+     *\r
+     * @param type  The type of entities to return (can be null to count all entities of type <T>)\r
+     * @param pageSize The maximum number of objects returned (can be null for all matching objects)\r
+     * @param pageNumber The offset (in pageSize chunks) from the start of the result set (0 - based,\r
+     *                   can be null, equivalent of starting at the beginning of the recordset)\r
+     * @param orderHints\r
+     *            Supports path like <code>orderHints.propertyNames</code> which\r
+     *            include *-to-one properties like createdBy.username or\r
+     *            authorTeam.persistentTitleCache\r
+     * @param propertyPaths properties to be initialized\r
+     * @return a pager of objects of type <T>\r
+     */\r
+    public Pager<T> page(Class<? extends T> type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths);\r
+\r
+    /**\r
+     * Re-read the state of the given instance from the underlying database.\r
+     *\r
+     * Hibernate claims that it is inadvisable to use refresh in long-running-sessions.\r
+     * I don't really see where we would get into a situation where problems as discussed\r
+     * this forum thread would apply for our scenario\r
+     *\r
+     * http://forum.hibernate.org/viewtopic.php?t=974544\r
+     *\r
+     * @param persistentObject the object to be refreshed\r
+     * @return the unique identifier\r
+     */\r
+    public UUID refresh(T persistentObject);\r
+\r
+    public List<T> rows(String tableName, int limit, int start);\r
+\r
+    /**\r
+     * Save a collection containing new entities (persists the entities)\r
+     * @param newInstances the new entities to be persisted\r
+     * @return A Map containing the new entities, keyed using the generated UUID's\r
+     *         of those entities\r
+     */\r
+    public Map<UUID,T> save(Collection<T> newInstances);\r
+\r
+    /**\r
+     * Save a new entity (persists the entity)\r
+     * @param newInstance the new entity to be persisted\r
+     * @return A generated UUID for the new persistent entity\r
+     */\r
+    public UUID save(T newInstance);\r
+\r
     /**\r
-        * Update the persistent state of an existing transient entity \r
-        * that has been persisted previously\r
-        * \r
-        * @param transientObject the entity to be persisted\r
-        * @return The unique identifier of the persisted entity\r
-        */\r
-       public UUID update(T transientObject);\r
-       \r
-       /**\r
-        * Method that lists the objects matching the example provided. \r
-        * The includeProperties property is used to specify which properties of the example are used.\r
-        * \r
-        * If includeProperties is null or empty, then all literal properties are used (restrictions are\r
-        * applied as in the Hibernate Query-By-Example API call Example.create(object)).\r
-        * \r
-        * If includeProperties is not empty then only literal properties that are named in the set are used to \r
-        * create restrictions, *PLUS* any *ToOne related entities. Related entities are matched on ID, not by \r
-        * their internal literal values (e.g. the call is criteria.add(Restrictions.eq(property,relatedObject)), not \r
-        * criteria.createCriteria(property).add(Example.create(relatedObject)))\r
-        * \r
-        * @param example\r
-        * @param includeProperties\r
-        * @param limit the maximum number of entities returned (can be null to return\r
-        *            all entities)\r
+     * Save a new entity or update the persistent state of an existing\r
+     * transient entity that has been persisted previously\r
+     *\r
+     * @param transientObject the entity to be persisted\r
+     * @return The unique identifier of the persisted entity\r
+     */\r
+    public UUID saveOrUpdate(T transientObject);\r
+\r
+    /**\r
+     * Save new entities or update the persistent state of existing\r
+     * transient entities that have been persisted previously\r
+     *\r
+     * @param transientObjects the entities to be persisted\r
+     * @return The unique identifier of the persisted entity\r
+     */\r
+    public Map<UUID,T> saveOrUpdate(Collection<T> transientObjects);\r
+\r
+    /**\r
+     * Update the persistent state of an existing transient entity\r
+     * that has been persisted previously\r
+     *\r
+     * @param transientObject the entity to be persisted\r
+     * @return The unique identifier of the persisted entity\r
+     */\r
+    public UUID update(T transientObject);\r
+\r
+    /**\r
+     * Method that lists the objects matching the example provided.\r
+     * The includeProperties property is used to specify which properties of the example are used.\r
+     *\r
+     * If includeProperties is null or empty, then all literal properties are used (restrictions are\r
+     * applied as in the Hibernate Query-By-Example API call Example.create(object)).\r
+     *\r
+     * If includeProperties is not empty then only literal properties that are named in the set are used to\r
+     * create restrictions, *PLUS* any *ToOne related entities. Related entities are matched on ID, not by\r
+     * their internal literal values (e.g. the call is criteria.add(Restrictions.eq(property,relatedObject)), not\r
+     * criteria.createCriteria(property).add(Example.create(relatedObject)))\r
+     *\r
+     * @param example\r
+     * @param includeProperties\r
+     * @param limit the maximum number of entities returned (can be null to return\r
+     *            all entities)\r
      * @param start The (0-based) offset from the start of the recordset\r
      * @param orderHints\r
-        *            Supports path like <code>orderHints.propertyNames</code> which\r
-        *            include *-to-one properties like createdBy.username or\r
+     *            Supports path like <code>orderHints.propertyNames</code> which\r
+     *            include *-to-one properties like createdBy.username or\r
      * @param propertyPaths paths initialized on the returned objects - only applied to the objects returned from the first grouping\r
-        * @return a list of matching objects\r
-        */\r
-       public List<T> list(T example, Set<String> includeProperties, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths);\r
-       \r
-       /**\r
+     * @return a list of matching objects\r
+     */\r
+    public List<T> list(T example, Set<String> includeProperties, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths);\r
+\r
+    /**\r
      * Evaluates whether the authenticated user has the rights to perform an specific action on the target object\r
      * @param authentication\r
      * @param target\r
      * @param permission\r
      * @return\r
      */\r
-       public boolean hasPermission(Authentication authentication, T target, CdmPermission permission);\r
+    @Deprecated // FIXME hasPermission(Authentication authentication, T target, EnumSet<CRUD> operation); should be in the CdmApplicationController\r
+    public boolean hasPermission(Authentication authentication, T target, EnumSet<CRUD> operation);\r
 \r
 }
\ No newline at end of file
index 04024dcf618f19ecad21960cda91668062dc806a..6d7ffe47f74fefae1724338bfc4305c566ca31ae 100644 (file)
@@ -1,9 +1,9 @@
 // $Id$
 /**
 * Copyright (C) 2007 EDIT
-* European Distributed Institute of Taxonomy 
+* European Distributed Institute of Taxonomy
 * http://www.e-taxonomy.eu
-* 
+*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * See LICENSE.TXT at the top of this package for the full license terms.
 */
@@ -36,7 +36,7 @@ import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
 
 /**
  * Quick and dirty implementation of a location service as needed by the editor
- * 
+ *
  * @author n.hoffman
  * @created 08.04.2009
  * @version 1.0
@@ -44,130 +44,130 @@ import eu.etaxonomy.cdm.persistence.dao.common.ITermVocabularyDao;
 @Service
 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
 public class LocationServiceImpl extends ServiceBase<DefinedTermBase,IDefinedTermDao> implements ILocationService {
-       private static final Logger logger = Logger
-                       .getLogger(LocationServiceImpl.class);
-
-       @Autowired
-       protected ITermVocabularyDao vocabularyDao;
-       
-       @Autowired
-       protected IDefinedTermDao definedTermDao;
-       
-       @Autowired
-       protected IOrderedTermVocabularyDao orderedVocabularyDao;
-       
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.api.service.ServiceBase#setDao(eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao)
-        */
-       @Override
-       protected void setDao(IDefinedTermDao dao) {
-               this.dao = dao;
-       }
-
-       /**
-        *  (non-Javadoc)
-        * @see eu.etaxonomy.cdm.api.service.ILocationService#getAbsenceTerms()
-        * FIXME Candidate for harmonization
-        * is this method a duplicate of termService.getVocabulary(VocabularyEnum.AbsenceTerm)?
-        */
-       public OrderedTermVocabulary<AbsenceTerm> getAbsenceTermVocabulary() {
-               String uuidString = "5cd438c8-a8a1-4958-842e-169e83e2ceee";
-               UUID uuid = UUID.fromString(uuidString);
-               OrderedTermVocabulary<AbsenceTerm> absenceTermVocabulary = 
-                       (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
-               return absenceTermVocabulary;
-       }
-       
-
-       /**
-        * (non-Javadoc)
-        * @see eu.etaxonomy.cdm.api.service.ILocationService#getPresenceTermVocabulary()
-        * FIXME Candidate for harmonization
-        * is this method a duplicate of termService.getVocabulary(VocabularyEnum.PresenceTerm)
-        */
-       public OrderedTermVocabulary<PresenceTerm> getPresenceTermVocabulary() {
-               String uuidString = "adbbbe15-c4d3-47b7-80a8-c7d104e53a05";
-               UUID uuid = UUID.fromString(uuidString);
-               OrderedTermVocabulary<PresenceTerm> presenceTermVocabulary = 
-                       (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
-               return presenceTermVocabulary;
-       }
-
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.api.service.ILocationService#getNamedAreaVocabularyTypes()
-        */
-       public List<NamedAreaVocabularyType> getNamedAreaVocabularyTypes() {
-               List<NamedAreaVocabularyType> result = new ArrayList<NamedAreaVocabularyType>(3);
-               result.add(NamedAreaVocabularyType.TDWG_AREA);
-               result.add(NamedAreaVocabularyType.WATERBODY_OR_COUNTRY);
-               result.add(NamedAreaVocabularyType.CONTINENT);
-               return result;
-       }
-
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.api.service.ILocationService#getNamedAreas(java.lang.Object)
-        */
-       public OrderedTermVocabulary<NamedArea> getNamedAreaVocabulary(NamedAreaVocabularyType vocabularyType) {
-
-               UUID namedAreaVocabularyUuid = null;
-                       
-               if(vocabularyType == NamedAreaVocabularyType.TDWG_AREA){
-                       namedAreaVocabularyUuid = UUID.fromString("1fb40504-d1d7-44b0-9731-374fbe6cac77"); 
-               }
-               if(vocabularyType == NamedAreaVocabularyType.CONTINENT){
-                       namedAreaVocabularyUuid = UUID.fromString("e72cbcb6-58f8-4201-9774-15d0c6abc128");
-               }
-               if(vocabularyType == NamedAreaVocabularyType.WATERBODY_OR_COUNTRY){
-                       namedAreaVocabularyUuid = UUID.fromString("006b1870-7347-4624-990f-e5ed78484a1a");
-               }
-
-               return (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(namedAreaVocabularyUuid);
-       }
-
-       /* (non-Javadoc)
-        * @see eu.etaxonomy.cdm.api.service.ILocationService#getNamedAreaLevelVocabulary()
-        */
-       public OrderedTermVocabulary<NamedAreaLevel> getNamedAreaLevelVocabulary() {
-               // TODO return namedAreaLevel filtered by NamedAreaVocabularyType               
-               String uuidString = "49034253-27c8-4219-97e8-f8d987d3d122";
-               UUID uuid = UUID.fromString(uuidString);
-               OrderedTermVocabulary<NamedAreaLevel> namedAreaLevelVocabulary = 
-                       (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
-               return namedAreaLevelVocabulary;
-       }
-
-       /**
-        * (non-Javadoc)
-        * @see eu.etaxonomy.cdm.api.service.ILocationService#getNamedAreaTypeVocabulary()
-        * FIXME Candidate for harmonization
-        * is this method a duplicate of termService.getVocabulary(VocabularyEnum.NamedAreaType)
-        */
-       public TermVocabulary<NamedAreaType> getNamedAreaTypeVocabulary() {
-               String uuidString = "e51d52d6-965b-4f7d-900f-4ba9c6f5dd33";
-               UUID uuid = UUID.fromString(uuidString);
-               TermVocabulary<NamedAreaType> namedAreaTypeVocabulary = 
-                       (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
-               return namedAreaTypeVocabulary;
-       }
-
-       public List<NamedArea> getTopLevelNamedAreasByVocabularyType(NamedAreaVocabularyType vocabularyType){
-               
-               OrderedTermVocabulary<NamedArea> vocabulary = getNamedAreaVocabulary(vocabularyType);
-
-               List<NamedArea> topLevelTerms = new ArrayList<NamedArea>();
-               
-//             for(NamedArea area : vocabulary){               
-               Iterator<NamedArea> it = vocabulary.iterator();
-               while(it.hasNext()){
-                       
-                       NamedArea area =  HibernateProxyHelper.deproxy(it.next(), NamedArea.class);
-                       if(area.getPartOfWorkaround() == null){
-                               topLevelTerms.add(area);
-                       }
-               }
-               
-               return topLevelTerms;
-       }
+
+    private static final Logger logger = Logger.getLogger(LocationServiceImpl.class);
+
+    @Autowired
+    protected ITermVocabularyDao vocabularyDao;
+
+    @Autowired
+    protected IDefinedTermDao definedTermDao;
+
+    @Autowired
+    protected IOrderedTermVocabularyDao orderedVocabularyDao;
+
+    /* (non-Javadoc)
+     * @see eu.etaxonomy.cdm.api.service.ServiceBase#setDao(eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao)
+     */
+    @Override
+    protected void setDao(IDefinedTermDao dao) {
+        this.dao = dao;
+    }
+
+    /**
+     *  (non-Javadoc)
+     * @see eu.etaxonomy.cdm.api.service.ILocationService#getAbsenceTerms()
+     * FIXME Candidate for harmonization
+     * is this method a duplicate of termService.getVocabulary(VocabularyEnum.AbsenceTerm)?
+     */
+    public OrderedTermVocabulary<AbsenceTerm> getAbsenceTermVocabulary() {
+        String uuidString = "5cd438c8-a8a1-4958-842e-169e83e2ceee";
+        UUID uuid = UUID.fromString(uuidString);
+        OrderedTermVocabulary<AbsenceTerm> absenceTermVocabulary =
+            (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
+        return absenceTermVocabulary;
+    }
+
+
+    /**
+     * (non-Javadoc)
+     * @see eu.etaxonomy.cdm.api.service.ILocationService#getPresenceTermVocabulary()
+     * FIXME Candidate for harmonization
+     * is this method a duplicate of termService.getVocabulary(VocabularyEnum.PresenceTerm)
+     */
+    public OrderedTermVocabulary<PresenceTerm> getPresenceTermVocabulary() {
+        String uuidString = "adbbbe15-c4d3-47b7-80a8-c7d104e53a05";
+        UUID uuid = UUID.fromString(uuidString);
+        OrderedTermVocabulary<PresenceTerm> presenceTermVocabulary =
+            (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
+        return presenceTermVocabulary;
+    }
+
+    /* (non-Javadoc)
+     * @see eu.etaxonomy.cdm.api.service.ILocationService#getNamedAreaVocabularyTypes()
+     */
+    public List<NamedAreaVocabularyType> getNamedAreaVocabularyTypes() {
+        List<NamedAreaVocabularyType> result = new ArrayList<NamedAreaVocabularyType>(3);
+        result.add(NamedAreaVocabularyType.TDWG_AREA);
+        result.add(NamedAreaVocabularyType.WATERBODY_OR_COUNTRY);
+        result.add(NamedAreaVocabularyType.CONTINENT);
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see eu.etaxonomy.cdm.api.service.ILocationService#getNamedAreas(java.lang.Object)
+     */
+    public OrderedTermVocabulary<NamedArea> getNamedAreaVocabulary(NamedAreaVocabularyType vocabularyType) {
+
+        UUID namedAreaVocabularyUuid = null;
+
+        if(vocabularyType == NamedAreaVocabularyType.TDWG_AREA){
+            namedAreaVocabularyUuid = UUID.fromString("1fb40504-d1d7-44b0-9731-374fbe6cac77");
+        }
+        if(vocabularyType == NamedAreaVocabularyType.CONTINENT){
+            namedAreaVocabularyUuid = UUID.fromString("e72cbcb6-58f8-4201-9774-15d0c6abc128");
+        }
+        if(vocabularyType == NamedAreaVocabularyType.WATERBODY_OR_COUNTRY){
+            namedAreaVocabularyUuid = UUID.fromString("006b1870-7347-4624-990f-e5ed78484a1a");
+        }
+
+        return (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(namedAreaVocabularyUuid);
+    }
+
+    /* (non-Javadoc)
+     * @see eu.etaxonomy.cdm.api.service.ILocationService#getNamedAreaLevelVocabulary()
+     */
+    public OrderedTermVocabulary<NamedAreaLevel> getNamedAreaLevelVocabulary() {
+        // TODO return namedAreaLevel filtered by NamedAreaVocabularyType
+        String uuidString = "49034253-27c8-4219-97e8-f8d987d3d122";
+        UUID uuid = UUID.fromString(uuidString);
+        OrderedTermVocabulary<NamedAreaLevel> namedAreaLevelVocabulary =
+            (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
+        return namedAreaLevelVocabulary;
+    }
+
+    /**
+     * (non-Javadoc)
+     * @see eu.etaxonomy.cdm.api.service.ILocationService#getNamedAreaTypeVocabulary()
+     * FIXME Candidate for harmonization
+     * is this method a duplicate of termService.getVocabulary(VocabularyEnum.NamedAreaType)
+     */
+    public TermVocabulary<NamedAreaType> getNamedAreaTypeVocabulary() {
+        String uuidString = "e51d52d6-965b-4f7d-900f-4ba9c6f5dd33";
+        UUID uuid = UUID.fromString(uuidString);
+        TermVocabulary<NamedAreaType> namedAreaTypeVocabulary =
+            (OrderedTermVocabulary)orderedVocabularyDao.findByUuid(uuid);
+        return namedAreaTypeVocabulary;
+    }
+
+    public List<NamedArea> getTopLevelNamedAreasByVocabularyType(NamedAreaVocabularyType vocabularyType){
+
+        OrderedTermVocabulary<NamedArea> vocabulary = getNamedAreaVocabulary(vocabularyType);
+
+        List<NamedArea> topLevelTerms = new ArrayList<NamedArea>();
+
+//             for(NamedArea area : vocabulary){
+        Iterator<NamedArea> it = vocabulary.iterator();
+        while(it.hasNext()){
+
+            NamedArea area =  HibernateProxyHelper.deproxy(it.next(), NamedArea.class);
+            if(area.getPartOfWorkaround() == null){
+                topLevelTerms.add(area);
+            }
+        }
+
+        return topLevelTerms;
+    }
 
 
 
index f8d8914aabb219965cb01430752cefa595ad5805..9bd5a19276fe0f6d4b9330c1f75f4a8c6d9bbffb 100644 (file)
@@ -1,9 +1,9 @@
 // $Id$\r
 /**\r
 * Copyright (C) 2007 EDIT\r
-* European Distributed Institute of Taxonomy \r
+* European Distributed Institute of Taxonomy\r
 * http://www.e-taxonomy.eu\r
-* \r
+*\r
 * The contents of this file are subject to the Mozilla Public License Version 1.1\r
 * See LICENSE.TXT at the top of this package for the full license terms.\r
 */\r
@@ -12,11 +12,14 @@ package eu.etaxonomy.cdm.api.service;
 \r
 import java.util.ArrayList;\r
 import java.util.Collection;\r
+import java.util.EnumSet;\r
 import java.util.List;\r
 import java.util.Map;\r
 import java.util.Set;\r
 import java.util.UUID;\r
 \r
+import javax.management.relation.RoleUnresolvedList;\r
+\r
 import org.apache.log4j.Logger;\r
 import org.hibernate.LockMode;\r
 import org.hibernate.Session;\r
@@ -30,173 +33,175 @@ import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
 import eu.etaxonomy.cdm.model.common.CdmBase;\r
 \r
 import eu.etaxonomy.cdm.persistence.dao.common.ICdmEntityDao;\r
-import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermission;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.Operation;\r
 import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator;\r
 import eu.etaxonomy.cdm.persistence.query.Grouping;\r
 import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
 \r
 public abstract class ServiceBase<T extends CdmBase, DAO extends ICdmEntityDao<T>> implements IService<T>, ApplicationContextAware {\r
-       @SuppressWarnings("unused")\r
-       private static final Logger logger = Logger.getLogger(ServiceBase.class);\r
-       \r
-       //flush after saving this number of objects\r
-       int flushAfterNo = 2000;\r
-       protected ApplicationContext appContext;\r
-\r
-       protected DAO dao;\r
-\r
-       @Transactional(readOnly = true)\r
-       public void lock(T t, LockMode lockMode) {\r
-               dao.lock(t, lockMode);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public void refresh(T t, LockMode lockMode, List<String> propertyPaths) {\r
-               dao.refresh(t, lockMode, propertyPaths);\r
-       }\r
-\r
-       @Transactional(readOnly = false)\r
-       public void clear() {\r
-               dao.clear();\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public int count(Class<? extends T> clazz) {\r
-               return dao.count(clazz);\r
-       }\r
-\r
-       @Transactional(readOnly = false)\r
-       public UUID delete(T persistentObject) {\r
-               return dao.delete(persistentObject);\r
-       }\r
-\r
-       @Transactional(readOnly = true)\r
-       public boolean exists(UUID uuid) {\r
-               return dao.exists(uuid);\r
-       }\r
-\r
-       @Transactional(readOnly = true)\r
-       public List<T> find(Set<UUID> uuidSet) {\r
-               return dao.findByUuid(uuidSet);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public List<T> findById(Set<Integer> idSet) {  //can't be called find(Set<Integer>) as this conflicts with find(Set<UUID)\r
-               return dao.findById(idSet);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public T find(UUID uuid) {\r
-               return dao.findByUuid(uuid);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public T find(int id) {\r
-               return dao.findById(id);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public Session getSession() {\r
-               return dao.getSession();\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public List<Object[]> group(Class<? extends T> clazz,Integer limit, Integer start, List<Grouping> groups, List<String> propertyPaths) {\r
-               return dao.group(clazz, limit, start, groups, propertyPaths);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public  List<T> list(Class<? extends T> type, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths){\r
-               return dao.list(type,limit, start, orderHints,propertyPaths);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public T load(UUID uuid) {\r
-               return dao.load(uuid);\r
-       }\r
-               \r
-       @Transactional(readOnly = true)\r
-       public T load(UUID uuid, List<String> propertyPaths){\r
-               return dao.load(uuid, propertyPaths);\r
-       }\r
-\r
-       @Transactional(readOnly = false)\r
-       public T merge(T newInstance) {\r
-               return dao.merge(newInstance);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public  Pager<T> page(Class<? extends T> type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){\r
-               Integer numberOfResults = dao.count(type);\r
-               List<T> results = new ArrayList<T>();\r
-               pageNumber = pageNumber == null ? 0 : pageNumber;\r
-               if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)\r
-                       Integer start = pageSize == null ? 0 : pageSize * pageNumber;\r
-                       results = dao.list(type, pageSize, start, orderHints,propertyPaths);\r
-               }\r
-               return new DefaultPagerImpl<T>(pageNumber, numberOfResults, pageSize, results);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
+    @SuppressWarnings("unused")\r
+    private static final Logger logger = Logger.getLogger(ServiceBase.class);\r
+\r
+    //flush after saving this number of objects\r
+    int flushAfterNo = 2000;\r
+    protected ApplicationContext appContext;\r
+\r
+    protected DAO dao;\r
+\r
+    @Transactional(readOnly = true)\r
+    public void lock(T t, LockMode lockMode) {\r
+        dao.lock(t, lockMode);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public void refresh(T t, LockMode lockMode, List<String> propertyPaths) {\r
+        dao.refresh(t, lockMode, propertyPaths);\r
+    }\r
+\r
+    @Transactional(readOnly = false)\r
+    public void clear() {\r
+        dao.clear();\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public int count(Class<? extends T> clazz) {\r
+        return dao.count(clazz);\r
+    }\r
+\r
+    @Transactional(readOnly = false)\r
+    public UUID delete(T persistentObject) {\r
+        return dao.delete(persistentObject);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public boolean exists(UUID uuid) {\r
+        return dao.exists(uuid);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public List<T> find(Set<UUID> uuidSet) {\r
+        return dao.findByUuid(uuidSet);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public List<T> findById(Set<Integer> idSet) {  //can't be called find(Set<Integer>) as this conflicts with find(Set<UUID)\r
+        return dao.findById(idSet);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public T find(UUID uuid) {\r
+        return dao.findByUuid(uuid);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public T find(int id) {\r
+        return dao.findById(id);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public Session getSession() {\r
+        return dao.getSession();\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public List<Object[]> group(Class<? extends T> clazz,Integer limit, Integer start, List<Grouping> groups, List<String> propertyPaths) {\r
+        return dao.group(clazz, limit, start, groups, propertyPaths);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public  List<T> list(Class<? extends T> type, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths){\r
+        return dao.list(type,limit, start, orderHints,propertyPaths);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public T load(UUID uuid) {\r
+        return dao.load(uuid);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public T load(UUID uuid, List<String> propertyPaths){\r
+        return dao.load(uuid, propertyPaths);\r
+    }\r
+\r
+    @Transactional(readOnly = false)\r
+    public T merge(T newInstance) {\r
+        return dao.merge(newInstance);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public  Pager<T> page(Class<? extends T> type, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths){\r
+        Integer numberOfResults = dao.count(type);\r
+        List<T> results = new ArrayList<T>();\r
+        pageNumber = pageNumber == null ? 0 : pageNumber;\r
+        if(numberOfResults > 0) { // no point checking again  //TODO use AbstractPagerImpl.hasResultsInRange(numberOfResults, pageNumber, pageSize)\r
+            Integer start = pageSize == null ? 0 : pageSize * pageNumber;\r
+            results = dao.list(type, pageSize, start, orderHints,propertyPaths);\r
+        }\r
+        return new DefaultPagerImpl<T>(pageNumber, numberOfResults, pageSize, results);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
     public UUID refresh(T persistentObject) {\r
-               return dao.refresh(persistentObject);\r
-       }\r
-       \r
-       /**\r
-        * FIXME Candidate for harmonization\r
-        * is this method used, and if so, should it be exposed in the service layer?\r
-        * it seems a bit incongruous that we use an ORM to hide the fact that there is a \r
-        * database, then expose a method that talks about "rows" . . .\r
-        */\r
-       @Transactional(readOnly = true)\r
-       public List<T> rows(String tableName, int limit, int start) {\r
-               return dao.rows(tableName, limit, start);\r
-       }\r
-       \r
-       @Transactional(readOnly = false)\r
-       public Map<UUID, T> save(Collection<T> newInstances) {\r
-               return dao.saveAll(newInstances);\r
-       }\r
-\r
-       @Transactional(readOnly = false)\r
-       public UUID save(T newInstance) {\r
-               return dao.save(newInstance);\r
-       }\r
-\r
-       @Transactional(readOnly = false)\r
-       public UUID saveOrUpdate(T transientObject) {\r
-               return dao.saveOrUpdate(transientObject);\r
-       }\r
-       \r
-       @Transactional(readOnly = false)\r
-       public Map<UUID, T> saveOrUpdate(Collection<T> transientInstances) {\r
-               return dao.saveOrUpdateAll(transientInstances);\r
-       }\r
-\r
-       /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.api.service.Iyyy#setApplicationContext(org.springframework.context.ApplicationContext)\r
-        */\r
-       public void setApplicationContext(ApplicationContext appContext){\r
-               this.appContext = appContext;\r
-       }\r
-\r
-\r
-       protected abstract void setDao(DAO dao);\r
-       \r
-       @Transactional(readOnly = false)\r
-       public UUID update(T transientObject) {\r
-               return dao.update(transientObject);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public List<T> list(T example, Set<String> includeProperties, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {\r
-               return dao.list(example, includeProperties, limit, start, orderHints, propertyPaths);\r
-       }\r
-       \r
-       @Transactional(readOnly = true)\r
-       public boolean hasPermission(Authentication authentication, T target, CdmPermission permission) {\r
-               CdmPermissionEvaluator permissionEvaluator = new CdmPermissionEvaluator();\r
-               return permissionEvaluator.hasPermission(authentication, target, permission);\r
-               \r
-       }\r
+        return dao.refresh(persistentObject);\r
+    }\r
+\r
+    /**\r
+     * FIXME Candidate for harmonization\r
+     * is this method used, and if so, should it be exposed in the service layer?\r
+     * it seems a bit incongruous that we use an ORM to hide the fact that there is a\r
+     * database, then expose a method that talks about "rows" . . .\r
+     */\r
+    @Transactional(readOnly = true)\r
+    public List<T> rows(String tableName, int limit, int start) {\r
+        return dao.rows(tableName, limit, start);\r
+    }\r
+\r
+    @Transactional(readOnly = false)\r
+    public Map<UUID, T> save(Collection<T> newInstances) {\r
+        return dao.saveAll(newInstances);\r
+    }\r
+\r
+    @Transactional(readOnly = false)\r
+    public UUID save(T newInstance) {\r
+        return dao.save(newInstance);\r
+    }\r
+\r
+    @Transactional(readOnly = false)\r
+    public UUID saveOrUpdate(T transientObject) {\r
+        return dao.saveOrUpdate(transientObject);\r
+    }\r
+\r
+    @Transactional(readOnly = false)\r
+    public Map<UUID, T> saveOrUpdate(Collection<T> transientInstances) {\r
+        return dao.saveOrUpdateAll(transientInstances);\r
+    }\r
+\r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.api.service.Iyyy#setApplicationContext(org.springframework.context.ApplicationContext)\r
+     */\r
+    public void setApplicationContext(ApplicationContext appContext){\r
+        this.appContext = appContext;\r
+    }\r
+\r
+\r
+    protected abstract void setDao(DAO dao);\r
+\r
+    @Transactional(readOnly = false)\r
+    public UUID update(T transientObject) {\r
+        return dao.update(transientObject);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public List<T> list(T example, Set<String> includeProperties, Integer limit, Integer start, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+        return dao.list(example, includeProperties, limit, start, orderHints, propertyPaths);\r
+    }\r
+\r
+    @Transactional(readOnly = true)\r
+    public boolean hasPermission(Authentication authentication, T target, EnumSet<CRUD> operation) {\r
+        throw new RuntimeException("hasPermissio() is umimplemented - do not use");\r
+//        CdmPermissionEvaluator permissionEvaluator = new CdmPermissionEvaluator();\r
+//        return permissionEvaluator.hasPermission(authentication, target, operation);\r
+\r
+    }\r
 }\r
index 0b9ce86af6a7434c87af0d5fe940c5f95f65cdb4..1b4f4ec80746998e48d40b49b0333171fab90785 100644 (file)
@@ -1,9 +1,9 @@
 // $Id$\r
 /**\r
  * Copyright (C) 2007 EDIT\r
- * European Distributed Institute of Taxonomy \r
+ * European Distributed Institute of Taxonomy\r
  * http://www.e-taxonomy.eu\r
- * \r
+ *\r
  * The contents of this file are subject to the Mozilla Public License Version 1.1\r
  * See LICENSE.TXT at the top of this package for the full license terms.\r
  */\r
@@ -55,333 +55,333 @@ import eu.etaxonomy.cdm.persistence.query.OrderHint;
 @Service\r
 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)\r
 public class UserService extends ServiceBase<User,IUserDao> implements IUserService {\r
-       \r
-       protected IGroupDao groupDao;\r
-       \r
-       protected IGrantedAuthorityDao grantedAuthorityDao;\r
-       \r
-       private SaltSource saltSource = new ReflectionSaltSource();\r
-       \r
-       private PasswordEncoder passwordEncoder = new Md5PasswordEncoder();\r
-       \r
-       private AuthenticationManager authenticationManager;\r
-       \r
-       private UserCache userCache = new NullUserCache();\r
-       \r
-       @Autowired(required = false)\r
-       public void setUserCache(UserCache userCache) {\r
-               Assert.notNull(userCache, "userCache cannot be null");\r
-               this.userCache = userCache;\r
-       }\r
-       \r
-       @Autowired(required = false)\r
-       public void setPasswordEncoder(PasswordEncoder passwordEncoder) {\r
-               \r
-               this.passwordEncoder = passwordEncoder;\r
-       }\r
-\r
-       @Autowired(required = false)\r
-       public void setSaltSource(SaltSource saltSource) {\r
-               this.saltSource = saltSource;\r
-       }\r
-       \r
-       @Autowired(required= false)\r
-       public void setAuthenticationManager(AuthenticationManager authenticationManager) {\r
-               this.authenticationManager = authenticationManager;\r
-       }\r
-       \r
-       @Override\r
-       @Autowired\r
-       protected void setDao(IUserDao dao) {\r
-               this.dao = dao;\r
-       }\r
-       \r
-       @Autowired\r
-       public void setGroupDao(IGroupDao groupDao) {\r
-               this.groupDao = groupDao;\r
-       }\r
-       \r
-       @Autowired\r
-       public void setGrantedAuthorityDao(IGrantedAuthorityDao grantedAuthorityDao) {\r
-               this.grantedAuthorityDao = grantedAuthorityDao;\r
-       }\r
-       \r
-       @Transactional(readOnly=false)\r
-       protected Authentication createNewAuthentication(Authentication currentAuth, String newPassword) {\r
-               UserDetails user = loadUserByUsername(currentAuth.getName());\r
-                       \r
-               UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());\r
-               newAuthentication.setDetails(currentAuth.getDetails());\r
-                       \r
-               return newAuthentication;\r
-       }\r
-       \r
-       @Override\r
-       @Transactional(readOnly=false)\r
-       \r
-       public void changePassword(String oldPassword, String newPassword) {\r
-               Assert.hasText(oldPassword);\r
-               Assert.hasText(newPassword);\r
-               Authentication authentication = SecurityContextHolder.getContext().getAuthentication();\r
-               if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {\r
-                       User user = (User)authentication.getPrincipal();\r
-                       \r
-                       authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), oldPassword));\r
-                       \r
-                       Object salt = this.saltSource.getSalt(user);\r
-                       \r
-                       String password = passwordEncoder.encodePassword(newPassword, salt);\r
-                       ((User)user).setPassword(password);\r
-                       \r
-                       dao.update((User)user);\r
-                       SecurityContextHolder.getContext().setAuthentication(createNewAuthentication(authentication, newPassword));\r
-                       userCache.removeUserFromCache(user.getUsername());\r
-               } else {\r
-                       throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");\r
-               }               \r
-       }\r
-       \r
-       @Override\r
-       @Transactional(readOnly=false)\r
-       public void changePasswordForUser(String username, String newPassword) {\r
-               Assert.hasText(username);\r
-               Assert.hasText(newPassword);\r
-               \r
-               try {\r
-                   User user = dao.findUserByUsername(username);\r
-                   if(user == null) {\r
-                               throw new UsernameNotFoundException(username);\r
-                       }\r
-                   \r
+\r
+    protected IGroupDao groupDao;\r
+\r
+    protected IGrantedAuthorityDao grantedAuthorityDao;\r
+\r
+    private SaltSource saltSource; // = new ReflectionSaltSource();\r
+\r
+    private PasswordEncoder passwordEncoder; // = new Md5PasswordEncoder();\r
+\r
+    private AuthenticationManager authenticationManager;\r
+\r
+    private UserCache userCache = new NullUserCache();\r
+\r
+    @Autowired(required = false)\r
+    public void setUserCache(UserCache userCache) {\r
+        Assert.notNull(userCache, "userCache cannot be null");\r
+        this.userCache = userCache;\r
+    }\r
+\r
+    @Autowired(required = false)\r
+    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {\r
+\r
+        this.passwordEncoder = passwordEncoder;\r
+    }\r
+\r
+    @Autowired(required = false)\r
+    public void setSaltSource(SaltSource saltSource) {\r
+        this.saltSource = saltSource;\r
+    }\r
+\r
+    @Autowired(required= false)\r
+    public void setAuthenticationManager(AuthenticationManager authenticationManager) {\r
+        this.authenticationManager = authenticationManager;\r
+    }\r
+\r
+    @Override\r
+    @Autowired\r
+    protected void setDao(IUserDao dao) {\r
+        this.dao = dao;\r
+    }\r
+\r
+    @Autowired\r
+    public void setGroupDao(IGroupDao groupDao) {\r
+        this.groupDao = groupDao;\r
+    }\r
+\r
+    @Autowired\r
+    public void setGrantedAuthorityDao(IGrantedAuthorityDao grantedAuthorityDao) {\r
+        this.grantedAuthorityDao = grantedAuthorityDao;\r
+    }\r
+\r
+    @Transactional(readOnly=false)\r
+    protected Authentication createNewAuthentication(Authentication currentAuth, String newPassword) {\r
+        UserDetails user = loadUserByUsername(currentAuth.getName());\r
+\r
+        UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());\r
+        newAuthentication.setDetails(currentAuth.getDetails());\r
+\r
+        return newAuthentication;\r
+    }\r
+\r
+    @Override\r
+    @Transactional(readOnly=false)\r
+    public void changePassword(String oldPassword, String newPassword) {\r
+        Assert.hasText(oldPassword);\r
+        Assert.hasText(newPassword);\r
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();\r
+        if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {\r
+            User user = (User)authentication.getPrincipal();\r
+\r
+            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), oldPassword));\r
+\r
             Object salt = this.saltSource.getSalt(user);\r
-                       \r
-                       String password = passwordEncoder.encodePassword(newPassword, salt);\r
-                       ((User)user).setPassword(password);\r
-                       \r
-                       dao.update((User)user);\r
-                       userCache.removeUserFromCache(user.getUsername());\r
-               } catch(NonUniqueResultException nure) {\r
-                       throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       @Transactional(readOnly=false)\r
-       public void createUser(UserDetails user) {\r
-               Assert.isInstanceOf(User.class, user);\r
-               \r
-               String rawPassword = user.getPassword();\r
-               Object salt = this.saltSource.getSalt(user);\r
-               \r
-               String password = passwordEncoder.encodePassword(rawPassword, salt);\r
-               ((User)user).setPassword(password);\r
-               \r
-               dao.save((User)user);\r
-       }\r
-\r
-       @Override\r
-       @Transactional(readOnly=false)\r
-       public void deleteUser(String username) {\r
-               Assert.hasLength(username);\r
-               \r
-               User user = dao.findUserByUsername(username); \r
-        if(user != null) {             \r
-                   dao.delete((User)user);\r
+\r
+            String password = passwordEncoder.encodePassword(newPassword, salt);\r
+            ((User)user).setPassword(password);\r
+\r
+            dao.update((User)user);\r
+            SecurityContextHolder.getContext().setAuthentication(createNewAuthentication(authentication, newPassword));\r
+            userCache.removeUserFromCache(user.getUsername());\r
+        } else {\r
+            throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");\r
         }\r
-        \r
+    }\r
+\r
+    @Override\r
+    @Transactional(readOnly=false)\r
+    @PreAuthorize("#username == authentication.name or hasRole('ROLE_ADMIN')")\r
+    public void changePasswordForUser(String username, String newPassword) {\r
+        Assert.hasText(username);\r
+        Assert.hasText(newPassword);\r
+\r
+        try {\r
+            User user = dao.findUserByUsername(username);\r
+            if(user == null) {\r
+                throw new UsernameNotFoundException(username);\r
+            }\r
+\r
+            Object salt = this.saltSource.getSalt(user);\r
+\r
+            String password = passwordEncoder.encodePassword(newPassword, salt);\r
+            ((User)user).setPassword(password);\r
+\r
+            dao.update((User)user);\r
+            userCache.removeUserFromCache(user.getUsername());\r
+        } catch(NonUniqueResultException nure) {\r
+            throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);\r
+        }\r
+    }\r
+\r
+    @Override\r
+    @Transactional(readOnly=false)\r
+    public void createUser(UserDetails user) {\r
+        Assert.isInstanceOf(User.class, user);\r
+\r
+        String rawPassword = user.getPassword();\r
+        Object salt = this.saltSource.getSalt(user);\r
+\r
+        String password = passwordEncoder.encodePassword(rawPassword, salt);\r
+        ((User)user).setPassword(password);\r
+\r
+        dao.save((User)user);\r
+    }\r
+\r
+    @Override\r
+    @Transactional(readOnly=false)\r
+    public void deleteUser(String username) {\r
+        Assert.hasLength(username);\r
+\r
+        User user = dao.findUserByUsername(username);\r
+        if(user != null) {\r
+            dao.delete((User)user);\r
+        }\r
+\r
         userCache.removeUserFromCache(username);\r
-       }\r
-\r
-       @Override\r
-       @Transactional(readOnly=false)\r
-       public void updateUser(UserDetails user) {\r
-               Assert.isInstanceOf(User.class, user);\r
-               \r
-               dao.update((User)user);\r
-               userCache.removeUserFromCache(user.getUsername());\r
-       }\r
-\r
-       @Override\r
-       public boolean userExists(String username) {\r
-               Assert.hasText(username);\r
-               \r
-               User user = dao.findUserByUsername(username);\r
-               return user != null;\r
-       }\r
-\r
-       /**\r
-        * DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS\r
-        * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE\r
-        */\r
-       public UserDetails loadUserByUsername(String username)\r
-                       throws UsernameNotFoundException, DataAccessException {\r
-               Assert.hasText(username);\r
-               try {\r
-                   User user = dao.findUserByUsername(username);\r
-                   if(user == null) {\r
-                               throw new UsernameNotFoundException(username);\r
-                       }\r
-                   return user;\r
-               } catch(NonUniqueResultException nure) {\r
-                       throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);\r
-               }\r
-       }\r
-\r
-       @Deprecated // use GroupService instead\r
-       @Transactional(readOnly=false)\r
-       public void addGroupAuthority(String groupName, GrantedAuthority authority) {\r
-               Assert.hasText(groupName);\r
-               Assert.notNull(authority);\r
-               \r
-               Group group = groupDao.findGroupByName(groupName);\r
-               if(group.getGrantedAuthorities().add(authority)) {\r
-                       groupDao.update(group);\r
-               }\r
-       }\r
-\r
-       @Deprecated // use GroupService instead\r
-       @Transactional(readOnly=false)\r
-       public void addUserToGroup(String username, String groupName) {\r
-               Assert.hasText(username);\r
-               Assert.hasText(groupName);\r
-               \r
-               Group group = groupDao.findGroupByName(groupName);\r
-               User user = dao.findUserByUsername(username);\r
-               \r
-               if(group.addMember(user)) {\r
-                       groupDao.update(group);\r
-                       userCache.removeUserFromCache(user.getUsername());\r
-               }               \r
-       }\r
-\r
-       @Deprecated // use GroupService instead\r
-       @Transactional(readOnly=false)\r
-       public void createGroup(String groupName, List<GrantedAuthority> authorities) {\r
-               Assert.hasText(groupName);\r
-               Assert.notNull(authorities);\r
-               \r
-               Group group = Group.NewInstance(groupName);\r
-               \r
-               for(GrantedAuthority authority : authorities) {\r
-                       group.getGrantedAuthorities().add(authority);\r
-               }\r
-               \r
-               groupDao.save(group);\r
-       }\r
-\r
-       @Deprecated // use GroupService instead\r
-       @Transactional(readOnly=false)\r
-       public void deleteGroup(String groupName) {\r
-               Assert.hasText(groupName);\r
-               \r
-               Group group = groupDao.findGroupByName(groupName);\r
-               groupDao.delete(group);\r
-       }\r
-\r
-       @Deprecated // use GroupService instead\r
-       public List<String> findAllGroups() {\r
-               return groupDao.listNames(null,null);\r
-       }\r
-\r
-       @Deprecated // use GroupService instead\r
-       public List<GrantedAuthority> findGroupAuthorities(String groupName) {\r
-               Assert.hasText(groupName);\r
-               Group group = groupDao.findGroupByName(groupName);\r
-               \r
-               return new ArrayList<GrantedAuthority>(group.getGrantedAuthorities());\r
-       }\r
-\r
-       @Deprecated // use GroupService instead\r
-       public List<String> findUsersInGroup(String groupName) {\r
-               Assert.hasText(groupName);\r
-               Group group = groupDao.findGroupByName(groupName);\r
-               \r
-               List<String> users = groupDao.listMembers(group, null, null);\r
-               \r
-               return users;\r
-       }\r
-\r
-       @Deprecated // use GroupService instead\r
-       @Transactional(readOnly=false)\r
-       public void removeGroupAuthority(String groupName,      GrantedAuthority authority) {\r
-               Assert.hasText(groupName);\r
-               Assert.notNull(authority);\r
-               \r
-               Group group = groupDao.findGroupByName(groupName);\r
-               \r
-               if(group.getGrantedAuthorities().remove(authority)) {\r
-                       groupDao.update(group);\r
-               }\r
-       }\r
-\r
-       @Deprecated // use GroupService instead\r
-       @Transactional(readOnly=false)\r
-       public void removeUserFromGroup(String username, String groupName) {\r
-               Assert.hasText(username);\r
-               Assert.hasText(groupName);\r
-               \r
-               Group group = groupDao.findGroupByName(groupName);\r
-               User user = dao.findUserByUsername(username);\r
-               \r
-               if(group.removeMember(user)) {\r
-                       groupDao.update(group);\r
-                       userCache.removeUserFromCache(user.getUsername());\r
-               }\r
-       }\r
-\r
-       @Deprecated // use GroupService instead\r
-       @Transactional(readOnly=false)\r
-       public void renameGroup(String oldName, String newName) {\r
-               Assert.hasText(oldName);\r
-               Assert.hasText(newName);\r
-               \r
-               Group group = groupDao.findGroupByName(oldName);\r
-               \r
-               group.setName(newName);\r
-               groupDao.update(group);\r
-       }\r
-       \r
-       @Transactional(readOnly=false)\r
-       public UUID save(User user) {\r
-               if(user.getId() == 0 || dao.load(user.getUuid()) == null){\r
-                       createUser(user);\r
-               }else{\r
-                       updateUser(user);\r
-               }\r
-               return user.getUuid(); \r
-       }\r
-\r
-       @Override\r
-       public UUID update(User user) {\r
-               updateUser(user);\r
-               return user.getUuid(); \r
-       }\r
-\r
-       @Override\r
-       @Transactional(readOnly=false)\r
-       public UUID saveGrantedAuthority(GrantedAuthority grantedAuthority) {\r
-               return grantedAuthorityDao.save((GrantedAuthorityImpl)grantedAuthority);\r
-       }\r
-       \r
-       @Deprecated // use GroupService instead\r
-       @Transactional(readOnly=false)\r
-       public UUID saveGroup(Group group) {\r
-               return groupDao.save(group);\r
-       }\r
-       \r
-       @Override\r
-       @Transactional(readOnly = true)\r
-       public List<User> listByUsername(String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
-                Integer numberOfResults = dao.countByUsername(queryString, matchmode, criteria);\r
-                       \r
-                List<User> results = new ArrayList<User>();\r
-                if(numberOfResults > 0) { \r
-                               results = dao.findByUsername(queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths); \r
-                }\r
-                return results;\r
-       }\r
-\r
-       \r
-\r
-       \r
-} \r
+    }\r
+\r
+    @Override\r
+    @Transactional(readOnly=false)\r
+    public void updateUser(UserDetails user) {\r
+        Assert.isInstanceOf(User.class, user);\r
+\r
+        dao.update((User)user);\r
+        userCache.removeUserFromCache(user.getUsername());\r
+    }\r
+\r
+    @Override\r
+    public boolean userExists(String username) {\r
+        Assert.hasText(username);\r
+\r
+        User user = dao.findUserByUsername(username);\r
+        return user != null;\r
+    }\r
+\r
+    /**\r
+     * DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS\r
+     * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE\r
+     */\r
+    public UserDetails loadUserByUsername(String username)\r
+            throws UsernameNotFoundException, DataAccessException {\r
+        Assert.hasText(username);\r
+        try {\r
+            User user = dao.findUserByUsername(username);\r
+            if(user == null) {\r
+                throw new UsernameNotFoundException(username);\r
+            }\r
+            return user;\r
+        } catch(NonUniqueResultException nure) {\r
+            throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);\r
+        }\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    @Transactional(readOnly=false)\r
+    public void addGroupAuthority(String groupName, GrantedAuthority authority) {\r
+        Assert.hasText(groupName);\r
+        Assert.notNull(authority);\r
+\r
+        Group group = groupDao.findGroupByName(groupName);\r
+        if(group.getGrantedAuthorities().add(authority)) {\r
+            groupDao.update(group);\r
+        }\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    @Transactional(readOnly=false)\r
+    public void addUserToGroup(String username, String groupName) {\r
+        Assert.hasText(username);\r
+        Assert.hasText(groupName);\r
+\r
+        Group group = groupDao.findGroupByName(groupName);\r
+        User user = dao.findUserByUsername(username);\r
+\r
+        if(group.addMember(user)) {\r
+            groupDao.update(group);\r
+            userCache.removeUserFromCache(user.getUsername());\r
+        }\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    @Transactional(readOnly=false)\r
+    public void createGroup(String groupName, List<GrantedAuthority> authorities) {\r
+        Assert.hasText(groupName);\r
+        Assert.notNull(authorities);\r
+\r
+        Group group = Group.NewInstance(groupName);\r
+\r
+        for(GrantedAuthority authority : authorities) {\r
+            group.getGrantedAuthorities().add(authority);\r
+        }\r
+\r
+        groupDao.save(group);\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    @Transactional(readOnly=false)\r
+    public void deleteGroup(String groupName) {\r
+        Assert.hasText(groupName);\r
+\r
+        Group group = groupDao.findGroupByName(groupName);\r
+        groupDao.delete(group);\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    public List<String> findAllGroups() {\r
+        return groupDao.listNames(null,null);\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    public List<GrantedAuthority> findGroupAuthorities(String groupName) {\r
+        Assert.hasText(groupName);\r
+        Group group = groupDao.findGroupByName(groupName);\r
+\r
+        return new ArrayList<GrantedAuthority>(group.getGrantedAuthorities());\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    public List<String> findUsersInGroup(String groupName) {\r
+        Assert.hasText(groupName);\r
+        Group group = groupDao.findGroupByName(groupName);\r
+\r
+        List<String> users = groupDao.listMembers(group, null, null);\r
+\r
+        return users;\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    @Transactional(readOnly=false)\r
+    public void removeGroupAuthority(String groupName, GrantedAuthority authority) {\r
+        Assert.hasText(groupName);\r
+        Assert.notNull(authority);\r
+\r
+        Group group = groupDao.findGroupByName(groupName);\r
+\r
+        if(group.getGrantedAuthorities().remove(authority)) {\r
+            groupDao.update(group);\r
+        }\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    @Transactional(readOnly=false)\r
+    public void removeUserFromGroup(String username, String groupName) {\r
+        Assert.hasText(username);\r
+        Assert.hasText(groupName);\r
+\r
+        Group group = groupDao.findGroupByName(groupName);\r
+        User user = dao.findUserByUsername(username);\r
+\r
+        if(group.removeMember(user)) {\r
+            groupDao.update(group);\r
+            userCache.removeUserFromCache(user.getUsername());\r
+        }\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    @Transactional(readOnly=false)\r
+    public void renameGroup(String oldName, String newName) {\r
+        Assert.hasText(oldName);\r
+        Assert.hasText(newName);\r
+\r
+        Group group = groupDao.findGroupByName(oldName);\r
+\r
+        group.setName(newName);\r
+        groupDao.update(group);\r
+    }\r
+\r
+    @Transactional(readOnly=false)\r
+    public UUID save(User user) {\r
+        if(user.getId() == 0 || dao.load(user.getUuid()) == null){\r
+            createUser(user);\r
+        }else{\r
+            updateUser(user);\r
+        }\r
+        return user.getUuid();\r
+    }\r
+\r
+    @Override\r
+    public UUID update(User user) {\r
+        updateUser(user);\r
+        return user.getUuid();\r
+    }\r
+\r
+    @Override\r
+    @Transactional(readOnly=false)\r
+    public UUID saveGrantedAuthority(GrantedAuthority grantedAuthority) {\r
+        return grantedAuthorityDao.save((GrantedAuthorityImpl)grantedAuthority);\r
+    }\r
+\r
+    @Deprecated // use GroupService instead\r
+    @Transactional(readOnly=false)\r
+    public UUID saveGroup(Group group) {\r
+        return groupDao.save(group);\r
+    }\r
+\r
+    @Override\r
+    @Transactional(readOnly = true)\r
+    public List<User> listByUsername(String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {\r
+         Integer numberOfResults = dao.countByUsername(queryString, matchmode, criteria);\r
+\r
+         List<User> results = new ArrayList<User>();\r
+         if(numberOfResults > 0) {\r
+                results = dao.findByUsername(queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);\r
+         }\r
+         return results;\r
+    }\r
+\r
+\r
+\r
+\r
+}\r
index 581109bdfc37a4e75b9d75a943d705ab7d9e8901..e290326eef0741e509851bf560fb502f3ebfd7fe 100644 (file)
@@ -1,15 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>\r
 <beans xmlns="http://www.springframework.org/schema/beans"\r
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
-       xmlns:context="http://www.springframework.org/schema/context"\r
-       xmlns:security="http://www.springframework.org/schema/security"\r
-       xsi:schemaLocation="http://www.springframework.org/schema/beans \r
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+  xmlns:context="http://www.springframework.org/schema/context"\r
+  xmlns:security="http://www.springframework.org/schema/security"\r
+  xsi:schemaLocation="http://www.springframework.org/schema/beans\r
     http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
     http://www.springframework.org/schema/context\r
     http://www.springframework.org/schema/context/spring-context-2.5.xsd\r
-       http://www.springframework.org/schema/security \r
+     http://www.springframework.org/schema/security\r
     http://www.springframework.org/schema/security/spring-security-3.0.4.xsd">\r
-    \r
+\r
     <bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">\r
         <property name="providers">\r
             <list>\r
             </list>\r
         </property>\r
     </bean>\r
-    \r
+\r
     <bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">\r
         <property name="userDetailsService" ref="userService"/>\r
         <property name="saltSource" ref="saltSource"/>\r
         <property name="passwordEncoder" ref="passwordEncoder"/>\r
     </bean>\r
-    \r
+\r
     <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"/>\r
-    \r
+\r
     <bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">\r
         <property name="userPropertyToUse" value="getUsername"/>\r
     </bean>\r
-    \r
-    <!-- Disabling in trunk. Method security is currently beeing developed in http://dev.e-taxonomy.eu/svn/branches/cdmlib/security/ -->\r
-    <!-- \r
+\r
+    <!-- Disabling in trunk. Method security is currently being developed in http://dev.e-taxonomy.eu/svn/branches/cdmlib/security/ -->\r
+    <!--\r
     <security:global-method-security pre-post-annotations="enabled">\r
-               <security:expression-handler ref="expressionHandler"/>\r
-       </security:global-method-security>\r
+        <security:expression-handler ref="expressionHandler"/>\r
+    </security:global-method-security>\r
 \r
-       <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">\r
-        <property name="permissionEvaluator" ref="cdmPermissionEvaluator"/>\r
-       </bean>\r
-       \r
-       \r
-       <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator" id="cdmPermissionEvaluator"/>\r
+    <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">\r
+            <property name="permissionEvaluator" ref="cdmPermissionEvaluator"/>\r
+    </bean>\r
+    <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator" id="cdmPermissionEvaluator"/>\r
      -->\r
+\r
 </beans>
\ No newline at end of file
index 3569bee7242740896845ae443c3a6551b1cb7cf3..e2e6e50bcafa2b729dff39a6b1e45b9e084d211e 100644 (file)
@@ -9,85 +9,66 @@
 package eu.etaxonomy.cdm.api.service;\r
 \r
 import static org.junit.Assert.assertEquals;\r
-import static org.junit.Assert.assertFalse;\r
 import static org.junit.Assert.assertTrue;\r
 \r
-\r
-import java.util.ArrayList;\r
 import java.util.Collection;\r
-import java.util.Iterator;\r
-import java.util.List;\r
 import java.util.Set;\r
 import java.util.UUID;\r
 \r
 import javax.sql.DataSource;\r
 \r
 import org.apache.log4j.Logger;\r
-\r
 import org.junit.Assert;\r
 import org.junit.Before;\r
 import org.junit.Ignore;\r
 import org.junit.Test;\r
-import org.junit.runner.RunWith;\r
-\r
-import org.springframework.beans.factory.annotation.Autowired;\r
-import org.springframework.orm.hibernate3.HibernateSystemException;\r
-import org.springframework.security.access.vote.RoleVoter;\r
+import org.springframework.security.access.AccessDeniedException;\r
 import org.springframework.security.authentication.AuthenticationManager;\r
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\r
 import org.springframework.security.authentication.dao.ReflectionSaltSource;\r
+import org.springframework.security.authentication.dao.SaltSource;\r
 import org.springframework.security.authentication.encoding.Md5PasswordEncoder;\r
+import org.springframework.security.authentication.encoding.PasswordEncoder;\r
 import org.springframework.security.core.Authentication;\r
 import org.springframework.security.core.GrantedAuthority;\r
 import org.springframework.security.core.context.SecurityContext;\r
 import org.springframework.security.core.context.SecurityContextHolder;\r
-import org.springframework.test.annotation.ExpectedException;\r
 import org.springframework.transaction.PlatformTransactionManager;\r
-\r
-\r
-import org.unitils.database.annotations.Transactional;\r
-import org.unitils.UnitilsJUnit4TestClassRunner;\r
 import org.unitils.database.annotations.TestDataSource;\r
-import org.unitils.database.util.TransactionMode;\r
 import org.unitils.dbunit.annotation.DataSet;\r
-import org.unitils.spring.annotation.SpringApplicationContext;\r
-import org.unitils.spring.annotation.SpringBeanByName;\r
+import org.unitils.spring.annotation.SpringBean;\r
 import org.unitils.spring.annotation.SpringBeanByType;\r
 \r
-\r
-import eu.etaxonomy.cdm.api.service.config.IFindTaxaAndNamesConfigurator;\r
-import eu.etaxonomy.cdm.api.service.config.FindTaxaAndNamesConfiguratorImpl;\r
-import eu.etaxonomy.cdm.api.service.pager.Pager;\r
 import eu.etaxonomy.cdm.database.EvaluationFailedException;\r
-import eu.etaxonomy.cdm.model.common.Language;\r
 import eu.etaxonomy.cdm.model.common.User;\r
-\r
-\r
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
 import eu.etaxonomy.cdm.model.description.Distribution;\r
 import eu.etaxonomy.cdm.model.description.Feature;\r
 import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
 import eu.etaxonomy.cdm.model.description.TextData;\r
-\r
-import eu.etaxonomy.cdm.model.media.Media;\r
 import eu.etaxonomy.cdm.model.name.BotanicalName;\r
 import eu.etaxonomy.cdm.model.name.Rank;\r
 import eu.etaxonomy.cdm.model.taxon.Synonym;\r
-\r
-import eu.etaxonomy.cdm.model.taxon.SynonymRelationshipType;\r
 import eu.etaxonomy.cdm.model.taxon.Taxon;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonBase;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonNode;\r
-import eu.etaxonomy.cdm.persistence.dao.BeanInitializer;\r
 import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator;\r
-import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest;\r
 import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTestWithSecurity;\r
-import eu.etaxonomy.cdm.test.unitils.CleanSweepInsertLoadStrategy;\r
 \r
 \r
 @DataSet\r
 public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{\r
 \r
+    private static final UUID UUID_ACHERONTINII = UUID.fromString("928a0167-98cd-4555-bf72-52116d067625");\r
+\r
+    private static final UUID UUID_ACHERONTIA_STYX = UUID.fromString("7b8b5cb3-37ba-4dba-91ac-4c6ffd6ac331");\r
+\r
+    private static final UUID PART_EDITOR_UUID = UUID.fromString("38a251bd-0ba4-426f-8fcb-5c09560749a7");\r
+\r
+    private static final String PASSWORD_TAXON_EDITOR = "test2";\r
+\r
+    private static final String PASSWORD_ADMIN = "sPePhAz6";\r
+\r
     private static final UUID ACHERONTIA_NODE_UUID = UUID.fromString("20c8f083-5870-4cbd-bf56-c5b2b98ab6a7");\r
 \r
     private static final UUID ACHERONTIINI_NODE_UUID = UUID.fromString("cecfa77f-f26a-4476-9d87-a8d993cb55d9");\r
@@ -123,16 +104,81 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
     @SpringBeanByType\r
     private AuthenticationManager authenticationManager;\r
 \r
+    @SpringBeanByType\r
+    private SaltSource saltSource;\r
+\r
+    @SpringBeanByType\r
+    private PasswordEncoder passwordEncoder;\r
+\r
+    @SpringBean("cdmPermissionEvaluator")\r
+    private CdmPermissionEvaluator permissionEvaluator;\r
 \r
+    private UsernamePasswordAuthenticationToken tokenForAdmin;\r
 \r
-    private UsernamePasswordAuthenticationToken token;\r
+    private UsernamePasswordAuthenticationToken tokenForTaxonEditor;\r
+\r
+    private UsernamePasswordAuthenticationToken tokenForDescriptionEditor;\r
+\r
+    private UsernamePasswordAuthenticationToken tokenForPartEditor;\r
+\r
+    private UsernamePasswordAuthenticationToken tokenForTaxonomist;\r
 \r
 \r
     @Before\r
     public void setUp(){\r
-        token = new UsernamePasswordAuthenticationToken("ben", "sPePhAz6");\r
+        /* User 'admin':\r
+            - ROLE_ADMIN\r
+            - ALL.ADMIN\r
+            - TAXONBASE.READ\r
+            - TAXONBASE.CREATE\r
+            - TAXONBASE.DELETE\r
+            - TAXONBASE.UPDATE\r
+        */\r
+        tokenForAdmin = new UsernamePasswordAuthenticationToken("admin", PASSWORD_ADMIN);\r
+\r
+        /* User 'taxonEditor':\r
+            - TAXONBASE.CREATE\r
+            - TAXONBASE.UPDATE\r
+        */\r
+        tokenForTaxonEditor = new UsernamePasswordAuthenticationToken("taxonEditor", PASSWORD_TAXON_EDITOR);\r
+\r
+        /*  User 'descriptionEditor':\r
+            - DESCRIPTIONBASE.CREATE\r
+            - DESCRIPTIONBASE.UPDATE\r
+            - DESCRIPTIONELEMENT(Ecology).CREATE\r
+            - DESCRIPTIONELEMENT(Ecology).UPDATE\r
+         */\r
+        tokenForDescriptionEditor = new UsernamePasswordAuthenticationToken("descriptionEditor", "test");\r
+\r
+        /* User 'partEditor':\r
+            - TAXONBASE.ADMIN\r
+            - TAXONNODE.CREATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}\r
+            - TAXONNODE.UPDATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}\r
+         */\r
+        tokenForPartEditor = new UsernamePasswordAuthenticationToken("partEditor", "test4");\r
+\r
+        /* User 'taxonomist':\r
+            - TAXONBASE.READ\r
+            - TAXONBASE.CREATE\r
+            - TAXONBASE.DELETE\r
+            - TAXONBASE.UPDATE\r
+         */\r
+        tokenForTaxonomist = new UsernamePasswordAuthenticationToken("taxonomist", "test4");\r
     }\r
 \r
+    /**\r
+     * no assertions in this test, since it is only used to create password hashes for test data\r
+     */\r
+    @Test\r
+    public void testEncryptPassword(){\r
+\r
+        String password = PASSWORD_ADMIN;\r
+        User user = User.NewInstance("admin", "");\r
+\r
+        Object salt = this.saltSource.getSalt(user);\r
+        String passwordEncrypted = passwordEncoder.encodePassword(password, salt);\r
+        logger.info("encrypted password: " + passwordEncrypted );\r
+    }\r
 \r
     /**\r
      * Test method for {@link eu.etaxonomy.cdm.api.service.TaxonServiceImpl#saveTaxon(eu.etaxonomy.cdm.model.taxon.TaxonBase)}.\r
@@ -147,29 +193,111 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         System.err.println(encoder.encodePassword("test4", saltSource.getSalt(user)));\r
 \r
         */\r
-        authentication = authenticationManager.authenticate(token);\r
+        authentication = authenticationManager.authenticate(tokenForAdmin);\r
         SecurityContext context = SecurityContextHolder.getContext();\r
         context.setAuthentication(authentication);\r
 \r
         Taxon expectedTaxon = Taxon.NewInstance(BotanicalName.NewInstance(Rank.SPECIES()), null);\r
         UUID uuid = taxonService.save(expectedTaxon);\r
+        commitAndStartNewTransaction(null);\r
         //taxonService.getSession().flush();\r
         TaxonBase<?> actualTaxon = taxonService.load(uuid);\r
         assertEquals(expectedTaxon, actualTaxon);\r
 \r
-        token = new UsernamePasswordAuthenticationToken("taxonEditor", "test2");\r
-        authentication = authenticationManager.authenticate(token);\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
         context = SecurityContextHolder.getContext();\r
         context.setAuthentication(authentication);\r
         expectedTaxon = Taxon.NewInstance(BotanicalName.NewInstance(Rank.GENUS()), null);\r
         taxonService.saveOrUpdate(actualTaxon);\r
+        commitAndStartNewTransaction(null);\r
 \r
     }\r
 \r
+    @Test\r
+    @Ignore //FIXME no need to test this, no access controll needed for userService.changePassword\r
+    public void testChangeOwnPassword(){\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        // authenticate as admin\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+        context.setAuthentication(authentication);\r
+\r
+//        User currentUser =  (User) context.getAuthentication().getPrincipal();\r
+\r
+        String newPass = "poiweorijo";\r
+        userService.changePassword(PASSWORD_TAXON_EDITOR, newPass);\r
+        commitAndStartNewTransaction(null);\r
+\r
+        // try to re-authenticate user with changed password\r
+        UsernamePasswordAuthenticationToken newTokenForTaxonEditor = new UsernamePasswordAuthenticationToken("taxonEditor", newPass);\r
+        authentication = authenticationManager.authenticate(newTokenForTaxonEditor);\r
+    }\r
+\r
+    @Test\r
+    public void testChangeOthersPassword(){\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        // (1) authenticate as admin\r
+        authentication = authenticationManager.authenticate(tokenForAdmin);\r
+        context.setAuthentication(authentication);\r
+\r
+        RuntimeException exception = null;\r
+\r
+        try{\r
+            userService.changePasswordForUser("taxonomist", "zuaisd");\r
+            commitAndStartNewTransaction(null);\r
+        } catch (AccessDeniedException e){\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            exception = e;\r
+        } catch (RuntimeException e){\r
+            exception = findThrowableOfTypeIn(EvaluationFailedException.class, e);\r
+            logger.debug("Unexpected failure of evaluation.", exception);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNull("must not fail here!", exception);\r
+\r
+        // ok, now try authenticating taxonomist with new password\r
+        UsernamePasswordAuthenticationToken newToken = new UsernamePasswordAuthenticationToken("taxonomist", "zuaisd");\r
+        authentication = authenticationManager.authenticate(newToken);\r
+\r
+        // (2) authenticate as under privileged user - not an admin !!!\r
+        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
+        context.setAuthentication(authentication);\r
+\r
+        // check test preconditions user name and authorities\r
+        Assert.assertEquals("descriptionEditor", context.getAuthentication().getName());\r
+        Collection<GrantedAuthority> authorities = context.getAuthentication().getAuthorities();\r
+        for(GrantedAuthority authority: authorities){\r
+            // role prefix 'ROLE_' is defined in org.springframework.security.access.vote.RoleVoter !!!\r
+            Assert.assertNotSame("user must not have authority 'ROLE_ADMIN'", "ROLE_ADMIN", authority.getAuthority());\r
+        }\r
+        // finally perform the test :\r
+        try{\r
+            userService.changePasswordForUser("partEditor", "poiweorijo");\r
+            commitAndStartNewTransaction(null);\r
+        } catch (AccessDeniedException e){\r
+            logger.debug("Expected failure of evaluation.", e);\r
+            exception = e;\r
+        } catch (RuntimeException e){\r
+            exception = findThrowableOfTypeIn(EvaluationFailedException.class, e);\r
+            logger.debug("Expected failure of evaluation.", exception);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNotNull("must fail here!", exception);\r
+    }\r
+\r
     @Test\r
     public void testUpdateUser(){\r
 \r
-        authentication = authenticationManager.authenticate(token);\r
+        authentication = authenticationManager.authenticate(tokenForAdmin);\r
         SecurityContext context = SecurityContextHolder.getContext();\r
         context.setAuthentication(authentication);\r
         String username = "standardUser";\r
@@ -182,105 +310,252 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         userService.updateUser(user);\r
         userService.update(user);\r
         userService.saveOrUpdate(user);\r
+        commitAndStartNewTransaction(null);\r
 \r
     }\r
 \r
     @Test\r
     public final void testSaveOrUpdateTaxon() {\r
-        authentication = authenticationManager.authenticate(token);\r
         SecurityContext context = SecurityContextHolder.getContext();\r
+\r
+        // 1) test with admin account - should succeed\r
+        authentication = authenticationManager.authenticate(tokenForAdmin);\r
         context.setAuthentication(authentication);\r
-        Taxon expectedTaxon = Taxon.NewInstance(null, null);\r
-        UUID uuid = taxonService.save(expectedTaxon);\r
-        TaxonBase<?> actualTaxon = taxonService.load(uuid);\r
-        assertEquals(expectedTaxon, actualTaxon);\r
 \r
-        actualTaxon.setName(BotanicalName.NewInstance(Rank.SPECIES()));\r
-        taxonService.saveOrUpdate(actualTaxon);\r
+        TaxonBase<?> taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        taxon.setDoubtful(true);\r
+        RuntimeException securityException= null;\r
+        try{\r
+            taxonService.saveOrUpdate(taxon);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException  = findSecurityRuntimeException(e);\r
+            logger.error("Unexpected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        // reload taxon\r
+        taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        Assert.assertTrue("The change must be persited", taxon.isDoubtful());\r
 \r
-        token = new UsernamePasswordAuthenticationToken("taxonEditor", "test2");\r
-        authentication = authenticationManager.authenticate(token);\r
-        context = SecurityContextHolder.getContext();\r
+        // 2) test with taxonEditor account - should succeed\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
         context.setAuthentication(authentication);\r
-        actualTaxon = taxonService.load(uuid);\r
 \r
-        actualTaxon.setDoubtful(true);\r
-        taxonService.saveOrUpdate(actualTaxon);\r
+        taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        taxon.setDoubtful(false);\r
+        securityException= null;\r
+        try{\r
+            taxonService.saveOrUpdate(taxon);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException  = findSecurityRuntimeException(e);\r
+            logger.debug("Unexpected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        // reload taxon\r
+        taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        Assert.assertFalse("The change must be persited", taxon.isDoubtful());\r
+\r
+        // 3) test with tokenForDescriptionEditor account - should fail\r
+//        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+//        context.setAuthentication(authentication);\r
+//        taxon = taxonService.load(uuid);\r
+//\r
+//        taxon.setDoubtful(true);\r
+//        taxonService.saveOrUpdate(taxon);\r
+//        commitAndStartNewTransaction(null);\r
 \r
     }\r
 \r
     @Test\r
-    public void testCascadingInSpringSecurityAccesDenied(){\r
-        /*authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("partEditor", "test4"));\r
-        SecurityContext context = SecurityContextHolder.getContext();\r
-        context.setAuthentication(authentication);\r
-        */\r
+    @Ignore //FIXME: adding taxa to a description must be protected at the side of the Description itself!!\r
+            //        => protecting method TaxonDescription.setTaxon() ?\r
+    public void testAddDescriptionToTaxon(){\r
 \r
-        authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("taxonEditor", "test2"));\r
         SecurityContext context = SecurityContextHolder.getContext();\r
+        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
         context.setAuthentication(authentication);\r
-        CdmPermissionEvaluator permissionEvaluator = new CdmPermissionEvaluator();\r
-\r
-        Taxon taxon =(Taxon) taxonService.load(ACHERONTIA_LACHESIS_UUID);\r
-        taxon.setDoubtful(false);\r
-        assertTrue(permissionEvaluator.hasPermission(authentication, taxon, "UPDATE"));\r
-        taxonService.save(taxon);\r
-        taxon = null;\r
-        commitAndStartNewTransaction(null);\r
 \r
-        //during cascading the permissions are not evaluated, but with hibernate listener every database transaction can be interrupted, but how to manage it,\r
-        //when someone has the rights to save descriptions, but not taxa (the editor always saves everything by saving the taxon)\r
-        //taxonService.saveOrUpdate(taxon);\r
+        RuntimeException securityException = null;\r
 \r
-\r
-        authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("descriptionEditor", "test"));\r
-        context = SecurityContextHolder.getContext();\r
-        context.setAuthentication(authentication);\r
-\r
-        //taxonService.saveOrUpdate(taxon);\r
-\r
-        taxon =(Taxon) taxonService.load(ACHERONTIA_LACHESIS_UUID);\r
+        Taxon taxon = (Taxon)taxonService.load(ACHERONTIA_LACHESIS_UUID);\r
 \r
         TaxonDescription description = TaxonDescription.NewInstance(taxon);\r
         description.setTitleCache("test");\r
-        descriptionService.saveOrUpdate(description);\r
-        commitAndStartNewTransaction(null);\r
+        try {\r
+            descriptionService.saveOrUpdate(description);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Expected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        /*\r
+         * Expectation:\r
+         * The user should not be granted to add the Description to a taxon\r
+         */\r
+        Assert.assertNotNull("evaluation should fail since the user is not permitted to edit Taxa", securityException);\r
         taxon = (Taxon)taxonService.load(ACHERONTIA_LACHESIS_UUID);\r
         assertTrue(taxon.getDescriptions().contains(description));\r
     }\r
 \r
     @Test\r
-    public void testCascadingInSpring(){\r
-        authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("descriptionEditor", "test"));\r
+    public void testCreateDescriptionWithElement(){\r
+        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
         SecurityContext context = SecurityContextHolder.getContext();\r
         context.setAuthentication(authentication);\r
 \r
-        Taxon taxon = (Taxon)taxonService.load(UUID.fromString("928a0167-98cd-4555-bf72-52116d067625"));\r
-        TaxonDescription description = TaxonDescription.NewInstance(taxon);\r
-        description.addElement(Distribution.NewInstance());\r
-        CdmPermissionEvaluator permissionEvaluator = new CdmPermissionEvaluator();\r
-        assertTrue(permissionEvaluator.hasPermission(authentication, description, "UPDATE"));\r
+        Taxon taxon = (Taxon)taxonService.load(UUID_ACHERONTINII);\r
+        Assert.assertTrue("taxon must not yet have descriptions", taxon.getDescriptions().size() == 0);\r
+\r
+        TaxonDescription description = null;\r
 \r
-        descriptionService.saveOrUpdate(description);\r
+        // 1) test for failure - description element but no feature\r
+        description = TaxonDescription.NewInstance(taxon);\r
+        DescriptionElementBase textdataNoFeature = TextData.NewInstance();\r
+        description.addElement(textdataNoFeature);\r
 \r
-        taxon = (Taxon)taxonService.load(UUID.fromString("928a0167-98cd-4555-bf72-52116d067625"));\r
+        RuntimeException securityException = null;\r
+        assertTrue(permissionEvaluator.hasPermission(authentication, description, "UPDATE"));\r
+        try{\r
+            descriptionService.saveOrUpdate(description);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Expected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        Assert.assertNotNull("evaluation should fail", securityException);\r
+        taxon = (Taxon)taxonService.load(UUID_ACHERONTINII);\r
         Set<TaxonDescription> descriptions = taxon.getDescriptions();\r
-        assertTrue(descriptions.contains(description));\r
+        assertTrue("taxon must not have any description", descriptions.size() == 0);\r
+\r
+        // 2) test for failure  - description element but not granted feature\r
+        description = TaxonDescription.NewInstance(taxon);\r
+        DescriptionElementBase descriptionText = TextData.NewInstance(Feature.DESCRIPTION());\r
+        description.addElement(descriptionText);\r
+\r
+        securityException = null;\r
+        assertTrue(permissionEvaluator.hasPermission(authentication, description, "UPDATE"));\r
+        try{\r
+            descriptionService.saveOrUpdate(description);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Expected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        Assert.assertNotNull("evaluation should fail", securityException);\r
+        taxon = (Taxon)taxonService.load(UUID_ACHERONTINII);\r
+        descriptions = taxon.getDescriptions();\r
+        assertTrue("taxon must not have any description", descriptions.size() == 0);\r
+\r
+        // 3) test for failure\r
+        description = TaxonDescription.NewInstance(taxon);\r
+        DescriptionElementBase ecologyText = TextData.NewInstance(Feature.ECOLOGY());\r
+        description.addElement(ecologyText);\r
+\r
+        securityException = null;\r
+        assertTrue(permissionEvaluator.hasPermission(authentication, description, "UPDATE"));\r
+        try{\r
+            descriptionService.saveOrUpdate(description);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Unexpected failure of evaluation.", e);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        taxon = (Taxon)taxonService.load(UUID_ACHERONTINII);\r
+        descriptions = taxon.getDescriptions();\r
+        assertTrue("taxon must now have one description", descriptions.size() == 1);\r
+        assertTrue("description should have one description element", descriptions.iterator().next().getElements().size() == 1);\r
 \r
     }\r
 \r
     @Test\r
     public void testSaveSynonym(){\r
-        authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("taxonomist", "test4"));\r
+\r
         SecurityContext context = SecurityContextHolder.getContext();\r
+\r
+        // 1) test for success\r
+        authentication = authenticationManager.authenticate(tokenForTaxonomist);\r
         context.setAuthentication(authentication);\r
 \r
+        RuntimeException securityException = null;\r
         Synonym syn = Synonym.NewInstance(BotanicalName.NewInstance(Rank.SPECIES()), null);\r
-        taxonService.saveOrUpdate(syn);\r
+        UUID synUuid = UUID.randomUUID();\r
+        syn.setUuid(synUuid);\r
+        try{\r
+            taxonService.saveOrUpdate(syn);\r
+            logger.debug("will commit ...");\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Unexpected failure of evaluation.", e);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        Assert.assertNotNull("The new Synonym must be persited", taxonService.find(synUuid));\r
+\r
+        // 2) test for denial\r
+        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
+        context.setAuthentication(authentication);\r
+        securityException = null;\r
+        syn = Synonym.NewInstance(BotanicalName.NewInstance(Rank.SPECIES()), null);\r
+        synUuid = syn.getUuid();\r
+        try{\r
+            taxonService.saveOrUpdate(syn);\r
+            logger.debug("will commit ...");\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Expected failure of evaluation: " + securityException.getClass());\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        Assert.assertNotNull("evaluation must fail since the user is not permitted", securityException);\r
+        Assert.assertNull("The Synonym must not be persited", taxonService.find(synUuid));\r
     }\r
 \r
     @Test\r
-    @Ignore //FIXME test must not fail !!!!!\r
     public void testEditPartOfClassification(){\r
         /*\r
          * the user 'partEditor' has the following authorities:\r
@@ -291,37 +566,56 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
          *  that is 'partEditor' is granted to edit the subtree of\r
          *  which ACHERONTIA_NODE_UUID [20c8f083-5870-4cbd-bf56-c5b2b98ab6a7] is the root node.\r
          */\r
-        authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("partEditor", "test4"));\r
+\r
+        authentication = authenticationManager.authenticate(tokenForPartEditor);\r
         SecurityContext context = SecurityContextHolder.getContext();\r
         context.setAuthentication(authentication);\r
 \r
         // test for success\r
+        RuntimeException securityException = null;\r
         TaxonNode acherontia_node = taxonNodeService.load(ACHERONTIA_NODE_UUID);\r
         long numOfChildNodes = acherontia_node.getChildNodes().size();\r
         TaxonNode childNode = acherontia_node.addChildTaxon(Taxon.NewInstance(BotanicalName.NewInstance(Rank.SPECIES()), null), null, null, null);\r
-        EvaluationFailedException evaluationFailedException = null;\r
+\r
         try{\r
             taxonNodeService.saveOrUpdate(acherontia_node);\r
             commitAndStartNewTransaction(null);\r
         } catch (RuntimeException e){\r
-            evaluationFailedException = findEvaluationFailedExceptionIn(e);\r
-            logger.debug("Unexpected failure of evaluation.", evaluationFailedException);\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Unexpected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
         }\r
-        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + evaluationFailedException.getMessage(), evaluationFailedException);\r
+\r
+        acherontia_node = taxonNodeService.load(ACHERONTIA_NODE_UUID);\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
         Assert.assertEquals("the acherontia_node must now have one more child node ", numOfChildNodes + 1 , acherontia_node.getChildNodes().size());\r
 \r
         // test for denial\r
-        evaluationFailedException = null;\r
+        securityException = null;\r
         TaxonNode acherontiini_node = taxonNodeService.load(ACHERONTIINI_NODE_UUID);\r
         numOfChildNodes = acherontiini_node.getCountChildren();\r
         acherontiini_node.addChildTaxon(Taxon.NewInstance(BotanicalName.NewInstance(Rank.GENUS()), null), null, null, null);\r
+\r
         try{\r
+            logger.debug("==============================");\r
             taxonNodeService.saveOrUpdate(acherontiini_node);\r
             commitAndStartNewTransaction(null);\r
         } catch (RuntimeException e){\r
-            evaluationFailedException = findEvaluationFailedExceptionIn(e);\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Expected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
         }\r
-        Assert.assertNotNull("evaluation must fail since the user is not permitted", evaluationFailedException);\r
+\r
+        acherontiini_node = taxonNodeService.load(ACHERONTIINI_NODE_UUID);\r
+        Assert.assertNotNull("evaluation must fail since the user is not permitted", securityException);\r
         Assert.assertEquals("the number of child nodes must be unchanged ", numOfChildNodes , acherontiini_node.getChildNodes().size());\r
 \r
     }\r
diff --git a/cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/SecurityTest.java.orig b/cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/SecurityTest.java.orig
new file mode 100644 (file)
index 0000000..db0e09f
--- /dev/null
@@ -0,0 +1,639 @@
+/**\r
+ * Copyright (C) 2011 EDIT\r
+ * European Distributed Institute of Taxonomy\r
+ * http://www.e-taxonomy.eu\r
+ *\r
+ * The contents of this file are subject to the Mozilla Public License Version 1.1\r
+ * See LICENSE.TXT at the top of this package for the full license terms.\r
+ */\r
+package eu.etaxonomy.cdm.api.service;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+import static org.junit.Assert.assertTrue;\r
+\r
+import java.util.Collection;\r
+import java.util.Set;\r
+import java.util.UUID;\r
+\r
+import javax.sql.DataSource;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.junit.Assert;\r
+import org.junit.Before;\r
+import org.junit.Ignore;\r
+import org.junit.Test;\r
+import org.springframework.security.access.AccessDeniedException;\r
+import org.springframework.security.authentication.AuthenticationManager;\r
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\r
+import org.springframework.security.authentication.dao.ReflectionSaltSource;\r
+import org.springframework.security.authentication.dao.SaltSource;\r
+import org.springframework.security.authentication.encoding.Md5PasswordEncoder;\r
+import org.springframework.security.authentication.encoding.PasswordEncoder;\r
+import org.springframework.security.core.Authentication;\r
+import org.springframework.security.core.GrantedAuthority;\r
+import org.springframework.security.core.context.SecurityContext;\r
+import org.springframework.security.core.context.SecurityContextHolder;\r
+import org.springframework.transaction.PlatformTransactionManager;\r
+import org.unitils.database.annotations.TestDataSource;\r
+import org.unitils.dbunit.annotation.DataSet;\r
+import org.unitils.spring.annotation.SpringBean;\r
+import org.unitils.spring.annotation.SpringBeanByType;\r
+\r
+import eu.etaxonomy.cdm.database.EvaluationFailedException;\r
+import eu.etaxonomy.cdm.model.common.User;\r
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
+import eu.etaxonomy.cdm.model.description.Distribution;\r
+import eu.etaxonomy.cdm.model.description.Feature;\r
+import eu.etaxonomy.cdm.model.description.TaxonDescription;\r
+import eu.etaxonomy.cdm.model.description.TextData;\r
+import eu.etaxonomy.cdm.model.name.BotanicalName;\r
+import eu.etaxonomy.cdm.model.name.Rank;\r
+import eu.etaxonomy.cdm.model.taxon.Synonym;\r
+import eu.etaxonomy.cdm.model.taxon.Taxon;\r
+import eu.etaxonomy.cdm.model.taxon.TaxonBase;\r
+import eu.etaxonomy.cdm.model.taxon.TaxonNode;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator;\r
+import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTestWithSecurity;\r
+\r
+\r
+@DataSet\r
+public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{\r
+\r
+    private static final UUID UUID_ACHERONTINII = UUID.fromString("928a0167-98cd-4555-bf72-52116d067625");\r
+\r
+    private static final UUID UUID_ACHERONTIA_STYX = UUID.fromString("7b8b5cb3-37ba-4dba-91ac-4c6ffd6ac331");\r
+\r
+    private static final UUID PART_EDITOR_UUID = UUID.fromString("38a251bd-0ba4-426f-8fcb-5c09560749a7");\r
+\r
+    private static final String PASSWORD_TAXON_EDITOR = "test2";\r
+\r
+    private static final String PASSWORD_ADMIN = "sPePhAz6";\r
+\r
+    private static final UUID ACHERONTIA_NODE_UUID = UUID.fromString("20c8f083-5870-4cbd-bf56-c5b2b98ab6a7");\r
+\r
+    private static final UUID ACHERONTIINI_NODE_UUID = UUID.fromString("cecfa77f-f26a-4476-9d87-a8d993cb55d9");\r
+\r
+    private static final UUID ACHERONTIA_LACHESIS_UUID = UUID.fromString("bc09aca6-06fd-4905-b1e7-cbf7cc65d783");\r
+\r
+    private static final Logger logger = Logger.getLogger(SecurityTest.class);\r
+\r
+    /**\r
+     * The transaction manager to use\r
+     */\r
+    @SpringBeanByType\r
+    PlatformTransactionManager transactionManager;\r
+\r
+    @SpringBeanByType\r
+    private ITaxonService taxonService;\r
+\r
+    @SpringBeanByType\r
+    private ITaxonNodeService taxonNodeService;\r
+\r
+    @SpringBeanByType\r
+    private IDescriptionService descriptionService;\r
+\r
+    @SpringBeanByType\r
+    private IUserService userService;\r
+\r
+\r
+    @TestDataSource\r
+    protected DataSource dataSource;\r
+\r
+    private Authentication authentication;\r
+\r
+    @SpringBeanByType\r
+    private AuthenticationManager authenticationManager;\r
+\r
+    @SpringBeanByType\r
+    private SaltSource saltSource;\r
+\r
+    @SpringBeanByType\r
+    private PasswordEncoder passwordEncoder;\r
+\r
+    @SpringBean("cdmPermissionEvaluator")\r
+    private CdmPermissionEvaluator permissionEvaluator;\r
+\r
+    private UsernamePasswordAuthenticationToken tokenForAdmin;\r
+\r
+    private UsernamePasswordAuthenticationToken tokenForTaxonEditor;\r
+\r
+    private UsernamePasswordAuthenticationToken tokenForDescriptionEditor;\r
+\r
+    private UsernamePasswordAuthenticationToken tokenForPartEditor;\r
+\r
+    private UsernamePasswordAuthenticationToken tokenForTaxonomist;\r
+\r
+\r
+    @Before\r
+    public void setUp(){\r
+        /* User 'admin':\r
+            - ROLE_ADMIN\r
+            - ALL.ADMIN\r
+            - TAXONBASE.READ\r
+            - TAXONBASE.CREATE\r
+            - TAXONBASE.DELETE\r
+            - TAXONBASE.UPDATE\r
+        */\r
+        tokenForAdmin = new UsernamePasswordAuthenticationToken("admin", PASSWORD_ADMIN);\r
+\r
+        /* User 'taxonEditor':\r
+            - TAXONBASE.CREATE\r
+            - TAXONBASE.UPDATE\r
+        */\r
+        tokenForTaxonEditor = new UsernamePasswordAuthenticationToken("taxonEditor", PASSWORD_TAXON_EDITOR);\r
+\r
+        /*  User 'descriptionEditor':\r
+            - DESCRIPTIONBASE.CREATE\r
+            - DESCRIPTIONBASE.UPDATE\r
+            - DESCRIPTIONELEMENT(Ecology).CREATE\r
+            - DESCRIPTIONELEMENT(Ecology).UPDATE\r
+         */\r
+        tokenForDescriptionEditor = new UsernamePasswordAuthenticationToken("descriptionEditor", "test");\r
+\r
+        /* User 'partEditor':\r
+            - TAXONBASE.ADMIN\r
+            - TAXONNODE.CREATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}\r
+            - TAXONNODE.UPDATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}\r
+         */\r
+        tokenForPartEditor = new UsernamePasswordAuthenticationToken("partEditor", "test4");\r
+\r
+        /* User 'taxonomist':\r
+            - TAXONBASE.READ\r
+            - TAXONBASE.CREATE\r
+            - TAXONBASE.DELETE\r
+            - TAXONBASE.UPDATE\r
+         */\r
+        tokenForTaxonomist = new UsernamePasswordAuthenticationToken("taxonomist", "test4");\r
+    }\r
+\r
+    /**\r
+     * no assertions in this test, since it is only used to create password hashes for test data\r
+     */\r
+    @Test\r
+    public void testEncryptPassword(){\r
+\r
+        String password = PASSWORD_ADMIN;\r
+        User user = User.NewInstance("admin", "");\r
+\r
+        Object salt = this.saltSource.getSalt(user);\r
+        String passwordEncrypted = passwordEncoder.encodePassword(password, salt);\r
+        logger.info("encrypted password: " + passwordEncrypted );\r
+    }\r
+\r
+    /**\r
+     * Test method for {@link eu.etaxonomy.cdm.api.service.TaxonServiceImpl#saveTaxon(eu.etaxonomy.cdm.model.taxon.TaxonBase)}.\r
+     */\r
+    @Test\r
+    public final void testSaveTaxon() {\r
+        /*\r
+        Md5PasswordEncoder encoder =new Md5PasswordEncoder();\r
+        ReflectionSaltSource saltSource = new ReflectionSaltSource();\r
+        saltSource.setUserPropertyToUse("getUsername");\r
+        User user = User.NewInstance("partEditor", "test4");\r
+        System.err.println(encoder.encodePassword("test4", saltSource.getSalt(user)));\r
+\r
+        */\r
+        authentication = authenticationManager.authenticate(tokenForAdmin);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        Taxon expectedTaxon = Taxon.NewInstance(BotanicalName.NewInstance(Rank.SPECIES()), null);\r
+        UUID uuid = taxonService.save(expectedTaxon);\r
+        commitAndStartNewTransaction(null);\r
+        //taxonService.getSession().flush();\r
+        TaxonBase<?> actualTaxon = taxonService.load(uuid);\r
+        assertEquals(expectedTaxon, actualTaxon);\r
+\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+        context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+        expectedTaxon = Taxon.NewInstance(BotanicalName.NewInstance(Rank.GENUS()), null);\r
+        taxonService.saveOrUpdate(actualTaxon);\r
+        commitAndStartNewTransaction(null);\r
+\r
+    }\r
+\r
+    @Test\r
+    @Ignore //FIXME no need to test this, no access controll needed for userService.changePassword\r
+    public void testChangeOwnPassword(){\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        // authenticate as admin\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+        context.setAuthentication(authentication);\r
+\r
+//        User currentUser =  (User) context.getAuthentication().getPrincipal();\r
+\r
+        String newPass = "poiweorijo";\r
+        userService.changePassword(PASSWORD_TAXON_EDITOR, newPass);\r
+        commitAndStartNewTransaction(null);\r
+\r
+        // try to re-authenticate user with changed password\r
+        UsernamePasswordAuthenticationToken newTokenForTaxonEditor = new UsernamePasswordAuthenticationToken("taxonEditor", newPass);\r
+        authentication = authenticationManager.authenticate(newTokenForTaxonEditor);\r
+    }\r
+\r
+    @Test\r
+    public void testChangeOthersPassword(){\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        // (1) authenticate as admin\r
+        authentication = authenticationManager.authenticate(tokenForAdmin);\r
+        context.setAuthentication(authentication);\r
+\r
+        RuntimeException exception = null;\r
+\r
+        try{\r
+            userService.changePasswordForUser("taxonomist", "zuaisd");\r
+            commitAndStartNewTransaction(null);\r
+        } catch (AccessDeniedException e){\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            exception = e;\r
+        } catch (RuntimeException e){\r
+            exception = findThrowableOfTypeIn(EvaluationFailedException.class, e);\r
+            logger.debug("Unexpected failure of evaluation.", exception);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNull("must not fail here!", exception);\r
+\r
+        // ok, now try authenticating taxonomist with new password\r
+        UsernamePasswordAuthenticationToken newToken = new UsernamePasswordAuthenticationToken("taxonomist", "zuaisd");\r
+        authentication = authenticationManager.authenticate(newToken);\r
+\r
+        // (2) authenticate as under privileged user - not an admin !!!\r
+        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
+        context.setAuthentication(authentication);\r
+\r
+        // check test preconditions user name and authorities\r
+        Assert.assertEquals("descriptionEditor", context.getAuthentication().getName());\r
+        Collection<GrantedAuthority> authorities = context.getAuthentication().getAuthorities();\r
+        for(GrantedAuthority authority: authorities){\r
+            // role prefix 'ROLE_' is defined in org.springframework.security.access.vote.RoleVoter !!!\r
+            Assert.assertNotSame("user must not have authority 'ROLE_ADMIN'", "ROLE_ADMIN", authority.getAuthority());\r
+        }\r
+        // finally perform the test :\r
+        try{\r
+            userService.changePasswordForUser("partEditor", "poiweorijo");\r
+            commitAndStartNewTransaction(null);\r
+        } catch (AccessDeniedException e){\r
+            logger.debug("Expected failure of evaluation.", e);\r
+            exception = e;\r
+        } catch (RuntimeException e){\r
+            exception = findThrowableOfTypeIn(EvaluationFailedException.class, e);\r
+            logger.debug("Expected failure of evaluation.", exception);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNotNull("must fail here!", exception);\r
+    }\r
+\r
+    @Test\r
+    public void testUpdateUser(){\r
+\r
+        authentication = authenticationManager.authenticate(tokenForAdmin);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+        String username = "standardUser";\r
+        String password = "pw";\r
+        User user = User.NewInstance(username, password);\r
+\r
+        userService.createUser(user);\r
+        user.setEmailAddress("test@bgbm.org");\r
+\r
+        userService.updateUser(user);\r
+        userService.update(user);\r
+        userService.saveOrUpdate(user);\r
+        commitAndStartNewTransaction(null);\r
+\r
+    }\r
+\r
+    @Test\r
+    public final void testSaveOrUpdateTaxon() {\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+\r
+        // 1) test with admin account - should succeed\r
+        authentication = authenticationManager.authenticate(tokenForAdmin);\r
+        context.setAuthentication(authentication);\r
+\r
+        TaxonBase<?> taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        taxon.setDoubtful(true);\r
+        RuntimeException securityException= null;\r
+        try{\r
+            taxonService.saveOrUpdate(taxon);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException  = findSecurityRuntimeException(e);\r
+            logger.error("Unexpected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        // reload taxon\r
+        taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        Assert.assertTrue("The change must be persited", taxon.isDoubtful());\r
+\r
+        // 2) test with taxonEditor account - should succeed\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+        context.setAuthentication(authentication);\r
+\r
+        taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        taxon.setDoubtful(false);\r
+        securityException= null;\r
+        try{\r
+            taxonService.saveOrUpdate(taxon);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException  = findSecurityRuntimeException(e);\r
+            logger.debug("Unexpected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        // reload taxon\r
+        taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        Assert.assertFalse("The change must be persited", taxon.isDoubtful());\r
+\r
+        // 3) test with tokenForDescriptionEditor account - should fail\r
+//        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+//        context.setAuthentication(authentication);\r
+//        taxon = taxonService.load(uuid);\r
+//\r
+//        taxon.setDoubtful(true);\r
+//        taxonService.saveOrUpdate(taxon);\r
+//        commitAndStartNewTransaction(null);\r
+\r
+    }\r
+\r
+    @Test\r
+    @Ignore //FIXME: adding taxa to a description must be protected at the side of the Description itself!!\r
+            //        => protecting method TaxonDescription.setTaxon() ?\r
+    public void testAddDescriptionToTaxon(){\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
+        context.setAuthentication(authentication);\r
+\r
+        RuntimeException securityException = null;\r
+\r
+        Taxon taxon = (Taxon)taxonService.load(ACHERONTIA_LACHESIS_UUID);\r
+\r
+        TaxonDescription description = TaxonDescription.NewInstance(taxon);\r
+        description.setTitleCache("test");\r
+        try {\r
+            descriptionService.saveOrUpdate(description);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Expected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        /*\r
+         * Expectation:\r
+         * The user should not be granted to add the Description to a taxon\r
+         */\r
+        Assert.assertNotNull("evaluation should fail since the user is not permitted to edit Taxa", securityException);\r
+        taxon = (Taxon)taxonService.load(ACHERONTIA_LACHESIS_UUID);\r
+        assertTrue(taxon.getDescriptions().contains(description));\r
+    }\r
+\r
+    @Test\r
+    public void testCreateDescriptionWithElement(){\r
+        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        Taxon taxon = (Taxon)taxonService.load(UUID_ACHERONTINII);\r
+        Assert.assertTrue("taxon must not yet have descriptions", taxon.getDescriptions().size() == 0);\r
+\r
+        TaxonDescription description = null;\r
+\r
+        // 1) test for failure - description element but no feature\r
+        description = TaxonDescription.NewInstance(taxon);\r
+        DescriptionElementBase textdataNoFeature = TextData.NewInstance();\r
+        description.addElement(textdataNoFeature);\r
+\r
+        RuntimeException securityException = null;\r
+        assertTrue(permissionEvaluator.hasPermission(authentication, description, "UPDATE"));\r
+        try{\r
+            descriptionService.saveOrUpdate(description);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Expected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        Assert.assertNotNull("evaluation should fail", securityException);\r
+        taxon = (Taxon)taxonService.load(UUID_ACHERONTINII);\r
+        Set<TaxonDescription> descriptions = taxon.getDescriptions();\r
+        assertTrue("taxon must not have any description", descriptions.size() == 0);\r
+\r
+        // 2) test for failure  - description element but not granted feature\r
+        description = TaxonDescription.NewInstance(taxon);\r
+        DescriptionElementBase descriptionText = TextData.NewInstance(Feature.DESCRIPTION());\r
+        description.addElement(descriptionText);\r
+\r
+        securityException = null;\r
+        assertTrue(permissionEvaluator.hasPermission(authentication, description, "UPDATE"));\r
+        try{\r
+            descriptionService.saveOrUpdate(description);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Expected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        Assert.assertNotNull("evaluation should fail", securityException);\r
+        taxon = (Taxon)taxonService.load(UUID_ACHERONTINII);\r
+        descriptions = taxon.getDescriptions();\r
+        assertTrue("taxon must not have any description", descriptions.size() == 0);\r
+\r
+        // 3) test for failure\r
+        description = TaxonDescription.NewInstance(taxon);\r
+        DescriptionElementBase ecologyText = TextData.NewInstance(Feature.ECOLOGY());\r
+        description.addElement(ecologyText);\r
+\r
+        securityException = null;\r
+        assertTrue(permissionEvaluator.hasPermission(authentication, description, "UPDATE"));\r
+        try{\r
+            descriptionService.saveOrUpdate(description);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Unexpected failure of evaluation.", e);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        taxon = (Taxon)taxonService.load(UUID_ACHERONTINII);\r
+        descriptions = taxon.getDescriptions();\r
+        assertTrue("taxon must now have one description", descriptions.size() == 1);\r
+        assertTrue("description should have one description element", descriptions.iterator().next().getElements().size() == 1);\r
+\r
+    }\r
+\r
+    @Test\r
+    public void testSaveSynonym(){\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+\r
+        // 1) test for success\r
+        authentication = authenticationManager.authenticate(tokenForTaxonomist);\r
+        context.setAuthentication(authentication);\r
+\r
+        RuntimeException securityException = null;\r
+        Synonym syn = Synonym.NewInstance(BotanicalName.NewInstance(Rank.SPECIES()), null);\r
+        UUID synUuid = UUID.randomUUID();\r
+        syn.setUuid(synUuid);\r
+        try{\r
+            taxonService.saveOrUpdate(syn);\r
+            logger.debug("will commit ...");\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Unexpected failure of evaluation.", e);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        Assert.assertNotNull("The new Synonym must be persited", taxonService.find(synUuid));\r
+\r
+        // 2) test for denial\r
+        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
+        context.setAuthentication(authentication);\r
+        securityException = null;\r
+        syn = Synonym.NewInstance(BotanicalName.NewInstance(Rank.SPECIES()), null);\r
+        synUuid = syn.getUuid();\r
+        try{\r
+            taxonService.saveOrUpdate(syn);\r
+            logger.debug("will commit ...");\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Expected failure of evaluation: " + securityException.getClass());\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        Assert.assertNotNull("evaluation must fail since the user is not permitted", securityException);\r
+        Assert.assertNull("The Synonym must not be persited", taxonService.find(synUuid));\r
+    }\r
+\r
+    @Test\r
+<<<<<<< HEAD
+    @Ignore //FIXME test must not fail !!!!!\r
+=======
+>>>>>>> methodSecurityExpressions
+    public void testEditPartOfClassification(){\r
+        /*\r
+         * the user 'partEditor' has the following authorities:\r
+         *\r
+         *  - TAXONNODE.CREATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}\r
+         *  - TAXONNODE.UPDATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}\r
+         *\r
+         *  that is 'partEditor' is granted to edit the subtree of\r
+         *  which ACHERONTIA_NODE_UUID [20c8f083-5870-4cbd-bf56-c5b2b98ab6a7] is the root node.\r
+         */\r
+\r
+        authentication = authenticationManager.authenticate(tokenForPartEditor);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        // test for success\r
+        RuntimeException securityException = null;\r
+        TaxonNode acherontia_node = taxonNodeService.load(ACHERONTIA_NODE_UUID);\r
+        long numOfChildNodes = acherontia_node.getChildNodes().size();\r
+        TaxonNode childNode = acherontia_node.addChildTaxon(Taxon.NewInstance(BotanicalName.NewInstance(Rank.SPECIES()), null), null, null, null);\r
+\r
+        try{\r
+            taxonNodeService.saveOrUpdate(acherontia_node);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Unexpected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        acherontia_node = taxonNodeService.load(ACHERONTIA_NODE_UUID);\r
+        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        Assert.assertEquals("the acherontia_node must now have one more child node ", numOfChildNodes + 1 , acherontia_node.getChildNodes().size());\r
+\r
+        // test for denial\r
+        securityException = null;\r
+        TaxonNode acherontiini_node = taxonNodeService.load(ACHERONTIINI_NODE_UUID);\r
+        numOfChildNodes = acherontiini_node.getCountChildren();\r
+        acherontiini_node.addChildTaxon(Taxon.NewInstance(BotanicalName.NewInstance(Rank.GENUS()), null), null, null, null);\r
+\r
+        try{\r
+            logger.debug("==============================");\r
+            taxonNodeService.saveOrUpdate(acherontiini_node);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.debug("Expected failure of evaluation.", securityException);\r
+        } finally {\r
+            // needed in case saveOrUpdate was interrupted by the RuntimeException\r
+            // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
+            endTransaction();\r
+            startNewTransaction();\r
+        }\r
+\r
+        acherontiini_node = taxonNodeService.load(ACHERONTIINI_NODE_UUID);\r
+        Assert.assertNotNull("evaluation must fail since the user is not permitted", securityException);\r
+        Assert.assertEquals("the number of child nodes must be unchanged ", numOfChildNodes , acherontiini_node.getChildNodes().size());\r
+\r
+    }\r
+\r
+    public static void main(String[] args){\r
+        Md5PasswordEncoder encoder =new Md5PasswordEncoder();\r
+\r
+        ReflectionSaltSource saltSource = new ReflectionSaltSource();\r
+        saltSource.setUserPropertyToUse("getUsername");\r
+        User user = User.NewInstance("taxonomist", "test4");\r
+        System.err.println(encoder.encodePassword("test4", saltSource.getSalt(user)));\r
+    }\r
+\r
+\r
+\r
+\r
+}\r
index 868003daad6c4e19dd80e9179039eb0fe631c8d6..a4d1b33308490f32f5a77bc5f23c125216191e08 100644 (file)
@@ -80,7 +80,7 @@ public class SecurityWithTransaction extends CdmTransactionalIntegrationTestWith
 \r
     @Before\r
     public void setUp(){\r
-        token = new UsernamePasswordAuthenticationToken("ben", "sPePhAz6");\r
+        token = new UsernamePasswordAuthenticationToken("admin", "sPePhAz6");\r
     }\r
 \r
     @Test\r
index 1dc4ed6c3cc7591b8264f08dc23695668f8a91c2..a079c6e60121e4e17118ac8b5b87e11d105ef0b3 100644 (file)
@@ -42,6 +42,7 @@ import eu.etaxonomy.cdm.api.service.pager.Pager;
 import eu.etaxonomy.cdm.api.service.search.ICdmMassIndexer;\r
 import eu.etaxonomy.cdm.api.service.search.SearchResult;\r
 import eu.etaxonomy.cdm.common.monitor.DefaultProgressMonitor;\r
+import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;\r
 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;\r
 import eu.etaxonomy.cdm.model.common.Language;\r
 import eu.etaxonomy.cdm.model.description.CategoricalData;\r
@@ -143,7 +144,7 @@ public class TaxonServiceSearchTest extends CdmTransactionalIntegrationTest {
                     nameCache = ((NonViralName<?>) list.get(i)).getNameCache();\r
                 } else if (list.get(i) instanceof TaxonBase) {\r
                     TaxonNameBase taxonNameBase = ((TaxonBase) list.get(i)).getName();\r
-                    nameCache = ((NonViralName) taxonNameBase).getNameCache();\r
+                    nameCache = HibernateProxyHelper.deproxy(taxonNameBase, NonViralName.class).getNameCache();\r
                 } else {\r
                 }\r
                 logger.debug(list.get(i).getClass() + "(" + i + ")" + ": Name Cache = " + nameCache + ", Title Cache = "\r
index 1f755ca4e0f01a31198d5e47a3f58f3c613a7d30..c06b71c5a4ed181b3b670109ef969f6fcb974752 100644 (file)
@@ -59,7 +59,7 @@ import eu.etaxonomy.cdm.model.common.User;
 import eu.etaxonomy.cdm.model.name.BotanicalName;\r
 import eu.etaxonomy.cdm.model.name.Rank;\r
 import eu.etaxonomy.cdm.model.taxon.Taxon;\r
-import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermission;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.Operation;\r
 import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator;\r
 import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
 import eu.etaxonomy.cdm.test.integration.CdmIntegrationTest;\r
@@ -299,10 +299,10 @@ public class UserServiceImplTest extends CdmIntegrationTest {
        @DataSet\r
        public void testHasPermission(){\r
                Taxon taxon = Taxon.NewInstance(BotanicalName.NewInstance(Rank.GENUS()),null);\r
-               boolean hasPermission = taxonService.hasPermission(authentication, taxon, CdmPermission.UPDATE);\r
+               boolean hasPermission = taxonService.hasPermission(authentication, taxon, Operation.UPDATE);\r
                assertFalse(hasPermission);\r
                User testUser = User.NewInstance("username123", "1234");\r
-               hasPermission = userService.hasPermission(authentication, testUser, CdmPermission.UPDATE);\r
+               hasPermission = userService.hasPermission(authentication, testUser, Operation.UPDATE);\r
                assertTrue(hasPermission);\r
        }\r
 \r
index ecfea548136e2cef4e59f5fbc7eeb6fef3c8a162..85948da7f93467c50b435bb3e5f8a920ea8db36e 100644 (file)
@@ -11,7 +11,7 @@
   <PERMISSIONGROUP_GRANTEDAUTHORITYIMPL PERMISSIONGROUP_ID="2" GRANTEDAUTHORITIES_ID="4"/>\r
   <PERMISSIONGROUP_GRANTEDAUTHORITYIMPL PERMISSIONGROUP_ID="3" GRANTEDAUTHORITIES_ID="8"/>\r
   <PERMISSIONGROUP_GRANTEDAUTHORITYIMPL PERMISSIONGROUP_ID="3" GRANTEDAUTHORITIES_ID="9"/>\r
-  <!-- <PERMISSIONGROUP_GRANTEDAUTHORITYIMPL PERMISSIONGROUP_ID="3" GRANTEDAUTHORITIES_ID="10"/> -->\r
+  <PERMISSIONGROUP_GRANTEDAUTHORITYIMPL PERMISSIONGROUP_ID="3" GRANTEDAUTHORITIES_ID="10"/>\r
 \r
 \r
   <GRANTEDAUTHORITYIMPL ID="1"  UUID="441a3c40-0c84-11de-8c30-0800200c9a66" CREATED="2009-02-03 17:52:26.0" AUTHORITY="TAXONBASE.READ"/>\r
   <GRANTEDAUTHORITYIMPL ID="4"  UUID="e5354c0e-657b-4b4d-bb2f-791612199711" CREATED="2009-02-03 17:52:26.0" AUTHORITY="TAXONBASE.DELETE"/>\r
   <GRANTEDAUTHORITYIMPL ID="10" UUID="2fbcbdd4-97f0-4561-b635-1e479dd00375" CREATED="2009-02-03 17:52:26.0" AUTHORITY="TAXONBASE.ADMIN"/>\r
   <GRANTEDAUTHORITYIMPL ID="5"  UUID="9eabd2c6-0590-4a1e-95f5-99cc58b63aa7" CREATED="2009-02-03 17:52:26.0" AUTHORITY="ALL.ADMIN"/>\r
+  <GRANTEDAUTHORITYIMPL ID="14" UUID="56eac992-67ba-40be-896c-4e992ca2afc0" CREATED="2009-02-03 17:52:26.0" AUTHORITY="ROLE_ADMIN"/><!-- role prefix 'ROLE_' defined in RoleVoter !!! -->\r
   <GRANTEDAUTHORITYIMPL ID="7"  UUID="2fc19d6c-a227-41d8-94e4-7c31accebc26" CREATED="2009-02-03 17:52:26.0" AUTHORITY="DESCRIPTIONBASE.UPDATE"/>\r
   <GRANTEDAUTHORITYIMPL ID="13" UUID="2883374b-9556-4f3b-9b17-08fdf3d4bba6" CREATED="2009-02-03 17:52:26.0" AUTHORITY="DESCRIPTIONBASE.CREATE"/>\r
-  <GRANTEDAUTHORITYIMPL ID="11" UUID="fc8088cf-cf96-42b6-9aa5-7c1ec5ccf145" CREATED="2009-02-03 17:52:26.0" AUTHORITY="DESCRIPTIONBASE.Ecology.UPDATE"/>\r
-  <GRANTEDAUTHORITYIMPL ID="12" UUID="8d131171-d281-4911-a960-16992de384c7" CREATED="2009-02-03 17:52:26.0" AUTHORITY="DESCRIPTIONBASE.Ecology.CREATE"/>\r
+  <GRANTEDAUTHORITYIMPL ID="11" UUID="fc8088cf-cf96-42b6-9aa5-7c1ec5ccf145" CREATED="2009-02-03 17:52:26.0" AUTHORITY="DESCRIPTIONELEMENTBASE(Ecology).UPDATE"/>\r
+  <GRANTEDAUTHORITYIMPL ID="12" UUID="8d131171-d281-4911-a960-16992de384c7" CREATED="2009-02-03 17:52:26.0" AUTHORITY="DESCRIPTIONELEMENTBASE(Ecology).CREATE"/>\r
   <GRANTEDAUTHORITYIMPL ID="8"  UUID="45b40e93-88b0-40eb-92a4-ffdd8f1d7bc3" CREATED="2009-02-03 17:52:26.0" AUTHORITY="TAXONNODE.UPDATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}"/>\r
   <GRANTEDAUTHORITYIMPL ID="9"  UUID="8696ef0f-e98c-4842-b9d2-456b82584c25" CREATED="2009-02-03 17:52:26.0" AUTHORITY="TAXONNODE.CREATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}"/>\r
 \r
-  <USERACCOUNT USERNAME="ben" ID="1" CREATED="2009-06-18 13:47:59.0" UUID="e4acf200-63b6-11dd-ad8b-0800200c9a66" PASSWORD="e141bbb8bb7f4579aa3156fdbe0d1226" ENABLED="true" ACCOUNTNONEXPIRED="true" CREDENTIALSNONEXPIRED="true" ACCOUNTNONLOCKED="true"/>\r
+  <USERACCOUNT USERNAME="admin" ID="1" CREATED="2009-06-18 13:47:59.0" UUID="e4acf200-63b6-11dd-ad8b-0800200c9a66" PASSWORD="656a8bc965ac42adc800013e1a8612b9" ENABLED="true" ACCOUNTNONEXPIRED="true" CREDENTIALSNONEXPIRED="true" ACCOUNTNONLOCKED="true"/>\r
   <USERACCOUNT USERNAME="descriptionEditor" ID="2" CREATED="2009-06-18 13:47:59.0" UUID="49efface-4c2b-40d2-84bb-e3915c0e77b1" PASSWORD="d211b476c3f3795f801c959fb9671b0c" ENABLED="true" ACCOUNTNONEXPIRED="true" CREDENTIALSNONEXPIRED="true" ACCOUNTNONLOCKED="true"/>\r
   <USERACCOUNT USERNAME="taxonEditor" ID="3" CREATED="2009-06-18 13:47:59.0" UUID="56eac992-67ba-40be-896c-4e992ca2afc0" PASSWORD="c949cc3b8939be4643577db7e9830e5b" ENABLED="true" ACCOUNTNONEXPIRED="true" CREDENTIALSNONEXPIRED="true" ACCOUNTNONLOCKED="true"/>\r
   <USERACCOUNT USERNAME="taxonomist" ID="4" CREATED="2009-06-18 13:47:59.0" UUID="0595de98-5e6e-4194-ab3b-3cb158716d4c" PASSWORD="3b49d0610ddde62faea639c35fb70a9c" ENABLED="true" ACCOUNTNONEXPIRED="true" CREDENTIALSNONEXPIRED="true" ACCOUNTNONLOCKED="true"/>\r
   <USERACCOUNT USERNAME="partEditor" ID="5" CREATED="2009-06-18 13:47:59.0" UUID="38a251bd-0ba4-426f-8fcb-5c09560749a7" PASSWORD="41af8a6dac9f86b1081aa5840df75a53" ENABLED="true" ACCOUNTNONEXPIRED="true" CREDENTIALSNONEXPIRED="true" ACCOUNTNONLOCKED="true"/>\r
   <USERACCOUNT_GRANTEDAUTHORITYIMPL USERACCOUNT_ID="1" GRANTEDAUTHORITIES_ID="5"/>\r
+  <USERACCOUNT_GRANTEDAUTHORITYIMPL USERACCOUNT_ID="1" GRANTEDAUTHORITIES_ID="14"/>\r
   <USERACCOUNT_GRANTEDAUTHORITYIMPL USERACCOUNT_ID="2" GRANTEDAUTHORITIES_ID="7"/>\r
   <USERACCOUNT_GRANTEDAUTHORITYIMPL USERACCOUNT_ID="2" GRANTEDAUTHORITIES_ID="11"/>\r
   <USERACCOUNT_GRANTEDAUTHORITYIMPL USERACCOUNT_ID="2" GRANTEDAUTHORITIES_ID="13"/>\r
@@ -90,7 +92,7 @@
   <CLASSIFICATION ID="1" CREATED="2009-06-18 13:47:59.0" UUID="aeee7448-5298-4991-b724-8d5b75a0a7a9" PROTECTEDTITLECACHE="false" TITLECACHE="TestBaum" NAME_ID="1"/>\r
   <CLASSIFICATION_AUD ID="1" REV="1025" REVTYPE="0" CREATED="2009-06-18 13:47:59.0" UUID="aeee7448-5298-4991-b724-8d5b75a0a7a9" PROTECTEDTITLECACHE="false" TITLECACHE="TestBaum" NAME_ID="1"/>\r
 \r
- <DESCRIPTIONBASE DTYPE="TaxonDescription" ID="3" CREATED="2009-06-25 18:26:26.0" UUID="eb17b80a-9be6-4642-a6a8-b19a318925e6" PROTECTEDTITLECACHE="true" TITLECACHE="desc3" IMAGEGALLERY="false" TAXON_ID="37"/>\r
 <DESCRIPTIONBASE DTYPE="TaxonDescription" ID="3" CREATED="2009-06-25 18:26:26.0" UUID="eb17b80a-9be6-4642-a6a8-b19a318925e6" PROTECTEDTITLECACHE="true" TITLECACHE="desc3" IMAGEGALLERY="false" TAXON_ID="37"/>\r
   <DESCRIPTIONELEMENTBASE DTYPE="TextData" ID="1" INDESCRIPTION_ID="3" CREATED="2008-12-10 09:56:07.0" UUID="31a0160a-51b2-4565-85cf-2be58cb561d6" UPDATED="2008-12-10 09:56:07.253" FEATURE_ID="940"/>\r
   <DESCRIPTIONELEMENTBASE_LANGUAGESTRING DESCRIPTIONELEMENTBASE_ID="1" MULTILANGUAGETEXT_ID="2" MULTILANGUAGETEXT_MAPKEY_ID="406"/>\r
   <LANGUAGESTRING ID="2" CREATED="2008-12-10 09:56:07.0" UUID="2a5ceebb-4830-4524-b330-78461bf8cb6b" UPDATED="2008-12-10 09:56:07.253" LANGUAGE_ID="352" TEXT="Lorem ipsum dolor sit amet, consectetur adipiscing elit."/>\r
index a90cfafcaa86e5f5428c6c6c2da793abd713468d..53ba15909b2725ab1f1ea079a1dabc2d187d9527 100644 (file)
         so we only need to add additional beans here:\r
      -->\r
 \r
+    <!--\r
+        we need to repeat the security configuration here only for test since it is disabled in\r
+        cdmlib-services/src/main/resources !!!\r
+     -->\r
     <security:global-method-security pre-post-annotations="enabled">\r
         <security:expression-handler ref="expressionHandler" />\r
     </security:global-method-security>\r
 \r
-    <bean id="expressionHandler"\r
-        class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">\r
+    <!--\r
+        To use "hasPermission()" in the Spring EL method annotations like @PreAuthorize we explicitly configure the permissionEvaluator\r
+        the cdmPermissionEvaluator is already defined in the persistence security context\r
+    -->\r
+    <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">\r
         <property name="permissionEvaluator" ref="cdmPermissionEvaluator" />\r
     </bean>\r
 \r
 \r
-    <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator" id="cdmPermissionEvaluator" />\r
     <bean id="userService" class="eu.etaxonomy.cdm.api.service.UserService">\r
     </bean>\r
 \r
-\r
     <bean id="groupServiceImpl" class="eu.etaxonomy.cdm.api.service.GroupServiceImpl">\r
     </bean>\r
+\r
 </beans>\r
index a0edf143850bd7d7bb08685638aa327691de96ef..fd787f0f46f8903967b435de33a12fd6895cf8e1 100644 (file)
@@ -37,7 +37,7 @@ log4j.logger.eu.etaxonomy.cdm.model.common.init = warn
 log4j.logger.eu.etaxonomy.cdm.test.function = info
 log4j.logger.eu.etaxonomy.cdm.test.integration = info
 log4j.logger.eu.etaxonomy.cdm.api.application = warn
-log4j.logger.eu.etaxonomy.cdm.api.service = info
+log4j.logger.eu.etaxonomy.cdm.api.service = debug
 #log4j.logger.eu.etaxonomy.cdm.database.VocabularyStoreImpl = warn
 #
 log4j.logger.eu.etaxonomy.cdm.database.init = warn
@@ -47,6 +47,7 @@ log4j.logger.eu.etaxonomy.cdm.persistence.dao.hibernate.common = warn
 
 
 
+
   ### *** SPRING ************ ###
 log4j.logger.org.springframework.transaction = warn
 log4j.logger.org.hibernate.engine.LoadContexts = warn
@@ -118,7 +119,12 @@ log4j.logger.org.hibernate.search.impl.SearchFactoryImpl=error
 #log4j.logger.org.dbunit.operation.DeleteAllOperation=debug
 
 #### log spring security #####
-#log4j.logger.eu.etaxonomy.cdm.permission.CdmPermissionEvaluator=debug
+log4j.logger.eu.etaxonomy.cdm.persistence.hibernate.permission=debug
+log4j.logger.eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority=warn
+log4j.logger.eu.etaxonomy.cdm.persistence.hibernate.CdmSecurityHibernateInterceptor=debug
+log4j.logger.org.springframework.security.access.intercept=debug
+log4j.logger.org.springframework.security.access.vote=debug
+#log4j.logger.eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest=debug
 
 #### Lucene Fulltext index and cdmlib search facility####
 #log4j.logger.org.apache.lucene=debug