All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sirius.biz.tenants.UserAccountController Maven / Gradle / Ivy

There is a newer version: 9.6
Show newest version
/*
 * Made with all the love in the world
 * by scireum in Remshalden, Germany
 *
 * Copyright by scireum GmbH
 * http://www.scireum.de - [email protected]
 */

package sirius.biz.tenants;

import sirius.biz.model.LoginData;
import sirius.biz.model.PermissionData;
import sirius.biz.model.PersonData;
import sirius.biz.web.BizController;
import sirius.biz.web.PageHelper;
import sirius.db.mixing.Entity;
import sirius.db.mixing.SmartQuery;
import sirius.db.mixing.constraints.Like;
import sirius.kernel.commons.Context;
import sirius.kernel.commons.Strings;
import sirius.kernel.di.std.ConfigValue;
import sirius.kernel.di.std.Part;
import sirius.kernel.di.std.Register;
import sirius.kernel.health.Exceptions;
import sirius.kernel.nls.NLS;
import sirius.web.controller.AutocompleteHelper;
import sirius.web.controller.Controller;
import sirius.web.controller.DefaultRoute;
import sirius.web.controller.Message;
import sirius.web.controller.Routed;
import sirius.web.http.WebContext;
import sirius.web.mails.Mails;
import sirius.web.security.LoginRequired;
import sirius.web.security.Permission;
import sirius.web.security.UserContext;
import sirius.web.services.JSONStructuredOutput;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * Provides a GUI for managing user accounts.
 */
@Register(classes = Controller.class, framework = "biz.tenants")
public class UserAccountController extends BizController {

    /**
     * The permission required to add, modify or delete accounts
     */
    public static final String PERMISSION_MANAGE_USER_ACCOUNTS = "permission-manage-user-accounts";

    private static final String PARAM_PASSWORD = "password";
    private static final String PARAM_CONFIRMATION = "confirmation";
    private static final String PARAM_NAME = "name";
    private static final String PARAM_USERNAME = "username";
    private static final String PARAM_URL = "url";
    private static final String PARAM_ROOT = "root";
    private static final String PARAM_EMAIL = "email";
    private static final String PARAM_REASON = "reason";

    @Part
    private Mails mails;

    @ConfigValue("product.wondergemRoot")
    private String wondergemRoot;

    @ConfigValue("security.roles")
    private List roles;

    /**
     * Shows a list of all available users of the current tenant.
     *
     * @param ctx the current request
     */
    @Routed("/user-accounts")
    @DefaultRoute
    @LoginRequired
    @Permission(PERMISSION_MANAGE_USER_ACCOUNTS)
    public void accounts(WebContext ctx) {
        PageHelper ph = PageHelper.withQuery(oma.select(UserAccount.class)
                                                             .orderAsc(UserAccount.PERSON.inner(PersonData.LASTNAME))
                                                             .orderAsc(UserAccount.PERSON.inner(PersonData.FIRSTNAME)));
        ph.forCurrentTenant();
        ph.withContext(ctx);
        ph.withSearchFields(UserAccount.EMAIL,
                            UserAccount.LOGIN.inner(LoginData.USERNAME),
                            UserAccount.PERSON.inner(PersonData.FIRSTNAME),
                            UserAccount.PERSON.inner(PersonData.LASTNAME));

        ctx.respondWith().template("templates/tenants/user-accounts.html.pasta", ph.asPage());
    }

    /**
     * Shows an editor for the given account.
     *
     * @param ctx       the current request
     * @param accountId the {@link UserAccount} to edit
     */
    @Routed("/user-account/:1")
    @LoginRequired
    @Permission(PERMISSION_MANAGE_USER_ACCOUNTS)
    public void account(WebContext ctx, String accountId) {
        UserAccount userAccount = findForTenant(UserAccount.class, accountId);

        boolean requestHandled = prepareSave(ctx).withAfterCreateURI("/user-account/${id}")
                                                 .withAfterSaveURI("/user-accounts")
                                                 .withPreSaveHandler(isNew -> {
                                                     userAccount.getPermissions().getPermissions().clear();
                                                     for (String role : ctx.getParameters("roles")) {
                                                         // Ensure that only real roles end up in the permissions list,
                                                         // as roles, permissions and flags later end up in the same vector
                                                         // therefore we don't want nothing else but user roles in this list
                                                         if (getRoles().contains(role)) {
                                                             userAccount.getPermissions().getPermissions().add(role);
                                                         }
                                                     }
                                                 })
                                                 .saveEntity(userAccount);

        if (!requestHandled) {
            validate(userAccount);
            ctx.respondWith().template("templates/tenants/user-account-details.html.pasta", userAccount, this);
        }
    }

