fix for permissions not honoring group

This commit is contained in:
Prabhjyot Singh 2016-06-09 17:21:48 +05:30
parent 4194f931f7
commit e8f1f97756
4 changed files with 385 additions and 22 deletions

View file

@ -22,8 +22,6 @@ import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.realm.ldap.JndiLdapRealm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.zeppelin.annotation.ZeppelinApi;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.server.JsonResponse;
@ -41,7 +39,6 @@ import java.util.*;
/**
* Zeppelin security rest api endpoint.
*
*/
@Path("/security")
@Produces("application/json")
@ -101,19 +98,16 @@ public class SecurityRestApi {
List<String> usersList = new ArrayList<>();
try {
GetUserList getUserListObj = new GetUserList();
DefaultWebSecurityManager defaultWebSecurityManager;
String key = ThreadContext.SECURITY_MANAGER_KEY;
defaultWebSecurityManager = (DefaultWebSecurityManager) ThreadContext.get(key);
Collection<Realm> realms = defaultWebSecurityManager.getRealms();
List realmsList = new ArrayList(realms);
for (int i = 0; i < realmsList.size(); i++) {
String name = ((Realm) realmsList.get(i)).getName();
Collection realmsList = SecurityUtils.getRealmsList();
for (Iterator<Realm> iterator = realmsList.iterator(); iterator.hasNext(); ) {
Realm realm = iterator.next();
String name = realm.getName();
if (name.equals("iniRealm")) {
usersList.addAll(getUserListObj.getUserList((IniRealm) realmsList.get(i)));
usersList.addAll(getUserListObj.getUserList((IniRealm) realm));
} else if (name.equals("ldapRealm")) {
usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realmsList.get(i)));
usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realm));
} else if (name.equals("jdbcRealm")) {
usersList.addAll(getUserListObj.getUserList((JdbcRealm) realmsList.get(i)));
usersList.addAll(getUserListObj.getUserList((JdbcRealm) realm));
}
}

View file

