org.graylog2.rest.resources.roles.RolesResource Maven / Gradle / Ivy
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* 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
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* .
*/
package org.graylog2.rest.resources.roles;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.permission.WildcardPermission;
import org.graylog.security.authservice.AuthServiceBackendDTO;
import org.graylog.security.authservice.GlobalAuthServiceConfig;
import org.graylog.security.permissions.GRNPermission;
import org.graylog2.audit.AuditEventTypes;
import org.graylog2.audit.jersey.AuditEvent;
import org.graylog2.database.NotFoundException;
import org.graylog2.plugin.database.ValidationException;
import org.graylog2.plugin.database.users.User;
import org.graylog2.rest.models.roles.responses.RoleMembershipResponse;
import org.graylog2.rest.models.roles.responses.RoleResponse;
import org.graylog2.rest.models.roles.responses.RolesResponse;
import org.graylog2.rest.models.users.responses.UserSummary;
import org.graylog2.shared.rest.resources.RestResource;
import org.graylog2.shared.security.RestPermissions;
import org.graylog2.shared.users.Role;
import org.graylog2.users.RoleImpl;
import org.graylog2.users.RoleService;
import org.joda.time.DateTimeZone;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.net.URI;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import static com.google.common.base.MoreObjects.firstNonNull;
import static jakarta.ws.rs.core.Response.status;
import static org.graylog2.shared.rest.documentation.generator.Generator.CLOUD_VISIBLE;
@RequiresAuthentication
@Path("/roles")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Api(value = "Roles", description = "User roles", tags = {CLOUD_VISIBLE})
public class RolesResource extends RestResource {
private static final Logger log = LoggerFactory.getLogger(RolesResource.class);
private final RoleService roleService;
private final GlobalAuthServiceConfig globalAuthServiceConfig;
@Inject
public RolesResource(RoleService roleService, GlobalAuthServiceConfig globalAuthServiceConfig) {
this.roleService = roleService;
this.globalAuthServiceConfig = globalAuthServiceConfig;
}
@GET
@RequiresPermissions(RestPermissions.ROLES_READ)
@ApiOperation("List all roles")
public RolesResponse listAll() {
final Set roles = roleService.loadAll();
Set roleResponses = Sets.newHashSetWithExpectedSize(roles.size());
for (Role role : roles) {
roleResponses.add(RoleResponse.create(role.getName(), Optional.ofNullable(role.getDescription()), role.getPermissions(), role.isReadOnly()));
}
return RolesResponse.create(roleResponses);
}
@GET
@Path("{rolename}")
@ApiOperation("Retrieve permissions for a single role")
public RoleResponse read(@ApiParam(name = "rolename", required = true) @PathParam("rolename") String name) throws NotFoundException {
checkPermission(RestPermissions.ROLES_READ, name);
final Role role = roleService.load(name);
return RoleResponse.create(role.getName(), Optional.ofNullable(role.getDescription()), role.getPermissions(), role.isReadOnly());
}
@POST
@RequiresPermissions(RestPermissions.ROLES_CREATE)
@ApiOperation("Create a new role")
@AuditEvent(type = AuditEventTypes.ROLE_CREATE)
public Response create(@ApiParam(name = "JSON body", value = "The new role to create", required = true) @Valid @NotNull RoleResponse roleResponse) {
if (roleService.exists(roleResponse.name())) {
throw new BadRequestException("Role " + roleResponse.name() + " already exists.");
}
Role role = new RoleImpl();
role.setName(roleResponse.name());
role.setPermissions(roleResponse.permissions());
roleResponse.description().ifPresent(role::setDescription);
try {
role = roleService.save(role);
} catch (ValidationException e) {
log.error("Invalid role creation request", e);
throw new BadRequestException("Couldn't create role \"" + roleResponse.name() + "\"", e);
}
final URI uri = getUriBuilderToSelf().path(RolesResource.class)
.path("{rolename}")
.build(role.getName());
return Response.created(uri).entity(RoleResponse.create(role.getName(),
Optional.ofNullable(role.getDescription()),
role.getPermissions(),
role.isReadOnly())).build();
}
@PUT
@Path("{rolename}")
@ApiOperation("Update an existing role")
@AuditEvent(type = AuditEventTypes.ROLE_UPDATE)
public RoleResponse update(
@ApiParam(name = "rolename", required = true) @PathParam("rolename") String name,
@ApiParam(name = "JSON Body", value = "The new representation of the role", required = true) RoleResponse roleRepresentation) throws NotFoundException {
checkPermission(RestPermissions.ROLES_EDIT, name);
final Role role = roleService.load(name);
if (role.isReadOnly()) {
throw new BadRequestException("Cannot update read only role " + name);
}
role.setName(roleRepresentation.name());
role.setPermissions(roleRepresentation.permissions());
roleRepresentation.description().ifPresent(role::setDescription);
try {
roleService.save(role);
} catch (ValidationException e) {
throw new BadRequestException("Couldn't update role \"" + roleRepresentation.name() + "\"", e);
}
return RoleResponse.create(role.getName(), Optional.ofNullable(role.getDescription()), role.getPermissions(),
roleRepresentation.readOnly());
}
@DELETE
@Path("{rolename}")
@ApiOperation("Remove the named role and dissociate any users from it")
@AuditEvent(type = AuditEventTypes.ROLE_DELETE)
public void delete(@ApiParam(name = "rolename", required = true) @PathParam("rolename") String name) throws NotFoundException {
checkPermission(RestPermissions.ROLES_DELETE, name);
final Role role = roleService.load(name);
if (role.isReadOnly()) {
throw new BadRequestException("Cannot delete read only system role " + name);
}
userService.dissociateAllUsersFromRole(role);
if (roleService.delete(name) == 0) {
throw new NotFoundException("Couldn't find role " + name);
}
}
@GET
@Path("{rolename}/members")
@RequiresPermissions({RestPermissions.USERS_LIST, RestPermissions.ROLES_READ})
@ApiOperation("Retrieve the role's members")
public RoleMembershipResponse getMembers(@ApiParam(name = "rolename", required = true) @PathParam("rolename") String name) throws NotFoundException {
final Role role = roleService.load(name);
final Collection users = userService.loadAllForRole(role);
Set userSummaries = Sets.newHashSetWithExpectedSize(users.size());
final Optional activeAuthService = globalAuthServiceConfig.getActiveBackendConfig();
for (User user : users) {
final Set roleNames = userService.getRoleNames(user);
List wildcardPermissions;
List grnPermissions;
if (isPermitted(RestPermissions.USERS_PERMISSIONSEDIT, user.getName())) {
wildcardPermissions = userService.getWildcardPermissionsForUser(user);
grnPermissions = userService.getGRNPermissionsForUser(user);
} else {
wildcardPermissions = ImmutableList.of();
grnPermissions = ImmutableList.of();
}
boolean authServiceEnabled =
Objects.isNull(user.getAuthServiceId()) ||
(activeAuthService.isPresent() && user.getAuthServiceId().equals(activeAuthService.get().id())
);
userSummaries.add(UserSummary.create(
user.getId(),
user.getName(),
user.getEmail(),
user.getFirstName().orElse(null),
user.getLastName().orElse(null),
user.getFullName(),
wildcardPermissions,
grnPermissions,
user.getPreferences(),
firstNonNull(user.getTimeZone(), DateTimeZone.UTC).getID(),
user.getSessionTimeoutMs(),
user.isReadOnly(),
user.isExternalUser(),
user.getStartpage(),
roleNames,
// there is no session information available in this call, so we set it to null
false,
null,
null,
user.getAccountStatus(),
user.isServiceAccount(),
authServiceEnabled));
}
return RoleMembershipResponse.create(role.getName(), userSummaries);
}
@PUT
@Path("{rolename}/members/{username}")
@ApiOperation("Add a user to a role")
@AuditEvent(type = AuditEventTypes.ROLE_MEMBERSHIP_UPDATE)
public Response addMember(@ApiParam(name = "rolename") @PathParam("rolename") String rolename,
@ApiParam(name = "username") @PathParam("username") String username,
@ApiParam(name = "JSON Body", value = "Placeholder because PUT requests should have a body. Set to '{}', the content will be ignored.", defaultValue = "{}") String body) throws NotFoundException {
checkPermission(RestPermissions.USERS_EDIT, username);
checkPermission(RestPermissions.ROLES_EDIT, rolename);
final User user = userService.load(username);
if (user == null) {
throw new NotFoundException("User " + username + " has not been found.");
}
// verify that the role exists
final Role role = roleService.load(rolename);
final HashSet roles = Sets.newHashSet(user.getRoleIds());
roles.add(role.getId());
user.setRoleIds(roles);
try {
userService.save(user);
} catch (ValidationException e) {
throw new BadRequestException("Validation failed", e);
}
return status(Response.Status.NO_CONTENT).build();
}
@DELETE
@Path("{rolename}/members/{username}")
@ApiOperation("Remove a user from a role")
@AuditEvent(type = AuditEventTypes.ROLE_MEMBERSHIP_DELETE)
public Response removeMember(@ApiParam(name = "rolename") @PathParam("rolename") String rolename,
@ApiParam(name = "username") @PathParam("username") String username) throws NotFoundException {
checkPermission(RestPermissions.USERS_EDIT, username);
checkPermission(RestPermissions.ROLES_EDIT, rolename);
final User user = userService.load(username);
if (user == null) {
throw new NotFoundException("User " + username + " has not been found.");
}
// verify that the role exists
final Role role = roleService.load(rolename);
final HashSet roles = Sets.newHashSet(user.getRoleIds());
roles.remove(role.getId());
user.setRoleIds(roles);
try {
userService.save(user);
} catch (ValidationException e) {
throw new BadRequestException("Validation failed", e);
}
return status(Response.Status.NO_CONTENT).build();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy