diff --git a/docs/security/shiroauthentication.md b/docs/security/shiroauthentication.md index 348c43889a..e608a141e6 100644 --- a/docs/security/shiroauthentication.md +++ b/docs/security/shiroauthentication.md @@ -128,6 +128,10 @@ Change the following values in the Shiro.ini file, and uncomment the line: ### LDAP +Two options exist for configuring an LDAP Realm. The simpler to use is the LdapGroupRealm. How ever it has limited +flexibility with mapping of ldap groups to users and for authorization for user groups. A sample configuration file for +this realm is given below. + ``` ldapRealm = org.apache.zeppelin.realm.LdapGroupRealm # search base for ldap groups (only relevant for LdapGroupRealm): @@ -137,6 +141,46 @@ ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM ldapRealm.contextFactory.authenticationMechanism = simple ``` +The other more flexible option is to use the LdapRealm. It allows for mapping of ldapgroups to roles and also allows for + role/group based authentication into the zeppelin server. Sample configuration for this realm is given below. + ``` +[main] +ldapRealm=org.apache.zeppelin.realm.LdapRealm + +ldapRealm.contextFactory.authenticationMechanism=simple +ldapRealm.contextFactory.url=ldap://localhost:33389 +ldapRealm.userDnTemplate=uid={0},ou=people,dc=hadoop,dc=apache,dc=org +# Ability to set ldap paging Size if needed default is 100 +ldapRealm.pagingSize = 200 +ldapRealm.authorizationEnabled=true +ldapRealm.contextFactory.systemAuthenticationMechanism=simple +ldapRealm.searchBase=dc=hadoop,dc=apache,dc=org +ldapRealm.userSearchBase = dc=hadoop,dc=apache,dc=org +ldapRealm.groupSearchBase = ou=groups,dc=hadoop,dc=apache,dc=org +ldapRealm.groupObjectClass=groupofnames +# Allow userSearchAttribute to be customized +ldapRealm.userSearchAttributeName = sAMAccountName +ldapRealm.memberAttribute=member +# force usernames returned from ldap to lowercase useful for AD +ldapRealm.userLowerCase = true +# ability set searchScopes subtree (default), one, base +ldapRealm.userSearchScope = subtree; +ldapRealm.groupSearchScope = subtree; +ldapRealm.memberAttributeValueTemplate=cn={0},ou=people,dc=hadoop,dc=apache,dc=org +ldapRealm.contextFactory.systemUsername=uid=guest,ou=people,dc=hadoop,dc=apache,dc=org +ldapRealm.contextFactory.systemPassword=S{ALIAS=ldcSystemPassword} +# enable support for nested groups using the LDAP_MATCHING_RULE_IN_CHAIN operator +ldapRealm.groupSearchEnableMatchingRuleInChain = true +# optional mapping from physical groups to logical application roles +ldapRealm.rolesByGroup = LDN_USERS: user_role, NYK_USERS: user_role, HKG_USERS: user_role, GLOBAL_ADMIN: admin_role +# optional list of roles that are allowed to authenticate. Incase not present all groups are allowed to authenticate (login). +# This changes nothing for url specific permissions that will continue to work as specified in [urls]. +ldapRealm.allowedRolesForAuthentication = admin_role,user_role +ldapRealm.permissionsByRole= user_role = *:ToDoItemsJdo:*:*, *:ToDoItem:*:*; admin_role = * +securityManager.sessionManager = $sessionManager +securityManager.realms = $ldapRealm + ``` + ### PAM [PAM](https://en.wikipedia.org/wiki/Pluggable_authentication_module) authentication support allows the reuse of existing authentication moduls on the host where Zeppelin is running. On a typical system modules are configured per service for example sshd, passwd, etc. under `/etc/pam.d/`. You can diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java index 3381a516c5..97c223c2fc 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java @@ -109,6 +109,9 @@ import javax.naming.ldap.PagedResultsControl; *

# optional mapping from physical groups to logical application roles * ldapRealm.rolesByGroup = \ LDN_USERS: user_role,\ NYK_USERS: user_role,\ * HKG_USERS: user_role,\ GLOBAL_ADMIN: admin_role,\ DEMOS: self-install_role + * + *

# optional list of roles that are allowed to authenticate + * ldapRealm.allowedRolesForAuthentication = admin_role,user_role * *

ldapRealm.permissionsByRole=\ user_role = *:ToDoItemsJdo:*:*,\ * *:ToDoItem:*:*; \ self-install_role = *:ToDoItemsFixturesService:install:* ; @@ -176,6 +179,7 @@ public class LdapRealm extends JndiLdapRealm { private String memberAttributeValueSuffix = ""; private final Map rolesByGroup = new LinkedHashMap(); + private final List allowedRolesForAuthentication = new ArrayList(); private final Map> permissionsByRole = new LinkedHashMap>(); @@ -202,6 +206,29 @@ public class LdapRealm extends JndiLdapRealm { } } + /** + * This overrides the implementation of queryForAuthenticationInfo inside JndiLdapRealm. + * In addition to calling the super method for authentication it also tries to validate + * if this user has atleast one of the allowed roles for authentication. In case the property + * allowedRolesForAuthentication is empty this check always returns true. + * + * @param token the submitted authentication token that triggered the authentication attempt. + * @param ldapContextFactory factory used to retrieve LDAP connections. + * @return AuthenticationInfo instance representing the authenticated user's information. + * @throws NamingException if any LDAP errors occur. + */ + @Override + protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, + LdapContextFactory ldapContextFactory) + throws NamingException { + AuthenticationInfo info = super.queryForAuthenticationInfo(token, ldapContextFactory); + // Credentials were verified. Verify that the principal has all allowedRulesForAuthentication + if (!hasAllowedAuthenticationRules(info.getPrincipals(), ldapContextFactory)) { + throw new NamingException("Principal does not have any of the allowedRolesForAuthentication"); + } + return info; + } + /** * Get groups from LDAP. * @@ -231,6 +258,23 @@ public class LdapRealm extends JndiLdapRealm { return simpleAuthorizationInfo; } + private boolean hasAllowedAuthenticationRules(PrincipalCollection principals, + final LdapContextFactory ldapContextFactory) + throws NamingException { + boolean allowed = allowedRolesForAuthentication.isEmpty(); + if (!allowed) { + Set roles = getRoles(principals, ldapContextFactory); + for (String allowedRole: allowedRolesForAuthentication) { + if (roles.contains(allowedRole)) { + log.debug("Allowed role for user [" + allowedRole + "] found."); + allowed = true; + break; + } + } + } + return allowed; + } + private Set getRoles(PrincipalCollection principals, final LdapContextFactory ldapContextFactory) throws NamingException { @@ -524,6 +568,10 @@ public class LdapRealm extends JndiLdapRealm { this.memberAttributeValueSuffix = suffix; } + public void setAllowedRolesForAuthentication(List allowedRolesForAuthencation) { + this.allowedRolesForAuthentication.addAll(allowedRolesForAuthencation); + } + public void setRolesByGroup(Map rolesByGroup) { this.rolesByGroup.putAll(rolesByGroup); }