de.steilerdev.myVerein.server.controller.admin.UserManagementController.java Source code

Java tutorial

Introduction

Here is the source code for de.steilerdev.myVerein.server.controller.admin.UserManagementController.java

Source

/**
 * Copyright (C) 2014 Frank Steiler <frank@steilerdev.de>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package de.steilerdev.myVerein.server.controller.admin;

import de.steilerdev.myVerein.server.model.*;
import de.steilerdev.myVerein.server.security.CurrentUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.ConstraintViolationException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * This controller is processing all requests associated with the user management.
 */
@RestController
@RequestMapping("/api/admin/user")
public class UserManagementController {
    private static Logger logger = LoggerFactory.getLogger(UserManagementController.class);

    @Autowired
    DivisionRepository divisionRepository;

    @Autowired
    UserRepository userRepository;

    @Autowired
    SettingsRepository settingsRepository;

    @Autowired
    EventRepository eventRepository;

    /**
     * If a modification on a division needs to be stored durable, this function is invoked by POSTing the parameters to the URI /api/admin/user.
     * @param firstName The first name of the user.
     * @param lastName The last name of the user.
     * @param email The email (unique identifier) of the user.
     * @param birthday The birthday of the user (Formatted: d/MM/y according to Java 8 DateTimeFormatter).
     * @param password The initial password of a new user (Only allowed for a new user).
     * @param gender The gender of the user.
     * @param street The street of the user's address.
     * @param streetNumber The street number of the user's address.
     * @param zip The zip code of the user's address.
     * @param city The city of the user's address.
     * @param country The country of the user.
     * @param activeMemberSince The date, when the user became an active member of the club (Formatted: d/MM/y according to Java 8 DateTimeFormatter).
     * @param passiveMemberSince The date, when the user became a passive member of the club (Formatted: d/MM/y according to Java 8 DateTimeFormatter).
     * @param resignationDate The date, when the user resigned (Formatted: d/MM/y according to Java 8 DateTimeFormatter).
     * @param iban The IBAN of the user's bank account.
     * @param bic The BIC of the user's bank account.
     * @param divisions A comma seperated list of divisions, the user is part of.
     * @param userFlag If it is a new user, the user flag is "true", otherwise it is holding the identifier (email) of the user, in case it changed.
     * @param parameters The complete map of all parameters, containing the custom user fields.
     * @param currentUser The currently logged in user.
     * @return An HTTP response with a status code. If an error occurred an error message is bundled into the response, otherwise a success message is available.
     */
    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<String> saveUser(@RequestParam String firstName, @RequestParam String lastName,
            @RequestParam String email, @RequestParam String birthday, @RequestParam String password,
            @RequestParam(required = false) String gender, @RequestParam String street,
            @RequestParam String streetNumber, @RequestParam String zip, @RequestParam String city,
            @RequestParam String country, @RequestParam String activeMemberSince,
            @RequestParam String passiveMemberSince, @RequestParam String resignationDate,
            @RequestParam String iban, @RequestParam String bic, @RequestParam String divisions,
            @RequestParam String userFlag, @RequestParam Map<String, String> parameters,
            @CurrentUser User currentUser) {
        logger.trace("[" + currentUser + "] Starting to save a user.");
        User userObject;

        logger.debug("[" + currentUser + "] Loading user");

        if (email.isEmpty() || userFlag.isEmpty()) {
            logger.warn("[" + currentUser + "] The email/user flag can not be empty");
            return new ResponseEntity<>("The email can not be empty", HttpStatus.BAD_REQUEST);
        } else if (userFlag.equals("true")) {
            logger.info("[" + currentUser + "] A new user is created using the email " + email);
            if (userRepository.findByEmail(email) == null) {
                userObject = new User();
                if (password.isEmpty()) {
                    logger.warn(
                            "[" + currentUser + "] The password of the new user " + email + " can not be empty");
                    return new ResponseEntity<>("The password can not be empty", HttpStatus.BAD_REQUEST);
                } else {
                    userObject.setPassword(password);
                }
            } else {
                logger.warn("[" + currentUser + "] A user with the given email " + email + " already exists");
                return new ResponseEntity<>("A user with the given email already exists.", HttpStatus.BAD_REQUEST);
            }
        } else {
            logger.info("[" + currentUser + "] An existing user " + userFlag + " is modified");
            if ((userObject = userRepository.findByEmail(userFlag)) == null) {
                logger.warn("[{}] Unable to find existing user object", currentUser);
                return new ResponseEntity<>("Unable to retrieve the user", HttpStatus.BAD_REQUEST);
            } else if (!userObject.getEmail().equals(email) && userRepository.findByEmail(email) != null) {
                logger.warn("[{}] The new email {} is already taken", currentUser, email);
                return new ResponseEntity<>("The new email is already taken", HttpStatus.BAD_REQUEST);
            }
        }

        logger.debug("[" + currentUser + "] Checking permissions");

        if (!currentUser.isAllowedToAdministrate(userObject, divisionRepository)) {
            logger.warn("[" + currentUser + "] The user is not allowed to save the user");
            return new ResponseEntity<>("You are not allowed to perform these changes.", HttpStatus.FORBIDDEN);
        }

        logger.debug("[" + currentUser + "] Parsing parameters and updating user");

        if (firstName.isEmpty() || lastName.isEmpty()) {
            logger.warn("[" + currentUser + "] Required parameter missing");
            return new ResponseEntity<>("Required parameter missing", HttpStatus.BAD_REQUEST);
        }
        userObject.setFirstName(firstName);
        userObject.setLastName(lastName);
        userObject.setEmail(email);

        if (!birthday.isEmpty()) {
            logger.debug("[" + currentUser + "] Parsing birthday for " + userObject.getEmail());
            try {
                userObject.setBirthday(LocalDate.parse(birthday, DateTimeFormatter.ofPattern("d/MM/y")));
            } catch (DateTimeParseException e) {
                logger.warn("[" + currentUser + "] Unrecognized date format (" + birthday + ")");
                return new ResponseEntity<>("Wrong date format within birthday field", HttpStatus.BAD_REQUEST);
            }
        } else {
            logger.debug("[" + currentUser + "] Clearing birthday field for " + userObject.getEmail());
            userObject.setBirthday(null);
        }

        if (gender != null && !gender.isEmpty() && !gender.equals("default")) {
            logger.debug("[" + currentUser + "] Parsing gender for " + userObject.getEmail());
            try {
                userObject.setGender(User.Gender.valueOf(gender));
            } catch (IllegalArgumentException e) {
                logger.warn("[" + currentUser + "] Unable to parse gender: " + e.getMessage());
                return new ResponseEntity<>("Unable to parse gender", HttpStatus.BAD_REQUEST);
            }
        } else {
            logger.debug("[" + currentUser + "] Clearing gender field for " + userObject.getEmail());
            userObject.setGender(null);
        }

        userObject.setStreet(street);
        userObject.setStreetNumber(streetNumber);
        userObject.setZipCode(zip);
        userObject.setCity(city);
        userObject.setCountry(country);

        if (!activeMemberSince.isEmpty()) {
            logger.debug("[" + currentUser + "] Parsing active member field for " + userObject.getEmail());
            try {
                userObject
                        .setActiveSince(LocalDate.parse(activeMemberSince, DateTimeFormatter.ofPattern("d/MM/y")));
            } catch (DateTimeParseException e) {
                logger.warn("[" + currentUser + "] Unrecognized date format (" + activeMemberSince + ")");
                return new ResponseEntity<>("Wrong date format within active member field", HttpStatus.BAD_REQUEST);
            }
        } else {
            logger.debug("[" + currentUser + "] Clearing active member field for " + userObject.getEmail());
            userObject.setActiveSince(null);
        }

        if (!passiveMemberSince.isEmpty()) {
            logger.debug("[" + currentUser + "] Parsing passive member field for " + userObject.getEmail());
            try {
                userObject.setPassiveSince(
                        LocalDate.parse(passiveMemberSince, DateTimeFormatter.ofPattern("d/MM/y")));
            } catch (DateTimeParseException e) {
                logger.warn("[" + currentUser + "] Unrecognized date format (" + passiveMemberSince + ")");
                return new ResponseEntity<>("Wrong date format within passive member field",
                        HttpStatus.BAD_REQUEST);
            }
        } else {
            logger.debug("[" + currentUser + "] Clearing passive member field for " + userObject.getEmail());
            userObject.setPassiveSince(null);
        }

        if (!resignationDate.isEmpty()) {
            logger.debug("[" + currentUser + "] Parsing resignation date field for " + userObject.getEmail());
            try {
                userObject.setResignationDate(
                        LocalDate.parse(resignationDate, DateTimeFormatter.ofPattern("d/MM/y")));
            } catch (DateTimeParseException e) {
                logger.warn("[" + currentUser + "] Unrecognized date format (" + resignationDate + ")");
                return new ResponseEntity<>("Wrong date format within resignation date field",
                        HttpStatus.BAD_REQUEST);
            }
        } else {
            logger.debug("[" + currentUser + "] Clearing resignation date field for " + userObject.getEmail());
            userObject.setResignationDate(null);
        }

        userObject.setIban(iban);
        userObject.setBic(bic);

        if (!divisions.isEmpty()) {
            logger.debug("[" + currentUser + "] Parsing divisions for " + userObject.getEmail());
            String[] divArray = divisions.split(",");
            List<Division> divisionList = new ArrayList<>();
            for (String division : divArray) {
                Division div = divisionRepository.findByName(division);
                if (div == null) {
                    logger.warn("[" + currentUser + "] Unrecognized division (" + division + ")");
                    return new ResponseEntity<>("Division " + division + " does not exist", HttpStatus.BAD_REQUEST);
                } else {
                    divisionList.add(div);
                }
            }
            userObject.replaceDivisions(divisionRepository, eventRepository, divisionList);
        } else {
            logger.debug("[" + currentUser + "] Clearing divisions for " + userObject.getEmail());
            userObject.replaceDivisions(divisionRepository, eventRepository, (List<Division>) null);
        }

        logger.debug("[" + currentUser + "] Parsing and setting custom user fields");
        userObject.setCustomUserField(parameters.keySet().parallelStream()
                .filter(key -> key.startsWith("cuf_") && !parameters.get(key).trim().isEmpty()) //Filtering all custom user fields, which are not empty
                .collect(Collectors.toMap(key -> key.substring(4), key -> parameters.get(key).trim()))); //Creating map of all fields

        logger.debug("[" + currentUser + "] Saving user " + userObject.getEmail());

        try {
            logger.debug("[{}] Saving user {}", currentUser, userObject);
            userRepository.save(userObject);
            logger.info("[" + currentUser + "] Successfully saved user " + userObject.getEmail());
            return new ResponseEntity<>("Successfully saved user", HttpStatus.OK);
        } catch (ConstraintViolationException e) {
            logger.warn("[" + currentUser + "] A database constraint was violated while saving the user: "
                    + e.getMessage());
            return new ResponseEntity<>("A database constraint was violated while saving the user.",
                    HttpStatus.BAD_REQUEST);
        }
    }