@ -0,0 +1,228 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.server;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.ldap.AbstractLdapRealm;
import org.apache.shiro.realm.ldap.DefaultLdapContextFactory;
import org.apache.shiro.realm.ldap.LdapContextFactory;
import org.apache.shiro.realm.ldap.LdapUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
import java.util.*;
/**
* Created for org.apache.zeppelin.server on 09/06/16.
*/
public class ActiveDirectoryGroupRealm extends AbstractLdapRealm {
private static final Logger log = LoggerFactory.getLogger(ActiveDirectoryGroupRealm.class);
private static final String ROLE_NAMES_DELIMETER = ",";
private Map<String, String> groupRolesMap;
private LdapContextFactory ldapContextFactory = null;
public ActiveDirectoryGroupRealm() {
}
public void setLdapContextFactory(LdapContextFactory ldapContextFactory) {
this.ldapContextFactory = ldapContextFactory;
}
public LdapContextFactory ensureContextFactory() {
if (this.ldapContextFactory == null) {
if (log.isDebugEnabled()) {
log.debug("No LdapContextFactory specified - creating a default instance.");
}
DefaultLdapContextFactory defaultFactory = new DefaultLdapContextFactory();
defaultFactory.setPrincipalSuffix(this.principalSuffix);
defaultFactory.setSearchBase(this.searchBase);
defaultFactory.setUrl(this.url);
defaultFactory.setSystemUsername(this.systemUsername);
defaultFactory.setSystemPassword(this.systemPassword);
this.ldapContextFactory = defaultFactory;
}
return this.ldapContextFactory;
}
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
try {
AuthenticationInfo info = this.queryForAuthenticationInfo(token, this.ensureContextFactory());
return info;
} catch (javax.naming.AuthenticationException var5) {
throw new AuthenticationException("LDAP authentication failed.", var5);
} catch (NamingException var6) {
String msg = "LDAP naming error while attempting to authenticate user.";
throw new AuthenticationException(msg, var6);
}
}
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
try {
AuthorizationInfo info = this.queryForAuthorizationInfo(principals,
this.ensureContextFactory());
return info;
} catch (NamingException var5) {
String msg = "LDAP naming error while attempting to retrieve authorization for user ["
+ principals + "].";
throw new AuthorizationException(msg, var5);
}
}
public void setGroupRolesMap(Map<String, String> groupRolesMap) {
this.groupRolesMap = groupRolesMap;
}
protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token,
LdapContextFactory ldapContextFactory)
throws NamingException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
LdapContext ctx = null;
try {
ctx = ldapContextFactory.getLdapContext(
upToken.getUsername(), upToken.getPassword());
} finally {
LdapUtils.closeContext(ctx);
}
return this.buildAuthenticationInfo(upToken.getUsername(), upToken.getPassword());
}
protected AuthenticationInfo buildAuthenticationInfo(String username, char[] password) {
return new SimpleAuthenticationInfo(username, password, this.getName());
}
protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals,
LdapContextFactory ldapContextFactory)
throws NamingException {
String username = (String) this.getAvailablePrincipal(principals);
LdapContext ldapContext = ldapContextFactory.getSystemLdapContext();
Set roleNames;
try {
roleNames = this.getRoleNamesForUser(username, ldapContext);
} finally {
LdapUtils.closeContext(ldapContext);
}
return this.buildAuthorizationInfo(roleNames);
}
protected AuthorizationInfo buildAuthorizationInfo(Set<String> roleNames) {
return new SimpleAuthorizationInfo(roleNames);
}
public Set<String> getRoleNamesForUser(String username, LdapContext ldapContext)
throws NamingException {
LinkedHashSet roleNames = new LinkedHashSet();
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String userPrincipalName = username;
if (this.principalSuffix != null) {
userPrincipalName = username + this.principalSuffix;
}
String searchFilter = "(&(objectClass=*)(userPrincipalName=*))";
Object[] searchArguments = new Object[]{userPrincipalName};
NamingEnumeration answer = ldapContext.search(
this.searchBase,
searchFilter,
searchArguments,
searchCtls);
while (true) {
Attributes attrs;
do {
if (!answer.hasMoreElements()) {
return roleNames;
}
SearchResult sr = (SearchResult) answer.next();
if (log.isDebugEnabled()) {
log.debug("Retrieving group names for user [" + sr.getName() + "]");
}
attrs = sr.getAttributes();
} while (attrs == null);
NamingEnumeration ae = attrs.getAll();
while (ae.hasMore()) {
Attribute attr = (Attribute) ae.next();
if (attr.getID().equals("memberOf")) {
Collection groupNames = LdapUtils.getAllAttributeValues(attr);
if (log.isDebugEnabled()) {
log.debug("Groups found for user [" + username + "]: " + groupNames);
}
Collection rolesForGroups = this.getRoleNamesForGroups(groupNames);
roleNames.addAll(rolesForGroups);
}
}
}
}
protected Collection<String> getRoleNamesForGroups(Collection<String> groupNames) {
HashSet roleNames = new HashSet(groupNames.size());
if (this.groupRolesMap != null) {
Iterator i$ = groupNames.iterator();
while (true) {
String groupName;
String strRoleNames;
do {
if (!i$.hasNext()) {
return roleNames;
}
groupName = (String) i$.next();
strRoleNames = (String) this.groupRolesMap.get(groupName);
} while (strRoleNames == null);
String[] arr$ = strRoleNames.split(",");
int len$ = arr$.length;
for (int i$1 = 0; i$1 < len$; ++i$1) {
String roleName = arr$[i$1];
if (log.isDebugEnabled()) {
log.debug("User is member of group [" +
groupName + "] so adding role [" + roleName + "]");
}
roleNames.add(roleName);
}
}
} else {
return roleNames;
}
}
}

View file

@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.server;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.ldap.JndiLdapRealm;
import org.apache.shiro.realm.ldap.LdapContextFactory;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Created for org.apache.zeppelin.server on 09/06/16.
*/
public class LdapGroupRealm extends JndiLdapRealm {
private static final Logger LOG = LoggerFactory.getLogger(LdapGroupRealm.class);
public AuthorizationInfo queryForAuthorizationInfo(
PrincipalCollection principals,
LdapContextFactory ldapContextFactory) throws NamingException {
String username = (String) getAvailablePrincipal(principals);
LdapContext ldapContext = ldapContextFactory.getSystemLdapContext();
Set<String> roleNames = getRoleNamesForUser(username, ldapContext, getUserDnTemplate());
return new SimpleAuthorizationInfo(roleNames);
}
public Set<String> getRoleNamesForUser(String username,
LdapContext ldapContext,
String userDnTemplate) throws NamingException {
try {
Set<String> roleNames;
roleNames = new LinkedHashSet<String>();
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "(&(objectClass=groupOfNames)(member=" + userDnTemplate + "))";
Object[] searchArguments = new Object[]{username};
NamingEnumeration<?> answer = ldapContext.search(
String.valueOf(ldapContext.getEnvironment().get("ldap.searchBase")),
searchFilter,
searchArguments,
searchCtls);
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
Attributes attrs = sr.getAttributes();
if (attrs != null) {
NamingEnumeration<?> ae = attrs.getAll();
while (ae.hasMore()) {
Attribute attr = (Attribute) ae.next();
if (attr.getID().equals("cn")) {
roleNames.add((String) attr.get());
}
}
}
}
return roleNames;
} catch (Exception e) {
LOG.error("Error", e);
}
return null;
}
}

