Commit 9fc9a60e authored by Matija Obreza's avatar Matija Obreza
Browse files

Services and tests updated for AclSid

parent 637b33e0
......@@ -16,10 +16,11 @@
package org.genesys.blocks.security.component;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.genesys.blocks.security.model.AclAwareModel;
import org.genesys.blocks.security.model.AclSid;
import org.genesys.blocks.security.service.CustomAclService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -45,7 +46,7 @@ public class AclAssignerAspect {
/** The acl service. */
@Autowired
private CustomAclService aclService;
/**
* Instantiates a new acl assigner aspect.
*/
......@@ -98,13 +99,19 @@ public class AclAssignerAspect {
*
* @param joinPoint the join point
*/
@After("execution(* org.springframework.data.repository.*.delete(..)) || execution(* org.springframework.data.jpa.repository.*.deleteInBatch(..))")
@Before("execution(* org.springframework.data.repository.*.delete(..)) || execution(* org.springframework.data.jpa.repository.*.deleteInBatch(..))")
public void afterDeleteAclObject(final JoinPoint joinPoint) {
final Object arg0 = joinPoint.getArgs()[0];
try {
if (arg0 instanceof Long) {
// NOOP
} else if (arg0 instanceof AclSid) {
final AclSid aclSid = (AclSid) arg0;
// Remove permissions owned by SID
aclService.removePermissionsFor(aclSid);
// Remove permissions on SID
maybeRemovePermissions(aclSid);
} else if (arg0 instanceof AclAwareModel) {
final AclAwareModel aclModel = (AclAwareModel) arg0;
maybeRemovePermissions(aclModel);
......
......@@ -15,7 +15,6 @@
*/
package org.genesys.blocks.security.model;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
......@@ -38,12 +37,12 @@ public class AclEntry extends BasicModel {
private static final long serialVersionUID = -1047000445685485825L;
/** The acl object identity. */
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@ManyToOne(fetch = FetchType.LAZY, cascade = {}, optional = false)
@JoinColumn(name = "acl_object_identity", nullable = false)
private AclObjectIdentity aclObjectIdentity;
/** The acl sid. */
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@ManyToOne(fetch = FetchType.LAZY, cascade = {}, optional = false)
@JoinColumn(name = "sid", nullable = false)
private AclSid aclSid;
......
......@@ -55,7 +55,7 @@ public class AclObjectIdentity extends BasicModel {
private AclObjectIdentity parentObject;
/** The owner sid. */
@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
@ManyToOne(fetch = FetchType.EAGER, cascade = {})
@JoinColumn(name = "owner_sid", nullable = true)
private AclSid ownerSid;
......
......@@ -60,7 +60,9 @@ public class AclSid extends AuditedVersionedModel {
/** The object identities. */
@JsonIgnore
@OneToMany(mappedBy = "ownerSid", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
// no cascade delete, those object identities must stay because other ACL
// entries rely on them
@OneToMany(mappedBy = "ownerSid", fetch = FetchType.LAZY, cascade = {})
private List<AclObjectIdentity> objectIdentities;
/** The acl entries. */
......
......@@ -21,6 +21,7 @@ import org.genesys.blocks.security.model.AclEntry;
import org.genesys.blocks.security.model.AclObjectIdentity;
import org.genesys.blocks.security.model.AclSid;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
......@@ -38,6 +39,16 @@ public interface AclEntryPersistence extends JpaRepository<AclEntry, Long> {
@Query("select ae from AclEntry ae where ae.aclObjectIdentity = :aclObjectIdentity")
List<AclEntry> findByObjectIdentity(@Param("aclObjectIdentity") AclObjectIdentity aclObjectIdentity);
/**
* List by SID and Object Identity
*
* @param sid
* @param objectIdentity
* @return
*/
@Query("select ae from AclEntry ae where ae.aclSid = :aclSid and ae.aclObjectIdentity = :aclObjectIdentity")
List<AclEntry> findBySidAndObjectIdentity(@Param("aclSid") AclSid sid, @Param("aclObjectIdentity") AclObjectIdentity objectIdentity);
/**
* Find by object identity and object class id and sid.
*
......@@ -102,4 +113,12 @@ public interface AclEntryPersistence extends JpaRepository<AclEntry, Long> {
@Query("select distinct ae.aclSid from AclEntry ae join ae.aclObjectIdentity aoi join aoi.aclClass ac where aoi.objectIdIdentity = :objectIdIdentity and ac.aclClass = :aclClass")
List<AclSid> getSids(@Param("objectIdIdentity") long objectIdIdentity, @Param("aclClass") String aclClass);
/**
* Delete AclEntries for a SID
* @param sid the SID
*/
@Modifying
@Query("delete from AclEntry ae where ae.aclSid = :sid")
<T extends AclSid> int deleteForSid(@Param("sid") T sid);
}
......@@ -38,12 +38,19 @@ public interface CustomAclService {
void addCreatorPermissions(AclAwareModel target);
/**
* Removes the permissions.
* Removes the permissions on ACL model.
*
* @param target the target
*/
void removePermissions(AclAwareModel target);
/**
* Removes the all permissions of SID.
*
* @param sid the sid
*/
void removePermissionsFor(AclSid sid);
/**
* Gets the object identity.
*
......@@ -111,6 +118,15 @@ public interface CustomAclService {
*/
void updatePermission(AclObjectIdentity entity, String sid, Map<Integer, Boolean> permissionMap);
/**
* Update permissions.
*
* @param entity the entity
* @param sid the sid
* @param permissionMap the permission map
*/
void updatePermission(AclAwareModel entity, AclSid sid, Map<Integer, Boolean> permissionMap);
/**
* Gets the acl entries.
*
......@@ -148,13 +164,24 @@ public interface CustomAclService {
*
* @param objectIdIdentity the object id identity
* @param className the class name
* @param uuid the uuid
* @param principal the principal
* @param sid TODO
* @param permissions the permissions
* @return true, if successful
*/
boolean addPermissions(long objectIdIdentity, String className, String uuid, boolean principal, Map<Integer, Boolean> permissions);
boolean addPermissions(long objectIdIdentity, String className, AclSid sid, Map<Integer, Boolean> permissions);
/**
* Adds the permissions.
*
* @param entity the entity
* @param sid the sid
* @param permissionMap the permission map
* @return true, if successful
*/
boolean addPermissions(AclAwareModel entity, AclSid sid, Map<Integer, Boolean> permissionMap);
/**
* Ensure object identity.
*
......@@ -183,4 +210,13 @@ public interface CustomAclService {
* @return the list
*/
List<Long> listIdentitiesForSid(Class<? extends AclAwareModel> clazz, UserDetails authUser, Permission permission);
/**
* Utility method that creates a non-granting permission map
*
* @return Map with all available permissions, set to false
*/
Map<Integer, Boolean> blankPermissionsMap();
}
......@@ -22,6 +22,7 @@ import java.util.Set;
import org.genesys.blocks.security.NoUserFoundException;
import org.genesys.blocks.security.model.BasicUser;
import org.genesys.blocks.security.model.BasicUser.AccountType;
import org.genesys.blocks.security.persistence.AclEntryPersistence;
import org.genesys.blocks.security.service.BasicUserService;
import org.genesys.blocks.security.service.PasswordPolicy;
import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyException;
......@@ -68,6 +69,9 @@ public abstract class BasicUserServiceImpl<R extends GrantedAuthority, T extends
@Autowired(required = false)
private PasswordPolicy passwordPolicy;
@Autowired(required = false)
protected AclEntryPersistence aclEntryRepository;
/**
* Sets the account lockout time.
*
......
......@@ -81,6 +81,20 @@ public class CustomAclServiceImpl implements CustomAclService {
/** The Constant LOG. */
private static final Logger LOG = LoggerFactory.getLogger(CustomAclServiceImpl.class);
/*
* (non-Javadoc)
* @see
* org.genesys.blocks.security.service.CustomAclService#blankPermissionsMap()
*/
@Override
public Map<Integer, Boolean> blankPermissionsMap() {
HashMap<Integer, Boolean> map = new HashMap<>();
for (Permission p : basePermissions) {
map.put(p.getMask(), false);
}
return map;
}
/*
* (non-Javadoc)
* @see
......@@ -95,16 +109,13 @@ public class CustomAclServiceImpl implements CustomAclService {
return;
}
final String uuid = SecurityContextUtil.getUsername();
if (uuid == null) {
LOG.warn("No user in security context, not doing ACL");
final AclSid ownerSid = SecurityContextUtil.getCurrentUser();
if (ownerSid == null) {
LOG.warn("No SID in security context, not doing ACL");
return;
}
LOG.debug("Inserting owner ACL entries for owner={} class={} id={}", uuid, target.getClass().getName(), target.getId());
// it can be pre-authorized Admin
final AclSid aclSid = ensureSid(uuid, true);
LOG.debug("Inserting owner ACL entries for owner={} class={} id={}", ownerSid, target.getClass().getName(), target.getId());
final AclClass aclClass = ensureAclClass(target.getClass().getName());
......@@ -112,7 +123,7 @@ public class CustomAclServiceImpl implements CustomAclService {
final AclObjectIdentity objectIdentity = new AclObjectIdentity();
objectIdentity.setObjectIdIdentity(target.getId());
objectIdentity.setAclClass(aclClass);
objectIdentity.setOwnerSid(aclSid);
objectIdentity.setOwnerSid(ownerSid);
objectIdentity.setParentObject(null);
objectIdentity.setEntriesInheriting(false);
......@@ -126,7 +137,7 @@ public class CustomAclServiceImpl implements CustomAclService {
permissionsMap.put(permission.getMask(), true);
}
addPermissions(aclSid, savedAclObjectIdentity, permissionsMap);
addPermissions(ownerSid, savedAclObjectIdentity, permissionsMap);
}
}
......@@ -139,30 +150,49 @@ public class CustomAclServiceImpl implements CustomAclService {
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void removePermissions(final AclAwareModel target) {
final AclObjectIdentity savedAclObjectIdentity = aclObjectIdentityPersistence.findByObjectIdIdentityAndClassName(target.getId(), target.getClass().getName());
LOG.debug("Deleting all ACL entries for {}", target);
final AclObjectIdentity savedAclObjectIdentity = getObjectIdentity(target);
if (savedAclObjectIdentity != null) {
final List<AclEntry> aclEntries = aclEntryPersistence.findByObjectIdentity(savedAclObjectIdentity);
if (aclEntries != null) {
aclEntryPersistence.delete(aclEntries);
aclObjectIdentityPersistence.delete(savedAclObjectIdentity.getId());
}
clearAclCache();
}
}
/*
* (non-Javadoc)
* @see
* org.genesys.blocks.security.service.CustomAclService#removePermissions(org.
* genesys.blocks.security.model.AclAwareModel)
*/
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void removePermissionsFor(final AclSid sid) {
int count = aclEntryPersistence.deleteForSid(sid);
LOG.debug("Deleting {} permision entries granted to {}", count, sid);
if (count > 0) {
clearAclCache();
}
}
/**
* Adds the permissions.
*
* @param ownerSid the owner sid
* @param sid the sid
* @param objectIdentity the object identity
* @param permissions the permissions
* @return
*/
private void addPermissions(final AclSid ownerSid, final AclObjectIdentity objectIdentity, final Map<Integer, Boolean> permissions) {
private boolean addPermissions(final AclSid sid, final AclObjectIdentity objectIdentity, final Map<Integer, Boolean> permissions) {
// create Acl Entry
for (final Permission permission : basePermissions) {
final int mask = permission.getMask();
final AclEntry aclEntry = new AclEntry();
aclEntry.setAclObjectIdentity(objectIdentity);
aclEntry.setAclSid(ownerSid);
aclEntry.setAclSid(sid);
aclEntry.setAceOrder(getAceOrder(objectIdentity.getId()));
aclEntry.setGranting(permissions.get(mask));
aclEntry.setAuditSuccess(true);
......@@ -174,6 +204,11 @@ public class CustomAclServiceImpl implements CustomAclService {
aclEntryPersistence.save(aclEntry);
}
clearAclCache();
return true;
}
private void clearAclCache() {
final Cache aclCache = cacheManager.getCache("aclCache");
if (aclCache != null)
aclCache.clear();
......@@ -210,30 +245,6 @@ public class CustomAclServiceImpl implements CustomAclService {
return aclClass;
}
/**
* Ensure sid.
*
* @param uuid the uuid
* @param principal the principal
* @return the acl sid
*/
private AclSid ensureSid(final String uuid, final boolean principal) {
AclSid aclSid = aclSidPersistence.findBySidAndPrincipal(uuid, principal);
if (aclSid == null) {
// create Acl Sid
aclSid = new AclSid();
aclSid.setPrincipal(principal);
aclSid.setSid(uuid);
// save it into db
LOG.warn("New SID sid={} principal={}", aclSid.getSid(), aclSid.isPrincipal());
return aclSidPersistence.save(aclSid);
}
return aclSid;
}
/*
* (non-Javadoc)
* @see
......@@ -326,6 +337,27 @@ public class CustomAclServiceImpl implements CustomAclService {
return getPermissions(entity.getId(), entity.getClass().getName());
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#entity, 'ADMINISTRATION')")
public void updatePermission(final AclAwareModel entity, final AclSid sid, final Map<Integer, Boolean> permissionMap) {
boolean oneGranting = false;
final AclObjectIdentity objectIdentity = getObjectIdentity(entity);
final List<AclEntry> aclEntries = aclEntryPersistence.findBySidAndObjectIdentity(sid, objectIdentity);
for (final AclEntry aclEntry : aclEntries) {
aclEntry.setGranting(permissionMap.get((int) aclEntry.getMask()));
oneGranting |= aclEntry.isGranting();
}
if (oneGranting) {
LOG.info("Saving " + aclEntries);
aclEntryPersistence.save(aclEntries);
} else {
LOG.info("Deleting " + aclEntries);
aclEntryPersistence.delete(aclEntries);
}
clearAclCache();
}
/*
* (non-Javadoc)
* @see
......@@ -349,7 +381,8 @@ public class CustomAclServiceImpl implements CustomAclService {
LOG.info("Deleting " + aclEntries);
aclEntryPersistence.delete(aclEntries);
}
cacheManager.getCache("aclCache").clear();
clearAclCache();
}
/*
......@@ -408,6 +441,11 @@ public class CustomAclServiceImpl implements CustomAclService {
return aclSidPersistence.findAll();
}
@Override
public boolean addPermissions(AclAwareModel entity, AclSid sid, Map<Integer, Boolean> permissionMap) {
return addPermissions(sid, getObjectIdentity(entity), permissionMap);
}
/*
* (non-Javadoc)
* @see
......@@ -416,12 +454,10 @@ public class CustomAclServiceImpl implements CustomAclService {
*/
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#objectIdIdentity, #className, 'ADMINISTRATION')")
public boolean addPermissions(final long objectIdIdentity, final String className, final String uuid, final boolean principal, final Map<Integer, Boolean> permissions) {
final AclSid sid = ensureSid(uuid, principal);
public boolean addPermissions(final long objectIdIdentity, final String className, AclSid sid, final Map<Integer, Boolean> permissions) {
final AclObjectIdentity oid = ensureObjectIdentity(className, objectIdIdentity);
addPermissions(sid, oid, permissions);
return true;
return addPermissions(sid, oid, permissions);
}
/*
......@@ -438,9 +474,7 @@ public class CustomAclServiceImpl implements CustomAclService {
aoi = new AclObjectIdentity();
aoi.setObjectIdIdentity(objectIdIdentity);
aoi.setAclClass(ensureAclClass(className));
// System user UUID
final String uuid = SecurityContextUtil.getMe().getUuid();
final AclSid ownerSid = ensureSid(uuid, true);
final AclSid ownerSid = SecurityContextUtil.getCurrentUser();
aoi.setOwnerSid(ownerSid);
aoi = aclObjectIdentityPersistence.save(aoi);
}
......
......@@ -20,17 +20,20 @@ import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import com.google.common.collect.Lists;
import org.genesys.blocks.security.NotUniqueUserException;
import org.genesys.blocks.security.UserException;
import org.genesys.blocks.security.model.AclEntity;
import org.genesys.blocks.security.model.BasicUser.AccountType;
import org.genesys.blocks.security.model.TestUser;
import org.genesys.blocks.security.persistence.AclEntityPersistence;
import org.genesys.blocks.security.persistence.AclEntryPersistence;
import org.genesys.blocks.security.persistence.AclObjectIdentityPersistence;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.blocks.security.service.PasswordPolicy.PasswordPolicyException;
import org.genesys.blocks.security.tests.BaseTest;
import org.junit.After;
import org.junit.Before;
......@@ -38,9 +41,7 @@ import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.context.transaction.AfterTransaction;
import org.springframework.transaction.annotation.Transactional;
......@@ -49,7 +50,6 @@ import org.springframework.transaction.annotation.Transactional;
*/
public class AclAssignerTest extends BaseTest {
private final UUID USER_UUID = UUID.randomUUID();
@Autowired
private CustomAclService aclService;
@Autowired
......@@ -58,62 +58,27 @@ public class AclAssignerTest extends BaseTest {
private AclEntryPersistence aclEntryPersistence;
@Autowired
private AclObjectIdentityPersistence aclObjectIdentityPersistence;
private TestUser user;
@Override
@AfterTransaction
@Transactional
public void cleanup() {
super.cleanup();
aclEntityPersistence.deleteAllInBatch();
aclEntryPersistence.deleteAllInBatch();
aclObjectIdentityPersistence.deleteAllInBatch();
super.cleanup();
}
@Before
public void setupSecurityContext() {
final UserDetails userDetails = new UserDetails() {
private static final long serialVersionUID = 1L;
private final UUID uuid = USER_UUID;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return uuid.toString();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
};
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userDetails, "bar"));
public void setupSecurityContext() throws NotUniqueUserException, PasswordPolicyException, UserException {
user = testUserService.createUser(USER_EMAIL, USER_FULLNAME, "password1!", AccountType.LOCAL);
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(user, "bar"));
}
@After
public void clearSecurityContext() {
SecurityContextHolder.getContext().setAuthentication(null);
......
......@@ -25,6 +25,9 @@ import org.genesys.blocks.security.config.AuthorizationServerConfig;
import org.genesys.blocks.security.config.SecurityConfig;
import org.genesys.blocks.security.model.TestUser;
import org.genesys.blocks.security.model.UserRole;
import org.genesys.blocks.security.persistence.AclEntityPersistence;
import org.genesys.blocks.security.persistence.AclEntryPersistence;
import org.genesys.blocks.security.persistence.AclObjectIdentityPersistence;
import org.genesys.blocks.security.persistence.TestUserPersistence;
import org.genesys.blocks.security.service.BasicUserService;
import org.junit.runner.RunWith;
......@@ -47,6 +50,10 @@ import org.springframework.transaction.annotation.Transactional;
public abstract class BaseTest {
protected final Logger LOG = LoggerFactory.getLogger(getClass());
protected static final String USER_EMAIL = "user@example.com";
protected static final String USER_FULLNAME = "Full Name";
@Autowired
protected BasicUserService<UserRole, TestUser> testUserService;
......@@ -67,10 +74,24 @@ public abstract class BaseTest {
@Autowired