    /**
     * This function gathers all users. The function is invoked by GETting the URI /api/admin/user.
     * @param term If this parameter is present, only users who are matching the term within their email, first or last name are returned.
     * @return An HTTP response with a status code, together with the JSON list-object of all users (matching the term, if present), or only an error code if an error occurred.
     */
    @RequestMapping(produces = "application/json", method = RequestMethod.GET)
    public ResponseEntity<List<User>> getUserList(@RequestParam(required = false) String term,
            @CurrentUser User currentUser) {
        List<User> userList;
        if (term == null || term.isEmpty()) {
            logger.trace("[" + currentUser + "] Retrieving all users");
            userList = userRepository.findAllEmailAndName();
        } else {
            logger.trace("[" + currentUser + "] Retrieving all users using the search term " + term);
            userList = userRepository.findAllEmailAndNameContainingString(term);
        }

        if (userList == null) {
            logger.warn(
                    "[" + currentUser + "] Unable to get users" + (term != null ? " matching term " + term : ""));
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        } else {
            userList.replaceAll(User::getSendingObjectOnlyEmailNameId);
            logger.info(
                    "[" + currentUser + "] Returning all users" + (term != null ? " matching term " + term : ""));
            return new ResponseEntity<>(userList, HttpStatus.OK);
        }
    }

