UserRestEndpoint.java

/*
 * The coLAB project
 * Copyright (C) 2021-2023 AlbaSim, MEI, HEIG-VD, HES-SO
 *
 * Licensed under the MIT License
 */
package ch.colabproject.colab.api.rest.user;

import ch.colabproject.colab.api.controller.RequestManager;
import ch.colabproject.colab.api.controller.user.UserManager;
import ch.colabproject.colab.api.exceptions.ColabMergeException;
import ch.colabproject.colab.api.model.user.*;
import ch.colabproject.colab.api.persistence.jpa.user.UserDao;
import ch.colabproject.colab.generator.model.annotations.AdminResource;
import ch.colabproject.colab.generator.model.annotations.AuthenticationRequired;
import ch.colabproject.colab.generator.model.annotations.ConsentNotRequired;
import ch.colabproject.colab.generator.model.exceptions.HttpErrorMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.List;

/**
 * User controller
 *
 * @author maxence
 */
@Path("users")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class UserRestEndpoint {

    /** logger */
    private static final Logger logger = LoggerFactory.getLogger(UserRestEndpoint.class);

    /**
     * Users related business logic
     */
    @Inject
    private UserManager userManager;

    /**
     * User persistence
     */
    @Inject
    private UserDao userDao;

    /**
     * Request related logic
     */
    @Inject
    private RequestManager requestManager;

    /**
     * Get the authentication method and its parameters a user shall use to authenticate with the
     * given email address.
     *
     * @param identifier email address or username the user want to sign in with
     *
     * @return Authentication method to user
     */
    @GET
    @Path("AuthMethod/{identifier : [^/]*}")
    public AuthMethod getAuthMethod(@PathParam("identifier") String identifier) {
        logger.debug("get auth method for {}", identifier);
        return userManager.getAuthenticationMethod(identifier);
    }

    /**
     * Get all users
     *
     * @return list of all users
     */
    @GET
    @AdminResource
    public List<User> getAllUsers() {
        logger.debug("get all users");
        return userDao.findAllUsers();
    }

    /**
     * Get users related to the given project
     *
     * @param projectId the id of the project
     *
     * @return users list
     */
    @GET
    @Path("byproject/{projectId: [0-9]+}")
    public List<User> getUsersForProject(@PathParam("projectId") Long projectId) {
        logger.debug("Get all users related to project #{}", projectId);
        return userManager.getUsersForProject(projectId);
    }

    /**
     * Return the current authenticated user.
     *
     * @param id id of the user
     *
     * @return user which match the given id or null
     */
    @GET
    @Path("{id: [0-9]+}")
    @AuthenticationRequired
    public User getUserById(@PathParam("id") Long id) {
        logger.debug("get user #{}", id);
        return userManager.getUserById(id);
    }

    /**
     * Return the current authenticated user.
     *
     * @return current user or null
     */
    @GET
    @Path("CurrentUser")
    public User getCurrentUser() {
        logger.debug("get current user");
        return requestManager.getCurrentUser();
    }

    /**
     * Return the current authenticated account.
     *
     * @return current account or null
     */
    @GET
    @Path("CurrentAccount")
    public Account getCurrentAccount() {
        logger.debug("get current account");
        return requestManager.getCurrentAccount();
    }

    /**
     * Get all accounts owned by the current user
     *
     * @return list of accounts
     */
    @GET
    @Path("AllCurrentUserAccount")
    public List<Account> getAllCurrentUserAccounts() {
        logger.debug("get all accounts of current user");
        User user = requestManager.getCurrentUser();
        if (user != null) {
            return user.getAccounts();
        } else {
            return List.of();
        }
    }

    /**
     * Create a new local account.
     *
     * @param signup all data required to create a local account
     *
     * @throws HttpErrorMessage if creation fails for any reason
     */
    @POST
    @Path("SignUp")
    public void signUp(SignUpInfo signup) {
        logger.debug("sign-up {}", signup);
        userManager.signup(signup);
    }

    /**
     * Authenticate with local-account credentials.
     *
     * @param authInfo credentials
     *
     * @throws HttpErrorMessage if authentication fails for any reason
     */
    @POST
    @Path("SignIn")
    public void signIn(AuthInfo authInfo) {
        logger.debug("sign-in {}", authInfo);
        userManager.authenticate(authInfo);
    }

    /**
     * Sign out.
     */
    @POST
    @Path("SignOut")
    public void signOut() {
        logger.debug("sign-out");
        userManager.logout();
    }

    /**
     * Get all active HTTP session for the current user
     *
     * @return list of active HTTP session linked to the current user
     */
    @GET
    @Path("Sessions")
    @AuthenticationRequired
    public List<HttpSession> getActiveHttpSessions() {
        return userManager.getCurrentUserActiveHttpSessions();
    }

    /**
     * Force HTTP session logout
     *
     * @param httpSessionId id of the HTTP session to delete
     */
    @DELETE
    @Path("Session/{id: [0-9]+}")
    @AuthenticationRequired
    public void forceLogout(@PathParam("id") Long httpSessionId) {
        userManager.forceLogout(httpSessionId);
    }

    /**
     * Update user. Only fields which are editable by users will be impacted.
     *
     * @param user user to update
     *
     * @throws ColabMergeException if update fails
     */
    @PUT
    @AuthenticationRequired
    public void updateUser(User user) throws ColabMergeException {
        logger.debug("update user profile: {}", user);
        userDao.updateUser(user);
    }

    /**
     * Update user's agreedTime of given id.
     *
     * @param id id of the user who's agreedTime to update
     */
    @POST
    @Path("{id : [1-9][0-9]*}/updateUserAgreedTime")
    @ConsentNotRequired
    public void updateUserAgreedTime(@PathParam("id") Long id) {
        logger.debug("update agreedTime to user: #{}", id);
        userManager.updateUserAgreedTime(id);
    }

    /**
     * Grant admin right to user identified by given id.
     *
     * @param id id of the user
     */
    @PUT
    @Path("{id : [1-9][0-9]*}/GrantAdminRight")
    @AdminResource
    public void grantAdminRight(@PathParam("id") Long id) {
        logger.debug("Grant admin right to user #{}", id);
        userManager.grantAdminRight(id);
    }

    /**
     * remove admin right to user identified by given id.
     *
     * @param id id of the user
     */
    @PUT
    @Path("{id : [1-9][0-9]*}/RevokeAdminRight")
    @AdminResource
    public void revokeAdminRight(@PathParam("id") Long id) {
        logger.debug("Grant admin right to user #{}", id);
        userManager.revokeAdminRight(id);
    }

    /**
     * Update password of a localAccount. The password MUST be hashed a first time by the client.
     * <p>
     *
     * @param authInfo identifier and new password
     *
     * @throws HttpErrorMessage if currentUser is not allowed to update the given account
     */
    @PUT
    @Path("/UpdatePassword")
    public void updateLocalAccountPassword(AuthInfo authInfo) {
        logger.debug("Update local account \"{}\" password", authInfo.getIdentifier());
        userManager.updatePassword(authInfo);
    }

    /**
     * Request a local account password reset. This method always returns 204 no content
     * <p>
     *
     * @param email email address of account
     */
    @PUT
    @Path("/RequestPasswordReset/{email}")
    public void requestPasswordReset(@PathParam("email") String email) {
        logger.debug("Request password reset token {}", email);
        userManager.requestPasswordReset(email);
    }

    /**
     * Update local account
     *
     * @param account local account with new email address inside
     *
     * @throws ColabMergeException if update fails
     */
    @PUT
    @Path("/ChangeEmail")
    @AuthenticationRequired
    public void updateLocalAccountEmailAddress(LocalAccount account) throws ColabMergeException {
        logger.debug("Update local account email address: {}", account);
        userManager.updateLocalAccountEmailAddress(account);
    }

    /**
     * ask the user to switch to new hash method on next sign-in.
     *
     * @param id id of the local account
     */
    @PUT
    @Path("{id : [1-9][0-9]*}/SwitchClientHashMethod")
    @AdminResource
    public void switchClientHashMethod(@PathParam("id") Long id) {
        logger.debug("Switch client hash method for account #{}", id);
        userManager.switchClientHashMethod(id);
    }

    /**
     * stage new server hash method.
     *
     * @param id id of the local account
     */
    @PUT
    @Path("{id : [1-9][0-9]*}/SwitchServerHashMethod")
    @AdminResource
    public void switchServerHashMethod(@PathParam("id") Long id) {
        logger.debug("Switch server hash method for account #{}", id);
        userManager.switchServerHashMethod(id);
    }

}