sirius.web.security.UserInfo Maven / Gradle / Ivy
Show all versions of sirius-web Show documentation
/*
* 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 sirius.kernel.commons.Strings;
import sirius.kernel.di.transformers.Composable;
import sirius.kernel.di.transformers.Transformable;
import sirius.kernel.health.Exceptions;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
/**
* Represents an user.
*
* A user is authenticated using a {@link UserManager}. To obtain or modify the current user, use {@link
* UserContext#getCurrentUser()} or {@link UserContext#setCurrentUser(UserInfo)}.
*/
public class UserInfo extends Composable {
/**
* This permission represents a user which was successfully authenticated by its user manager.
*/
public static final String PERMISSION_LOGGED_IN = "flag-logged-in";
/**
* Fallback user if no user is currently available. This user has no permissions.
*/
public static final UserInfo NOBODY = Builder.createUser("ANONYMOUS").withUsername("(no user)").build();
/**
* Represents a special permission which is never granted - therefore {@link #hasPermission(String)} will always
* return false.
*/
private static final String DISABLED = "disabled";
/**
* Represents a special permission which is always granted - therefore {@link #hasPermission(String)} will always
* return true.
*/
private static final String ENABLED = "enabled";
protected String tenantId;
protected String tenantName;
protected String userId;
protected String username;
protected String lang;
protected Set permissions = null;
protected Function settingsSupplier;
protected Function userSupplier;
/**
* Builder pattern to create a new {@link UserInfo}.
*/
public static class Builder {
private UserInfo user;
private Builder() {
}
/**
* Creates a new builder and initializes it with an id for the user.
*
* @param id the id of the user to build.
* @return the builder itself for fluent method calls
*/
@CheckReturnValue
public static Builder createUser(@Nonnull String id) {
Builder builder = new Builder();
builder.user = new UserInfo();
builder.user.userId = id;
return builder;
}
/**
* Sets the name of the user.
*
* @param name the name of the user.
* @return the builder itself for fluent method calls
*/
public Builder withUsername(String name) {
verifyState();
user.username = name;
return this;
}
/**
* Sets the id of the tenant the user belongs to.
*
* @param id the id of the tenant
* @return the builder itself for fluent method calls
*/
public Builder withTenantId(String id) {
verifyState();
user.tenantId = id;
return this;
}
/**
* Sets the name of the tenant the user belongs to.
*
* @param name the name of the tenant
* @return the builder itself for fluent method calls
*/
public Builder withTenantName(String name) {
verifyState();
user.tenantName = name;
return this;
}
private void verifyState() {
if (user == null) {
throw new IllegalStateException("UserInfo already built.");
}
}
/**
* Sets the language code of the user.
*
* @param lang a two-letter language code which should be understood by {@link sirius.kernel.nls.NLS}.
* @return the builder itself for fluent method calls
*/
public Builder withLang(String lang) {
verifyState();
user.lang = lang;
return this;
}
/**
* Sets the permissions granted to the user.
*
* @param permissions the set of permissions granted to the user
* @return the builder itself for fluent method calls
*/
public Builder withPermissions(Set permissions) {
verifyState();
user.permissions = permissions;
return this;
}
/**
* Sets a config supplier which can provide an individual configuration for the current user.
*
* @param settingsSupplier the function which fetches or computes the configuration for this user on demand.
* @return the builder itself for fluent method calls
*/
public Builder withSettingsSupplier(Function settingsSupplier) {
verifyState();
user.settingsSupplier = settingsSupplier;
return this;
}
/**
* Sets a user supplier which returns the underlying user object (e.g. a database entity).
*
* @param userSupplier the function which fetches or computes the user object
* @return the builder itself for fluent method calls
*/
public Builder withUserSupplier(Function userSupplier) {
verifyState();
user.userSupplier = userSupplier;
return this;
}
/**
* Builds the user, with the previously given settings.
*
* @return the resulting user
*/
public UserInfo build() {
UserInfo result = user;
user = null;
return result;
}
}
protected UserInfo() {
}
/**
* Returns the unique ID of the user.
*
* @return the unique ID of the user
*/
public String getUserId() {
return userId;
}
/**
* Returns the login or descriptive name of the user.
*
* @return the name of the user
*/
public String getUserName() {
return username;
}
/**
* The unique ID of the tenant.
*
* @return the unique ID the tenant the user belongs to
*/
@Nullable
public String getTenantId() {
return tenantId;
}
/**
* The name of the tenant.
*
* @return the name of the tenant the user belongs to
*/
@Nullable
public String getTenantName() {
return tenantName;
}
/**
* The language code of the user.
*
* @return the two-letter language code of the user
*/
public String getLang() {
return lang;
}
/**
* Determines if the user has the requested permissions
*
* @param permissions the permissions to check
* @return true if the user has all the requested permissions, false otherwise
*/
public boolean hasPermissions(String... permissions) {
for (String permission : permissions) {
if (!hasPermission(permission)) {
return false;
}
}
return true;
}
/**
* Determines if the user has the given permission.
*
* Next to plain permission names, permissions can also negated using !permission and on top of that,
* whole
* logical expressions in DNF (disjuctive normal form)can be passed in.
*
* Such a formula is a set of expressions where a , represents an or and a + represents an
* and. An example would be "logged-in,important-customer+!locked". This would translate to "the user has
* to be logged in or it has to be an important customer and not be locked".
*
* @param permission the permission to check
* @return true if the user has the permission, false otherwise
*/
public boolean hasPermission(String permission) {
if (Strings.isEmpty(permission)) {
return true;
}
if (DISABLED.equals(permission)) {
return false;
}
if (ENABLED.equals(permission)) {
return true;
}
for (String orClause : permission.split(",")) {
if (permissionsFullfilled(orClause)) {
return true;
}
}
return false;
}
protected boolean permissionsFullfilled(String permissions) {
for (String permission : permissions.split("\\+")) {
if (!permissionFullfilled(permission)) {
return false;
}
}
return true;
}
protected boolean permissionFullfilled(String permission) {
if (permission.startsWith("!")) {
return permissions == null || !permissions.contains(permission.substring(1));
} else {
return permissions != null && permissions.contains(permission);
}
}
/**
* Asserts that the user has the given permission.
*
* If the user does not have the given permission, an exception is thrown.
*
* @param permission the permission to check
*/
public void assertPermission(String permission) {
if (!hasPermission(permission)) {
throw Exceptions.createHandled()
.withNLSKey("UserInfo.missingPermission")
.set("permission", permission)
.handle();
}
}
/**
* Determines if the user is logged in.
*
* @return true if the user has the permission {@link #PERMISSION_LOGGED_IN}, false otherwise
*/
public boolean isLoggedIn() {
return hasPermission(PERMISSION_LOGGED_IN);
}
/**
* Fetches the underlying user object of the given type.
*
* @param clazz the excepted type of the user object
* @param the excepted type of the user object
* @return the underlying user object or null if no object is present or if it has a non matching type
*/
@SuppressWarnings("unchecked")
@Nullable
public T getUserObject(Class clazz) {
if (userSupplier == null) {
return null;
}
Object user = userSupplier.apply(this);
if (user != null && clazz.isAssignableFrom(user.getClass())) {
return (T) user;
}
return null;
}
@Override
public boolean is(@Nonnull Class> type) {
Transformable userObject = getUserObject(Transformable.class);
if (userObject != null) {
return userObject.is(type);
}
return super.is(type);
}
@SuppressWarnings("unchecked")
@Override
public Optional tryAs(@Nonnull Class adapterType) {
Transformable userObject = getUserObject(Transformable.class);
if (userObject != null) {
return userObject.tryAs(adapterType);
}
return super.tryAs(adapterType);
}
/**
* Returns a set of all permissions granted to the user.
*
* @return all permissions granted to the user.
*/
public Set getPermissions() {
return Collections.unmodifiableSet(permissions);
}
/**
* Obtains the user specific config.
*
* This can be used to make parts of the system behave specific to the current scope, current tenant and user.
*
* @return the config object which contains all settings of the current scope, current tenant and user.
*/
public UserSettings getSettings() {
if (settingsSupplier == null) {
return UserContext.getCurrentScope().getSettings();
} else {
return settingsSupplier.apply(this);
}
}
}