    /**
     * Shows an editor for the custom configuration of the given user.
     *
     * @param ctx       the current request
     * @param accountId the id of the account which config will be edited
     */
    @Routed("/user-account/:1/config")
    @LoginRequired
    @Permission(PERMISSION_MANAGE_USER_ACCOUNTS)
    public void accountConfig(WebContext ctx, String accountId) {
        UserAccount userAccount = findForTenant(UserAccount.class, accountId);
        assertNotNew(userAccount);
        ctx.respondWith().template("templates/tenants/user-account-config.html.pasta", userAccount);
    }

    /**
     * Provides a JSON API to change the settings of an account, including its configuration.
     *
     * @param ctx       the current request
     * @param out       the JSON response being generated
     * @param accountId the id of the account to update
     */
    @Routed(value = "/user-account/:1/update", jsonCall = true)
    @LoginRequired
    @Permission(PERMISSION_MANAGE_USER_ACCOUNTS)
    public void accountUpdate(WebContext ctx, JSONStructuredOutput out, String accountId) {
        UserAccount userAccount = findForTenant(UserAccount.class, accountId);
        assertNotNew(userAccount);
        load(ctx, userAccount);
        if (ctx.hasParameter(UserAccount.PERMISSIONS.inner(PermissionData.CONFIG_STRING).getName())) {
            userAccount.getPermissions().getConfig();
        }
        oma.update(userAccount);
    }

    /**
     * Lists all roles which can be granted to a user.
     *
     * @return all roles which can be granted to a user
     */
    public List getRoles() {
        return Collections.unmodifiableList(roles);
    }

    /**
     * Returns the translated name of a role.
     *
     * @param role the role to translate
     * @return a translated name for the given role
     */
    public String getRoleName(String role) {
        return NLS.get("Role." + role);
    }

    /**
     * Returns a description of the role.
     *
     * @param role the role to fetch the description for
     * @return the description of the given role
     */
    public String getRoleDescription(String role) {
        return NLS.get("Role." + role + ".description");
    }

    /**
     * Shows an editor to change the password of an account.
     *
     * @param ctx the current request
     * @param id  the account to change the password for
     */
    @Routed("/user-account/:1/password")
    @LoginRequired
    @Permission(PERMISSION_MANAGE_USER_ACCOUNTS)
    public void password(final WebContext ctx, String id) {
        UserAccount userAccount = findForTenant(UserAccount.class, id);
        assertNotNew(userAccount);

        if (ctx.isPOST()) {
            try {
                String password = ctx.get(PARAM_PASSWORD).asString();
                String confirmation = ctx.get(PARAM_CONFIRMATION).asString();
                userAccount.getLogin().verifyPassword(password, confirmation, userAccount.getMinPasswordLength());
                userAccount.getLogin().setCleartextPassword(password);
                oma.update(userAccount);
                showSavedMessage();
                accounts(ctx);
                return;
            } catch (Exception e) {
                UserContext.handle(e);
            }
        }
        ctx.respondWith().template("templates/tenants/user-account-password.html.pasta", userAccount);
    }