View file

@ -16,15 +16,26 @@
*/
package org.apache.zeppelin.utils;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.ldap.JndiLdapContextFactory;
import org.apache.shiro.realm.ldap.JndiLdapRealm;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.server.ActiveDirectoryGroupRealm;
import org.apache.zeppelin.server.LdapGroupRealm;
import javax.naming.NamingException;
import javax.naming.ldap.LdapContext;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/**
* Tools for securing Zeppelin
@ -33,7 +44,7 @@ public class SecurityUtils {
public static Boolean isValidOrigin(String sourceHost, ZeppelinConfiguration conf)
throws UnknownHostException, URISyntaxException {
if (sourceHost == null || sourceHost.isEmpty()){
if (sourceHost == null || sourceHost.isEmpty()) {
return false;
}
String sourceUriHost = new URI(sourceHost).getHost();
@ -43,13 +54,14 @@ public class SecurityUtils {
String currentHost = InetAddress.getLocalHost().getHostName().toLowerCase();
return conf.getAllowedOrigins().contains("*") ||
currentHost.equals(sourceUriHost) ||
"localhost".equals(sourceUriHost) ||
conf.getAllowedOrigins().contains(sourceHost);
currentHost.equals(sourceUriHost) ||
"localhost".equals(sourceUriHost) ||
conf.getAllowedOrigins().contains(sourceHost);
}
/**
* Return the authenticated user if any otherwise returns "anonymous"
*
* @return shiro principal
*/
public static String getPrincipal() {
@ -58,16 +70,24 @@ public class SecurityUtils {
String principal;
if (subject.isAuthenticated()) {
principal = subject.getPrincipal().toString();
}
else {
} else {
principal = "anonymous";
}
return principal;
}
public static Collection getRealmsList() {
DefaultWebSecurityManager defaultWebSecurityManager;
String key = ThreadContext.SECURITY_MANAGER_KEY;
defaultWebSecurityManager = (DefaultWebSecurityManager) ThreadContext.get(key);
Collection<Realm> realms = defaultWebSecurityManager.getRealms();
return realms;
}
/**
* Return the roles associated with the authenticated user if any otherwise returns empty set
* TODO(prasadwagle) Find correct way to get user roles (see SHIRO-492)
*
* @return shiro roles
*/
public static HashSet<String> getRoles() {
@ -75,9 +95,36 @@ public class SecurityUtils {
HashSet<String> roles = new HashSet<>();
if (subject.isAuthenticated()) {
for (String role : Arrays.asList("role1", "role2", "role3")) {
if (subject.hasRole(role)) {
roles.add(role);
Collection realmsList = SecurityUtils.getRealmsList();
for (Iterator<Realm> iterator = realmsList.iterator(); iterator.hasNext(); ) {
Realm realm = iterator.next();
String name = realm.getName();
if (name.equals("iniRealm")) {
for (String role : Arrays.asList("role1", "role2", "role3")) {
if (subject.hasRole(role)) {
roles.add(role);
}
}
} else if (name.equals("ldapRealm")) {
JndiLdapRealm r = (JndiLdapRealm) realm;
JndiLdapContextFactory CF = (JndiLdapContextFactory) r.getContextFactory();
try {
LdapContext ctx = CF.getSystemLdapContext();
LdapGroupRealm ldapGroupRealm = new LdapGroupRealm();
String userDnTemplate = r.getUserDnTemplate();
return (HashSet<String>) ldapGroupRealm.getRoleNamesForUser(
subject.getPrincipal().toString(), ctx, userDnTemplate);
} catch (NamingException e) {
e.printStackTrace();
}
} else if (name.equals("activeDirectoryRealm")) {
ActiveDirectoryGroupRealm r = (ActiveDirectoryGroupRealm) realm;
try {
return (HashSet<String>) r.getRoleNamesForUser(subject.getPrincipal().toString(),
r.ensureContextFactory().getSystemLdapContext());
} catch (NamingException e) {
e.printStackTrace();
}
}
}
}