package org.gcube.data.access.storagehub.services; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.inject.Inject; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; import javax.jcr.security.AccessControlManager; import javax.jcr.security.Privilege; import javax.servlet.ServletContext; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.Group; import org.apache.jackrabbit.api.security.user.Query; import org.apache.jackrabbit.api.security.user.QueryBuilder; import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils; import org.gcube.common.authorization.control.annotations.AuthorizationControl; import org.gcube.common.authorization.library.provider.AuthorizationProvider; import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.common.storagehub.model.acls.AccessType; import org.gcube.common.storagehub.model.exceptions.BackendGenericError; import org.gcube.common.storagehub.model.exceptions.InvalidCallParameters; import org.gcube.common.storagehub.model.exceptions.InvalidItemException; import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException; import org.gcube.common.storagehub.model.types.NodeProperty; import org.gcube.common.storagehub.model.types.PrimaryNodeType; import org.gcube.data.access.storagehub.Constants; import org.gcube.data.access.storagehub.Utils; import org.gcube.data.access.storagehub.exception.MyAuthException; import org.gcube.data.access.storagehub.handlers.CredentialHandler; import org.gcube.smartgears.utils.InnerMethodName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Path("groups") public class GroupManager { @Context ServletContext context; private static final String VREMANAGER_ROLE = "VRE-Manager"; private static final String INFRASTRUCTURE_MANAGER_ROLE = "Infrastructure-Manager"; private static final Logger log = LoggerFactory.getLogger(GroupManager.class); @Inject RepositoryInitializer repository; @GET @Path("") @Produces(MediaType.APPLICATION_JSON) public List getGroups(){ InnerMethodName.instance.set("getGroups"); JackrabbitSession session = null; List groups= new ArrayList<>(); try { session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); Iterator result = session.getUserManager().findAuthorizables(new Query() { @Override public void build(QueryBuilder builder) { builder.setSelector(Group.class); } }); while (result.hasNext()) { Authorizable group = result.next(); log.info("group {} found",group.getPrincipal().getName()); groups.add(group.getPrincipal().getName()); } }catch(Exception e) { log.error("jcr error getting groups", e); GXOutboundErrorResponse.throwException(new BackendGenericError(e)); } finally { if (session!=null) session.logout(); } return groups; } @POST @Path("") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) public String createGroup(@FormParam("group") String group, @FormParam("accessType") AccessType accessType){ InnerMethodName.instance.set("createGroup"); JackrabbitSession session = null; String groupId = null; try { if (!isValidGroupForContext(groupId)) throw new UserNotAuthorizedException("only VREManager can execute this operation"); session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); Group createdGroup = usrManager.createGroup(group); groupId = createdGroup.getID(); createVreFolder(groupId, session, accessType!=null?accessType:AccessType.WRITE_OWNER); session.save(); }catch(Exception e) { log.error("jcr error creating group {}", group, e); GXOutboundErrorResponse.throwException(new BackendGenericError(e)); } finally { if (session!=null) session.logout(); } return groupId; } @DELETE @Path("{group}") @AuthorizationControl(allowedRoles={INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) public String deleteGroup(@PathParam("group") String group){ InnerMethodName.instance.set("deleteGroup"); JackrabbitSession session = null; try { if (!isValidGroupForContext(group)) throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation"); session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); try { getVreFolderNode(session, group).removeSharedSet(); }catch (Exception e) { log.warn("vreFolder {} not found, removing only the group", group); } Authorizable authorizable = usrManager.getAuthorizable(group); if (authorizable.isGroup()) authorizable.remove(); session.save(); }catch(Exception e) { log.error("jcr error getting users", e); GXOutboundErrorResponse.throwException(new BackendGenericError(e)); } finally { if (session!=null) session.logout(); } return group; } public boolean isAdmin() { return AuthorizationProvider.instance.get().getClient().getRoles().contains(INFRASTRUCTURE_MANAGER_ROLE); } @PUT @Path("{id}") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @AuthorizationControl(allowedRoles={VREMANAGER_ROLE, INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) public boolean addUserToGroup(@PathParam("id") String groupId, @FormParam("userId") String userId){ InnerMethodName.instance.set("addUserToGroup"); JackrabbitSession session = null; boolean success = false; try { if (!isValidGroupForContext(groupId) && !isAdmin()) throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation"); session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); Group group = (Group)usrManager.getAuthorizable(groupId); User user = (User)usrManager.getAuthorizable(userId); if (group.isMember(user)) throw new InvalidCallParameters("user "+userId+" is already member of group "+groupId); success = group.addMember(user); String folderName = group.getPrincipal().getName(); Node folder = getVreFolderNode(session, folderName); String userPath = String.format("%s%s/%s",Utils.getWorkspacePath(user.getPrincipal().getName()).toPath(),Constants.VRE_FOLDER_PARENT_NAME, folderName); log.debug("creating folder in user path {}", userPath ); session.getWorkspace().clone(session.getWorkspace().getName(), folder.getPath(),userPath , false); session.save(); }catch(Exception e) { log.error("jcr error adding user {} to group {}", userId, groupId, e); GXOutboundErrorResponse.throwException(new BackendGenericError(e)); } finally { if (session!=null) session.logout(); } return success; } @DELETE @Path("{groupId}/users/{userId}") @AuthorizationControl(allowedRoles={VREMANAGER_ROLE, INFRASTRUCTURE_MANAGER_ROLE}, exception=MyAuthException.class) public boolean removeUserFromGroup(@PathParam("groupId") String groupId, @PathParam("userId") String userId){ InnerMethodName.instance.set("removeUserFromGroup"); JackrabbitSession session = null; boolean success = false; try { if (!isValidGroupForContext(groupId) && !isAdmin()) throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation"); session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); Group group = (Group)usrManager.getAuthorizable(groupId); User user = (User)usrManager.getAuthorizable(userId); if (!group.isMember(user)) throw new InvalidCallParameters("user "+userId+" is not member of group "+groupId); //delete folder on user String folderName = group.getPrincipal().getName(); Node folder = getVreFolderNode(session, folderName); NodeIterator ni = folder.getSharedSet(); while (ni.hasNext()) { Node node = ni.nextNode(); if (node.getPath().startsWith(Utils.getWorkspacePath(user.getPrincipal().getName()).toPath())) { node.removeShare(); break; } } success = group.removeMember(user); session.save(); }catch(Exception e) { log.error("jcr error adding user {} to group {}", userId, groupId, e); GXOutboundErrorResponse.throwException(new BackendGenericError(e)); } finally { if (session!=null) session.logout(); } return success; } @GET @Path("{groupId}/users") @Produces(MediaType.APPLICATION_JSON) @AuthorizationControl(allowedRoles={VREMANAGER_ROLE}, exception=MyAuthException.class) public List getUsersOfGroup(@PathParam("groupId") String groupId){ InnerMethodName.instance.set("getUsersOfGroup"); JackrabbitSession session = null; List users = new ArrayList<>(); try { if (!isValidGroupForContext(groupId)) throw new UserNotAuthorizedException("only VREManager of the selected VRE can execute this operation"); session = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); org.apache.jackrabbit.api.security.user.UserManager usrManager = session.getUserManager(); Group group = (Group)usrManager.getAuthorizable(groupId); Iterator it = group.getMembers(); while (it.hasNext()) { Authorizable user = it.next(); users.add(user.getPrincipal().getName()); } }catch(Exception e) { log.error("jcr error getting users of group {}", groupId, e); GXOutboundErrorResponse.throwException(new BackendGenericError(e)); } finally { if (session!=null) session.logout(); } return users; } private void createVreFolder(String groupId, JackrabbitSession session, AccessType defaultAccessType) throws Exception{ Node sharedRootNode = session.getNode(Constants.SHARED_FOLDER_PATH); String name = groupId; String title = groupId.substring(groupId.lastIndexOf("-")+1); Node folder= Utils.createFolderInternally(session, sharedRootNode, name, "VREFolder for "+groupId, false, AuthorizationProvider.instance.get().getClient().getId(), null); folder.setPrimaryType(PrimaryNodeType.NT_WORKSPACE_SHARED_FOLDER); folder.setProperty(NodeProperty.IS_VRE_FOLDER.toString(), true); folder.setProperty(NodeProperty.TITLE.toString(), name); folder.setProperty(NodeProperty.DISPLAY_NAME.toString(), title); session.save(); AccessControlManager acm = session.getAccessControlManager(); JackrabbitAccessControlList acls = AccessControlUtils.getAccessControlList(acm, folder.getPath()); Privilege[] adminPrivileges = new Privilege[] { acm.privilegeFromName(AccessType.ADMINISTRATOR.getValue()) }; acls.addAccessControlEntry(AccessControlUtils.getPrincipal(session, AuthorizationProvider.instance.get().getClient().getId()), adminPrivileges ); Privilege[] usersPrivileges = new Privilege[] { acm.privilegeFromName(defaultAccessType.getValue()) }; acls.addAccessControlEntry(AccessControlUtils.getPrincipal(session,groupId), usersPrivileges ); acm.setPolicy(folder.getPath(), acls); } private Node getVreFolderNode(JackrabbitSession session, String name) throws InvalidItemException, Exception { Node sharedRootNode = session.getNode(Constants.SHARED_FOLDER_PATH); Node vreFolder = null; try { vreFolder = sharedRootNode.getNode(name); }catch (PathNotFoundException e) { log.debug("is an old HL VRE"); } NodeIterator nodes = sharedRootNode.getNodes(); while (nodes.hasNext()) { Node node = nodes.nextNode(); if (node.getProperty(NodeProperty.TITLE.toString()).getString().equals(name)) { vreFolder= node; break; } } if (vreFolder==null) throw new InvalidItemException("vre folder not found"); return vreFolder; } private boolean isValidGroupForContext(String group){ String currentContext = ScopeProvider.instance.get(); String expectedGroupId= currentContext.replace("/", "-").substring(1); return group.equals(expectedGroupId); } }