Commit a3588258 authored by Matija Obreza's avatar Matija Obreza

Merge branch 'class-acloid' into 'master'

Introduced ClassAclOid as AclAwareModel

See merge request genesys-pgr/application-blocks!40
parents 4a94d934 c161f60b
...@@ -15,21 +15,19 @@ ...@@ -15,21 +15,19 @@
*/ */
package org.genesys.blocks.security.model; package org.genesys.blocks.security.model;
import java.io.Serializable;
import com.fasterxml.jackson.databind.annotation.JsonAppend;
import org.genesys.blocks.model.EntityId; import org.genesys.blocks.model.EntityId;
import org.genesys.blocks.security.serialization.Permissions;
import org.genesys.blocks.security.serialization.CurrentPermissionsWriter; import org.genesys.blocks.security.serialization.CurrentPermissionsWriter;
import org.genesys.blocks.security.serialization.Permissions;
import org.genesys.blocks.util.JsonClassNameWriter; import org.genesys.blocks.util.JsonClassNameWriter;
import com.fasterxml.jackson.databind.annotation.JsonAppend;
/** /**
* Interface label for entities that require ACL security. * Interface label for entities that require ACL security.
*/ */
@JsonAppend(props = { @JsonAppend.Prop(name = "_class", value = JsonClassNameWriter.class, type = String.class), @JsonAppend(props = { @JsonAppend.Prop(name = "_class", value = JsonClassNameWriter.class, type = String.class),
@JsonAppend.Prop(name = "_permissions", value = CurrentPermissionsWriter.class, type = Permissions.class) }) @JsonAppend.Prop(name = "_permissions", value = CurrentPermissionsWriter.class, type = Permissions.class) })
public interface AclAwareModel extends Serializable, EntityId { public interface AclAwareModel extends EntityId {
/** /**
* Objects belonging to a parent entity can override this method. * Objects belonging to a parent entity can override this method.
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package org.genesys.blocks.security.model; package org.genesys.blocks.security.model;
import javax.persistence.Cacheable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
...@@ -26,6 +27,7 @@ import org.genesys.blocks.model.BasicModel; ...@@ -26,6 +27,7 @@ import org.genesys.blocks.model.BasicModel;
*/ */
@Entity @Entity
@Table(name = "acl_class") @Table(name = "acl_class")
@Cacheable
public class AclClass extends BasicModel { public class AclClass extends BasicModel {
/** The Constant serialVersionUID. */ /** The Constant serialVersionUID. */
......
...@@ -17,6 +17,7 @@ package org.genesys.blocks.security.model; ...@@ -17,6 +17,7 @@ package org.genesys.blocks.security.model;
import java.util.List; import java.util.List;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorColumn;
...@@ -53,6 +54,7 @@ import org.hibernate.annotations.DiscriminatorOptions; ...@@ -53,6 +54,7 @@ import org.hibernate.annotations.DiscriminatorOptions;
@DiscriminatorValue(value = "0") @DiscriminatorValue(value = "0")
@DiscriminatorOptions(force = false) @DiscriminatorOptions(force = false)
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "sid") @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "sid")
@Cacheable
public class AclSid extends AuditedVersionedModel { public class AclSid extends AuditedVersionedModel {
/** The Constant serialVersionUID. */ /** The Constant serialVersionUID. */
......
...@@ -33,6 +33,7 @@ import org.genesys.blocks.security.persistence.AclSidPersistence; ...@@ -33,6 +33,7 @@ import org.genesys.blocks.security.persistence.AclSidPersistence;
import org.genesys.blocks.security.serialization.Permissions; import org.genesys.blocks.security.serialization.Permissions;
import org.genesys.blocks.security.serialization.SidPermissions; import org.genesys.blocks.security.serialization.SidPermissions;
import org.genesys.blocks.security.service.CustomAclService; import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.blocks.util.ClassAclOid;
import org.hibernate.proxy.HibernateProxyHelper; import org.hibernate.proxy.HibernateProxyHelper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -121,12 +122,17 @@ public class CustomAclServiceImpl implements CustomAclService { ...@@ -121,12 +122,17 @@ public class CustomAclServiceImpl implements CustomAclService {
@Override @Override
@Transactional(propagation = Propagation.REQUIRED) @Transactional(propagation = Propagation.REQUIRED)
public void createOrUpdatePermissions(final AclAwareModel target) { public void createOrUpdatePermissions(final AclAwareModel target) {
if ((target == null) || (target.getId() <= 0l)) { if (target == null || (target.getId() <= 0l && !(target instanceof ClassAclOid<?>))) {
LOG.warn("No target specified for ACL permissions, bailing out!"); LOG.warn("No target specified for ACL permissions, bailing out!");
return; return;
} }
final AclClass aclClass = ensureAclClass(target.getClass().getName()); String className = target.getClass().getName();
if (target instanceof ClassAclOid<?>) {
className = ((ClassAclOid<?>) target).getClassName();
}
final AclClass aclClass = ensureAclClass(className);
// save object identity // save object identity
AclObjectIdentity objectIdentity = aclObjectIdentityPersistence.findByObjectIdAndClassname(target.getId(), aclClass.getAclClass()); AclObjectIdentity objectIdentity = aclObjectIdentityPersistence.findByObjectIdAndClassname(target.getId(), aclClass.getAclClass());
...@@ -416,6 +422,11 @@ public class CustomAclServiceImpl implements CustomAclService { ...@@ -416,6 +422,11 @@ public class CustomAclServiceImpl implements CustomAclService {
return null; return null;
} }
String className = HibernateProxyHelper.getClassWithoutInitializingProxy(entity).getName(); String className = HibernateProxyHelper.getClassWithoutInitializingProxy(entity).getName();
if (entity instanceof ClassAclOid<?>) {
className = ((ClassAclOid<?>) entity).getClassName();
}
final AclObjectIdentity oid = aclObjectIdentityPersistence.findByObjectIdAndClassname(entity.getId(), className); final AclObjectIdentity oid = aclObjectIdentityPersistence.findByObjectIdAndClassname(entity.getId(), className);
if (oid == null) { if (oid == null) {
LOG.warn("ACL object identity not found for class={} id={}", className, entity.getId()); LOG.warn("ACL object identity not found for class={} id={}", className, entity.getId());
...@@ -463,7 +474,7 @@ public class CustomAclServiceImpl implements CustomAclService { ...@@ -463,7 +474,7 @@ public class CustomAclServiceImpl implements CustomAclService {
return getPermissions(entity.getId(), entity.getClass().getName()); return getPermissions(entity.getId(), entity.getClass().getName());
} }
@Transactional(propagation = Propagation.REQUIRED) @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_UNCOMMITTED)
@Override @Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#entity, 'ADMINISTRATION')") @PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#entity, 'ADMINISTRATION')")
public AclObjectIdentity setPermissions(final AclAwareModel entity, final AclSid sid, final Permissions permissions) { public AclObjectIdentity setPermissions(final AclAwareModel entity, final AclSid sid, final Permissions permissions) {
...@@ -481,7 +492,7 @@ public class CustomAclServiceImpl implements CustomAclService { ...@@ -481,7 +492,7 @@ public class CustomAclServiceImpl implements CustomAclService {
return setPermissions(objectIdentity, sid, permissions); return setPermissions(objectIdentity, sid, permissions);
} }
@Transactional(propagation = Propagation.REQUIRED) @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_UNCOMMITTED)
@Override @Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#objectIdentity.objectIdIdentity, #objectIdentity.aclClass.aclClass, 'ADMINISTRATION')") @PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#objectIdentity.objectIdIdentity, #objectIdentity.aclClass.aclClass, 'ADMINISTRATION')")
public AclObjectIdentity setPermissions(AclObjectIdentity objectIdentity, AclSid sid, final Permissions permissions) { public AclObjectIdentity setPermissions(AclObjectIdentity objectIdentity, AclSid sid, final Permissions permissions) {
...@@ -496,33 +507,38 @@ public class CustomAclServiceImpl implements CustomAclService { ...@@ -496,33 +507,38 @@ public class CustomAclServiceImpl implements CustomAclService {
} }
try { try {
if (permissions.isOneGranting()) {
// need to update or add permissions
final List<AclEntry> aclEntries = aclEntryPersistence.findBySidAndObjectIdentity(sid, objectIdentity);
if (aclEntries.isEmpty()) { final List<AclEntry> aclEntries = aclEntryPersistence.findBySidAndObjectIdentity(sid, objectIdentity);
// add
return addPermissions(objectIdentity, sid, permissions);
} else {
// updarte
for (final AclEntry aclEntry : aclEntries) {
aclEntry.setGranting(permissions.isGranting(aclEntry.getMask()));
}
LOG.info("Saving " + aclEntries);
aclEntryPersistence.save(aclEntries);
return getObjectIdentity(objectIdentity.getId());
}
if (aclEntries.isEmpty()) {
// add
return addPermissions(objectIdentity, sid, permissions);
} else { } else {
// delete existing // update
final List<AclEntry> aclEntries = aclEntryPersistence.findBySidAndObjectIdentity(sid, objectIdentity); for (final AclEntry aclEntry : aclEntries) {
if (!aclEntries.isEmpty()) { aclEntry.setGranting(permissions.isGranting(aclEntry.getMask()));
LOG.info("Deleting " + aclEntries);
aclEntryPersistence.delete(aclEntries);
entityManager.flush();
} }
LOG.info("Saving " + aclEntries);
aclEntryPersistence.save(aclEntries);
return getObjectIdentity(objectIdentity.getId()); return getObjectIdentity(objectIdentity.getId());
} }
// // This is the original code, one that cleared permissions when none were granted
// // It didn't take into account inherited permissions.
// if (permissions.isOneGranting()) {
// need to update or add permissions
// ...
// } else {
// // delete existing
// final List<AclEntry> aclEntries =
// aclEntryPersistence.findBySidAndObjectIdentity(sid, objectIdentity);
// if (!aclEntries.isEmpty()) {
// LOG.info("Deleting " + aclEntries);
// aclEntryPersistence.delete(aclEntries);
// entityManager.flush();
// }
// return getObjectIdentity(objectIdentity.getId());
// }
} finally { } finally {
clearAclCache(); clearAclCache();
} }
......
/*
* Copyright 2018 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.blocks.util;
import org.genesys.blocks.security.model.AclAwareModel;
/**
* The ACL object identity for our Java classes. Mostly used to make entity
* types READ-able to EVERYONE by default.
*
* @param <T> the generic type
*/
public class ClassAclOid<T extends AclAwareModel> implements AclAwareModel {
private Class<T> clazz;
@Override
public Long getId() {
return -419l; // We use -419l for ACL OID#id for classes
}
/**
* For class.
*
* @param <T> the generic type
* @param clazz the clazz
* @return the class acl
*/
public static <T extends AclAwareModel> ClassAclOid<T> forClass(Class<T> clazz) {
ClassAclOid<T> classAcl = new ClassAclOid<T>();
classAcl.clazz = clazz;
return classAcl;
}
/**
* Gets the clazz.
*
* @return the clazz
*/
public Class<T> getClazz() {
return clazz;
}
/**
* Gets the class name.
*
* @return the class name
*/
public String getClassName() {
return clazz.getName();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment