package jackrabbit; import java.security.AccessControlException; import java.security.Principal; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import javax.jcr.AccessDeniedException; import javax.jcr.ItemNotFoundException; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.lock.LockException; import javax.jcr.security.AccessControlManager; import javax.jcr.security.AccessControlPolicy; import javax.jcr.security.AccessControlPolicyIterator; import javax.jcr.security.Privilege; import javax.jcr.version.VersionException; import javax.security.auth.Subject; import org.apache.commons.lang.NotImplementedException; import org.apache.commons.lang3.StringUtils; import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.core.id.ItemId; import org.apache.jackrabbit.core.id.NodeId; import org.apache.jackrabbit.core.id.PropertyId; import org.apache.jackrabbit.core.security.AMContext; import org.apache.jackrabbit.core.security.AccessManager; import org.apache.jackrabbit.core.security.authorization.AccessControlProvider; import org.apache.jackrabbit.core.security.authorization.WorkspaceAccessManager; import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.Path; import org.apache.jackrabbit.spi.commons.name.PathBuilder; import com.google.common.collect.Lists; import com.google.common.collect.Maps; public class AorraAccessManager implements AccessControlManager, AccessManager { private AMContext ctx; private void out(String msg) { //System.out.println("XXXXXXXXXXXXXXXXXXXXX " + msg); } @Override public AccessControlPolicyIterator getApplicablePolicies(String arg0) throws PathNotFoundException, AccessDeniedException, RepositoryException { out("getApplicablePolicies"); throw new NotImplementedException("not implemented"); } @Override public AccessControlPolicy[] getEffectivePolicies(String arg0) throws PathNotFoundException, AccessDeniedException, RepositoryException { out("getEffectivePolicies"); throw new NotImplementedException("not implemented"); } @Override public AccessControlPolicy[] getPolicies(String arg0) throws PathNotFoundException, AccessDeniedException, RepositoryException { out("getPolicies"); throw new NotImplementedException("not implemented"); } @Override public Privilege[] getPrivileges(String arg0) throws PathNotFoundException, RepositoryException { out("getPrivileges"); throw new NotImplementedException("not implemented"); } @Override public Privilege[] getSupportedPrivileges(String arg0) throws PathNotFoundException, RepositoryException { out("getSupportedPrivileges"); throw new NotImplementedException("not implemented"); } @Override public boolean hasPrivileges(String arg0, Privilege[] arg1) throws PathNotFoundException, RepositoryException { out("hasPrivileges"); throw new NotImplementedException("not implemented"); } @Override public Privilege privilegeFromName(String arg0) throws AccessControlException, RepositoryException { out("privilegeFromName"); throw new NotImplementedException("not implemented"); } @Override public void removePolicy(String arg0, AccessControlPolicy arg1) throws PathNotFoundException, AccessControlException, AccessDeniedException, LockException, VersionException, RepositoryException { out("removePolicy"); throw new NotImplementedException("not implemented"); } @Override public void setPolicy(String arg0, AccessControlPolicy arg1) throws PathNotFoundException, AccessControlException, AccessDeniedException, LockException, VersionException, RepositoryException { out("setPolicy"); throw new NotImplementedException("not implemented"); } @Override public boolean canAccess(String workspaceName) { out("canAccess"); // grant access to all workspaces return true; } @Override public boolean canRead(Path itemPath, ItemId itemId) throws RepositoryException { out(String.format("canRead %s %s denotesNode:%s", itemPath, itemId, itemId.denotesNode())); Path path = ctx.getHierarchyManager().getPath(itemId); out("ItemId resolved to path: "+path.getString()); Permission p = getPermission(itemId); out("found permission: "+p.toString()); return p.isRead(); } @Deprecated @Override public void checkPermission(ItemId id, int permissions) throws AccessDeniedException, ItemNotFoundException, RepositoryException { out(String.format("checkPermission %s %s", id, permissions)); if(!isGranted(id, permissions)) { throw new AccessDeniedException(String.format( "access denied on %s with permissions %s", id, permissions)); } } @Override public void checkPermission(Path absPath, int permissions) throws AccessDeniedException, RepositoryException { out(String.format("checkPermission %s %s", absPath, permissions)); if(!isGranted(absPath, permissions)) { throw new AccessDeniedException(String.format( "access denied on %s with permissions %s", absPath, permissions)); } } @Override public void checkRepositoryPermission(int permissions) throws AccessDeniedException, RepositoryException { out("checkRepositoryPermission"); } @Override public void close() throws Exception { out("close"); } @Override public void init(AMContext context) throws AccessDeniedException, Exception { this.init(context, null, null); } @Override public void init(AMContext context, AccessControlProvider acProvider, WorkspaceAccessManager wspAccessMgr) throws AccessDeniedException, Exception { out("init"); this.ctx = context; Subject subject = ctx.getSubject(); Set<Principal> p = subject.getPrincipals(); for(Principal principal : p) { out("principal: "+principal.getName()); } out("home dir: "+ctx.getHomeDir().getAbsolutePath()); out("workspace: "+ctx.getWorkspaceName()); } @Deprecated @Override public boolean isGranted(ItemId id, int permissions) throws ItemNotFoundException, RepositoryException { out(String.format("isGranted %s %s", id, permissions)); Permission p = getPermission(id); boolean result; result = (permissions == org.apache.jackrabbit.core.security.authorization.Permission.READ) ?p.isRead():p.isWrite(); out("isGranted: "+result); return result; } @Override public boolean isGranted(Path path, int permissions) throws RepositoryException { out(String.format("isGranted %s %s", path.toString(), permissions)); ItemId id = resolvePathtoRoot(path); if(id == null) { return true; } else { return isGranted(id, permissions); } } @Override public boolean isGranted(Path parentPath, Name childName, int permissions) throws RepositoryException { out(String.format("isGranted %s %s %s", parentPath.toString(), childName, permissions)); PathBuilder builder = new PathBuilder(parentPath); builder.addLast(childName); return isGranted(builder.getPath(), permissions); } private ItemId resolvePathtoRoot(Path path) throws RepositoryException { Path current = path; while(current != null) { ItemId id = resolvePath(current); if(id != null) { out(String.format("resolved path %s to id %s", current, id)); return id; } out(String.format("could not resolve path %s to id, trying to resolve parent...", current)); String jcrPath = ctx.getNamePathResolver().getJCRPath(current); out(String.format("jcr path: %s", jcrPath)); Path qpath = ctx.getNamePathResolver().getQPath(jcrPath); id = resolvePath(qpath); if(id != null) { out(String.format("resolved path %s to id %s", qpath, id)); return id; } else { out(qpath.toString()); } try { current = current.getAncestor(1); } catch(PathNotFoundException e) { break; } } return null; } private ItemId resolvePath(Path path) throws RepositoryException { ItemId id = ctx.getHierarchyManager().resolveNodePath(path); if(id == null) { id = ctx.getHierarchyManager().resolvePropertyPath(path); } return id; } private Permission getPermission(ItemId id) throws RepositoryException { // system and admin principal have full access if(isPrincipal("system") || isPrincipal("admin")) { return Permission.RW; } List<Permission> pList = Lists.newArrayList(); Subject subject = ctx.getSubject(); Set<Principal> p = subject.getPrincipals(); for(Principal principal : p) { Permission permission = getPermission(id, principal); if(permission != null) { pList.add(permission); //optimise: if this is a RW permission exit loop, can't get better than this if(Permission.RW.equals(permission)) { break; } } } if(pList.isEmpty()) { return getDefaultPermission(); } else { Collections.sort(pList); Permission permission = pList.get(pList.size()-1); if(Permission.NONE.equals(permission) && id.denotesNode()) { // if permission is NONE and id is a node (property can't be an ancestor see below) try to widen to RO: // 1. get all RO and RW permissions of subjects principals // 2. check if id is an ancestor of any of the ids configured in the permissions if so return RO for(NodeId checkId : getRwRoIdsFromPermissions()) { if(ctx.getHierarchyManager().isAncestor((NodeId)id, checkId)) { out(String.format("%s is ancestor of %s, granting RO", id, checkId)); return Permission.RO; } } } return permission; } } private ItemId getParentId(ItemId id) throws RepositoryException { try { Path path = ctx.getHierarchyManager().getPath(id); Path parent = path.getAncestor(1); NodeId parentId = ctx.getHierarchyManager().resolveNodePath(parent); return parentId; } catch(PathNotFoundException e) { return null; } } private List<NodeId> getRwRoIdsFromPermissions() { List<NodeId> result = Lists.newArrayList(); Subject subject = ctx.getSubject(); Set<Principal> p = subject.getPrincipals(); for(Principal principal : p) { for(Map.Entry<PermissionKey, Permission> me : PermissionStore.getInstance().getPermissions().entrySet()) { PermissionKey key = me.getKey(); if(!key.getWorkspace().equals(ctx.getWorkspaceName())) { continue; } if(!key.getPrincipal().equals(principal.getName())) { continue; } Permission permission = me.getValue(); if(!(Permission.RO.equals(permission) || Permission.RW.equals(permission))) { continue; } NodeId id = new NodeId(key.getId()); if(nodeExists(id)) { result.add(id); } } } return result; } private boolean nodeExists(NodeId id) { try { return ctx.getHierarchyManager().getPath(id) != null; } catch(Exception e) { return false; } } private Permission getPermission(ItemId id, Principal principal) throws RepositoryException { ItemId i = id; while(true) { Permission p = getItemPermission(i, principal); if(p!= null) { return p; } i = getParentId(i); if(i == null) { break; } } return null; } private Permission getItemPermission(ItemId id, Principal principal) { PermissionKey key = new PermissionKey(ctx.getWorkspaceName(), principal.getName(), id.toString()); //out("key: "+key.toString()); return PermissionStore.getInstance().getPermission(key); } private boolean isPrincipal(String name) { return getPrincipal(name) != null?true:false; } private Principal getPrincipal(String name) { Subject subject = ctx.getSubject(); Set<Principal> p = subject.getPrincipals(); for(Principal principal : p) { if(name.equals(principal.getName())) { return principal; } } return null; } private Permission getDefaultPermission() { return Permission.RW; } private ItemId makeId(String id) throws ItemNotFoundException { try { return new NodeId(id); } catch (IllegalArgumentException e) {} try { return PropertyId.valueOf(id); } catch (IllegalArgumentException e) {} throw new IllegalArgumentException(String.format( "\"%s\" is not a valid node or property ID", id)); } public String getId(String path) throws RepositoryException { Path p = ctx.getNamePathResolver().getQPath(path); ItemId id = resolvePath(p); return id.toString(); } public String getPath(String id) throws ItemNotFoundException, RepositoryException { Path path = ctx.getHierarchyManager().getPath(makeId(id)); return ctx.getNamePathResolver().getJCRPath(path); } public Map<PermissionKey, Permission> getPermissions() { return PermissionStore.getInstance().getPermissions(); } public void grant(String workspace, String principal, String id, Permission permission) throws RepositoryException { PermissionStore.getInstance().grant(ctx.getSession(), workspace, principal, id, permission); } public void grant(Principal principal, String path, Permission permission) throws RepositoryException { ItemId id = resolvePath(ctx.getNamePathResolver().getQPath(path)); if(id != null) { PermissionStore.getInstance().grant(ctx.getSession(), "default", principal.getName(), id.toString(), permission); } else { throw new ItemNotFoundException(String.format("could not resolve path %s to id", path)); } } public boolean revoke(String workspace, String principal, String id) throws RepositoryException { return PermissionStore.getInstance().revoke(ctx.getSession(), workspace, principal, id); } public void initStore(Session session) throws RepositoryException { PermissionStore.getInstance().init(session); } public Map<Principal, Permission> getPermissions(String workspace, String path) throws RepositoryException { out(String.format("getPermissions %s %s", workspace, path)); Map<Principal, Permission> result = Maps.newHashMap(); Path p = ctx.getNamePathResolver().getQPath(path); ItemId id = this.resolvePath(p); if(id == null) { return result; } for(Map.Entry<PermissionKey, Permission> me : PermissionStore.getInstance().getPermissions().entrySet()) { PermissionKey key = me.getKey(); if(StringUtils.equals(workspace, key.getWorkspace()) && StringUtils.equals(id.toString(), key.getId())) { Principal principal = ((JackrabbitSession)ctx.getSession()).getPrincipalManager( ).getPrincipal(key.getPrincipal()); if(principal != null) { result.put(principal, me.getValue()); } } } return result; } public Map<Principal, Permission> getPermissions(String path) throws RepositoryException { return getPermissions(ctx.getWorkspaceName(), path); } }