/*
* This program is part of the OpenLMIS logistics management information system platform software.
* Copyright © 2013 VillageReach
*
* 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. For additional information contact info@OpenLMIS.org.
*/
package org.openlmis.web.controller;
import lombok.NoArgsConstructor;
import org.openlmis.core.domain.ConfigurationSettingKey;
import org.openlmis.core.domain.Pagination;
import org.openlmis.core.domain.RightName;
import org.openlmis.core.domain.User;
import org.openlmis.core.exception.DataException;
import org.openlmis.core.service.ConfigurationSettingService;
import org.openlmis.core.service.FacilityService;
import org.openlmis.core.service.RoleRightsService;
import org.openlmis.core.service.UserService;
import org.openlmis.core.web.OpenLmisResponse;
import org.openlmis.core.web.controller.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.stereotype.Controller;
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.RequestParam;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static java.lang.Boolean.TRUE;
import static java.lang.Integer.parseInt;
import static org.openlmis.authentication.web.UserAuthenticationSuccessHandler.USER;
import static org.openlmis.authentication.web.UserAuthenticationSuccessHandler.USER_ID;
import static org.openlmis.core.domain.RightName.MANAGE_USER;
import static org.openlmis.core.web.OpenLmisResponse.*;
import static org.springframework.http.HttpStatus.*;
import static org.springframework.web.bind.annotation.RequestMethod.*;
/**
* This controller handles endpoints to related to user management like resetting password, disabling a user, etc.
*/
@Controller
@NoArgsConstructor
public class UserController extends BaseController {
public static final String TOKEN_VALID = "TOKEN_VALID";
public static final String USERS = "userList";
static final String MSG_USER_DISABLE_SUCCESS = "msg.user.disable.success";
static final String USER_CREATED_SUCCESS_MSG = "message.user.created.success.email.sent";
private static final String RESET_PASSWORD_PATH = "/public/pages/reset-password.html#/token/";
@Value("${mail.base.url}")
public String baseUrl;
@Autowired
private RoleRightsService roleRightService;
@Autowired
private UserService userService;
@Autowired
private SessionRegistry sessionRegistry;
@Autowired
private FacilityService facilityService;
@Autowired
private ConfigurationSettingService settingService;
@RequestMapping(value = "/user-context", method = GET, headers = ACCEPT_JSON)
public ResponseEntity<OpenLmisResponse> user(HttpServletRequest httpServletRequest) {
Long userId = (Long) httpServletRequest.getSession().getAttribute(USER_ID);
if (userId != null) {
String userName = (String) httpServletRequest.getSession().getAttribute(USER);
User userObject = userService.getById(userId);
OpenLmisResponse openLmisResponse = new OpenLmisResponse("name", userName);
openLmisResponse.addData("authenticated", TRUE);
openLmisResponse.addData("userId", userId);
openLmisResponse.addData("rights", roleRightService.getRights(userId));
openLmisResponse.addData("preferences", userService.getPreferences(userId));
openLmisResponse.addData("fullName", userObject.getFullName() );
openLmisResponse.addData("homePage", settingService.getConfigurationStringValue(ConfigurationSettingKey.LOGIN_SUCCESS_DEFAULT_LANDING_PAGE));
return openLmisResponse.response(OK);
} else {
return authenticationError();
}
}
@RequestMapping(value = "/authentication-error", method = POST, headers = ACCEPT_JSON)
public ResponseEntity<OpenLmisResponse> authenticationError() {
return error(messageService.message("user.login.error"), UNAUTHORIZED);
}
@RequestMapping(value = "/forgot-password", method = POST, headers = ACCEPT_JSON)
public ResponseEntity<OpenLmisResponse> sendPasswordTokenEmail(@RequestBody User user) {
try {
String resetPasswordLink = baseUrl + RESET_PASSWORD_PATH;
userService.sendForgotPasswordEmail(user, resetPasswordLink);
return success(messageService.message("email.sent"));
} catch (DataException e) {
return error(e, BAD_REQUEST);
}
}
@RequestMapping(value = "/users", method = POST, headers = ACCEPT_JSON)
@PreAuthorize("@permissionEvaluator.hasPermission(principal,'MANAGE_USER')")
public ResponseEntity<OpenLmisResponse> create(@RequestBody User user, HttpServletRequest request) {
user.setCreatedBy(loggedInUserId(request));
user.setModifiedBy(loggedInUserId(request));
if (user.isMobileUser()) {
user.setVerified(true);
} else {
user.setIsMobileUser(false);
}
try {
String resetPasswordBaseLink = baseUrl + RESET_PASSWORD_PATH;
userService.create(user, resetPasswordBaseLink);
} catch (DataException e) {
return error(e, BAD_REQUEST);
}
ResponseEntity<OpenLmisResponse> success = success(USER_CREATED_SUCCESS_MSG);
success.getBody().addData("user", user);
return success;
}
@RequestMapping(value = "/users/{id}", method = PUT, headers = ACCEPT_JSON)
@PreAuthorize("@permissionEvaluator.hasPermission(principal,'MANAGE_USER')")
public ResponseEntity<OpenLmisResponse> update(@RequestBody User user,
@PathVariable("id") Long id,
HttpServletRequest request) {
user.setModifiedBy(loggedInUserId(request));
user.setId(id);
if (user.isMobileUser()) {
user.setVerified(true);
} else {
user.setIsMobileUser(false);
}
try {
userService.update(user);
} catch (DataException e) {
return error(e, BAD_REQUEST);
}
return new OpenLmisResponse().response(OK);
}
@RequestMapping(value = "/users", method = GET)
@PreAuthorize("@permissionEvaluator.hasPermission(principal,'MANAGE_USER')")
public ResponseEntity<OpenLmisResponse> searchUser(@RequestParam(value = "searchParam", required = false) String searchParam,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@Value("${search.page.size}") String limit) {
Pagination pagination = new Pagination(page, parseInt(limit));
pagination.setTotalRecords(userService.getTotalSearchResultCount(searchParam));
List<User> users = userService.searchUser(searchParam, pagination);
ResponseEntity<OpenLmisResponse> response = OpenLmisResponse.response(USERS, users);
response.getBody().addData("pagination", pagination);
return response;
}
@RequestMapping(value = "/users/{id}", method = GET)
@PreAuthorize("@permissionEvaluator.hasPermission(principal,'MANAGE_USER')")
public User get(@PathVariable(value = "id") Long id) {
return userService.getUserWithRolesById(id);
}
@RequestMapping(value = "/users/{id}", method = DELETE, headers = ACCEPT_JSON)
public ResponseEntity<OpenLmisResponse> disable(@PathVariable("id") Long id,
HttpServletRequest request) {
userService.disable(id, loggedInUserId(request));
deactivateUserSessions(id);
return success(MSG_USER_DISABLE_SUCCESS);
}
private void deactivateUserSessions(Long id) {
List<Object> principals = sessionRegistry.getAllPrincipals();
List<SessionInformation> disabledUserSessions = new ArrayList<>();
if (principals.contains(id)) {
Object disabledUserPrincipal = getDisabledUserPrincipal(principals, id);
disabledUserSessions = sessionRegistry.getAllSessions(disabledUserPrincipal, false);
}
for (SessionInformation disabledUserSession : disabledUserSessions) {
sessionRegistry.getSessionInformation(disabledUserSession.getSessionId()).expireNow();
}
}
private Object getDisabledUserPrincipal(List<Object> principals, Long id) {
Object disabledUserPrincipal = null;
for (Object principal : principals) {
if (principal.equals(id)) {
disabledUserPrincipal = principal;
}
}
return disabledUserPrincipal;
}
@RequestMapping(value = "/user/validatePasswordResetToken/{token}", method = GET)
public ResponseEntity<OpenLmisResponse> validatePasswordResetToken(@PathVariable(value = "token") String token) throws IOException, ServletException {
try {
userService.getUserIdByPasswordResetToken(token);
} catch (DataException e) {
return error(e, BAD_REQUEST);
}
return response(TOKEN_VALID, true);
}
@RequestMapping(value = "/user/resetPassword/{token}", method = PUT)
public ResponseEntity<OpenLmisResponse> resetPassword(@PathVariable(value = "token") String token, @RequestBody String password) {
try {
userService.updateUserPassword(token, password);
} catch (DataException e) {
return error(e, BAD_REQUEST);
}
return success(messageService.message("password.reset"));
}
@RequestMapping(value = "/admin/resetPassword/{userId}", method = PUT)
public ResponseEntity<OpenLmisResponse> updateUserPassword(@PathVariable(value = "userId") Long userId, @RequestBody String password) {
try {
userService.updateUserPassword(userId, password);
} catch (DataException e) {
return error(e, BAD_REQUEST);
}
return success(messageService.message("password.reset.success"));
}
@RequestMapping(value = "/user/preferences", method = GET)
public ResponseEntity<OpenLmisResponse> getUserPreferences(HttpServletRequest request){
return response("preferences", userService.getPreferences(this.loggedInUserId(request)));
}
@RequestMapping(value = "/users/{userId}/preferences", method = GET)
public ResponseEntity<OpenLmisResponse> getUserPreferences(@PathVariable("userId") Long userId){
return response("preferences", userService.getPreferences(userId));
}
@RequestMapping(value = "/users/{userId}/preferences", method = PUT, headers = ACCEPT_JSON)
public ResponseEntity<OpenLmisResponse> updateUserPreferences(@PathVariable(value = "userId") Long userId, @RequestParam("programId") Long programId,
@RequestParam("facilityId") Long facilityId, @RequestParam("products") List<Long> productListId,
@RequestParam("geographicZoneId") Long geographicZoneId,
@RequestBody User user,
HttpServletRequest request) {
Long currentUser = loggedInUserId(request);
if (userId.equals(currentUser) || roleRightService.getRights(currentUser).contains(MANAGE_USER)){
user.setModifiedBy(currentUser);
user.setId(userId);
try {
userService.updateUserPreferences(userId, user, programId, facilityId, productListId,geographicZoneId);
} catch (DataException e) {
return error(e, BAD_REQUEST);
}
return success(messageService.message("user.preference.set.successfully"));
}
return new OpenLmisResponse().errorEntity(FORBIDDEN.getReasonPhrase(),FORBIDDEN);
}
@RequestMapping(value = "/preference/users/{id}", method = GET)
public User getUser(@PathVariable(value = "id") Long id, HttpServletRequest request) {
Long userId = loggedInUserId(request);
if (id.equals(userId) || roleRightService.getRights(userId).contains(MANAGE_USER)){
return userService.getUserWithRolesById(id);
}
return null;
}
@RequestMapping(value = "/users/supervisory/rights.json", method= GET)
public ResponseEntity<OpenLmisResponse> getRights(HttpServletRequest request){
return response("rights", userService.getSupervisoryRights(loggedInUserId(request)));
}
@RequestMapping(value = "/users/program/supervised/facilities.json")
public ResponseEntity<OpenLmisResponse> getSupervisedFacilities(@RequestParam Long programId, HttpServletRequest request){
return response("facilities", facilityService.getUserSupervisedFacilities(loggedInUserId(request), programId,
RightName.CREATE_IVD, RightName.VIEW_IVD, RightName.APPROVE_IVD, RightName.CREATE_REQUISITION, RightName.VIEW_REQUISITION, RightName.APPROVE_REQUISITION ));
}
}