/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ngrinder.user.controller;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.gson.annotations.Expose;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
import org.ngrinder.common.constant.ControllerConstants;
import org.ngrinder.common.controller.BaseController;
import org.ngrinder.common.controller.RestAPI;
import org.ngrinder.infra.config.Config;
import org.ngrinder.model.Permission;
import org.ngrinder.model.Role;
import org.ngrinder.model.User;
import org.ngrinder.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import static org.ngrinder.common.util.CollectionUtils.newArrayList;
import static org.ngrinder.common.util.ObjectUtils.defaultIfNull;
import static org.ngrinder.common.util.Preconditions.*;
/**
* User management controller.
*
* @author JunHo Yoon
* @author Alex Quin
* @since 3.0
*/
@Controller
@RequestMapping("/user")
public class UserController extends BaseController {
@Autowired
private UserService userService;
@Autowired
protected Config config;
public static final Sort DEFAULT_SORT = new Sort(Direction.ASC, "userName");
/**
* Get user list on the given role.
*
* @param model model
* @param role role
* @param pageable page info
* @param keywords search keyword.
* @return user/userList
*/
@PreAuthorize("hasAnyRole('A')")
@RequestMapping({"", "/"})
public String getAll(ModelMap model, @RequestParam(required = false) Role role,
@PageableDefault(page = 0, size = 10) Pageable pageable,
@RequestParam(required = false) String keywords) {
pageable = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), defaultIfNull(pageable.getSort(), DEFAULT_SORT));
Pageable defaultPageable = new PageRequest(0, pageable.getPageSize(), defaultIfNull(pageable.getSort(), DEFAULT_SORT));
Page<User> pagedUser;
if (StringUtils.isEmpty(keywords)) {
pagedUser = userService.getPagedAll(role, pageable);
if (pagedUser.getNumberOfElements() == 0) {
pagedUser = userService.getPagedAll(role, defaultPageable);
}
} else {
pagedUser = userService.getPagedAll(keywords, pageable);
if (pagedUser.getNumberOfElements() == 0) {
pagedUser = userService.getPagedAll(keywords, defaultPageable);
}
model.put("keywords", keywords);
}
model.addAttribute("users", pagedUser);
EnumSet<Role> roleSet = EnumSet.allOf(Role.class);
model.addAttribute("roleSet", roleSet);
model.addAttribute("role", role);
putPageIntoModelMap(model, pageable);
return "user/list";
}
/**
* Get user creation form page.
*
* @param user current user
* @param model mode
* @return "user/detail"
*/
@RequestMapping("/new")
@PreAuthorize("hasAnyRole('A') or #user.userId == #userId")
public String openForm(User user, final ModelMap model) {
User one = User.createNew();
model.addAttribute("user", one);
model.addAttribute("allowUserIdChange", true);
model.addAttribute("allowPasswordChange", true);
model.addAttribute("allowRoleChange", false);
model.addAttribute("newUser", true);
model.addAttribute("roleSet", EnumSet.allOf(Role.class));
model.addAttribute("showPasswordByDefault", true);
attachCommonAttribute(one, model);
return "user/detail";
}
/**
* Get user detail page.
*
* @param model mode
* @param userId user to get
* @return "user/detail"
*/
@RequestMapping("/{userId}")
@PreAuthorize("hasAnyRole('A')")
public String getOne(@PathVariable final String userId, ModelMap model) {
User one = userService.getOne(userId);
model.addAttribute("user", one);
model.addAttribute("allowPasswordChange", true);
model.addAttribute("allowRoleChange", true);
model.addAttribute("roleSet", EnumSet.allOf(Role.class));
model.addAttribute("showPasswordByDefault", false);
attachCommonAttribute(one, model);
return "user/detail";
}
/**
* Get the current user profile.
*
* @param user current user
* @param model model
* @return "user/info"
*/
@RequestMapping("/profile")
public String getOne(User user, ModelMap model) {
checkNotEmpty(user.getUserId(), "UserID should not be NULL!");
User one = userService.getOne(user.getUserId());
model.addAttribute("user", one);
model.addAttribute("allowPasswordChange", !config.isDemo());
model.addAttribute("allowRoleChange", false);
model.addAttribute("showPasswordByDefault", false);
attachCommonAttribute(one, model);
return "user/info";
}
/**
* Save or Update user detail info.
*
* @param user current user
* @param model model
* @param updatedUser user to be updated.
* @return "redirect:/user/list" if current user change his info, otherwise return "redirect:/"
*/
@RequestMapping("/save")
@PreAuthorize("hasAnyRole('A') or #user.id == #updatedUser.id")
public String save(User user, @ModelAttribute("user") User updatedUser, ModelMap model) {
checkArgument(updatedUser.validate());
if (user.getRole() == Role.USER) {
// General user can not change their role.
User updatedUserInDb = userService.getOne(updatedUser.getUserId());
checkNotNull(updatedUserInDb);
updatedUser.setRole(updatedUserInDb.getRole());
// prevent user to modify with other user id
checkArgument(updatedUserInDb.getId().equals(updatedUser.getId()), "Illegal request to update user:%s",
updatedUser);
}
save(updatedUser);
model.clear();
if (user.getId().equals(updatedUser.getId())) {
return "redirect:/";
} else {
return "redirect:/user/";
}
}
private User save(User user) {
if (StringUtils.isBlank(user.getPassword())) {
return userService.saveWithoutPasswordEncoding(user);
} else {
return userService.save(user);
}
}
/**
* Delete users.
*
* @param model model
* @param userIds comma separated user ids.
* @return "redirect:/user/"
*/
@PreAuthorize("hasAnyRole('A')")
@RequestMapping("/delete")
public String delete(User user, @RequestParam String userIds, ModelMap model) {
String[] ids = userIds.split(",");
for (String eachId : Arrays.asList(ids)) {
if (!user.getUserId().equals(eachId)) {
userService.delete(eachId);
}
}
model.clear();
return "redirect:/user/";
}
/**
* Get the follower list.
*
* @param user current user
* @param keywords search keyword.
* @return json message
*/
@RestAPI
@RequestMapping("/api/switch_options")
public HttpEntity<String> switchOptions(User user,
@RequestParam(required = true) final String keywords) {
return toJsonHttpEntity(getSwitchableUsers(user, keywords));
}
/**
* Get the follower list.
*
* @param user current user
* @param model model
* @return json message
*/
@RequestMapping("/switch_options")
public String switchOptions(User user,
ModelMap model) {
model.addAttribute("switchableUsers", getSwitchableUsers(user, ""));
return "user/switch_options";
}
private List<UserSearchResult> getSwitchableUsers(User user, String keywords) {
List<UserSearchResult> result = newArrayList();
if (user.getRole().hasPermission(Permission.SWITCH_TO_ANYONE)) {
for (User each : userService.getPagedAll(keywords, new PageRequest(0, 10))) {
result.add(new UserSearchResult(each));
}
} else {
User currUser = userService.getOne(user.getUserId());
for (User each : currUser.getOwners()) {
result.add(new UserSearchResult(each));
}
}
return result;
}
/**
* Switch user identity.
*
* @param model model
* @param to the user to whom a user will switch
* @param response response
* @return redirect:/perftest/
*/
@RequestMapping("/switch")
public String switchUser(@RequestParam(required = false, defaultValue = "") String to,
HttpServletRequest request, HttpServletResponse response, ModelMap model) {
Cookie cookie = new Cookie("switchUser", to);
cookie.setPath("/");
// Delete Cookie if empty switchUser
if (StringUtils.isEmpty(to)) {
cookie.setMaxAge(0);
}
response.addCookie(cookie);
model.clear();
final String referer = request.getHeader("referer");
return "redirect:" + StringUtils.defaultIfBlank(referer, "/");
}
/**
* Get user list that current user will be shared, excluding current user.
*
* @param user current user
* @param model model
*/
protected void attachCommonAttribute(User user, ModelMap model) {
List list = user.getFollowers() == null ? Lists.newArrayList() : user.getFollowers();
model.addAttribute("followers", Lists.transform(list, new Function<User, UserSearchResult>() {
public UserSearchResult apply(User user) {
return new UserSearchResult(user);
}
}));
model.addAttribute("allowShareChange", true);
model.addAttribute("userSecurityEnabled", config.isUserSecurityEnabled());
}
/**
* Check if the given user id already exists.
*
* @param userId userId to be checked
* @return success json if true.
*/
@RestAPI
@PreAuthorize("hasAnyRole('A')")
@RequestMapping("/api/{userId}/check_duplication")
public HttpEntity<String> checkDuplication(@PathVariable String userId) {
User user = userService.getOne(userId);
return (user == null) ? successJsonHttpEntity() : errorJsonHttpEntity();
}
/**
* Get users by the given role.
*
* @param role user role
* @return json message
*/
@RestAPI
@PreAuthorize("hasAnyRole('A')")
@RequestMapping(value = {"/api/", "/api"}, method = RequestMethod.GET)
public HttpEntity<String> getAll(Role role) {
return toJsonHttpEntity(userService.getAll(role));
}
/**
* Get the user by the given user id.
*
* @param userId user id
* @return json message
*/
@RestAPI
@PreAuthorize("hasAnyRole('A')")
@RequestMapping(value = "/api/{userId}", method = RequestMethod.GET)
public HttpEntity<String> getOne(@PathVariable("userId") String userId) {
return toJsonHttpEntity(userService.getOne(userId));
}
/**
* Create an user.
*
* @param newUser new user
* @return json message
*/
@RestAPI
@PreAuthorize("hasAnyRole('A')")
@RequestMapping(value = {"/api/", "/api"}, method = RequestMethod.POST)
public HttpEntity<String> create(@ModelAttribute("user") User newUser) {
checkNull(newUser.getId(), "User DB ID should be null");
return toJsonHttpEntity(save(newUser));
}
/**
* Update the user.
*
* @param userId user id
* @param update update user
* @return json message
*/
@RestAPI
@PreAuthorize("hasAnyRole('A')")
@RequestMapping(value = "/api/{userId}", method = RequestMethod.PUT)
public HttpEntity<String> update(@PathVariable("userId") String userId, User update) {
update.setUserId(userId);
checkNull(update.getId(), "User DB ID should be null");
return toJsonHttpEntity(save(update));
}
/**
* Delete the user by the given userId.
*
* @param userId user id
* @return json message
*/
@RestAPI
@PreAuthorize("hasAnyRole('A')")
@RequestMapping(value = "/api/{userId}", method = RequestMethod.DELETE)
public HttpEntity<String> delete(User user, @PathVariable("userId") String userId) {
if (!user.getUserId().equals(userId)) {
userService.delete(userId);
}
return successJsonHttpEntity();
}
/**
* Search user list on the given keyword.
*
* @param pageable page info
* @param keywords search keyword.
* @return json message
*/
@RestAPI
@RequestMapping(value = "/api/search", method = RequestMethod.GET)
public HttpEntity<String> search(User user, @PageableDefault Pageable pageable,
@RequestParam(required = true) String keywords) {
pageable = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(),
defaultIfNull(pageable.getSort(),
new Sort(Direction.ASC, "userName")));
Page<User> pagedUser = userService.getPagedAll(keywords, pageable);
List<UserSearchResult> result = newArrayList();
for (User each : pagedUser) {
result.add(new UserSearchResult(each));
}
final String currentUserId = user.getUserId();
CollectionUtils.filter(result, new Predicate() {
@Override
public boolean evaluate(Object object) {
UserSearchResult each = (UserSearchResult) object;
return !(each.getId().equals(currentUserId) || each.getId().equals(ControllerConstants.NGRINDER_INITIAL_ADMIN_USERID));
}
});
return toJsonHttpEntity(result);
}
public static class UserSearchResult {
@Expose
final private String id;
@Expose
final private String text;
public UserSearchResult(User user) {
id = user.getUserId();
final String email = user.getEmail();
final String userName = user.getUserName();
if (StringUtils.isEmpty(email)) {
this.text = userName + " (" + id + ")";
} else {
this.text = userName + " (" + email + " / " + id + ")";
}
}
public String getText() {
return text;
}
public String getId() {
return id;
}
}
}