Extend step-by-step deletion test
[cdmlib.git] / cdmlib-services / src / test / java / eu / etaxonomy / cdm / api / service / SecurityTest.java
index ff2019c980b106dedde09122a8e46d4dd338ff27..1c56ab3bbd056f8c846d5f59637b450e0e5ff9f6 100644 (file)
@@ -9,9 +9,14 @@
 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
+import java.io.FileNotFoundException;\r
 import java.util.Collection;\r
+import java.util.EnumSet;\r
+import java.util.HashSet;\r
+import java.util.List;\r
 import java.util.Set;\r
 import java.util.UUID;\r
 \r
@@ -19,75 +24,66 @@ import javax.sql.DataSource;
 \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.junit.runners.Parameterized.Parameters;\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 com.mchange.util.AssertException;\r
+\r
+import sun.security.provider.PolicyParser.ParsingException;\r
+import eu.etaxonomy.cdm.database.PermissionDeniedException;\r
+import eu.etaxonomy.cdm.model.common.GrantedAuthorityImpl;\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.name.TaxonNameBase;\r
+import eu.etaxonomy.cdm.model.name.ZoologicalName;\r
+import eu.etaxonomy.cdm.model.reference.Reference;\r
+import eu.etaxonomy.cdm.model.reference.ReferenceFactory;\r
+import eu.etaxonomy.cdm.model.taxon.Classification;\r
 import eu.etaxonomy.cdm.model.taxon.Synonym;\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.hibernate.permission.CRUD;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionClass;\r
 import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator;\r
