package org.gcube.data.access.storagehub.services; import java.util.Collections; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.servlet.ServletContext; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.gcube.common.gxrest.response.outbound.GXOutboundErrorResponse; import org.gcube.common.storagehub.model.Excludes; 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.StorageHubException; import org.gcube.common.storagehub.model.exceptions.UserNotAuthorizedException; import org.gcube.common.storagehub.model.items.FolderItem; import org.gcube.common.storagehub.model.items.Item; import org.gcube.common.storagehub.model.items.SharedFolder; import org.gcube.common.storagehub.model.items.VreFolder; import org.gcube.common.storagehub.model.types.ACLList; import org.gcube.data.access.storagehub.AuthorizationChecker; import org.gcube.data.access.storagehub.PathUtil; import org.gcube.data.access.storagehub.StorageHubAppllicationManager; import org.gcube.data.access.storagehub.handlers.CredentialHandler; import org.gcube.data.access.storagehub.handlers.UnshareHandler; import org.gcube.data.access.storagehub.handlers.items.Node2ItemConverter; import org.gcube.data.access.storagehub.services.interfaces.ACLManagerInterface; import org.gcube.smartgears.annotations.ManagedBy; import org.gcube.smartgears.utils.InnerMethodName; import org.glassfish.jersey.media.multipart.FormDataParam; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.webcohesion.enunciate.metadata.rs.RequestHeader; import com.webcohesion.enunciate.metadata.rs.RequestHeaders; @Path("items") @ManagedBy(StorageHubAppllicationManager.class) @RequestHeaders({ @RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"), }) public class ACLManager extends Impersonable { private static final Logger log = LoggerFactory.getLogger(ACLManager.class); RepositoryInitializer repository = StorageHubAppllicationManager.repository; @RequestScoped @PathParam("id") String id; @Inject AuthorizationChecker authChecker; @Inject PathUtil pathUtil; @Context ServletContext context; @Inject Node2ItemConverter node2Item; @Inject UnshareHandler unshareHandler; @Inject ACLManagerInterface aclManagerDelegate; /** * returns the AccessType for all the users in a shared folder * * @exception {@link RepositoryException} when a generic jcr error occurs * @exception {@link UserNotAuthorizedException} when the caller is not authorized to access to the shared folder */ @GET @Path("{id}/acls") @Produces(MediaType.APPLICATION_JSON) public ACLList getACL() { InnerMethodName.instance.set("getACLById"); Session ses = null; try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); Item item = node2Item.getItem(ses.getNodeByIdentifier(id), Excludes.ALL); authChecker.checkReadAuthorizationControl(ses, currentUser, id); return new ACLList(aclManagerDelegate.get(item, ses)); }catch(RepositoryException re){ log.error("jcr error getting acl", re); throw new WebApplicationException(new BackendGenericError("jcr error getting acl", re)); }catch(StorageHubException she ){ log.error(she.getErrorMessage(), she); throw new WebApplicationException(she, Response.Status.fromStatusCode(she.getStatus())); }finally{ if (ses!=null) ses.logout(); } } /** * Set a new AccessType for a user in a shared folder or VRE folder * * * @param String user * @param accessType accessType * * @exception {@link RepositoryException} when a generic jcr error occurs * @exception {@link UserNotAuthorizedException} when the caller is not ADMINISTRATOR of the shared folder * @exception {@link InvalidCallParameters} when the folder is not shared with the specified user * @exception {@link InvalidItemException} when the folder is not share */ @PUT @Consumes(MediaType.MULTIPART_FORM_DATA) @Path("{id}/acls") public void updateACL(@FormDataParam("user") String user, @FormDataParam("access") AccessType accessType) { InnerMethodName.instance.set("setACLById"); Session ses = null; try { if (user==currentUser) throw new InvalidCallParameters("own ACLs cannot be modified"); ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); Node node = ses.getNodeByIdentifier(id); Item item = node2Item.getItem(node, Excludes.ALL); if (!(item instanceof SharedFolder)) throw new InvalidItemException("the item is not a shared folder"); if (item.getOwner().equals(user)) throw new UserNotAuthorizedException("owner acl cannot be changed"); SharedFolder folder = (SharedFolder) item; authChecker.checkAdministratorControl(ses, currentUser, folder); if (folder.isVreFolder()) { if (accessType==AccessType.ADMINISTRATOR) throw new InvalidCallParameters("a VRE admin cannot be changed with this method"); if (!user.equals(folder.getTitle())) throw new InvalidCallParameters("the groupId in the argument is different to the one of the VREFolder"); } else { NodeIterator sharedSet = node.getSharedSet(); boolean found = false; while (sharedSet.hasNext() && !found) { Node current = sharedSet.nextNode(); if (current.getPath().startsWith(pathUtil.getWorkspacePath(user).toPath())) found = true; } if (!found) throw new InvalidCallParameters("shared folder with id "+folder.getId()+" is not shared with user "+user); } aclManagerDelegate.update(user, folder, accessType, ses); }catch(RepositoryException re){ log.error("jcr error extracting archive", re); throw new WebApplicationException(new BackendGenericError("jcr error setting acl", re)); }catch(StorageHubException she ){ log.error(she.getErrorMessage(), she); throw new WebApplicationException(she, Response.Status.fromStatusCode(she.getStatus())); }finally{ if (ses!=null) ses.logout(); } } /** * remove right for a user only on Shared folder * * * @param String user * * * @exception {@link RepositoryException} when a generic jcr error occurs * @exception {@link UserNotAuthorizedException} when the caller is not ADMINISTRATOR of the shared folder * @exception {@link InvalidCallParameters} when the folder is not shared with the specified user * @exception {@link InvalidItemException} when the folder is not share */ //TODO: is this method correct? can ACL be removed, is correct that this means an unshare operation? @DELETE @Consumes(MediaType.TEXT_PLAIN) @Path("{id}/acls/{user}") public void removeACL(@PathParam("user") String user) { InnerMethodName.instance.set("removeACLById"); Session ses = null; try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); Node node = ses.getNodeByIdentifier(id); Item item = node2Item.getItem(node, Excludes.ALL); if (!(item instanceof SharedFolder)) throw new InvalidItemException("the item is not a shared folder"); if (item instanceof VreFolder || ((SharedFolder) item).isVreFolder()) throw new InvalidCallParameters("acls in vreFolder cannot be removed with this method"); authChecker.checkAdministratorControl(ses, currentUser, (SharedFolder) item); unshareHandler.unshare(ses, Collections.singleton(user), node, currentUser); }catch(RepositoryException re){ log.error("jcr error extracting archive", re); GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error removing acl", re)); }catch(StorageHubException she ){ log.error(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); }finally{ if (ses!=null) ses.logout(); } } @GET @Path("{id}/acls/write") public Boolean canWriteInto() { InnerMethodName.instance.set("canWriteIntoFolder"); Session ses = null; Boolean canWrite = false; try{ ses = repository.getRepository().login(CredentialHandler.getAdminCredentials(context)); Node node = ses.getNodeByIdentifier(id); Item item = node2Item.getItem(node, Excludes.ALL); if (!(item instanceof FolderItem)) throw new InvalidItemException("this method can be applied only to folder"); try { authChecker.checkWriteAuthorizationControl(ses, currentUser, id, true); }catch (UserNotAuthorizedException e) { return false; } return true; }catch(RepositoryException re){ log.error("jcr error getting acl", re); GXOutboundErrorResponse.throwException(new BackendGenericError("jcr error getting acl", re)); }catch(StorageHubException she ){ log.error(she.getErrorMessage(), she); GXOutboundErrorResponse.throwException(she, Response.Status.fromStatusCode(she.getStatus())); }finally{ if (ses!=null) ses.logout(); } return canWrite; } }