CCM NG: Permissions are now written recursivly

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4534 8810af33-2d31-482b-a856-94f89814c4df
jensp 2017-01-23 20:39:42 +00:00
parent 413edda57a
commit a8c85aaca9
8 changed files with 179 additions and 17 deletions

View File

@ -19,7 +19,9 @@
package org.librecms.contentsection;
import org.hibernate.envers.Audited;
import org.libreccm.core.CcmObject;
import org.libreccm.core.Identifiable;
import org.libreccm.security.Relation;
import java.io.Serializable;
import java.util.Objects;

View File

@ -38,7 +38,7 @@ import java.util.Objects;
import static org.libreccm.core.CoreConstants.DB_SCHEMA;
import org.libreccm.security.RecursivePermissions;
import org.libreccm.security.Relation;
/**
* Association class describing the association between a category and an
@ -70,7 +70,7 @@ import org.libreccm.security.RecursivePermissions;
+ "WHERE c.category = :category "
+ "AND c.index = TRUE")
})
public class Categorization implements Serializable, Portable {
public class Categorization implements Serializable, Relation, Portable {
private static final long serialVersionUID = 201504301320L;
@ -93,7 +93,6 @@ public class Categorization implements Serializable, Portable {
/**
* The categorised object.
*/
@RecursivePermissions
@ManyToOne
@JoinColumn(name = "OBJECT_ID")
@JsonBackReference(value = "object-categorization")
@ -145,6 +144,11 @@ public class Categorization implements Serializable, Portable {
return category;
}
@Override
public CcmObject getOwner() {
return getCategory();
}
protected void setCategory(final Category category) {
this.category = category;
}
@ -152,6 +156,11 @@ public class Categorization implements Serializable, Portable {
public CcmObject getCategorizedObject() {
return categorizedObject;
}
@Override
public CcmObject getRelatedObject() {
return getCategorizedObject();
}
protected void setCategorizedObject(final CcmObject categorizedObject) {
this.categorizedObject = categorizedObject;

View File

@ -162,7 +162,7 @@ public class Permission implements Serializable, Portable {
*/
@OneToOne
@JoinColumn(name = "INHERITED_FROM_ID")
private CcmObject inheritedForm;
private CcmObject inheritedFrom;
protected Permission() {
//Nothing
@ -236,12 +236,12 @@ public class Permission implements Serializable, Portable {
this.inherited = inherited;
}
public CcmObject getInheritedForm() {
return inheritedForm;
public CcmObject getInheritedFrom() {
return inheritedFrom;
}
protected void setInheritedForm(CcmObject inheritedForm) {
this.inheritedForm = inheritedForm;
protected void setInheritedFrom(CcmObject inheritedFrom) {
this.inheritedFrom = inheritedFrom;
}
@Override
@ -277,7 +277,7 @@ public class Permission implements Serializable, Portable {
if (!Objects.equals(creationDate, other.getCreationDate())) {
return false;
}
if (!Objects.equals(creationIp, other.getCreationIp())) {
return false;
}

View File

@ -18,6 +18,8 @@
*/
package org.libreccm.security;
import com.arsdigita.util.UncheckedWrapperException;
import java.util.List;
import javax.inject.Inject;
@ -31,6 +33,7 @@ import org.libreccm.core.CoreConstants;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
import javax.enterprise.context.RequestScoped;
@ -101,6 +104,69 @@ public class PermissionManager {
permission.setObject(object);
entityManager.persist(permission);
grantRecursive(privilege, grantee, object, object.getClass());
}
}
private void grantRecursive(final String privilege,
final Role grantee,
final CcmObject object,
final Class<?> clazz) {
final Field[] fields = clazz.getDeclaredFields();
Arrays.stream(fields)
.filter(field -> field.isAnnotationPresent(
RecursivePermissions.class))
.forEach(field -> {
field.setAccessible(true);
grantRecursive(privilege, grantee, field, object);
});
if (clazz.getSuperclass() != null) {
grantRecursive(privilege, grantee, object, clazz.getSuperclass());
}
}
private void grantRecursive(final String privilege,
final Role grantee,
final Field field,
final CcmObject owner) {
final Object value;
try {
value = field.get(owner);
} catch (IllegalAccessException ex) {
throw new UncheckedWrapperException(ex);
}
if (value == null) {
return;
}
if (field.getType().equals(Collection.class)) {
final Collection<?> collection = (Collection<?>) value;
collection.stream()
.filter(obj -> obj instanceof CcmObject)
.map(obj -> (CcmObject) obj)
.forEach(obj -> grantPrivilege(privilege, grantee, obj));
collection.stream()
.filter(obj -> obj instanceof Relation)
.map(obj -> (Relation) obj)
.filter(relation -> relation.getRelatedObject() != null)
.map(relation -> relation.getRelatedObject())
.forEach(obj -> grantPrivilege(privilege, grantee, obj));
} else if (field.getType().equals(CcmObject.class)) {
grantPrivilege(privilege, grantee, (CcmObject) value);
} else if (field.getType().equals(Relation.class)) {
final Relation relation = (Relation) value;
if (relation.getRelatedObject() != null) {
grantPrivilege(privilege, grantee, relation.getRelatedObject());
}
} else {
throw new IllegalArgumentException(String.format(
"Found a field annotated with \"%s\" but the field is not a "
+ "collection nor a CcmObject nore a Relation object. This "
+ "is not supported.",
RecursivePermissions.class));
}
}
@ -166,15 +232,26 @@ public class PermissionManager {
}
if (existsPermission(privilege, grantee, object)) {
final Query query = entityManager.createQuery(
final Query deleteQuery = entityManager.createQuery(
"DELETE FROM Permission p "
+ "WHERE p.grantedPrivilege = :privilege "
+ "AND p.grantee = :grantee "
+ "AND p.object = :object");
query.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
query.setParameter(QUERY_PARAM_GRANTEE, grantee);
query.setParameter(QUERY_PARAM_OBJECT, object);
query.executeUpdate();
deleteQuery.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
deleteQuery.setParameter(QUERY_PARAM_GRANTEE, grantee);
deleteQuery.setParameter(QUERY_PARAM_OBJECT, object);
deleteQuery.executeUpdate();
final Query deleteInheritedQuery = entityManager.createQuery(
"DELETE FROM Permission p "
+ "WHERE p.grantedPrivilege = :privilege "
+ "AND p.grantee = :grantee "
+ "AND p.inheritedFrom = :object "
+ "AND p.inherited = true");
deleteInheritedQuery.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
deleteInheritedQuery.setParameter(QUERY_PARAM_GRANTEE, grantee);
deleteInheritedQuery.setParameter("p.inheritedFrom", object);
deleteQuery.executeUpdate();
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2017 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.libreccm.security;
import org.libreccm.core.CcmObject;
import org.libreccm.security.RecursivePermissions;
/**
* If N:M or relation with attributes is annotated with {@link RecursivePermissions} and the
* relation object is not a {@link CcmObject} the relation object must
* implement this interface.
*
* An example are {@link Category#objects} and {@link Categorization}.
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
public interface Relation {
/**
* Get the owning object of the relation.
* @return
*/
CcmObject getOwner();
/**
* Get the related object of the relation.
* @return
*/
CcmObject getRelatedObject();
}

View File

@ -0,0 +1,10 @@
alter table CCM_CORE.PERMISSIONS
add column INHERITED boolean;
alter table CCM_CORE.PERMISSIONS
add column INHERITED_FROM_ID int8;
alter table CCM_CORE.PERMISSIONS
add constraint FKc1x3h1p3o20qiwmonpmva7t5i
foreign key (INHERITED_FROM_ID)
references CCM_CORE.CCM_OBJECTS;

View File

@ -21,6 +21,7 @@ package org.libreccm.portation;
import org.libreccm.categorization.CategorizationMarshaller;
import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryMarshaller;
import org.libreccm.security.Group;
import org.libreccm.security.GroupMarshaller;
import org.libreccm.security.GroupMembershipMarshaller;
import org.libreccm.security.PermissionMarshaller;
@ -34,6 +35,7 @@ import org.libreccm.workflow.WorkflowTemplateMarshaller;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import java.util.List;
/**
@ -57,6 +59,7 @@ class ImportHelper {
@Inject
private UserMarshaller userMarshaller;
@Inject
@Marshals(Group.class)
private GroupMarshaller groupMarshaller;
@Inject
private GroupMembershipMarshaller groupMembershipMarshaller;

View File

@ -229,7 +229,12 @@ public class PermissionManagerTest {
excludeColumns = {"permission_id"})
@InSequence(211)
public void grantPermissionRecursively() {
fail();
final Role role1 = roleRepository.findByName("role1");
final CcmObject category1 = ccmObjectRepository.findById(-20001L);
shiro.getSystemUser().execute(() -> {
permissionManager.grantPrivilege("privilege4", role1, category1);
});
}
/**
@ -246,7 +251,12 @@ public class PermissionManagerTest {
excludeColumns = {"permission_id"})
@InSequence(212)
public void grantInheritedPermission() {
fail();
final Role role1 = roleRepository.findByName("role1");
final CcmObject category2 = ccmObjectRepository.findById(-20001L);
shiro.getSystemUser().execute(() -> {
permissionManager.grantPrivilege("privilege4", role1, category2);
});
}
/**
@ -480,7 +490,12 @@ public class PermissionManagerTest {
+ "after-revoke-recursivly.yml")
@InSequence(311)
public void revokePermissionRecursivly() {
fail();
final Role role1 = roleRepository.findByName("role1");
final CcmObject category1 = ccmObjectRepository.findById(-20001L);
shiro.getSystemUser().execute(() -> {
permissionManager.revokePrivilege("privilege4", role1, category1);
});
}
/**