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

sirius.web.security.UserContext Maven / Gradle / Ivy

/*
 * 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.web.security;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.typesafe.config.Config;
import sirius.kernel.async.CallContext;
import sirius.kernel.async.SubContext;
import sirius.kernel.commons.Strings;
import sirius.kernel.di.GlobalContext;
import sirius.kernel.di.std.Context;
import sirius.kernel.di.std.Part;
import sirius.kernel.extensions.Extension;
import sirius.kernel.extensions.Extensions;
import sirius.kernel.health.Log;
import sirius.kernel.nls.NLS;
import sirius.web.controller.Message;
import sirius.web.http.WebContext;

import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Used to access the current user and scope.
 * 

* An instance of this class is present in the {@link sirius.kernel.async.CallContext} and takes care of * picking the right user manager to authenticate users or store / load them from a session. *

* This class also manages messages shown to the user. */ public class UserContext implements SubContext { /** * The key used to store the current scope in the MDC */ public static final String MDC_SCOPE = "scope"; /** * The key used to store the current user id in the MDC */ public static final String MDC_USER_ID = "userId"; /** * The key used to store the current user name in the MDC */ public static final String MDC_USER_NAME = "username"; /** * Contains the logger user used by the auth framework */ public static final Log LOG = Log.get("user"); @Part private static ScopeDetector detector; private static Map managers = Maps.newConcurrentMap(); private UserInfo currentUser = null; /* * As getUserForScope will most probalby only hit one other scope * we cache the last user and scope here to speed up almost all cases * without the need for a map. */ private String spaceIdOfCachedUser = null; private UserInfo cachedUser = null; private ScopeInfo currentScope = null; private List msgList = Lists.newArrayList(); private Map fieldErrors = Maps.newHashMap(); @Context private static GlobalContext context; /* * Determines which UserManager to use for a given scope */ private static UserManager getManager(ScopeInfo scope) { Extension ext = Extensions.getExtension("security.scopes", scope.getScopeType()); return context.getPart(ext.get("manager").asString("public"), UserManagerFactory.class) .createManager(scope, ext); } /** * Retrieves the current UserContext from the {@link sirius.kernel.async.CallContext}. * * @return the current user context. */ public static UserContext get() { return CallContext.getCurrent().get(UserContext.class); } /** * Boilerplate method to quickly access the current user. * * @return the current user * @see #getUser() */ public static UserInfo getCurrentUser() { return get().getUser(); } public static Config getConfig() { return get().getUser().getConfig(); } public static H getHelper(Class helperType) { return getCurrentScope().getHelper(helperType); } /** * Boilerplate method to quickly access the current scope. * * @return the currently active scope * @see #getScope() */ public static ScopeInfo getCurrentScope() { return get().getScope(); } /** * Handles the given exception by passing it to {@link sirius.kernel.health.Exceptions} and by creating an * appropriate message for the user. * * @param e the exception to handle. If the given exception is null nothing will happen. */ public static void handle(@Nullable Throwable e) { if (e == null) { return; } message(Message.error(e)); } /** * Adds a message to the current UserContext. * * @param msg the message to add */ public static void message(Message msg) { get().addMessage(msg); } /** * Adds a field error to the current UserContext. * * @param field the field for which an error occurred * @param value the value which was rejected */ public static void setFieldError(String field, Object value) { get().addFieldError(field, NLS.toUserString(value)); } /* * Loads the current scope from the given web context. */ private void bindScopeToRequest(WebContext ctx) { if (ctx != null && ctx.isValid() && detector != null) { ScopeInfo scope = detector.detectScope(ctx); setCurrentScope(scope); CallContext.getCurrent().setLang(scope.getLang()); } else { setCurrentScope(ScopeInfo.DEFAULT_SCOPE); } } /* * Loads the current user from the given web context. */ private void bindUserToRequest(WebContext ctx) { if (ctx != null && ctx.isValid()) { UserManager manager = getUserManager(); UserInfo user = manager.bindToRequest(ctx); setCurrentUser(user); CallContext.getCurrent().setLang(user.getLang()); } else { setCurrentUser(UserInfo.NOBODY); } } /** * Installs the given scope as current scope. *

* For generic web requests, this is not necessary, as the scope is auto-detected. * * @param scope the scope to set */ public void setCurrentScope(ScopeInfo scope) { this.currentScope = scope == null ? ScopeInfo.DEFAULT_SCOPE : scope; if (this.currentUser != null) { this.currentUser = null; CallContext.getCurrent().addToMDC(MDC_USER_ID, null); CallContext.getCurrent().addToMDC(MDC_USER_NAME, null); } Message msg = this.currentScope.tryAs(MaintenanceInfo.class).map(MaintenanceInfo::maintenanceMessage).orElse(null); if (msg != null) { addMessage(msg); } CallContext.getCurrent().addToMDC(MDC_SCOPE, currentScope.getScopeId()); } /** * Installs the given user as current user. *

* For generic web requests, this is not necessary, as the user is auto-detected. * * @param user the user to set */ public void setCurrentUser(UserInfo user) { this.currentUser = user == null ? UserInfo.NOBODY : user; CallContext.getCurrent().addToMDC(MDC_USER_ID, currentUser.getUserId()); CallContext.getCurrent().addToMDC(MDC_USER_NAME, currentUser.getUserName()); } /** * Adds a message to be shown to the user. * * @param msg the message to be shown to the user */ public void addMessage(Message msg) { msgList.add(msg); } /** * Returns all messages to be shown to the user. * * @return a list of messages to be shown to the user */ public List getMessages() { return msgList; } /** * Adds an error for a given field * * @param field the name of the field * @param value the value which was supplied and rejected */ public void addFieldError(String field, String value) { fieldErrors.put(field, value); } /** * Determines if there is an error for the given field * * @param field the field to check for errors * @return true if an error was added for the field, false otherwise */ public boolean hasError(String field) { return fieldErrors.containsKey(field); } /** * Returns "error" if an error was added for the given field. * * @param field the field to check * @return "error" if an error was added for the given field, an empty string otherwise */ public String signalFieldError(String field) { return hasError(field) ? "error" : ""; } /** * Returns the originally submitted field value even if it was rejected due to an error. * * @param field the name of the form field * @param value the entity value (used if no error occurred) * @return the originally submitted value (if an error occurred), the given value otherwise */ public String getFieldValue(String field, Object value) { if (fieldErrors.containsKey(field)) { return fieldErrors.get(field); } return NLS.toUserString(value); } /** * Returns the originally submitted field value even if it was rejected due to an error. * * @param field the name of the form field * @return the originally submitted value (if an error occurred) or the parameter (using field as name) * from the current {@link WebContext} otherwise */ public String getFieldValue(String field) { if (fieldErrors.containsKey(field)) { return fieldErrors.get(field); } return CallContext.getCurrent().get(WebContext.class).get(field).getString(); } /** * Returns all values submitted for the given field * * @param field the name of the field which values should be extracted * @return a list of values submitted for the given field */ public Collection getFieldValues(String field) { return CallContext.getCurrent().get(WebContext.class).getParameters(field); } /** * Returns the current user. *

* If no user is present yet, it tries to parse the current {@link WebContext} and retireve the user from the * session. * * @return the currently active user */ public UserInfo getUser() { if (currentUser == null) { bindUserToRequest(CallContext.getCurrent().get(WebContext.class)); } return currentUser; } /** * Returns the used which would be the current user if the space with the given id would be active. *

* You can use {@link ScopeInfo#DEFAULT_SCOPE} to access the user of the default scope which will * most probably the administrative backend. *

* Note that this method will only check the session ({@link UserManager#findUserForRequest(WebContext)}) and will * not try to perform a login via credentials as given in the current request. * * @param scopeId the id of the scope to fethc the user for * @return the user found for the given scope or {@link UserInfo#NOBODY} if no user was found */ public UserInfo getUserForScope(String scopeId) { if (cachedUser != null && Strings.areEqual(scopeId, spaceIdOfCachedUser)) { return cachedUser; } cachedUser = getUserManagerForScope(scopeId).findUserForRequest(CallContext.getCurrent().get(WebContext.class)); spaceIdOfCachedUser = scopeId; return cachedUser; } /** * Determines if the user is present. *

* This can be either direclty via a {@link #setCurrentUser(UserInfo)} or implicitely via {@link #getUser()} * * @return the currently active user */ public boolean isUserPresent() { return currentUser != null; } /** * Binds the currently active user to the session. *

* This will make the authentication of a user persistent als long as the session remains */ public void attachUserToSession() { WebContext ctx = CallContext.getCurrent().get(WebContext.class); if (ctx == null || !ctx.isValid()) { return; } if (!getUser().isLoggedIn()) { return; } UserManager manager = getUserManager(); manager.attachToSession(getUser(), ctx); } /** * Determines and returns the current user manager. *

* The user manager is determined by the current scope. * * @return the currently active user manager * @see #getCurrentScope() * @see ScopeDetector */ public UserManager getUserManager() { return getUserManagerForScope(getScope().getScopeId()); } private UserManager getUserManagerForScope(String scopeId) { UserManager manager = managers.get(scopeId); if (manager == null) { manager = getManager(currentScope); managers.put(scopeId, manager); } return manager; } /** * Removes the authentication and user identity from the session. *

* This can be considered a logout. */ public void detachUserFromSession() { WebContext ctx = CallContext.getCurrent().get(WebContext.class); if (ctx == null || !ctx.isValid()) { return; } UserManager manager = getUserManager(); manager.detachFromSession(getCurrentUser(), ctx); } /** * Returns the currently active scope. *

* This is determined using the {@link ScopeDetector} or it will always be the {@link ScopeInfo#DEFAULT_SCOPE} if * no scope detector is present. * * @return the currently active scope */ public ScopeInfo getScope() { if (currentScope == null) { bindScopeToRequest(CallContext.getCurrent().get(WebContext.class)); } return currentScope; } @Override public void detach() { // No action needed when this is detached from the current thread... } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy