This is an automated email from the ASF dual-hosted git repository. heneveld pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git
The following commit(s) were added to refs/heads/master by this push: new 6efba98 Logs the users login attempt status and its roles if required new 1becadb This closes #1218 6efba98 is described below commit 6efba9878343a62c5aa5abaf419f227d3afa21c2 Author: Juan Cabrerizo <j...@cloudsoft.io> AuthorDate: Fri Aug 6 17:50:36 2021 +0100 Logs the users login attempt status and its roles if required --- .../apache/brooklyn/rest/BrooklynWebConfig.java | 11 +++++ .../security/provider/LdapSecurityProvider.java | 54 ++++++++++++++++++++-- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynWebConfig.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynWebConfig.java index 5fa4c7e..f78ccdd 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynWebConfig.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/BrooklynWebConfig.java @@ -26,6 +26,7 @@ import org.apache.brooklyn.core.config.ConfigPredicates; import org.apache.brooklyn.rest.security.provider.DelegatingSecurityProvider; import org.apache.brooklyn.rest.security.provider.ExplicitUsersSecurityProvider; import org.apache.brooklyn.rest.security.provider.SecurityProvider; +import org.apache.brooklyn.util.text.Strings; public class BrooklynWebConfig { @@ -88,6 +89,16 @@ public class BrooklynWebConfig { "Whether user groups should be fetched from the LDAP server", false); + public final static ConfigKey<Boolean> LDAP_LOGIN_INFO_LOG = ConfigKeys.newBooleanConfigKey( + BASE_NAME_SECURITY+".ldap.login_info_log", + "Whether the users attempt to login and its groups are print on the `info` log", + false); + + public final static ConfigKey<String> GROUP_CONFIG_KEY_NAME = ConfigKeys.newStringConfigKey( + BASE_NAME_SECURITY+".ldap.group_config_key", + "Config key name for defining groups-role relationship", + Strings.EMPTY); + public final static ConfigKey<Boolean> HTTPS_REQUIRED = ConfigKeys.newBooleanConfigKey( BASE_NAME+".security.https.required", "Whether HTTPS is required; false here can be overridden by CLI option", false); diff --git a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/LdapSecurityProvider.java b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/LdapSecurityProvider.java index 4360623..00c9bf2 100644 --- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/LdapSecurityProvider.java +++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/security/provider/LdapSecurityProvider.java @@ -26,17 +26,22 @@ import com.google.common.collect.Lists; import java.util.Arrays; import java.util.Hashtable; import java.util.List; +import java.util.Map; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; + import org.apache.brooklyn.api.mgmt.ManagementContext; +import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.config.StringConfigMap; +import org.apache.brooklyn.core.config.ConfigPredicates; import org.apache.brooklyn.rest.BrooklynWebConfig; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.text.Strings; @@ -49,6 +54,14 @@ import static org.apache.brooklyn.core.mgmt.entitlement.WebEntitlementContext.US /** * A {@link SecurityProvider} implementation that relies on LDAP to authenticate. * + * CONFIG EXAMPLE + * brooklyn.webconsole.security.ldap.url=ldap://<server>:<port>/ + * brooklyn.webconsole.security.ldap.realm=<,>realm> + * brooklyn.webconsole.security.ldap.ou=<ou>.<parent_ou> + * brooklyn.webconsole.security.ldap.fetch_user_group=true + * brooklyn.webconsole.security.ldap.group_config_key=<role_resolver_config_key> + * brooklyn.webconsole.security.ldap.login_info_log=true + * * @author Peter Veentjer. */ public class LdapSecurityProvider extends AbstractSecurityProvider implements SecurityProvider { @@ -59,13 +72,22 @@ public class LdapSecurityProvider extends AbstractSecurityProvider implements Se private final String ldapUrl; private final String defaultLdapRealm; private final String organizationUnit; + private boolean logUserLoginAttempt; private boolean fetchUserGroups = false; + private List<String> validGroups; public LdapSecurityProvider(ManagementContext mgmt) { StringConfigMap properties = mgmt.getConfig(); ldapUrl = properties.getConfig(BrooklynWebConfig.LDAP_URL); Strings.checkNonEmpty(ldapUrl, "LDAP security provider configuration missing required property " + BrooklynWebConfig.LDAP_URL); fetchUserGroups = properties.getConfig(BrooklynWebConfig.LDAP_FETCH_USER_GROUPS); + logUserLoginAttempt = properties.getConfig(BrooklynWebConfig.LDAP_LOGIN_INFO_LOG); + String prefix = properties.getConfig(BrooklynWebConfig.GROUP_CONFIG_KEY_NAME); + if (fetchUserGroups && Strings.isNonBlank(prefix)) { + validGroups = getConfiguredGroups(properties, prefix); + } else { + validGroups = ImmutableList.of(); + } String realmConfig = properties.getConfig(BrooklynWebConfig.LDAP_REALM); if (Strings.isNonBlank(realmConfig)) { @@ -98,7 +120,7 @@ public class LdapSecurityProvider extends AbstractSecurityProvider implements Se // InitialDirContext doesn't do authentication if no password is supplied! return false; } - + addToInfoLog("Login attempt with " + user); try { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); @@ -111,17 +133,38 @@ public class LdapSecurityProvider extends AbstractSecurityProvider implements Se if (fetchUserGroups) { List<String> userGroups = getUserGroups(user, ctx); if (userGroups.isEmpty()) { + addToInfoLog("Unsuccessful for " + user); + LOG.trace("User {} is not member of any group", user); return false; } // adds user groups to the session sessionSupplierOnSuccess.get().setAttribute(USER_GROUPS, userGroups); + addToInfoLog("Successful for " + user + " member of " + userGroups); + } else { + addToInfoLog("Successful for " + user); } return allow(sessionSupplierOnSuccess.get(), user); } catch (NamingException e) { + addToInfoLog("Unsuccessful for " + user); return false; } } + private List<String> getConfiguredGroups(StringConfigMap properties, String prefix) { + ImmutableList.Builder<String> configuredGroupsBuilder = ImmutableList.builder(); + StringConfigMap roles = properties.submap(ConfigPredicates.nameStartsWith(prefix + ".")); + for (Map.Entry<ConfigKey<?>, ?> entry : roles.getAllConfigLocalRaw().entrySet()) { + configuredGroupsBuilder.add(Strings.removeFromStart(entry.getKey().getName(), prefix + ".")); + } + return configuredGroupsBuilder.build(); + } + + private void addToInfoLog(String s) { + if (logUserLoginAttempt) { + LOG.info(s); + } + } + private List<String> getUserGroups(String user, DirContext ctx) throws NamingException { ImmutableList.Builder<String> groupsListBuilder = ImmutableList.builder(); @@ -135,14 +178,19 @@ public class LdapSecurityProvider extends AbstractSecurityProvider implements Se Attributes attrs = rslt.getAttributes(); Attribute memberOf = attrs.get("memberOf"); - if(memberOf != null){ + if (memberOf != null) { NamingEnumeration<?> groups = memberOf.getAll(); while (groups.hasMore()) { groupsListBuilder.add(getGroupName(groups.next().toString())); } } } - return groupsListBuilder.build(); + ImmutableList<String> ldapGroups = groupsListBuilder.build(); + if (LOG.isTraceEnabled()) { + LOG.trace("LDAP groups for {}: {}", user, ldapGroups); + } + // only store return the LDAP groups with a matching role in the configuration file + return ldapGroups.stream().filter(ldapGroup -> validGroups.contains(ldapGroup)).collect(Collectors.toList()); } private String buildUserContainer() {