    /**
     * This function returns a single user as JSON object, respecting the privacy of the user, by only returning information the current user is allowed to see. The function is invoked by GETting the URI /api/admin/user using the parameter email.
     * @param email The email of the user.
     * @param currentUser The currently logged in user.
     * @return An HTTP response with a status code, together with the JSON object of the user, or only an error code if an error occurred.
     */
    @RequestMapping(produces = "application/json", params = "email", method = RequestMethod.GET)
    public ResponseEntity<User> getUser(@RequestParam String email, @CurrentUser User currentUser) {
        logger.trace("[" + currentUser + "] Getting user " + email);
        User searchedUser, queriedUser;
        if (email.isEmpty()) {
            logger.warn("[" + currentUser + "] The email is not allowed to be empty");
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        } else if ((queriedUser = userRepository.findByEmail(email)) == null) {
            logger.warn("[" + currentUser + "] Unable to retrieve user with the email " + email);
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        } else if (!currentUser.isAllowedToAdministrate(queriedUser, divisionRepository)) {
            logger.warn("[" + currentUser + "] The user is not administrating selected user ("
                    + queriedUser.getEmail() + "). Hiding private information");
            searchedUser = queriedUser.getSendingObjectNoPrivateInformation();
            searchedUser.setAdministrationNotAllowedMessage(
                    "You are not allowed to modify this user, since you are not his administrator");
        } else {
            logger.debug("[" + currentUser + "] Currently logged in user (" + currentUser.getEmail()
                    + ") is administrating selected user (" + queriedUser.getEmail() + ")");
            searchedUser = queriedUser.getSendingObject();
            searchedUser.setAdministrationNotAllowedMessage(null);
            //Adding all custom user fields defined within the settings file to the object
            Settings settings = Settings.loadSettings(settingsRepository);
            if (settings.getCustomUserFields() != null && !settings.getCustomUserFields().isEmpty()) {
                settings.getCustomUserFields().stream()
                        .forEach(fieldKey -> searchedUser.addCustomUserField(fieldKey, "", false));
            } else if (searchedUser.getCustomUserField() == null || searchedUser.getCustomUserField().isEmpty()) {
                searchedUser.setCustomUserField(null);
            }
        }

        logger.info("[" + currentUser + "] Returning user " + searchedUser.getEmail());
        return new ResponseEntity<>(searchedUser, HttpStatus.OK);
    }

