/**
* Copyright (c) 2009 - 2012 Red Hat, Inc.
*
* This software is licensed to you under the GNU General Public License,
* version 2 (GPLv2). There is NO WARRANTY for this software, express or
* implied, including the implied warranties of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
* along with this software; if not, see
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* Red Hat trademarks are not licensed under GPLv2. No permission is
* granted to use or replicate Red Hat trademarks that are incorporated
* in this software or its documentation.
*/
package org.candlepin.resource;
import org.candlepin.auth.Access;
import org.candlepin.common.exceptions.NotFoundException;
import org.candlepin.model.Owner;
import org.candlepin.model.OwnerCurator;
import org.candlepin.model.PermissionBlueprint;
import org.candlepin.model.PermissionBlueprintCurator;
import org.candlepin.model.Role;
import org.candlepin.model.User;
import org.candlepin.service.UserServiceAdapter;
import com.google.inject.Inject;
import org.jboss.resteasy.annotations.providers.jaxb.Wrapped;
import org.jboss.resteasy.spi.BadRequestException;
import org.xnap.commons.i18n.I18n;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
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.MediaType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.Authorization;
/**
*
*/
@Path("/roles")
@Api(value = "roles", authorizations = { @Authorization("basic") })
public class RoleResource {
private UserServiceAdapter userService;
private OwnerCurator ownerCurator;
private PermissionBlueprintCurator permissionCurator;
private I18n i18n;
@Inject
public RoleResource(UserServiceAdapter userService, OwnerCurator ownerCurator,
PermissionBlueprintCurator permCurator, I18n i18n) {
this.userService = userService;
this.ownerCurator = ownerCurator;
this.i18n = i18n;
this.permissionCurator = permCurator;
}
@ApiOperation(notes = "Creates a Role", value = "createRole")
@ApiResponses({ @ApiResponse(code = 404, message = "") })
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Role createRole(@ApiParam(name = "role", required = true) Role role) {
// Attach actual owner objects to each incoming permission:
for (PermissionBlueprint p : role.getPermissions()) {
Owner temp = p.getOwner();
Owner actual = ownerCurator.lookupByKey(temp.getKey());
if (actual == null) {
throw new NotFoundException(i18n.tr("No such owner: {0}", temp.getKey()));
}
p.setOwner(actual);
}
Role r = this.userService.createRole(role);
return r;
}
@ApiOperation(notes = "Updates a Role. To avoid race conditions, we do not support " +
"updating the user or permission collections. Currently this call will only update " +
"the role name. See the specific nested POST/DELETE calls for modifying users and" +
" permissions.", value = "updateRole")
@ApiResponses({ @ApiResponse(code = 404, message = "") })
@PUT
@Path("{role_id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Role updateRole(@PathParam("role_id") String roleId,
@ApiParam(name = "role", required = true) Role role) {
//Only operate here if you only have 1 ID to pull,
//but if the user passes in an ID in the body of the JSON
//and that ID is NOT equal to what the ID in the URL is, then throw an error
if (role.getId() != null && !roleId.equals(role.getId())) {
throw new BadRequestException(i18n.tr("Role ID does not match path."));
}
Role existingRole = lookupRole(roleId);
existingRole.setName(role.getName());
return this.userService.updateRole(existingRole);
}
@ApiOperation(notes = "Adds a Permission to a Role. Returns the updated Role.",
value = "addRolePermission")
@ApiResponses({ @ApiResponse(code = 404, message = ""), @ApiResponse(code = 400, message = "") })
@POST
@Path("{role_id}/permissions")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Role addRolePermission(@PathParam("role_id") String roleId,
@ApiParam(name = "permissionBlueprint", required = true) PermissionBlueprint permission) {
Role existingRole = lookupRole(roleId);
// Don't allow NONE permissions to be created, this is currently just for
// internal use:
if (permission.getAccess().equals(Access.NONE)) {
throw new BadRequestException(i18n.tr("Access type NONE not supported."));
}
// Attach actual owner objects to each incoming permission:
Owner temp = permission.getOwner();
Owner real = ownerCurator.lookupByKey(temp.getKey());
permission.setOwner(real);
existingRole.addPermission(permission);
Role r = this.userService.updateRole(existingRole);
return r;
}
@ApiOperation(notes = "Removes a Permission from a Role. Returns the updated Role.",
value = "removeRolePermission")
@ApiResponses({ @ApiResponse(code = 404, message = "") }) @DELETE
@Path("{role_id}/permissions/{perm_id}")
@Produces(MediaType.APPLICATION_JSON)
public Role removeRolePermission(@PathParam("role_id") String roleId,
@PathParam("perm_id") String permissionId) {
Role existingRole = lookupRole(roleId);
Set<PermissionBlueprint> picks = new HashSet<PermissionBlueprint>();
boolean found = true;
PermissionBlueprint toRemove = null;
for (PermissionBlueprint op : existingRole.getPermissions()) {
if (!op.getId().equals(permissionId)) {
picks.add(op);
}
else {
found = true;
toRemove = op;
}
}
if (!found) {
throw new NotFoundException(i18n.tr("No such permission: {0} in role: {1}",
permissionId, roleId));
}
existingRole.setPermissions(picks);
Role r = this.userService.updateRole(existingRole);
toRemove.setOwner(null);
permissionCurator.delete(toRemove);
return r;
}
private Role lookupRole(String roleId) {
Role role = userService.getRole(roleId);
if (role == null) {
throw new NotFoundException(i18n.tr("No such role: {0}", roleId));
}
return role;
}
private User lookupUser(String username) {
User user = userService.findByLogin(username);
if (user == null) {
throw new NotFoundException(i18n.tr("No such user: {0}", username));
}
return user;
}
@ApiOperation(notes = "Retrieves a single Role", value = "getRole")
@GET
@Path("{role_id}")
@Produces(MediaType.APPLICATION_JSON)
public Role getRole(@PathParam("role_id") String roleId) {
return lookupRole(roleId);
}
@ApiOperation(notes = "Removes a Role", value = "deleteRole")
@DELETE
@Path("/{role_id}")
@Produces(MediaType.WILDCARD)
public void deleteRole(@PathParam("role_id") String roleId) {
this.userService.deleteRole(roleId);
}
@ApiOperation(notes = "Adds a User to a Role", value = "addUser")
@ApiResponses({ @ApiResponse(code = 404, message = "") })
@POST
@Path("/{role_id}/users/{username}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.WILDCARD)
public Role addUser(@PathParam("role_id") String roleId,
@PathParam("username") String username) {
Role role = lookupRole(roleId);
User user = lookupUser(username);
userService.addUserToRole(role, user);
return role;
}
@ApiOperation(notes = "Removes a User from a Role", value = "deleteUser")
@ApiResponses({ @ApiResponse(code = 404, message = "") })
@DELETE
@Path("/{role_id}/users/{username}")
@Produces(MediaType.APPLICATION_JSON)
public Role deleteUser(@PathParam("role_id") String roleId,
@PathParam("username") String username) {
Role role = lookupRole(roleId);
User user = lookupUser(username);
userService.removeUserFromRole(role, user);
return role;
}
@ApiOperation(notes = "Retrieves a list of Roles", value = "getRoles")
@GET
@Produces(MediaType.APPLICATION_JSON)
@Wrapped(element = "roles")
public List<Role> getRoles() {
// TODO: Add in filter options
return userService.listRoles();
}
}