    /**
     * Generates a new password for the given account and send a mail to the user.
     *
     * @param ctx the current request
     * @param id  the account for which a password is to be created
     */
    @Routed("/user-account/:1/generate-password")
    @LoginRequired
    @Permission(PERMISSION_MANAGE_USER_ACCOUNTS)
    public void generatePassword(final WebContext ctx, String id) {
        UserAccount userAccount = findForTenant(UserAccount.class, id);
        assertNotNew(userAccount);

        userAccount.getLogin().setGeneratedPassword(Strings.generatePassword());
        oma.update(userAccount);

        UserContext.message(Message.info(NLS.fmtr("UserAccountConroller.passwordGenerated")
                                            .set(PARAM_EMAIL, userAccount.getEmail())
                                            .format()));

        if (Strings.isFilled(userAccount.getEmail())) {
            Context context = Context.create()
                                     .set(PARAM_PASSWORD, userAccount.getLogin().getGeneratedPassword())
                                     .set(PARAM_NAME, userAccount.getPerson().getAddressableName())
                                     .set(PARAM_USERNAME, userAccount.getLogin().getUsername())
                                     .set(PARAM_URL, getBaseUrl())
                                     .set(PARAM_ROOT, wondergemRoot);
            mails.createEmail()
                 .to(userAccount.getEmail(), userAccount.getPerson().toString())
                 .subject(NLS.get("mail-password.subject"))
                 .textTemplate("mail/useraccount/password.pasta", context)
                 .htmlTemplate("mail/useraccount/password.html.pasta", context)
                 .send();
        }

        accounts(ctx);
    }

    /**
     * Provides a JSON API which re-sends the password to the account with the given email address.
     *
     * @param ctx the current request
     * @param out the JSON response being generated
     */
    @Routed(value = "/forgotPassword", jsonCall = true)
    public void forgotPassword(final WebContext ctx, JSONStructuredOutput out) {
        List accounts = oma.select(UserAccount.class)
                                        .eq(UserAccount.EMAIL, ctx.get(PARAM_EMAIL).asString())
                                        .limit(2)
                                        .queryList();
        if (accounts.isEmpty()) {
            throw Exceptions.createHandled().withNLSKey("UserAccountController.noUserFoundForEmail").handle();
        }

        if (accounts.size() > 1) {
            throw Exceptions.createHandled().withNLSKey("UserAccountController.tooManyUsersFoundForEmail").handle();
        }

        UserAccount account = accounts.get(0);
        if (account.getLogin().isAccountLocked()) {
            throw Exceptions.createHandled().withNLSKey("LoginData.accountIsLocked").handle();
        }
        account.getLogin().setGeneratedPassword(Strings.generatePassword());
        oma.update(account);

        if (Strings.isFilled(account.getEmail())) {
            Context context = Context.create()
                                     .set(PARAM_REASON,
                                          NLS.fmtr("UserAccountController.forgotPassword.reason")
                                             .set("ip", ctx.getRemoteIP().toString())
                                             .format())
                                     .set(PARAM_PASSWORD, account.getLogin().getGeneratedPassword())
                                     .set(PARAM_NAME, account.getPerson().getAddressableName())
                                     .set(PARAM_USERNAME, account.getLogin().getUsername())
                                     .set(PARAM_URL, getBaseUrl())
                                     .set(PARAM_ROOT, wondergemRoot);
            mails.createEmail()
                 .to(account.getEmail(), account.getPerson().toString())
                 .subject(NLS.get("mail-password.subject"))
                 .textTemplate("mail/useraccount/password.pasta", context)
                 .htmlTemplate("mail/useraccount/password.html.pasta", context)
                 .send();
        }
    }

    /**
     * Deletes the given account.
     *
     * @param ctx the current request
     * @param id  the account to delete
     */
    @LoginRequired
    @Routed("/user-account/:1/delete")
    @Permission(PERMISSION_MANAGE_USER_ACCOUNTS)
    public void deleteAdmin(final WebContext ctx, String id) {
        Optional account = tryFindForTenant(UserAccount.class, id);
        if (account.isPresent()) {
            oma.delete(account.get());
            showDeletedMessage();
        }
        accounts(ctx);
    }

    /**
     * Executes a logout for the current scope.
     *
     * @param ctx the current request
     */
    @Routed("/logout")
    public void logout(WebContext ctx) {
        UserContext.get().getUserManager().detachFromSession(getUser(), ctx);
        ctx.respondWith().redirectToGet(wondergemRoot);
    }

