sirius.web.security.LDAPUserManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sirius-web Show documentation
Show all versions of sirius-web Show documentation
Provides a modern and scalable web server as SIRIUS module
/*
* 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.Sets;
import sirius.kernel.di.std.Register;
import sirius.kernel.extensions.Extension;
import sirius.kernel.health.Exceptions;
import sirius.web.http.WebContext;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapName;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
/**
* Uses an LDAP directory to authenticate users.
*/
public class LDAPUserManager extends GenericUserManager {
/**
* Used to create ldap user managers.
*/
@Register(name = "ldap")
public static class Factory implements UserManagerFactory {
@Nonnull
@Override
public UserManager createManager(@Nonnull ScopeInfo scope, @Nonnull Extension config) {
return new LDAPUserManager(scope, config);
}
}
@SuppressWarnings("unchecked")
protected LDAPUserManager(ScopeInfo scope, Extension config) {
super(scope, config);
this.authPrefix = config.get("authPrefix").asString(config.get("prefix").asString());
this.authSuffix = config.get("authSuffix").asString(config.get("suffix").asString());
this.searchPrefix = config.get("searchPrefix").asString(config.get("prefix").asString());
this.searchSuffix = config.get("searchSuffix").asString(config.get("suffix").asString());
this.server = config.get("server").asString();
this.useSSL = config.get("ssl").asBoolean(false);
this.objectClass = config.get("objectClass").asString("user");
this.nameAttribute = config.get("nameAttribute").asString("userPrincipalName");
List attrs =
(List) config.get("returnedAtts").get(List.class, Collections.singletonList("memberOf"));
this.returnedAtts = attrs.toArray(new String[attrs.size()]);
this.searchBase = config.get("searchBase").asString();
this.requiredRoles = (List) config.get("requiredRoles").get(List.class, Collections.emptyList());
if (sessionStorage == SESSION_STORAGE_TYPE_CLIENT) {
UserContext.LOG.WARN(
"LDAPUserManager (ldap) for scope %s does not support 'client' as session type! Switching to 'server'.",
scope.getScopeType());
sessionStorage = SESSION_STORAGE_TYPE_SERVER;
}
}
@Override
public UserInfo findUserByName(WebContext ctx, String user) {
return null;
}
private String authPrefix;
private String authSuffix;
private String searchPrefix;
private String searchSuffix;
private String server;
private boolean useSSL;
private String objectClass;
private String nameAttribute;
private String[] returnedAtts;
private String searchBase;
private final List requiredRoles;
@Override
public UserInfo findUserByCredentials(@Nullable WebContext wc, String user, String password) {
try {
String logonUser = authPrefix + user + authSuffix;
String searchUser = searchPrefix + user + searchSuffix;
log("User: %s, logonUser: %s, searchUser: %s", user, logonUser, searchUser);
DirContext ctx = createInitialContext(password, logonUser);
try {
NamingEnumeration answer = searchInDirectory(searchUser, ctx);
Set roles = Sets.newTreeSet();
if (answer.hasMoreElements()) {
SearchResult sr = answer.next();
log("Found user: %s", sr.getName());
Set permissions = computePermissions(roles, sr, wc);
if (!permissions.containsAll(requiredRoles)) {
return null;
}
return UserInfo.Builder.createUser(user).withUsername(user).withPermissions(permissions).build();
}
} finally {
ctx.close();
}
return null;
} catch (AuthenticationException e) {
log("Auth-Exception for %s: %s", user, e.getMessage());
return null;
} catch (Throwable e) {
throw Exceptions.handle(UserContext.LOG, e);
}
}
private DirContext createInitialContext(String password, String logonUser) throws NamingException {
Hashtable env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, server);
if (useSSL) {
log("using ssl...");
env.put(Context.SECURITY_PROTOCOL, "ssl");
}
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, logonUser);
env.put(Context.SECURITY_CREDENTIALS, password);
return new InitialDirContext(env);
}
private Set computePermissions(Set roles, SearchResult sr, @Nullable WebContext ctx) {
Attributes attrs = sr.getAttributes();
if (attrs != null) {
extractRoles(roles, attrs);
}
Set permissions = transformRoles(roles, ctx != null && ctx.isTrusted());
permissions.add(UserInfo.PERMISSION_LOGGED_IN);
return permissions;
}
@SuppressWarnings("unchecked")
private void extractRoles(Set roles, Attributes attrs) {
try {
NamingEnumeration ae = (NamingEnumeration) attrs.getAll();
while (ae.hasMore()) {
Attribute attr = ae.next();
NamingEnumeration