mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
fix for permissions not honoring group
This commit is contained in:
parent
4194f931f7
commit
e8f1f97756
4 changed files with 385 additions and 22 deletions
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue