/** * PODD is an OWL ontology database used for scientific project management * * Copyright (C) 2009-2013 The University Of Queensland * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see <http://www.gnu.org/licenses/>. */ package com.github.podd.resources; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.openrdf.OpenRDFException; import org.openrdf.model.Model; import org.openrdf.model.URI; import org.openrdf.model.impl.LinkedHashModel; import org.openrdf.rio.RDFFormat; import org.openrdf.rio.RDFParseException; import org.openrdf.rio.Rio; import org.openrdf.rio.UnsupportedRDFormatException; import org.restlet.data.MediaType; import org.restlet.data.Status; import org.restlet.representation.ByteArrayRepresentation; import org.restlet.representation.Representation; import org.restlet.representation.Variant; import org.restlet.resource.Get; import org.restlet.resource.Post; import org.restlet.resource.ResourceException; import org.restlet.security.User; import com.github.ansell.restletutils.RestletUtilRole; import com.github.ansell.restletutils.SesameRealmConstants; import com.github.podd.restlet.PoddAction; import com.github.podd.restlet.PoddSesameRealm; import com.github.podd.restlet.PoddWebServiceApplication; import com.github.podd.restlet.RestletUtils; import com.github.podd.utils.PODD; import com.github.podd.utils.PoddObjectLabel; import com.github.podd.utils.PoddRoles; import com.github.podd.utils.PoddUser; import com.github.podd.utils.PoddWebConstants; /** * * @author kutila */ public class UserRolesResourceImpl extends AbstractUserResourceImpl { /** * Handle an HTTP POST request submitting RDF data to update (i.e. map/unmap) a PoddUser's * Roles. <br> * <br> * User authorization is checked for each Role to modify. The service proceeds to modify Role * mappings in the Realm only if the current user has sufficient privileges carry out ALL the * modifications. */ @Post("rdf|rj|json|ttl") public Representation editUserRolesRdf(final Representation entity, final Variant variant) throws ResourceException { this.log.debug("editUserRolesRdf"); final User user = this.getRequest().getClientInfo().getUser(); this.log.debug("authenticated user: {}", user); final String userIdentifier = this.getUserParameter(); this.log.debug("editing Roles of user: {}", userIdentifier); // - validate User whose Roles are to be edited if(userIdentifier == null) { throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Did not specify user to edit Roles"); } final PoddSesameRealm nextRealm = ((PoddWebServiceApplication)this.getApplication()).getRealm(); final PoddUser poddUser = nextRealm.findUser(userIdentifier); if(poddUser == null) { throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "User not found"); } // - retrieve 'delete' parameter boolean isDelete = false; final String deleteQueryParam = this.getQuery().getFirstValue(PoddWebConstants.KEY_DELETE, true); if(deleteQueryParam != null) { isDelete = Boolean.valueOf(deleteQueryParam); } this.log.debug(" edit Roles is a 'delete' = {}", isDelete); Map<RestletUtilRole, Collection<URI>> rolesToEdit = null; // parse input content to a Model try (final InputStream inputStream = entity.getStream();) { final RDFFormat inputFormat = Rio.getParserFormatForMIMEType(entity.getMediaType().getName(), RDFFormat.RDFXML); final Model model = Rio.parse(inputStream, "", inputFormat); rolesToEdit = PoddRoles.extractRoleMappingsUser(model); } catch(IOException | RDFParseException | UnsupportedRDFormatException e1) { throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Could not parse input"); } if(rolesToEdit.isEmpty()) { throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Did not specify any role edits in body of request"); } // - check authorization for each Role mapping for(final Entry<RestletUtilRole, Collection<URI>> nextEntry : rolesToEdit.entrySet()) { final RestletUtilRole role = nextEntry.getKey(); for(final URI mappedUri : nextEntry.getValue()) { PoddAction action = PoddAction.PROJECT_ROLE_EDIT; if(PoddRoles.getRepositoryRoles().contains(role)) { action = PoddAction.REPOSITORY_ROLE_EDIT; if(mappedUri != null) { throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "Unwanted optional Object URI found for role: " + role); } } this.checkAuthentication(action, mappedUri); } } // - do the mapping/unmapping of Roles only if all of the authorisations succeeded for(final Entry<RestletUtilRole, Collection<URI>> nextEntry : rolesToEdit.entrySet()) { final RestletUtilRole role = nextEntry.getKey(); for(final URI mappedUri : nextEntry.getValue()) { if(isDelete) { nextRealm.unmap(poddUser, role.getRole(), mappedUri); this.log.debug(" User [{}] unmapped from Role [{}]", poddUser.getIdentifier(), role.getName()); } else { nextRealm.map(poddUser, role.getRole(), mappedUri); this.log.debug(" User [{}] mapped to Role [{}], [{}]", poddUser.getIdentifier(), role.getName(), mappedUri); } } } // - prepare response final ByteArrayOutputStream output = new ByteArrayOutputStream(8096); final RDFFormat outputFormat = Rio.getWriterFormatForMIMEType(variant.getMediaType().getName(), RDFFormat.RDFXML); try { Rio.write(Arrays.asList(PODD.VF.createStatement(poddUser.getUri(), SesameRealmConstants.OAS_USERIDENTIFIER, PODD.VF.createLiteral(poddUser.getIdentifier()))), output, outputFormat); } catch(final OpenRDFException e) { throw new ResourceException(Status.SERVER_ERROR_INTERNAL, "Could not create response"); } return new ByteArrayRepresentation(output.toByteArray(), MediaType.valueOf(outputFormat.getDefaultMIMEType())); } /** * Display the HTML page for User Role Management */ @Get(":html") public Representation getRoleManagementPageHtml(final Variant variant) throws ResourceException { this.log.debug("getRoleManagementHtml"); final String requestedUserIdentifier = this.getUserParameter(); final PoddAction action = this.getAction(requestedUserIdentifier, PoddAction.OTHER_USER_EDIT, PoddAction.CURRENT_USER_EDIT); this.log.debug("requesting role management for user: {}", requestedUserIdentifier); this.checkAuthentication(action); // completed checking authorization final Map<String, Object> dataModel = RestletUtils.getBaseDataModel(this.getRequest()); dataModel.put("contentTemplate", "editUserRoles.html.ftl"); dataModel.put("pageTitle", "User Role Management"); dataModel.put("authenticatedUserIdentifier", this.getRequest().getClientInfo().getUser().getIdentifier()); final PoddSesameRealm realm = ((PoddWebServiceApplication)this.getApplication()).getRealm(); final PoddUser poddUser = realm.findUser(requestedUserIdentifier); if(poddUser == null) { throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "User not found."); } else { dataModel.put("requestedUser", poddUser); // - include all available PoddRoles dataModel.put("repositoryRolesList", PoddRoles.getRepositoryRoles()); // - include user's current Roles and optional mapped objects final List<Entry<RestletUtilRole, PoddObjectLabel>> roleList = RestletUtils.getUsersRoles(realm, poddUser, this.getPoddArtifactManager()); dataModel.put("userRoleList", roleList); } // Output the base template, with contentTemplate from the dataModel // defining the // template to use for the content in the body of the page return RestletUtils.getHtmlRepresentation( this.getPoddApplication().getPropertyUtil() .get(PoddWebConstants.PROPERTY_TEMPLATE_BASE, PoddWebConstants.DEFAULT_TEMPLATE_BASE), dataModel, MediaType.TEXT_HTML, this.getPoddApplication().getTemplateConfiguration()); } /** * Display the HTML page for User Role Management */ @Get(":rdf|rj|json|ttl") public Representation getRoleManagementRdf(final Variant variant) throws ResourceException { this.log.debug("getRoleManagementHtml"); final String requestedUserIdentifier = this.getUserParameter(); final PoddAction action = this.getAction(requestedUserIdentifier, PoddAction.OTHER_USER_EDIT, PoddAction.CURRENT_USER_EDIT); this.log.debug("requesting role management for user: {}", requestedUserIdentifier); this.checkAuthentication(action); // completed checking authorization final PoddSesameRealm realm = ((PoddWebServiceApplication)this.getApplication()).getRealm(); final PoddUser poddUser = realm.findUser(requestedUserIdentifier); if(poddUser == null) { throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "User not found."); } else { // - include user's current Roles and optional mapped objects final Map<RestletUtilRole, Collection<URI>> mappings = RestletUtils.getUsersRoles(realm, poddUser); final Model results = new LinkedHashModel(); PoddRoles.dumpRoleMappingsUser(mappings, results); // - prepare response final ByteArrayOutputStream output = new ByteArrayOutputStream(8096); final RDFFormat outputFormat = Rio.getWriterFormatForMIMEType(variant.getMediaType().getName(), RDFFormat.RDFXML); try { Rio.write(results, output, outputFormat); } catch(final OpenRDFException e) { throw new ResourceException(Status.SERVER_ERROR_INTERNAL, "Could not create response"); } return new ByteArrayRepresentation(output.toByteArray(), MediaType.valueOf(outputFormat .getDefaultMIMEType())); } } }