    /**
     * This function is deleting a user, defined by his email. The function is invoked by DELETEing the URI /api/admin/user.
     * @param email The email of the user, who needs to be deleted.
     * @param currentUser The currently logged in user.
     * @return An HTTP response with a status code. If an error occurred an error message is bundled into the response, otherwise a success message is available.
     */
    @RequestMapping(method = RequestMethod.DELETE)
    public ResponseEntity<String> deleteUser(@RequestParam String email, @CurrentUser User currentUser) {
        logger.trace("[" + currentUser + "] Deleting user " + email);
        User deletedUser;
        if (email.isEmpty()) {
            logger.warn("[" + currentUser + "] The email is not allowed to be empty");
            return new ResponseEntity<>("The email is not allowed to be empty", HttpStatus.BAD_REQUEST);
        } else if ((deletedUser = userRepository.findByEmail(email)) == null) {
            logger.warn("[" + currentUser + "] Unable to find stated user " + email);
            return new ResponseEntity<>("Unable to find the stated user", HttpStatus.INTERNAL_SERVER_ERROR);
        } else if (!currentUser.isAllowedToAdministrate(deletedUser, divisionRepository)) {
            logger.warn(
                    "[" + currentUser + "] The user is not allowed to delete the user " + deletedUser.getEmail());
            return new ResponseEntity<>("You are not allowed to delete the selected user", HttpStatus.BAD_REQUEST);
        } else {
            try {
                logger.trace("[" + currentUser + "] Deleting user " + deletedUser.getEmail());
                userRepository.delete(deletedUser);
                logger.info("[" + currentUser + "] Successfully delete the user " + deletedUser.getEmail());
                return new ResponseEntity<>("Successfully deleted selected user", HttpStatus.OK);
            } catch (IllegalArgumentException e) {
                logger.warn("[" + currentUser + "] Unable to delete selected user: " + e.getMessage());
                return new ResponseEntity<>("Unable to delete the selected user", HttpStatus.INTERNAL_SERVER_ERROR);
            }
        }
    }
}