CCM NG: Permissions are now written recursivly
git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4534 8810af33-2d31-482b-a856-94f89814c4df
parent
413edda57a
commit
a8c85aaca9
|
|
@ -19,7 +19,9 @@
|
||||||
package org.librecms.contentsection;
|
package org.librecms.contentsection;
|
||||||
|
|
||||||
import org.hibernate.envers.Audited;
|
import org.hibernate.envers.Audited;
|
||||||
|
import org.libreccm.core.CcmObject;
|
||||||
import org.libreccm.core.Identifiable;
|
import org.libreccm.core.Identifiable;
|
||||||
|
import org.libreccm.security.Relation;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ import java.util.Objects;
|
||||||
|
|
||||||
import static org.libreccm.core.CoreConstants.DB_SCHEMA;
|
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
|
* Association class describing the association between a category and an
|
||||||
|
|
@ -70,7 +70,7 @@ import org.libreccm.security.RecursivePermissions;
|
||||||
+ "WHERE c.category = :category "
|
+ "WHERE c.category = :category "
|
||||||
+ "AND c.index = TRUE")
|
+ "AND c.index = TRUE")
|
||||||
})
|
})
|
||||||
public class Categorization implements Serializable, Portable {
|
public class Categorization implements Serializable, Relation, Portable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 201504301320L;
|
private static final long serialVersionUID = 201504301320L;
|
||||||
|
|
||||||
|
|
@ -93,7 +93,6 @@ public class Categorization implements Serializable, Portable {
|
||||||
/**
|
/**
|
||||||
* The categorised object.
|
* The categorised object.
|
||||||
*/
|
*/
|
||||||
@RecursivePermissions
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "OBJECT_ID")
|
@JoinColumn(name = "OBJECT_ID")
|
||||||
@JsonBackReference(value = "object-categorization")
|
@JsonBackReference(value = "object-categorization")
|
||||||
|
|
@ -145,6 +144,11 @@ public class Categorization implements Serializable, Portable {
|
||||||
return category;
|
return category;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CcmObject getOwner() {
|
||||||
|
return getCategory();
|
||||||
|
}
|
||||||
|
|
||||||
protected void setCategory(final Category category) {
|
protected void setCategory(final Category category) {
|
||||||
this.category = category;
|
this.category = category;
|
||||||
}
|
}
|
||||||
|
|
@ -153,6 +157,11 @@ public class Categorization implements Serializable, Portable {
|
||||||
return categorizedObject;
|
return categorizedObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CcmObject getRelatedObject() {
|
||||||
|
return getCategorizedObject();
|
||||||
|
}
|
||||||
|
|
||||||
protected void setCategorizedObject(final CcmObject categorizedObject) {
|
protected void setCategorizedObject(final CcmObject categorizedObject) {
|
||||||
this.categorizedObject = categorizedObject;
|
this.categorizedObject = categorizedObject;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ public class Permission implements Serializable, Portable {
|
||||||
*/
|
*/
|
||||||
@OneToOne
|
@OneToOne
|
||||||
@JoinColumn(name = "INHERITED_FROM_ID")
|
@JoinColumn(name = "INHERITED_FROM_ID")
|
||||||
private CcmObject inheritedForm;
|
private CcmObject inheritedFrom;
|
||||||
|
|
||||||
protected Permission() {
|
protected Permission() {
|
||||||
//Nothing
|
//Nothing
|
||||||
|
|
@ -236,12 +236,12 @@ public class Permission implements Serializable, Portable {
|
||||||
this.inherited = inherited;
|
this.inherited = inherited;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CcmObject getInheritedForm() {
|
public CcmObject getInheritedFrom() {
|
||||||
return inheritedForm;
|
return inheritedFrom;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setInheritedForm(CcmObject inheritedForm) {
|
protected void setInheritedFrom(CcmObject inheritedFrom) {
|
||||||
this.inheritedForm = inheritedForm;
|
this.inheritedFrom = inheritedFrom;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@
|
||||||
*/
|
*/
|
||||||
package org.libreccm.security;
|
package org.libreccm.security;
|
||||||
|
|
||||||
|
import com.arsdigita.util.UncheckedWrapperException;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
@ -31,6 +33,7 @@ import org.libreccm.core.CoreConstants;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
|
@ -101,6 +104,69 @@ public class PermissionManager {
|
||||||
permission.setObject(object);
|
permission.setObject(object);
|
||||||
|
|
||||||
entityManager.persist(permission);
|
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)) {
|
if (existsPermission(privilege, grantee, object)) {
|
||||||
final Query query = entityManager.createQuery(
|
final Query deleteQuery = entityManager.createQuery(
|
||||||
"DELETE FROM Permission p "
|
"DELETE FROM Permission p "
|
||||||
+ "WHERE p.grantedPrivilege = :privilege "
|
+ "WHERE p.grantedPrivilege = :privilege "
|
||||||
+ "AND p.grantee = :grantee "
|
+ "AND p.grantee = :grantee "
|
||||||
+ "AND p.object = :object");
|
+ "AND p.object = :object");
|
||||||
query.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
|
deleteQuery.setParameter(QUERY_PARAM_PRIVILEGE, privilege);
|
||||||
query.setParameter(QUERY_PARAM_GRANTEE, grantee);
|
deleteQuery.setParameter(QUERY_PARAM_GRANTEE, grantee);
|
||||||
query.setParameter(QUERY_PARAM_OBJECT, object);
|
deleteQuery.setParameter(QUERY_PARAM_OBJECT, object);
|
||||||
query.executeUpdate();
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -21,6 +21,7 @@ package org.libreccm.portation;
|
||||||
import org.libreccm.categorization.CategorizationMarshaller;
|
import org.libreccm.categorization.CategorizationMarshaller;
|
||||||
import org.libreccm.categorization.Category;
|
import org.libreccm.categorization.Category;
|
||||||
import org.libreccm.categorization.CategoryMarshaller;
|
import org.libreccm.categorization.CategoryMarshaller;
|
||||||
|
import org.libreccm.security.Group;
|
||||||
import org.libreccm.security.GroupMarshaller;
|
import org.libreccm.security.GroupMarshaller;
|
||||||
import org.libreccm.security.GroupMembershipMarshaller;
|
import org.libreccm.security.GroupMembershipMarshaller;
|
||||||
import org.libreccm.security.PermissionMarshaller;
|
import org.libreccm.security.PermissionMarshaller;
|
||||||
|
|
@ -34,6 +35,7 @@ import org.libreccm.workflow.WorkflowTemplateMarshaller;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -57,6 +59,7 @@ class ImportHelper {
|
||||||
@Inject
|
@Inject
|
||||||
private UserMarshaller userMarshaller;
|
private UserMarshaller userMarshaller;
|
||||||
@Inject
|
@Inject
|
||||||
|
@Marshals(Group.class)
|
||||||
private GroupMarshaller groupMarshaller;
|
private GroupMarshaller groupMarshaller;
|
||||||
@Inject
|
@Inject
|
||||||
private GroupMembershipMarshaller groupMembershipMarshaller;
|
private GroupMembershipMarshaller groupMembershipMarshaller;
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,12 @@ public class PermissionManagerTest {
|
||||||
excludeColumns = {"permission_id"})
|
excludeColumns = {"permission_id"})
|
||||||
@InSequence(211)
|
@InSequence(211)
|
||||||
public void grantPermissionRecursively() {
|
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"})
|
excludeColumns = {"permission_id"})
|
||||||
@InSequence(212)
|
@InSequence(212)
|
||||||
public void grantInheritedPermission() {
|
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")
|
+ "after-revoke-recursivly.yml")
|
||||||
@InSequence(311)
|
@InSequence(311)
|
||||||
public void revokePermissionRecursivly() {
|
public void revokePermissionRecursivly() {
|
||||||
fail();
|
final Role role1 = roleRepository.findByName("role1");
|
||||||
|
final CcmObject category1 = ccmObjectRepository.findById(-20001L);
|
||||||
|
|
||||||
|
shiro.getSystemUser().execute(() -> {
|
||||||
|
permissionManager.revokePrivilege("privilege4", role1, category1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue