Commit c041cea8 authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge commit 'fcb0c1b3'

* commit 'fcb0c1b3':
  Final version of user story "Acl editor"

Conflicts:
	src/main/java/org/genesys2/server/service/AclService.java
	src/main/resources/spring/spring.properties
	src/main/webapp/WEB-INF/jsp/acl/editor.jsp
parents 6e1e2cb7 fcb0c1b3
......@@ -16,9 +16,6 @@
package org.genesys2.server.service;
import java.util.List;
import java.util.Map;
import org.genesys2.server.model.AclAwareModel;
import org.genesys2.server.model.acl.AclEntry;
import org.genesys2.server.model.acl.AclObjectIdentity;
......@@ -26,6 +23,9 @@ import org.genesys2.server.model.acl.AclSid;
import org.genesys2.server.security.AuthUserDetails;
import org.springframework.security.acls.model.Permission;
import java.util.List;
import java.util.Map;
public interface AclService {
void addCreatorPermissions(AclAwareModel target);
......@@ -62,6 +62,9 @@ public interface AclService {
Map<AclSid, Map<Long, Boolean>> getPermissions(long id, String className);
boolean addPermissions(long objectIdIdentity, String className, String uuid, boolean principal, Permission... permissions);
boolean addPermissions(long objectIdIdentity, String className, String uuid, boolean principal, Map<Integer, Boolean> permissions);
void updatePermission(AclObjectIdentity entity, String sid, Map<Integer, Boolean> permissionMap);
List<AclSid> getAllSids();
}
......@@ -16,10 +16,7 @@
package org.genesys2.server.service.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.ehcache.CacheManager;
import org.genesys2.server.aspect.AsAdminAspect;
import org.genesys2.server.model.AclAwareModel;
import org.genesys2.server.model.acl.AclClass;
......@@ -43,6 +40,10 @@ import org.springframework.security.acls.model.Permission;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* TODO Add support for cleaning up after objects are removed
*/
......@@ -72,11 +73,15 @@ public class AclServiceImpl implements AclService {
@Autowired
private AsAdminAspect asAdminAspect;
@Autowired
private CacheManager cacheManager;
static {
basePermissions = new Permission[] { BasePermission.CREATE, BasePermission.READ, BasePermission.WRITE, BasePermission.DELETE,
BasePermission.ADMINISTRATION };
}
@Override
@Transactional(readOnly = true)
public Permission[] getAvailablePermissions(String className) {
......@@ -86,8 +91,8 @@ public class AclServiceImpl implements AclService {
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#id, #className, 'ADMINISTRATION')")
public boolean addPermissions(long objectIdIdentity, String className, String uuid, boolean principal, Permission... permissions) {
// @PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#id, #className, 'ADMINISTRATION')")
public boolean addPermissions(long objectIdIdentity, String className, String uuid, boolean principal, Map<Integer, Boolean> permissions) {
AclSid sid = ensureSid(uuid, principal);
AclObjectIdentity oid = ensureObjectIdentity(className, objectIdIdentity);
......@@ -127,7 +132,12 @@ public class AclServiceImpl implements AclService {
// save object identity
aclObjectIdentityPersistence.save(objectIdentity);
addPermissions(aclSid, objectIdentity, basePermissions);
Map<Integer,Boolean> permissionsMap=new HashMap<>();
for (Permission permission:basePermissions){
permissionsMap.put(permission.getMask(),true);
}
addPermissions(aclSid, objectIdentity, permissionsMap);
}
private AclClass ensureAclClass(String className) {
......@@ -160,18 +170,18 @@ public class AclServiceImpl implements AclService {
return aclSid;
}
private void addPermissions(AclSid ownerSid, AclObjectIdentity objectIdentity, Permission... permissions) {
private void addPermissions(AclSid ownerSid, AclObjectIdentity objectIdentity, Map<Integer,Boolean>permissions) {
// create Acl Entry
for (Permission permission : permissions) {
for (Integer mask : permissions.keySet()) {
AclEntry aclEntry = new AclEntry();
aclEntry.setAclObjectIdentity(objectIdentity);
aclEntry.setAclSid(ownerSid);
aclEntry.setAceOrder(getAceOrder(objectIdentity.getId()));
aclEntry.setGranting(true);
aclEntry.setGranting(permissions.get(mask));
aclEntry.setAuditSuccess(true);
aclEntry.setAuditFailure(true);
// set full access for own organization
aclEntry.setMask(permission.getMask());
aclEntry.setMask(mask);
// save ACL
aclEntryPersistence.save(aclEntry);
......@@ -257,19 +267,24 @@ public class AclServiceImpl implements AclService {
public List<AclSid> getSids(AclAwareModel entity) {
return aclEntryPersistence.getSids(entity.getId(), entity.getClass().getName());
}
@Override
@Transactional(readOnly = true)
public List<AclSid> getAllSids() {
return aclSidPersistence.findAll();
}
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#id, #className, 'ADMINISTRATION')")
public Map<AclSid, Map<Long, Boolean>> getPermissions(long id, String className) {
Map<AclSid, Map<Long, Boolean>> perm = new HashMap<AclSid, Map<Long, Boolean>>();
public Map<String, Map<Integer, Boolean>> getPermissions(long id, String className) {
Map<String, Map<Integer, Boolean>> perm = new HashMap<>();
List<AclEntry> aclEntries = getAclEntries(getObjectIdentity(className, id));
for (AclEntry aclEntry : aclEntries) {
Map<Long, Boolean> granted = perm.get(aclEntry.getAclSid());
Map<Integer, Boolean> granted = perm.get(aclEntry.getAclSid().getSid());
if (granted == null) {
perm.put(aclEntry.getAclSid(), granted = new HashMap<Long, Boolean>());
perm.put(aclEntry.getAclSid().getSid(), granted = new HashMap<>());
}
granted.put(aclEntry.getMask(), Boolean.TRUE);
granted.put((int)aclEntry.getMask(), aclEntry.isGranting());
}
return perm;
......@@ -277,10 +292,21 @@ public class AclServiceImpl implements AclService {
@Override
@PreAuthorize("hasRole('ADMINISTRATOR') or hasPermission(#entity, 'ADMINISTRATION')")
public Map<AclSid, Map<Long, Boolean>> getPermissions(AclAwareModel entity) {
public Map<String, Map<Integer, Boolean>> getPermissions(AclAwareModel entity) {
return getPermissions(entity.getId(), entity.getClass().getName());
}
@Override
public void updatePermission(AclObjectIdentity entity, String sid, Map<Integer, Boolean> permissionMap) {
List<AclEntry> aclEntries = aclEntryPersistence.findBySidAndAclClass(sid, entity.getAclClass().getAclClass());
for (AclEntry aclEntry :aclEntries ) {
aclEntry.setGranting(permissionMap.get((int) aclEntry.getMask()));
}
aclEntryPersistence.save(aclEntries);
cacheManager.getCache("acl").removeAll();
}
// // private helpers
// private <T extends BusinessModel & AclAwareModel> void
// removeAssociations(T model) {
......
......@@ -17,16 +17,24 @@
package org.genesys2.server.servlet.controller;
import org.genesys2.server.model.acl.AclObjectIdentity;
import org.genesys2.server.model.acl.AclSid;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.service.AclService;
import org.genesys2.server.service.UserService;
import org.genesys2.server.servlet.model.PermissionJson;
import org.genesys2.server.servlet.util.PermissionJsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
@Scope("request")
......@@ -37,6 +45,9 @@ public class AclEditController extends BaseController {
@Autowired
private AclService aclService;
@Autowired
private UserService userService;
@RequestMapping("/{clazz}/{id}/permissions")
public String permissions(ModelMap model, @PathVariable(value = "clazz") String className, @PathVariable("id") long id,
@RequestParam(value = "back", required = false) String backUrl) {
......@@ -51,13 +62,36 @@ public class AclEditController extends BaseController {
model.addAttribute("aclEntries", aclService.getPermissions(id, className));
model.addAttribute("backUrl", backUrl);
// FIXME Make src/main/webapp/WEB-INF/jsp/acl/editor.jsp work
return "/acl/editor";
//Username
Map<String, String> userNamesMap = new HashMap<>();
List<String> userNames = new ArrayList<>();
for (AclSid aclSid : aclService.getAllSids()) {
User user = userService.getUserByUuid(aclSid.getSid());
userNamesMap.put(aclSid.getSid(), user.getEmail());
if (!user.getEmail().equals(userService.getMe().getEmail())){
userNames.add(user.getEmail());
}
}
@RequestMapping(value = "/{clazz}/{id}/permissions", method = RequestMethod.POST)
public String update(ModelMap model, @PathVariable("clazz") String className, @PathVariable("id") long id) {
model.addAttribute("userNames", userNames);
model.addAttribute("userNameMap", userNamesMap);
return "/acl/editor";
}
@RequestMapping(value = "/{clazz}/{id}/permissions/update", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public
@ResponseBody Object updatePermissions(@PathVariable("clazz") String className, @PathVariable("id") long id,
@RequestBody PermissionJson permissionJson) {
Map<Integer, Boolean> permissionMap = PermissionJsonUtil.createPermissionsMap(permissionJson);
AclObjectIdentity objectIdentity = aclService.ensureObjectIdentity(className, id);
aclService.updatePermission(objectIdentity, permissionJson.getUuid(), permissionMap);
// return "redirect:/acl/"+className+"/"+id+"/permissions";
return "Success";
}
}
......@@ -18,49 +18,48 @@ package org.genesys2.server.servlet.controller.rest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.genesys2.server.model.impl.User;
import org.genesys2.server.service.AclService;
import org.genesys2.server.service.UserService;
import org.genesys2.server.servlet.model.PermissionJson;
import org.genesys2.server.servlet.util.PermissionJsonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.model.Permission;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Map;
@Controller
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = { "/api/v0/permission", "/json/v0/permission" })
@RequestMapping(value = {"/api/v0/permission", "/json/v0/permission"})
public class PermissionController extends RestController {
private static final Log LOG = LogFactory.getLog(PermissionController.class);
@Autowired
protected AclService aclService;
@Autowired
private UserService userService;
@RequestMapping(value = "/add", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody
public
@ResponseBody
Object addPermission(@RequestBody PermissionJson permissionJson) {
LOG.info("Adding permission " + permissionJson);
User user = userService.getUserByEmail(permissionJson.getUuid());
// TODO FIXME Add only selected permissions
Permission[] permissions = new Permission[] { BasePermission.CREATE, BasePermission.DELETE, BasePermission.READ, BasePermission.WRITE,
BasePermission.ADMINISTRATION };
if (user != null) {
Map<Integer, Boolean> permissionMap = PermissionJsonUtil.createPermissionsMap(permissionJson);
aclService.addPermissions(permissionJson.oid, permissionJson.clazz, permissionJson.uuid, permissionJson.principal, permissions);
return JSON_OK;
aclService.addPermissions(permissionJson.getOid(), permissionJson.getClazz(), user.getUuid(), permissionJson.isPrincipal(), permissionMap);
}
public static class PermissionJson {
public long oid;
public String clazz;
public String uuid;
public boolean principal;
@Override
public String toString() {
return "PJ oid=" + oid + " class=" + clazz + " uuid=" + uuid + " principal=" + principal;
}
return JSON_OK;
}
}
\ No newline at end of file
package org.genesys2.server.servlet.model;
public class PermissionJson {
private long oid;
private String clazz;
private String uuid;
private boolean principal;
private boolean create;
private boolean read;
private boolean write;
private boolean delete;
private boolean manage;
@Override
public String toString() {
return "PJ oid=" + oid + " class=" + clazz + " uuid=" + uuid + " principal=" + principal;
}
public long getOid() {
return oid;
}
public void setOid(long oid) {
this.oid = oid;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public boolean isPrincipal() {
return principal;
}
public void setPrincipal(boolean principal) {
this.principal = principal;
}
public boolean isCreate() {
return create;
}
public void setCreate(boolean create) {
this.create = create;
}
public boolean isRead() {
return read;
}
public void setRead(boolean read) {
this.read = read;
}
public boolean isWrite() {
return write;
}
public void setWrite(boolean write) {
this.write = write;
}
public boolean isDelete() {
return delete;
}
public void setDelete(boolean delete) {
this.delete = delete;
}
public boolean isManage() {
return manage;
}
public void setManage(boolean manage) {
this.manage = manage;
}
}
\ No newline at end of file
package org.genesys2.server.servlet.util;
import org.genesys2.server.servlet.model.PermissionJson;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.security.acls.domain.BasePermission.*;
public final class PermissionJsonUtil {
private PermissionJsonUtil() {
}
public static Map<Integer, Boolean> createPermissionsMap(PermissionJson permissionJson) {
Map<Integer, Boolean> permissionMap = new HashMap<>();
permissionMap.put(CREATE.getMask(), permissionJson.isCreate());
permissionMap.put(READ.getMask(), permissionJson.isRead());
permissionMap.put(WRITE.getMask(), permissionJson.isWrite());
permissionMap.put(DELETE.getMask(), permissionJson.isDelete());
permissionMap.put(ADMINISTRATION.getMask(),permissionJson.isManage());
return permissionMap;
}
}
......@@ -44,14 +44,14 @@
</property>
</bean>
<!-- <bean name="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" autowire="no">
<bean name="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" autowire="no">
<property name="featuresToDisable">
<list>
<value type="com.fasterxml.jackson.databind.SerializationFeature">FAIL_ON_EMPTY_BEANS</value>
<value type="com.fasterxml.jackson.databind.DeserializationFeature">FAIL_ON_UNKNOWN_PROPERTIES</value>
</list>
</property>
</bean> -->
</bean>
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="4" />
......
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<%@include file="/WEB-INF/jsp/init.jsp"%>
<%@include file="/WEB-INF/jsp/init.jsp" %>
<html>
<head>
<title><spring:message code="acl.page.permission-manager" /></title>
<title><spring:message code="acl.page.permission-manager"/></title>
</head>
<body>
<h1>
<c:out value="${aclObjectIdentity.aclClass.aclClass}" />
<small><c:out value="${aclObjectIdentity.objectIdIdentity}" /></small>
</h1>
<h1>
<c:out value="${aclAware['class'].name}"/>
<small><c:out value="${aclAware.id}"/></small>
</h1>
<p>TODO FIXME Provide UI to view and manage ACL entries</p>
<p>TODO FIXME Provide UI to view and manage ACL entries</p>
<p><spring:message code="acl.owner" />: <c:out value="${aclObjectIdentity.ownerSid.sid}" /></p>
<table class="accessions">
<p><spring:message code="acl.owner"/>: <c:out value="${aclObjectIdentity.ownerSid.sid}"/></p>
<table class="accessions">
<thead>
<tr>
<td><spring:message code="acl.sid" /></td>
......@@ -27,31 +27,58 @@
</tr>
</thead>
<tbody>
<c:forEach items="${aclSids}" var="aclSid" varStatus="status">
<tr class="${status.count % 2 == 0 ? 'even' : 'odd'}">
<!-- TODO Show username or email/Role -->
<td><c:out value="${aclSid.sid}" /></td>
<td><c:out value="${userNameMap[aclSid.sid]}"/></td>
<input type="hidden" name="aclSid" class="aclSid" value="${aclSid.sid}"/>
<c:forEach items="${aclPermissions}" var="aclPermission">
<td><input type="checkbox" value="1" checked="${aclEntries[aclSid][aclPermission.mask] ? 'checked' : '' }" /></td>
<td><input type="checkbox" value="1" class="check"
name="permissionValue${aclPermission.mask}"
id="permissionValue${aclPermission.mask}" disabled="disabled"
${aclEntries[aclSid.sid][aclPermission.mask] ? 'checked' : '' }/></td>
</c:forEach>
<td><input type="button" class="btn btn-primary edit" value="<spring:message code="edit" />" /></td>
<td><input type="submit" class="btn btn-primary save" style="display: none" value="<spring:message code="save"/>">
<button class="btn btn-default cancel" style="display: none"><spring:message code="cancel"/></button></td>
</tr>
</c:forEach>
<tr id="permissionAdder" class="${aclSids.size()-1 % 2 == 0 ? 'even' : 'odd'}">
<td><input type="text" name="uuid" /></td>
<td><input type="button" value="<spring:message code="add" />" /></td>
<td><input type="text" class="required form-control" name="uuid" id="autocomplete" /></td>
<c:forEach items="${aclPermissions}" var="aclPermission">
<td><input type="checkbox" id="autoCheck${aclPermission.mask}" value="1"
name="acPermissionValue${aclPermission.mask}"
${aclEntries[aclSid.sid][aclPermission.mask] ? 'checked' : '' }/></td>
</c:forEach>
<td><input type="button" class="btn btn-primary" value="<spring:message code="add" />" /></td>
<td></td>
</tr>
</tbody>
</table>
<button class="btn btn-primary"><spring:message code="save" /></button>
<a href="<c:url value="${backUrl}" />" class="btn btn-default"><spring:message code="cancel" /></a>
</table>
<a href="<c:url value="${backUrl}" />" class="btn btn-default"><spring:message code="cancel" /></a>
<content tag="javascript">
<script type="text/javascript">
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/trontastic/jquery-ui.css">
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<script type="text/javascript">
jQuery(document).ready(function() {
$("#permissionAdder input[type=button]").on("click", function(a,b,c) {
var object = { "oid": ${aclObjectIdentity.objectIdIdentity},"clazz":"${aclObjectIdentity.aclClass.aclClass}","uuid":$("#permissionAdder input[type=text]")[0].value,"principal":true };
var create=$("#autoCheck4").is(':checked');
var read=$("#autoCheck1").is(':checked');
var write=$("#autoCheck2").is(':checked');
var remove=$("#autoCheck8").is(':checked');
var manage=$("#autoCheck16").is(':checked');
var object = { "oid": ${aclObjectIdentity.objectIdIdentity},"clazz":"${aclObjectIdentity.aclClass.aclClass}","uuid":$("#permissionAdder input[type=text]")[0].value,"principal":true,
"create":create,"read":read,"write":write,"delete":remove,"manage":manage};
debugger;
$.ajax("/json/v0/permission/add", {
type : 'POST',
......@@ -62,6 +89,7 @@
},
success : function(respObject) {
window.location.reload();
console.log(respObject);
},
error: function(jqXHR, textStatus, errorThrown) {
......@@ -70,8 +98,65 @@
}
});
});
$(".save").on("click", function() {
var create=$(this).parent().parent().find('#permissionValue4').is(':checked');
var read=$(this).parent().parent().find('#permissionValue1').is(':checked');
var write=$(this).parent().parent().find('#permissionValue2').is(':checked');
var remove=$(this).parent().parent().find('#permissionValue8').is(':checked');
var manage=$(this).parent().parent().find('#permissionValue16').is(':checked');
var uuid=$(this).parent().parent().find('.aclSid').val();
var object = { "oid": ${aclObjectIdentity.objectIdIdentity},"clazz":"${aclObjectIdentity.aclClass.aclClass}","uuid":uuid,"principal":true,