-import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTestWithSecurity;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.Operation;\r
+import eu.etaxonomy.cdm.persistence.query.MatchMode;\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 UUID_LACTUCA = UUID.fromString("b2b007a4-9c8c-43a1-8da4-20ed85464cf2");\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
+public class SecurityTest extends AbstractSecurityTestBase{\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
+    private ITaxonService taxonService;\r
 \r
     @SpringBeanByType\r
-    private ITaxonService taxonService;\r
+    private INameService nameService;\r
+\r
+    @SpringBeanByType\r
+    private IReferenceService referenceService;\r
 \r
     @SpringBeanByType\r
     private ITaxonNodeService taxonNodeService;\r
@@ -98,11 +94,8 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
     @SpringBeanByType\r
     private IUserService userService;\r
 \r
-\r
-    @TestDataSource\r
-    protected DataSource dataSource;\r
-\r
-    private Authentication authentication;\r
+    @SpringBeanByType\r
+    private IClassificationService classificationService;\r
 \r
     @SpringBeanByType\r
     private AuthenticationManager authenticationManager;\r
@@ -116,94 +109,137 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
     @SpringBean("cdmPermissionEvaluator")\r
     private CdmPermissionEvaluator permissionEvaluator;\r
 \r
-    private UsernamePasswordAuthenticationToken tokenForAdmin;\r
+    @TestDataSource\r
+    protected DataSource dataSource;\r
 \r
-    private UsernamePasswordAuthenticationToken tokenForTaxonEditor;\r
+    private Authentication authentication;\r
 \r
-    private UsernamePasswordAuthenticationToken tokenForDescriptionEditor;\r
 \r
-    private UsernamePasswordAuthenticationToken tokenForPartEditor;\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
-    private UsernamePasswordAuthenticationToken tokenForTaxonomist;\r
+        String password = PASSWORD_ADMIN;\r
+        User user = User.NewInstance("userManager", "");\r
 \r
+        Object salt = this.saltSource.getSalt(user);\r
+        String passwordEncrypted = passwordEncoder.encodePassword(password, salt);\r
+        logger.info("encrypted password: " + passwordEncrypted );\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
+    @Test\r
+    @DataSet\r
+    public void testHasPermission(){\r
 \r
-        /* User 'taxonEditor':\r
-            - TAXONBASE.CREATE\r
-            - TAXONBASE.UPDATE\r
-        */\r
-        tokenForTaxonEditor = new UsernamePasswordAuthenticationToken("taxonEditor", PASSWORD_TAXON_EDITOR);\r
+        Taxon taxon = Taxon.NewInstance(BotanicalName.NewInstance(Rank.GENUS()),null);\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
+        authentication = authenticationManager.authenticate(tokenForTaxonomist);\r
+        boolean hasPermission = permissionEvaluator.hasPermission(authentication, taxon, Operation.UPDATE);\r
+        assertTrue(hasPermission);\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
+        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
+        hasPermission = permissionEvaluator.hasPermission(authentication, taxon, Operation.UPDATE);\r
+        assertFalse(hasPermission);\r
+    }\r
 \r
-        /* User 'taxonomist':\r
-            - TAXONBASE.READ\r
-            - TAXONBASE.CREATE\r
-            - TAXONBASE.DELETE\r
-            - TAXONBASE.UPDATE\r
-         */\r
-        tokenForTaxonomist = new UsernamePasswordAuthenticationToken("taxonomist", "test4");\r
+    @Test\r
+    @DataSet\r
+    public void testListByUsernameAllow(){\r
+\r
+        authentication = authenticationManager.authenticate(tokenForTaxonomist);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        List<User> userList = userService.listByUsername("Editor", MatchMode.ANYWHERE, null, null, 0, null, null);\r
+        Assert.assertTrue("The user list must have elements", userList.size() > 0 );\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
+    @DataSet\r
+    public void testUserService_CreateDeny(){\r
 \r
-        String password = PASSWORD_ADMIN;\r
-        User user = User.NewInstance("admin", "");\r
+        authentication = authenticationManager.authenticate(tokenForTaxonomist);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        RuntimeException exception = null;\r
+        try {\r
+            userService.createUser(User.NewInstance("new guy", "alkjdsfalkj"));\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(PermissionDeniedException.class, e);\r
+            logger.debug("Expected 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.assertNotNull("Must fail here!", exception);\r
 \r
-        Object salt = this.saltSource.getSalt(user);\r
-        String passwordEncrypted = passwordEncoder.encodePassword(password, salt);\r
-        logger.info("encrypted password: " + passwordEncrypted );\r
     }\r
 \r
+    @Test\r
+    @DataSet\r
+    public void testUserService_CreateAllow(){\r
+\r
+        authentication = authenticationManager.authenticate(tokenForUserManager);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        RuntimeException exception = null;\r
+        try {\r
+            userService.createUser(User.NewInstance("new guy", "alkjdsfalkj"));\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(PermissionDeniedException.class, e);\r
+            logger.error("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
+    }\r
+\r
+\r
+    @Test\r
+    @DataSet\r
+    @Ignore // FIXME http://dev.e-taxonomy.eu/trac/ticket/3098\r
+    public void testHasPermissions(){\r
+\r
+        Taxon taxon = Taxon.NewInstance(BotanicalName.NewInstance(Rank.GENUS()),null);\r
+\r
+        authentication = authenticationManager.authenticate(tokenForTaxonomist);\r
+        boolean hasPermission = permissionEvaluator.hasPermission(authentication, taxon, Operation.ALL);\r
+        assertTrue(hasPermission);\r
+    }\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
+        expectedTaxon.getName().setTitleCache("Newby admin", true);\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
@@ -211,9 +247,193 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         context = SecurityContextHolder.getContext();\r
         context.setAuthentication(authentication);\r
         expectedTaxon = Taxon.NewInstance(BotanicalName.NewInstance(Rank.GENUS()), null);\r
-        taxonService.saveOrUpdate(actualTaxon);\r
+        expectedTaxon.getName().setTitleCache("Newby taxonEditor", true);\r
+        uuid = taxonService.saveOrUpdate(expectedTaxon);\r
+        commitAndStartNewTransaction(null);\r
+        actualTaxon = taxonService.load(uuid);\r
+        assertEquals(expectedTaxon, actualTaxon);\r
+\r
+    }\r
+\r
+    @Test\r
+    public final void testSaveNameAllow() {\r
+\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        ZoologicalName newName = ZoologicalName.NewInstance(Rank.SPECIES());\r
+        newName.setTitleCache("Newby taxonEditor", true);\r
+        UUID uuid = nameService.saveOrUpdate(newName);\r
         commitAndStartNewTransaction(null);\r
+        TaxonNameBase savedName = nameService.load(uuid);\r
+        assertEquals(newName, savedName);\r
+    }\r
+\r
+\r
+    @Test\r
+    public final void testReuseNameAllow() {\r
+\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        TaxonBase taxon = taxonService.find(UUID_ACHERONTIA_STYX);\r
+        TaxonNameBase n_acherontia_thetis = taxon.getName();\r
+\r
+        Taxon newTaxon = Taxon.NewInstance(n_acherontia_thetis, ReferenceFactory.newGeneric());\r
+        Exception exception = null;\r
+        try {\r
+            UUID uuid = taxonService.save(newTaxon);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (AccessDeniedException e){\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            exception = e;\r
+        } catch (RuntimeException e){\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            exception = findThrowableOfTypeIn(PermissionDeniedException.class, 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("must not fail here!", exception);\r
+    }\r
+\r
+    @Test\r
+    public final void testMakeTaxonNodeASynonymOfAnotherTaxonNodeAllow_1() {\r
+\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
 \r
+        Reference book = referenceService.load(BOOK1_UUID);\r
+\r
+        TaxonNode n_acherontia_styx = taxonNodeService.find(ACHERONTIA_STYX_NODE_UUID);\r
+        TaxonNode n_acherontia_lachersis = taxonNodeService.find(ACHERONTIA_LACHESIS_NODE_UUID);\r
+\r
+        Exception exception = null;\r
+        try {\r
+            taxonNodeService.makeTaxonNodeASynonymOfAnotherTaxonNode(n_acherontia_styx, n_acherontia_lachersis, SynonymRelationshipType.HETEROTYPIC_SYNONYM_OF(), book , "33");\r
+            commitAndStartNewTransaction(null);\r
+        } catch (AccessDeniedException e){\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            exception = e;\r
+        } catch (RuntimeException e){\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            exception = findThrowableOfTypeIn(PermissionDeniedException.class, 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("must not fail here!", exception);\r
+    }\r
+\r
+    @Test\r
+    public final void testMakeTaxonNodeASynonymOfAnotherTaxonNodeAllow_2() {\r
+\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        Reference book = referenceService.load(BOOK1_UUID);\r
+\r
+        TaxonNode n_acherontia_styx = taxonNodeService.find(ACHERONTIA_STYX_NODE_UUID);\r
+        TaxonNode n_acherontia_lachersis = taxonNodeService.find(ACHERONTIA_LACHESIS_NODE_UUID);\r
+\r
+        Exception exception = null;\r
+        try {\r
+            taxonNodeService.makeTaxonNodeASynonymOfAnotherTaxonNode(n_acherontia_lachersis, n_acherontia_styx, SynonymRelationshipType.HOMOTYPIC_SYNONYM_OF(), book , "33");\r
+            commitAndStartNewTransaction(null);\r
+        } catch (AccessDeniedException e){\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            exception = e;\r
+        } catch (RuntimeException e){\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            exception = findThrowableOfTypeIn(PermissionDeniedException.class, 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("must not fail here!", exception);\r
+    }\r
+\r
+    @Test\r
+    public final void testUpdateReferenceAllow() throws ParsingException {\r
+\r
+\r
+        authentication = authenticationManager.authenticate(tokenForUserManager);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        // add REFERENCE[UPDATE] to taxonEditor\r
+        User taxonEditor = userService.load(TAXON_EDITOR_UUID);\r
+        Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();\r
+        grantedAuthorities.addAll(taxonEditor.getGrantedAuthorities());\r
+        GrantedAuthorityImpl referenceUpdate_ga = new CdmAuthority(CdmPermissionClass.REFERENCE, null, EnumSet.of(CRUD.UPDATE), null).asNewGrantedAuthority();\r
+        grantedAuthorities.add(referenceUpdate_ga);\r
+        taxonEditor.setGrantedAuthorities(grantedAuthorities);\r
+        userService.saveOrUpdate(taxonEditor);\r
+        commitAndStartNewTransaction(null);\r
+\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+        context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        Reference book = referenceService.load(BOOK1_UUID);\r
+        book.setTitleCache("Mobydick", true);\r
+        Exception exception = null;\r
+        try {\r
+            referenceService.saveOrUpdate(book);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (AccessDeniedException e){\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            exception = e;\r
+        } catch (RuntimeException e){\r
+            logger.error("Unexpected failure of evaluation.", e);\r
+            exception = findThrowableOfTypeIn(PermissionDeniedException.class, 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("must not fail here!", exception);\r
+        book = referenceService.load(BOOK1_UUID);\r
+        Assert.assertEquals("Mobydick", book.getTitleCache());\r
+    }\r
+\r
+    @Test\r
+    public final void testUpateReferenceDeny() {\r
+\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        context.setAuthentication(authentication);\r
+\r
+        TaxonBase taxon = taxonService.find(UUID_ACHERONTIA_STYX);\r
+        taxon.getName().getNomenclaturalReference().setTitleCache("Mobydick", true);\r
+        Exception exception = null;\r
+        try {\r
+            UUID uuid = taxonService.saveOrUpdate(taxon);\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(PermissionDeniedException.class, e);\r
+            logger.debug("Expected 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.assertNotNull("must fail here!", exception);\r
     }\r
 \r
     @Test\r
@@ -253,7 +473,7 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
             logger.error("Unexpected failure of evaluation.", e);\r
             exception = e;\r
         } catch (RuntimeException e){\r
-            exception = findThrowableOfTypeIn(EvaluationFailedException.class, e);\r
+            exception = findThrowableOfTypeIn(PermissionDeniedException.class, e);\r
             logger.error("Unexpected failure of evaluation.", exception);\r
         } finally {\r
             // needed in case saveOrUpdate was interrupted by the RuntimeException\r
@@ -280,7 +500,7 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
 \r
         // check test preconditions user name and authorities\r
         Assert.assertEquals("descriptionEditor", context.getAuthentication().getName());\r
-        Collection<GrantedAuthority> authorities = context.getAuthentication().getAuthorities();\r
+        Collection<? extends 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
@@ -293,8 +513,8 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
             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
+            exception = findThrowableOfTypeIn(PermissionDeniedException.class, e);\r
+            logger.debug("Expected failure of evaluation.", e);\r
         } finally {\r
             // needed in case saveOrUpdate was interrupted by the RuntimeException\r
             // commitAndStartNewTransaction() would raise an UnexpectedRollbackException\r
@@ -336,7 +556,7 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         context.setAuthentication(authentication);\r
         RuntimeException securityException= null;\r
 \r
-        TaxonBase<?> taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        TaxonBase<?> taxon = taxonService.find(UUID_ACHERONTIA_STYX);\r
         Assert.assertFalse(taxon.isDoubtful());\r
         taxon.setDoubtful(true);\r
         try{\r
@@ -353,8 +573,8 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         }\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
+        taxon = taxonService.find(UUID_ACHERONTIA_STYX);\r
+        Assert.assertTrue("The change must be persisted", taxon.isDoubtful());\r
     }\r
 \r
     /**\r
@@ -372,7 +592,7 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
 \r
         context.setAuthentication(authentication);\r
 \r
-        TaxonBase<?>  taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        TaxonBase<?>  taxon = taxonService.find(UUID_ACHERONTIA_STYX);\r
         Assert.assertFalse(taxon.isDoubtful());\r
         taxon.setDoubtful(true);\r
         try{\r
@@ -389,7 +609,7 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         }\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
+        taxon = taxonService.find(UUID_ACHERONTIA_STYX);\r
         Assert.assertTrue("The change must be persited", taxon.isDoubtful());\r
     }\r
 \r
@@ -405,7 +625,7 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
         context.setAuthentication(authentication);\r
 \r
-        TaxonBase<?> taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        TaxonBase<?> taxon = taxonService.find(UUID_ACHERONTIA_STYX);\r
 \r
         Assert.assertFalse(taxon.isDoubtful());\r
         taxon.setDoubtful(true);\r
@@ -424,15 +644,12 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
 \r
         Assert.assertNotNull("evaluation must fail since the user is not permitted", securityException);\r
         // reload taxon\r
-        taxon = taxonService.load(UUID_ACHERONTIA_STYX);\r
+        taxon = taxonService.find(UUID_ACHERONTIA_STYX);\r
         Assert.assertFalse("The change must not be persited", taxon.isDoubtful());\r
     }\r
 \r
-    /**\r
-     * test with admin account - should succeed\r
-     */\r
     @Test\r
-    public final void testTaxonDeleteAllow_1() {\r
+    public final void testTaxonPublishAllow_ROLE_ADMIN() {\r
 \r
         SecurityContext context = SecurityContextHolder.getContext();\r
 \r
@@ -440,9 +657,12 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         context.setAuthentication(authentication);\r
         RuntimeException securityException= null;\r
 \r
-        TaxonBase<?> taxon = taxonService.load(UUID_LACTUCA);\r
+        Taxon taxon = (Taxon) taxonService.find(UUID_ACHERONTIA_STYX);\r
+\r
+        boolean lastIsPublish = taxon.isPublish();\r
+        taxon.setPublish(!lastIsPublish);\r
         try{\r
-            taxonService.delete(taxon);\r
+            taxonService.saveOrUpdate(taxon);\r
             commitAndStartNewTransaction(null);\r
         } catch (RuntimeException e){\r
             securityException  = findSecurityRuntimeException(e);\r
@@ -453,31 +673,31 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
             endTransaction();\r
             startNewTransaction();\r
         }\r
-        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        Assert.assertNull("evaluation must not fail since the user has ROLE_ADMIN, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
         // reload taxon\r
-        taxon = taxonService.load(UUID_LACTUCA);\r
-        Assert.assertNull("The taxon must be deleted", taxon);\r
+        taxon = (Taxon) taxonService.find(UUID_ACHERONTIA_STYX);\r
+        Assert.assertTrue("The change must be persisted", taxon.isPublish() != lastIsPublish);\r
     }\r
 \r
+\r
     /**\r
-     * test with admin account - should succeed\r
+     * test with Taxonomist account which has the ROLE_PUBLISH\r
      */\r
     @Test\r
-    @Ignore\r
-    /*FIXME fails due to org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations)\r
-     *       see ticket #3086\r
-     */\r
-    public final void testTaxonDeleteAllow_2() {\r
+    public final void testTaxonPublishAllow_ROLE_PUBLISH() {\r
 \r
         SecurityContext context = SecurityContextHolder.getContext();\r
 \r
-        authentication = authenticationManager.authenticate(tokenForAdmin);\r
+        authentication = authenticationManager.authenticate(tokenForTaxonomist);\r
         context.setAuthentication(authentication);\r
         RuntimeException securityException= null;\r
 \r
-        TaxonBase<?> taxon = taxonService.load(UUID_ACHERONTINII);\r
+        Taxon taxon = (Taxon) taxonService.find(UUID_ACHERONTIA_STYX);\r
+\r
+        boolean lastIsPublish = taxon.isPublish();\r
+        taxon.setPublish(!lastIsPublish);\r
         try{\r
-            taxonService.delete(taxon);\r
+            taxonService.saveOrUpdate(taxon);\r
             commitAndStartNewTransaction(null);\r
         } catch (RuntimeException e){\r
             securityException  = findSecurityRuntimeException(e);\r
@@ -488,29 +708,30 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
             endTransaction();\r
             startNewTransaction();\r
         }\r
-        Assert.assertNull("evaluation must not fail since the user is permitted, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
+        Assert.assertNull("evaluation must not fail since the user has ROLE_ADMIN, CAUSE :" + (securityException != null ? securityException.getMessage() : ""), securityException);\r
         // reload taxon\r
-        taxon = taxonService.load(UUID_ACHERONTINII);\r
-        Assert.assertNull("The taxon must be deleted", taxon);\r
+        taxon = (Taxon) taxonService.find(UUID_ACHERONTIA_STYX);\r
+        Assert.assertTrue("The change must be persisted", taxon.isPublish() != lastIsPublish);\r
     }\r
 \r
-\r
     /**\r
-     * test with tokenForDescriptionEditor account - should fail\r
+     * test with TaxonEditor account which has not the ROLE_PUBLISH\r
      */\r
     @Test\r
-    public final void testTaxonDeleteDeny() {\r
+    public final void testTaxonPublishDeny() {\r
 \r
         SecurityContext context = SecurityContextHolder.getContext();\r
-        RuntimeException securityException = null;\r
 \r
-        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
+        authentication = authenticationManager.authenticate(tokenForTaxonEditor);\r
         context.setAuthentication(authentication);\r
+        RuntimeException securityException= null;\r
 \r
-        TaxonBase<?> taxon = taxonService.load(UUID_LACTUCA);\r
+        Taxon taxon = (Taxon) taxonService.find(UUID_ACHERONTIA_STYX);\r
 \r
+        boolean lastIsPublish = taxon.isPublish();\r
+        taxon.setPublish(!lastIsPublish);\r
         try {\r
-            taxonService.delete(taxon);\r
+            taxonService.saveOrUpdate(taxon);\r
             commitAndStartNewTransaction(null);\r
         } catch (RuntimeException e){\r
             securityException = findSecurityRuntimeException(e);\r
@@ -524,8 +745,104 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
 \r
         Assert.assertNotNull("evaluation must fail since the user is not permitted", securityException);\r
         // reload taxon\r
+        taxon = (Taxon) taxonService.find(UUID_ACHERONTIA_STYX);\r
+        Assert.assertTrue("The taxon must be unchanged", taxon.isPublish() == lastIsPublish);\r
+    }\r
+\r
+    /**\r
+     * test with admin account - should succeed\r
+     */\r
+    @Test\r
+    public final void testTaxonDeleteAllow_1() {\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+\r
+        authentication = authenticationManager.authenticate(tokenForAdmin);\r
+        context.setAuthentication(authentication);\r
+        RuntimeException securityException= null;\r
+\r
+        TaxonBase<?> taxon = taxonService.load(UUID_LACTUCA);\r
+        taxonService.delete(taxon);\r
+        commitAndStartNewTransaction(null);\r
+       \r
+       \r
+            \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_LACTUCA);\r
+        Assert.assertNull("The taxon must be deleted", taxon);\r
+    }\r
+\r
+    /**\r
+     * test with admin account - should succeed\r
+     */\r
+    @Test\r
+   public final void testTaxonDeleteAllow_2() {\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+\r
+        authentication = authenticationManager.authenticate(tokenForAdmin);\r
+        context.setAuthentication(authentication);\r
+        RuntimeException securityException= null;\r
+\r
+        Taxon taxon = (Taxon)taxonService.load(UUID_ACHERONTINII);\r
+        try{\r
+           // try {\r
+               DeleteResult result = taxonService.deleteTaxon(taxon.getUuid(), null, null);\r
+            /*} catch (DataChangeNoRollbackException e) {\r
+                Assert.fail();\r
+            }*/\r
+            if (!result.isOk()){\r
+               Assert.fail();\r
+            }\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException  = findSecurityRuntimeException(e);\r
+            logger.error("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
+        // reload taxon\r
+\r
+        taxon = (Taxon)taxonService.find(UUID_ACHERONTINII);\r
+        Assert.assertNull("The taxon must be deleted", taxon);\r
+    }\r
+\r
+\r
+    /**\r
+     * test with tokenForDescriptionEditor account - should fail\r
+     */\r
+    @Test\r
+    public final void testTaxonDeleteDeny() {\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        RuntimeException securityException = null;\r
+\r
+        authentication = authenticationManager.authenticate(tokenForDescriptionEditor);\r
+        context.setAuthentication(authentication);\r
+\r
+        Taxon taxon = (Taxon)taxonService.load(UUID_LACTUCA);\r
+        try{\r
+        DeleteResult result = taxonService.deleteTaxon(taxon.getUuid(), null, null);\r
+        Assert.fail();\r
+        }catch(PermissionDeniedException e){\r
+               \r
+        }\r
+       endTransaction();\r
+       startNewTransaction();\r
+       \r
+\r
+        //Assert.assertNotNull("evaluation must fail since the user is not permitted", securityException);\r
+        // reload taxon\r
+        taxon = (Taxon)taxonService.load(UUID_LACTUCA);\r
+        \r
         Assert.assertNotNull("The change must still exist", taxon);\r
+        Assert.assertNotNull("The name must still exist",taxon.getName());\r
     }\r
 \r
 \r
@@ -565,6 +882,90 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         assertTrue(taxon.getDescriptions().contains(description));\r
     }\r
 \r
+    @Test\r
+    public void testMoveDescriptionElement(){\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        authentication = authenticationManager.authenticate(tokenForTaxonomist);\r
+        context.setAuthentication(authentication);\r
+\r
+        RuntimeException securityException = null;\r
+\r
+        Taxon t_acherontia_lachesis = (Taxon)taxonService.load(ACHERONTIA_LACHESIS_UUID);\r
+        Taxon t_acherontia_styx = (Taxon)taxonService.load(UUID_ACHERONTIA_STYX);\r
+\r
+        TaxonDescription description_acherontia_styx = t_acherontia_styx.getDescriptions().iterator().next();\r
+        TaxonDescription description_acherontia_lachesis = t_acherontia_lachesis.getDescriptions().iterator().next();\r
+\r
+        try {\r
+            descriptionService.moveDescriptionElementsToDescription(description_acherontia_styx.getElements(), description_acherontia_lachesis, false);\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
+         * Expectation:\r
+         */\r
+        Assert.assertNull("evaluation should not fail since the user has sufficient permissions", securityException);\r
+\r
+    }\r
+\r
+//    @Ignore // FIXME http://dev.e-taxonomy.eu/trac/ticket/4081 : #4081 (TaxonNodeServiceImpl.makeTaxonNodeASynonymOfAnotherTaxonNode() requires TAXONNAMEBASE.[UPDATE])\r
+    @Test\r
+    public void testAcceptedTaxonToSynomym(){\r
+\r
+        SecurityContext context = SecurityContextHolder.getContext();\r
+        authentication = authenticationManager.authenticate(tokenForPartEditor);\r
+        context.setAuthentication(authentication);\r
+\r
+        RuntimeException securityException = null;\r
+\r
+        Taxon t_acherontia_lachesis = (Taxon)taxonService.load(ACHERONTIA_LACHESIS_UUID);\r
+        UUID name_acherontia_lachesis_uuid = t_acherontia_lachesis.getName().getUuid();\r
+        Taxon t_acherontia_styx = (Taxon)taxonService.load(UUID_ACHERONTIA_STYX);\r
+        int countSynsBefore = t_acherontia_styx.getSynonyms().size();\r
+\r
+        TaxonNode n_acherontia_lachesis = t_acherontia_lachesis.getTaxonNodes().iterator().next();\r
+        TaxonNode n_acherontia_styx = t_acherontia_styx.getTaxonNodes().iterator().next();\r
+\r
+        int numOfSynonymsBefore_styx = t_acherontia_styx.getSynonyms().size();\r
+        int numOfSynonymsBefore_lachesis = t_acherontia_lachesis.getSynonyms().size();\r
+\r
+       \r
+        try {\r
+            DeleteResult result = taxonNodeService.makeTaxonNodeASynonymOfAnotherTaxonNode(n_acherontia_lachesis, n_acherontia_styx, SynonymRelationshipType.SYNONYM_OF(), null, null);\r
+//            synonymUuid = synonym.getUuid();\r
+//            taxonService.saveOrUpdate(synonym);\r
+            commitAndStartNewTransaction(null);\r
+        } catch (RuntimeException e){\r
+            securityException = findSecurityRuntimeException(e);\r
+            logger.error("Unexpected Exception ", e);\r
+            Assert.fail("Unexpected Exception: " + e.getMessage());\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
+         */\r
+        Assert.assertNull("evaluation should not fail since the user has sufficient permissions", securityException);\r
+\r
+        // reload from db and check assertions\r
+        t_acherontia_styx = (Taxon)taxonService.load(UUID_ACHERONTIA_STYX);\r
+        Assert.assertEquals(numOfSynonymsBefore_styx +1 + numOfSynonymsBefore_lachesis, t_acherontia_styx.getSynonyms().size());\r
+        \r
+        Assert.assertNotNull(nameService.load(name_acherontia_lachesis_uuid) );\r
+        Assert.assertNull("The old TaxonNode should no longer exist", taxonNodeService.find(n_acherontia_lachesis.getUuid()));\r
+    }\r
+\r
     @Test\r
     public void testCreateDescriptionWithElement(){\r
 \r
@@ -589,6 +990,7 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
             commitAndStartNewTransaction(null);\r
         } catch (RuntimeException e){\r
             securityException = findSecurityRuntimeException(e);\r
+            logger.error("RuntimeException caught");\r
             logger.debug("Expected failure of evaluation.", securityException);\r
         } finally {\r
             // needed in case saveOrUpdate was interrupted by the RuntimeException\r
@@ -748,11 +1150,11 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         SecurityContext context = SecurityContextHolder.getContext();\r
         context.setAuthentication(authentication);\r
         RuntimeException securityException = null;\r
-\r
+        Classification classification = classificationService.load(UUID.fromString("aeee7448-5298-4991-b724-8d5b75a0a7a9"));\r
         // test for success\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
+        TaxonNode childNode = acherontia_node.addChildTaxon(Taxon.NewInstance(BotanicalName.NewInstance(Rank.SPECIES()), null), null, null);\r
 \r
         try{\r
             taxonNodeService.saveOrUpdate(acherontia_node);\r
@@ -784,7 +1186,7 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
         securityException = null;\r
         TaxonNode acherontiini_node = taxonNodeService.load(ACHERONTIINI_NODE_UUID);\r
         int numOfChildNodes = acherontiini_node.getCountChildren();\r
-        acherontiini_node.addChildTaxon(Taxon.NewInstance(BotanicalName.NewInstance(Rank.GENUS()), null), null, null, null);\r
+        acherontiini_node.addChildTaxon(Taxon.NewInstance(BotanicalName.NewInstance(Rank.GENUS()), null), null, null);\r
 \r
         try{\r
             logger.debug("==============================");\r
@@ -806,4 +1208,13 @@ public class SecurityTest extends CdmTransactionalIntegrationTestWithSecurity{
 \r
     }\r
 \r
+    /* (non-Javadoc)\r
+     * @see eu.etaxonomy.cdm.test.integration.CdmIntegrationTest#createTestData()\r
+     */\r
+    @Override\r
+    public void createTestDataSet() throws FileNotFoundException {\r
+        // TODO Auto-generated method stub\r
+        \r
+    }\r
+\r
 }\r