    /**
     * Autocompletion for UserAccounts.
     * 

* Only accepts UserAccounts which belong to the current Tenant. * * @param ctx the current request */ @LoginRequired @Routed("/user-accounts/autocomplete") public void usersAutocomplete(final WebContext ctx) { AutocompleteHelper.handle(ctx, (query, result) -> { oma.select(UserAccount.class) .eq(UserAccount.TENANT, currentTenant().getId()) .where(Like.allWordsInAnyField(query, UserAccount.EMAIL, UserAccount.LOGIN.inner(LoginData.USERNAME), UserAccount.PERSON.inner(PersonData.FIRSTNAME), UserAccount.PERSON.inner(PersonData.LASTNAME))) .limit(10) .iterateAll(userAccount -> { result.accept(new AutocompleteHelper.Completion(userAccount.getIdAsString(), userAccount.toString(), userAccount.toString())); }); }); } /** * Lists all users which the current user can "become" (switch to). * * @param ctx the current request */ @Routed("/user-accounts/select") @LoginRequired @Permission(TenantUserManager.PERMISSION_SELECT_USER_ACCOUNT) public void selectUserAccounts(WebContext ctx) { SmartQuery baseQuery = oma.select(UserAccount.class) .orderAsc(UserAccount.PERSON.inner(PersonData.LASTNAME)) .orderAsc(UserAccount.PERSON.inner(PersonData.FIRSTNAME)); if (!UserContext.getCurrentUser().hasPermission(TenantUserManager.PERMISSION_SYSTEM_TENANT)) { baseQuery.eq(UserAccount.TENANT, currentTenant()); } baseQuery.fields(Entity.ID, UserAccount.PERSON.inner(PersonData.LASTNAME), UserAccount.PERSON.inner(PersonData.FIRSTNAME), UserAccount.LOGIN.inner(LoginData.USERNAME), UserAccount.TENANT.join(Tenant.NAME), UserAccount.TENANT.join(Tenant.ACCOUNT_NUMBER)); PageHelper ph = PageHelper.withQuery(baseQuery); ph.withContext(ctx); ph.withSearchFields(UserAccount.PERSON.inner(PersonData.LASTNAME), UserAccount.PERSON.inner(PersonData.FIRSTNAME), UserAccount.LOGIN.inner(LoginData.USERNAME), UserAccount.EMAIL, UserAccount.TENANT.join(Tenant.NAME), UserAccount.TENANT.join(Tenant.ACCOUNT_NUMBER)); ctx.respondWith() .template("templates/tenants/select-user-account.html.pasta", ph.asPage(), isCurrentlySpying(ctx)); } private boolean isCurrentlySpying(WebContext ctx) { return ctx.getSessionValue(UserContext.getCurrentScope().getScopeId() + TenantUserManager.SPY_ID_SUFFIX) .isFilled(); } /** * Makes the current user belong to the given tenant. * * @param ctx the current request * @param id the id of the tenant to switch to */ @LoginRequired @Routed("/user-accounts/select/:1") public void selectUserAccount(final WebContext ctx, String id) { if ("main".equals(id)) { ctx.setSessionValue(UserContext.getCurrentScope().getScopeId() + TenantUserManager.SPY_ID_SUFFIX, null); ctx.respondWith().redirectTemporarily("/user-accounts/select"); return; } assertPermission(TenantUserManager.PERMISSION_SELECT_USER_ACCOUNT); UserAccount user = oma.find(UserAccount.class, id).orElse(null); if (user == null) { UserContext.get().addMessage(Message.error(NLS.get("UserAccountController.cannotBecomeUser"))); selectUserAccounts(ctx); } else { if (!UserContext.getCurrentUser().hasPermission(TenantUserManager.PERMISSION_SYSTEM_TENANT)) { assertTenant(user); } ctx.setSessionValue(UserContext.getCurrentScope().getScopeId() + TenantUserManager.SPY_ID_SUFFIX, user.getUniqueName()); ctx.respondWith().redirectTemporarily(ctx.get("goto").asString(wondergemRoot)); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy