diff --git a/CHANGELOG.md b/CHANGELOG.md index 625a619037d4b6281f7a80d93d16501092522305..c68bd28e8b50fbac0650284984bd21cdb03daa1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security +* [caosdb-deploy#225](https://gitlab.indiscale.com/caosdb/src/caosdb-deploy/-/issues/225) - Denied Edit permission leads to retrieve permission. + ## [v0.6.0] - 2021-11-17 (Timm Fitschen) diff --git a/pom.xml b/pom.xml index d495e32de4989c90febb4e40dc731f0ff86a0d01..609971c4ac73fac467ed0ff11fde92708bd080ad 100644 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,7 @@ <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> - <version>1.5.3</version> + <version>1.8.0</version> </dependency> <dependency> <groupId>junit</groupId> diff --git a/src/main/java/org/caosdb/server/CaosDBServer.java b/src/main/java/org/caosdb/server/CaosDBServer.java index 17bb1e500ed5aeffec25cf9aa2535150ceb40b4c..b445749d68fafaeac74cc398912769acd6841a8b 100644 --- a/src/main/java/org/caosdb/server/CaosDBServer.java +++ b/src/main/java/org/caosdb/server/CaosDBServer.java @@ -326,6 +326,8 @@ public class CaosDBServer extends Application { // ChecksumUpdater ChecksumUpdater.start(); + + ThreadContext.remove(); } } else { logger.info("NO BACKEND"); @@ -566,9 +568,9 @@ public class CaosDBServer extends Application { setSessionCookies(response); } finally { - // remove subject from this thread so that we can reuse the - // thread. - ThreadContext.unbindSubject(); + // remove subject and all other session data from this thread so + // that we can reuse the thread. + ThreadContext.remove(); } } diff --git a/src/main/java/org/caosdb/server/jobs/Job.java b/src/main/java/org/caosdb/server/jobs/Job.java index 2de6b08537d82504bb989a4cf661f89ff473e4c2..9bca9eb2e1b9a78e9e73a79b5f31161b345f2dc1 100644 --- a/src/main/java/org/caosdb/server/jobs/Job.java +++ b/src/main/java/org/caosdb/server/jobs/Job.java @@ -27,7 +27,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Set; -import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.Permission; import org.apache.shiro.subject.Subject; import org.caosdb.server.CaosDBException; @@ -301,10 +301,8 @@ public abstract class Job { } protected final void checkPermission(final EntityInterface entity, final Permission permission) - throws Message { - if (!entity.getEntityACL().isPermitted(SecurityUtils.getSubject(), permission)) { - throw ServerMessages.AUTHORIZATION_ERROR; - } + throws AuthorizationException { + entity.checkPermission(permission); } /** diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java b/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java index 9ea7d4b3867b781ead42a50b23b5a7befff048f9..dd87d5298365a4b95abc52c66c17823c5c52687a 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckDatatypePresent.java @@ -23,6 +23,7 @@ package org.caosdb.server.jobs.core; import java.util.List; +import org.apache.shiro.authz.AuthorizationException; import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.database.exceptions.EntityWasNotUniqueException; import org.caosdb.server.datatype.AbstractCollectionDatatype; @@ -97,13 +98,15 @@ public final class CheckDatatypePresent extends EntityJob { // finally, no data type throw ServerMessages.PROPERTY_HAS_NO_DATATYPE; } - } catch (final Message m) { if (m == ServerMessages.ENTITY_DOES_NOT_EXIST) { getEntity().addError(ServerMessages.UNKNOWN_DATATYPE); } else { getEntity().addError(m); } + } catch (AuthorizationException exc) { + getEntity().addError(ServerMessages.AUTHORIZATION_ERROR); + getEntity().addInfo(exc.getMessage()); } catch (final EntityDoesNotExistException exc) { getEntity().addError(ServerMessages.UNKNOWN_DATATYPE); } catch (final EntityWasNotUniqueException exc) { @@ -152,8 +155,8 @@ public final class CheckDatatypePresent extends EntityJob { } } - private void assertAllowedToUse(final EntityInterface datatype) throws Message { - checkPermission(datatype, EntityPermission.USE_AS_DATA_TYPE); + private void assertAllowedToUse(final EntityInterface datatype) { + datatype.checkPermission(EntityPermission.USE_AS_DATA_TYPE); } private void checkIfOverride() throws Message { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java index 4e6b097e66bf2e844cad34ae22f45eedf1b6cd67..d3b28bd5f158b767469e6d7fcc3cebe9fa75edb8 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckParValid.java @@ -23,6 +23,7 @@ package org.caosdb.server.jobs.core; import com.google.common.base.Objects; +import org.apache.shiro.authz.AuthorizationException; import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.database.exceptions.EntityWasNotUniqueException; import org.caosdb.server.entity.Affiliation; @@ -118,13 +119,16 @@ public class CheckParValid extends EntityJob { } } - addError(parent, ServerMessages.ENTITY_DOES_NOT_EXIST); + parent.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); } catch (final Message m) { - addError(parent, m); + parent.addError(m); + } catch (AuthorizationException e) { + parent.addError(ServerMessages.AUTHORIZATION_ERROR); + parent.addInfo(e.getMessage()); } catch (final EntityDoesNotExistException exc) { - addError(parent, ServerMessages.ENTITY_DOES_NOT_EXIST); + parent.addError(ServerMessages.ENTITY_DOES_NOT_EXIST); } catch (final EntityWasNotUniqueException exc) { - addError(parent, ServerMessages.ENTITY_NAME_DUPLICATES); + parent.addError(ServerMessages.ENTITY_NAME_DUPLICATES); } } } @@ -191,12 +195,7 @@ public class CheckParValid extends EntityJob { throw ServerMessages.AFFILIATION_ERROR; } - private void assertAllowedToUse(final EntityInterface entity) throws Message { - checkPermission(entity, EntityPermission.USE_AS_PARENT); - } - - private void addError(final EntityInterface parent, final Message m) { - parent.addError(m); - parent.setEntityStatus(EntityStatus.UNQUALIFIED); + private void assertAllowedToUse(final EntityInterface entity) { + entity.checkPermission(EntityPermission.USE_AS_PARENT); } } diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java index 390deedde211c0931eca1c3677ac5ff9c8ee9d8f..eeea52b85b9c1629b426660020c7e65f0e4b96ef 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckPropValid.java @@ -25,6 +25,7 @@ package org.caosdb.server.jobs.core; import static org.caosdb.server.utils.ServerMessages.ENTITY_DOES_NOT_EXIST; import com.google.common.base.Objects; +import org.apache.shiro.authz.AuthorizationException; import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.database.exceptions.EntityWasNotUniqueException; import org.caosdb.server.entity.EntityInterface; @@ -124,32 +125,23 @@ public class CheckPropValid extends EntityJob { } } } catch (final Message m) { - addError(property, m); + property.addError(m); + } catch (AuthorizationException e) { + property.addError(ServerMessages.AUTHORIZATION_ERROR); + property.addInfo(e.getMessage()); } catch (final EntityDoesNotExistException e) { - addError(property, ENTITY_DOES_NOT_EXIST); + property.addError(ENTITY_DOES_NOT_EXIST); } catch (final EntityWasNotUniqueException e) { - addError(property, ServerMessages.ENTITY_NAME_DUPLICATES); + property.addError(ServerMessages.ENTITY_NAME_DUPLICATES); } } // process names appendJob(ProcessNameProperties.class); - // final ProcessNameProperties processNameProperties = new - // ProcessNameProperties(); - // processNameProperties.init(getMode(), getEntity(), getContainer(), - // getTransaction()); - // getTransaction().getSchedule().add(processNameProperties); - // getTransaction().getSchedule().runJob(processNameProperties); - - } - - private void assertAllowedToUse(final EntityInterface property) throws Message { - checkPermission(property, EntityPermission.USE_AS_PROPERTY); } - private void addError(final EntityInterface property, final Message m) { - property.addError(m); - property.setEntityStatus(EntityStatus.UNQUALIFIED); + private void assertAllowedToUse(final EntityInterface property) { + property.checkPermission(EntityPermission.USE_AS_PROPERTY); } private static void deriveOverrideStatus(final Property child, final EntityInterface parent) { diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java b/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java index f19424b127feb3273571a7826bf05382299ea58a..645f87d0b2eb0bfc045eb3d4237a40b702c044a3 100644 --- a/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java +++ b/src/main/java/org/caosdb/server/jobs/core/CheckRefidValid.java @@ -24,6 +24,7 @@ */ package org.caosdb.server.jobs.core; +import org.apache.shiro.authz.AuthorizationException; import org.caosdb.server.database.exceptions.EntityDoesNotExistException; import org.caosdb.server.database.exceptions.EntityWasNotUniqueException; import org.caosdb.server.datatype.CollectionValue; @@ -71,13 +72,13 @@ public class CheckRefidValid extends EntityJob implements Observer { } } catch (final Message m) { getEntity().addError(m); - getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); + } catch (AuthorizationException exc) { + getEntity().addError(ServerMessages.AUTHORIZATION_ERROR); + getEntity().addInfo(exc.getMessage()); } catch (final EntityDoesNotExistException e) { getEntity().addError(ServerMessages.REFERENCED_ENTITY_DOES_NOT_EXIST); - getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); } catch (final EntityWasNotUniqueException e) { getEntity().addError(ServerMessages.REFERENCE_NAME_DUPLICATES); - getEntity().setEntityStatus(EntityStatus.UNQUALIFIED); } } @@ -139,8 +140,8 @@ public class CheckRefidValid extends EntityJob implements Observer { } } - private void assertAllowedToUse(final EntityInterface referencedEntity) throws Message { - checkPermission(referencedEntity, EntityPermission.USE_AS_REFERENCE); + private void assertAllowedToUse(final EntityInterface referencedEntity) { + referencedEntity.checkPermission(EntityPermission.USE_AS_REFERENCE); } @Override diff --git a/src/main/java/org/caosdb/server/permissions/EntityACI.java b/src/main/java/org/caosdb/server/permissions/EntityACI.java index ccc889decafc941484432c99bf48908c21dff209..34d713eb69179cf7e88103cca2dd901077ffc092 100644 --- a/src/main/java/org/caosdb/server/permissions/EntityACI.java +++ b/src/main/java/org/caosdb/server/permissions/EntityACI.java @@ -1,9 +1,10 @@ /* - * ** header v3.0 * This file is a part of the CaosDB Project. * * Copyright (C) 2018 Research Group Biomedical Physics, * Max-Planck-Institute for Dynamics and Self-Organization Göttingen + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,11 +19,12 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * - * ** end header */ + package org.caosdb.server.permissions; import java.util.HashMap; +import java.util.Set; public final class EntityACI { @@ -72,4 +74,16 @@ public final class EntityACI { map.put("bitSet", getBitSet()); return map; } + + public boolean isGrant() { + return EntityACL.isAllowance(bitSet); + } + + public boolean isPriority() { + return EntityACL.isPriorityBitSet(bitSet); + } + + public Set<EntityPermission> getPermission() { + return EntityACL.getPermissionsFromBitSet(bitSet); + } } diff --git a/src/main/java/org/caosdb/server/permissions/EntityACL.java b/src/main/java/org/caosdb/server/permissions/EntityACL.java index cfa436d59ae25971a08d4314a7f668a70cf75bbf..ce76cf5283847ad0e17886daa80b06548541c030 100644 --- a/src/main/java/org/caosdb/server/permissions/EntityACL.java +++ b/src/main/java/org/caosdb/server/permissions/EntityACL.java @@ -191,7 +191,9 @@ public class EntityACL { public static final List<ResponsibleAgent> getOwners(final Collection<EntityACI> acl) { final List<ResponsibleAgent> owners = new ArrayList<>(); for (final EntityACI aci : acl) { - if (isOwnerBitSet(aci.getBitSet()) && !aci.getResponsibleAgent().equals(OWNER_ROLE)) { + if (aci.isGrant() + && isOwnerBitSet(aci.getBitSet()) + && !aci.getResponsibleAgent().equals(OWNER_ROLE)) { owners.add(aci.getResponsibleAgent()); } } diff --git a/src/test/java/org/caosdb/server/permissions/EntityACLTest.java b/src/test/java/org/caosdb/server/permissions/EntityACLTest.java index 1787c902f48124d692f8c53e4a73ed04564dfe8f..28b4322333f771a3480e4d386b7e77fb2977590b 100644 --- a/src/test/java/org/caosdb/server/permissions/EntityACLTest.java +++ b/src/test/java/org/caosdb/server/permissions/EntityACLTest.java @@ -23,6 +23,7 @@ package org.caosdb.server.permissions; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -30,6 +31,7 @@ import java.io.IOException; import java.util.BitSet; import java.util.HashSet; import java.util.LinkedList; +import java.util.Set; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.caosdb.server.CaosDBServer; @@ -395,4 +397,37 @@ public class EntityACLTest { assertTrue(EntityACL.isPriorityBitSet(aci.getBitSet())); } } + + @Test + public void testOwnership() { + EntityACLFactory f = new EntityACLFactory(); + f.grant( + org.caosdb.server.permissions.Role.create("the_owner"), false, EntityPermission.EDIT_ACL); + f.deny( + org.caosdb.server.permissions.Role.create("someone_else"), + false, + EntityPermission.EDIT_ACL); + EntityACL acl = f.create(); + assertEquals(1, acl.getOwners().size()); + assertEquals("the_owner", acl.getOwners().get(0).toString()); + } + + @Test + public void testPermissionsFor() { + EntityACLFactory f = new EntityACLFactory(); + f.deny(org.caosdb.server.permissions.Role.ANONYMOUS_ROLE, false, EntityPermission.EDIT_ACL); + f.grant(org.caosdb.server.permissions.Role.OWNER_ROLE, false, "*"); + EntityACL acl = f.create(); + + Subject anonymous = SecurityUtils.getSubject(); + anonymous.login(AnonymousAuthenticationToken.getInstance()); + assertTrue(AuthenticationUtils.isAnonymous(anonymous)); + + assertNotNull(acl); + assertTrue(acl.getOwners().isEmpty()); + final Set<EntityPermission> permissionsFor = + EntityACL.getPermissionsFor(anonymous, acl.getRules()); + + assertFalse(permissionsFor.contains(EntityPermission.RETRIEVE_ENTITY)); + } }