package alien4cloud.security.groups.rest;
import java.io.IOException;
import java.util.List;
import javax.annotation.Resource;
import javax.validation.Valid;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import alien4cloud.Constants;
import alien4cloud.audit.annotation.Audit;
import alien4cloud.dao.model.GetMultipleDataResult;
import alien4cloud.exception.InvalidArgumentException;
import alien4cloud.rest.model.FilteredSearchRequest;
import alien4cloud.rest.model.RestErrorBuilder;
import alien4cloud.rest.model.RestErrorCode;
import alien4cloud.rest.model.RestResponse;
import alien4cloud.rest.model.RestResponseBuilder;
import alien4cloud.security.ResourceRoleService;
import alien4cloud.security.groups.GroupService;
import alien4cloud.security.groups.IAlienGroupDao;
import alien4cloud.security.model.Group;
import alien4cloud.security.model.User;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
/**
* GroupController allows ALIEN administrators to create, delete, update, or
* search groups. A default internal group is created to make some security
* check easier ({@link Constants}) Event ADMIN role can't make CRUD operation
* on this Group
*
* @author igor ngouagna
*/
@RestController
@Slf4j
@RequestMapping({"/rest/groups", "/rest/v1/groups", "/rest/latest/groups"})
public class GroupController {
@Resource
private IAlienGroupDao alienGroupDao;
@Resource
private GroupService groupService;
@Resource
private ResourceRoleService resourceRoleService;
/**
* Create a new group in the system.
*
* @param request A {@link CreateGroupRequest} with information of the new group to create.
* @return a rest {@link RestResponse} containing the id of the newly created group.
*/
@ApiOperation(value = "Create a new group in ALIEN.")
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('ADMIN')")
@Audit
public RestResponse<String> create(@Valid @RequestBody CreateGroupRequest request) {
String groupId = groupService.createGroup(request.getName(), request.getEmail(), request.getDescription(), request.getRoles(), request.getUsers());
return RestResponseBuilder.<String> builder().data(groupId).build();
}
@RequestMapping(value = "/{groupId}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation("Update a group by merging the groupUpdateRequest into the existing group")
@PreAuthorize("hasAuthority('ADMIN')")
@Audit
public RestResponse<Void> update(@PathVariable String groupId, @RequestBody UpdateGroupRequest groupUpdateRequest) {
if (!isInternalAllUserGroup(groupId)) {
groupService.updateGroup(groupId, groupUpdateRequest);
} else {
log.info("You can not update the group with id <{}> corresponding to an internal group <{}>", groupId, Constants.GROUP_NAME_ALL_USERS);
return RestResponseBuilder
.<Void> builder()
.data(null)
.error(RestErrorBuilder
.builder(RestErrorCode.INTERNAL_OBJECT_ERROR)
.message(
"You can not update the group with id <" + groupId + "> corresponding to an internal group <"
+ Constants.GROUP_NAME_ALL_USERS + ">").build()).build();
}
return RestResponseBuilder.<Void> builder().build();
}
/**
* Get a group from it's id.
*
* @param groupId
* The unique id of the group to retrieve.
* @return a rest {@link RestResponse} containing the group matching the
* requested id.
*/
@ApiOperation(value = "Get a group based on it's id.", notes = "Returns a rest response that contains the group's details.")
@RequestMapping(value = "/{groupId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('ADMIN')")
public RestResponse<Group> getGroup(@PathVariable String groupId) {
if (groupId == null || groupId.isEmpty()) {
throw new InvalidArgumentException("Group with id <" + groupId + "> does not exist");
}
Group group = alienGroupDao.find(groupId);
return RestResponseBuilder.<Group> builder().data(group).build();
}
/**
* Delete a group from the store based on it's id.
*
* @param groupId
* The unique id of the group to delete.
* @return an empty (void) rest {@link RestResponse}.
* @throws IOException
* @throws ClassNotFoundException
*/
@ApiOperation(value = "Delete an existing group from the repository.")
@RequestMapping(value = "/{groupId}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('ADMIN')")
@Audit
public RestResponse<Void> deleteGroup(@PathVariable String groupId) throws ClassNotFoundException, IOException {
if (groupId == null || groupId.isEmpty()) {
throw new InvalidArgumentException("Group with id <" + groupId + "> does not exist");
}
if (!isInternalAllUserGroup(groupId)) {
groupService.deleteGroup(groupId);
} else {
log.info("You can not update the group with id <{}> corresponding to an internal group <{}>", groupId, Constants.GROUP_NAME_ALL_USERS);
return RestResponseBuilder
.<Void> builder()
.data(null)
.error(RestErrorBuilder
.builder(RestErrorCode.INTERNAL_OBJECT_ERROR)
.message(
"You can not update the group with id <" + groupId + "> corresponding to an internal group <"
+ Constants.GROUP_NAME_ALL_USERS + ">").build()).build();
}
return RestResponseBuilder.<Void> builder().build();
}
/**
* Search for groups.
*
* @param searchRequest
* The request that contains parameters of the search request.
* @return A {@link RestResponse} that contains a {@link GetMultipleDataResult} of {@link Group}.
*/
@ApiOperation(value = "Search for user's registered in alien.")
@RequestMapping(value = "/search", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("isAuthenticated()")
public RestResponse<GetMultipleDataResult> searchGroups(@RequestBody FilteredSearchRequest searchRequest) {
GetMultipleDataResult searchResult = alienGroupDao.search(searchRequest.getQuery(), searchRequest.getFilters(), searchRequest.getFrom(),
searchRequest.getSize());
return RestResponseBuilder.<GetMultipleDataResult> builder().data(searchResult).build();
}
/**
* Get multiple groups from their names.
*
* @param ids
* The list of ids of the groups to retrieve.
* @return a rest {@link RestResponse} containing the group matching the
* given names.
*/
@ApiOperation(value = "Get multiple groups from their ids.", notes = "Returns a rest response that contains the list of requested groups.")
@RequestMapping(value = "/getGroups", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("isAuthenticated()")
public RestResponse<List<Group>> getGroups(@RequestBody List<String> ids) {
if (ids == null || ids.isEmpty()) {
throw new InvalidArgumentException("ids cannot be null or empty");
}
List<Group> groups = alienGroupDao.find(ids.toArray(new String[ids.size()]));
return RestResponseBuilder.<List<Group>> builder().data(groups).build();
}
/**
* Add a role to a given group.
*
* @param groupId
* The unique id of the group for which to add a role.
* @param role
* The role to add to the group.
*/
@ApiOperation(value = "Add a role to a group.")
@RequestMapping(value = "/{groupId}/roles/{role}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('ADMIN')")
@Audit
public RestResponse<Void> addRoleToGroup(@PathVariable String groupId, @PathVariable String role) {
if (groupId == null || groupId.isEmpty()) {
throw new InvalidArgumentException("groupId cannot be null or empty");
}
groupService.addRoleToGroup(groupId, role);
return RestResponseBuilder.<Void> builder().build();
}
/**
* Removes a role from a given group.
*
* @param groupId
* The unique id of the group from which to remove a role.
* @param role
* The role to remove from the group.
* @return an empty (void) rest {@link RestResponse}.
*/
@ApiOperation(value = "Remove a role from a group.")
@RequestMapping(value = "/{groupId}/roles/{role}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('ADMIN')")
@Audit
public RestResponse<Void> removeRoleFromGroup(@PathVariable String groupId, @PathVariable String role) {
if (groupId == null || groupId.isEmpty()) {
throw new InvalidArgumentException("groupId cannot be null or empty");
}
groupService.removeRoleFromGroup(groupId, role);
return RestResponseBuilder.<Void> builder().build();
}
/**
* Add a user to a group.
*
* @param groupId
* The group in which to add the given user.
* @param username
* The username of the user to add to the group.
* @return added user
*/
@ApiOperation(value = "Add a user to a group.")
@RequestMapping(value = "/{groupId}/users/{username}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('ADMIN')")
@Audit
public RestResponse<User> addUserToGroup(@PathVariable String groupId, @PathVariable String username) {
if (groupId == null || groupId.isEmpty()) {
throw new InvalidArgumentException("groupId cannot be null or empty");
}
User user;
if (!isInternalAllUserGroup(groupId)) {
user = groupService.addUserToGroup(username, groupId);
} else {
log.info("You can not update the group with id <{}> corresponding to an internal group <{}>", groupId, Constants.GROUP_NAME_ALL_USERS);
return RestResponseBuilder
.<User> builder()
.data(null)
.error(RestErrorBuilder
.builder(RestErrorCode.INTERNAL_OBJECT_ERROR)
.message(
"You can not update the group with id <" + groupId + "> corresponding to an internal group <"
+ Constants.GROUP_NAME_ALL_USERS + ">").build()).build();
}
return RestResponseBuilder.<User> builder().data(user).build();
}
/**
* remove a user from a group.
*
* @param groupId
* The group from which to remove the given user.
* @param username
* The username of the user to remove from the group.
* @return response which contain the removed user
*/
@ApiOperation(value = "Remove a user from a group.")
@RequestMapping(value = "/{groupId}/users/{username}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAuthority('ADMIN')")
@Audit
public RestResponse<User> removeUserFromGroup(@PathVariable String groupId, @PathVariable String username) {
if (groupId == null || groupId.isEmpty()) {
throw new InvalidArgumentException("groupId cannot be null or empty");
}
User user;
if (!isInternalAllUserGroup(groupId)) {
user = groupService.removeUserFromGroup(username, groupId);
} else {
log.info("You can not update the group with id <{}> corresponding to an internal group <{}>", groupId, Constants.GROUP_NAME_ALL_USERS);
return RestResponseBuilder
.<User> builder()
.data(null)
.error(RestErrorBuilder
.builder(RestErrorCode.INTERNAL_OBJECT_ERROR)
.message(
"You can not update the group with id <" + groupId + "> corresponding to an internal group <"
+ Constants.GROUP_NAME_ALL_USERS + ">").build()).build();
}
return RestResponseBuilder.<User> builder().data(user).build();
}
/**
* Determines if the requested group is the internal one corresponding to
* "All users"
*
* @param groupId the group id to verify
* @return boolean
*/
private boolean isInternalAllUserGroup(String groupId) {
Group group = alienGroupDao.find(groupId);
return group != null && Constants.GROUP_NAME_ALL_USERS.equals(group.getName());
}
}