This is an automated email from the ASF dual-hosted git repository.
zjffdu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git
The following commit(s) were added to refs/heads/master by this push:
new 4ef4859 [ZEPPELIN-5258] Add Knox realm group lookup to shiro auth
service
4ef4859 is described below
commit 4ef4859330c9deca5394110bac82825fe3df20dd
Author: Adam Binford <[email protected]>
AuthorDate: Tue Feb 16 06:30:55 2021 -0500
[ZEPPELIN-5258] Add Knox realm group lookup to shiro auth service
### What is this PR for?
This PR aims to add support for Knox groups in
ShiroAuthenticationService.getAssociatedRoles. This allows Knox realm groups
(which uses hadoop groups under the hood) to be used for things like the
Notebook API that doesn't use native Shiro group checks.
Additionally, I removed some unused parts of the Knox realm. I can undo
this if desired, but I figured I would clean it up since it's not used.
### What type of PR is it?
Improvement
### Todos
### What is the Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-5258
### How should this be tested?
Added new UT and tested manually
### Screenshots (if appropriate)
### Questions:
* Does the licenses files need update? No
* Is there breaking changes for older versions? No
* Does this needs documentation? No
Author: Adam Binford <[email protected]>
Closes #4059 from Kimahriman/feature/knox-notebook-groups and squashes the
following commits:
d786220f7 [Adam Binford] Add Knox realm group lookup to shiro auth service
---
conf/shiro.ini.template | 2 -
docs/setup/security/shiro_authentication.md | 2 -
.../apache/zeppelin/realm/jwt/KnoxJwtRealm.java | 32 +-----
.../apache/zeppelin/realm/jwt/PrincipalMapper.java | 46 --------
.../realm/jwt/PrincipalMappingException.java | 31 -----
.../zeppelin/realm/jwt/SimplePrincipalMapper.java | 126 ---------------------
.../service/ShiroAuthenticationService.java | 14 ++-
.../service/ShiroAuthenticationServiceTest.java | 29 ++++-
8 files changed, 38 insertions(+), 244 deletions(-)
diff --git a/conf/shiro.ini.template b/conf/shiro.ini.template
index f853346..13db835 100644
--- a/conf/shiro.ini.template
+++ b/conf/shiro.ini.template
@@ -66,8 +66,6 @@ user3 = password4, role2
#knoxJwtRealm.cookieName = hadoop-jwt
#knoxJwtRealm.publicKeyPath = /etc/zeppelin/conf/knox-sso.pem
#
-#knoxJwtRealm.groupPrincipalMapping = group.principal.mapping
-#knoxJwtRealm.principalMapping = principal.mapping
#authc = org.apache.zeppelin.realm.jwt.KnoxAuthenticationFilter
### A sample for configuring Kerberos Realm
diff --git a/docs/setup/security/shiro_authentication.md
b/docs/setup/security/shiro_authentication.md
index f3864d3..bfd61f8 100644
--- a/docs/setup/security/shiro_authentication.md
+++ b/docs/setup/security/shiro_authentication.md
@@ -241,8 +241,6 @@ knoxJwtRealm.logout = gateway/knoxssout/api/v1/webssout
knoxJwtRealm.redirectParam = originalUrl
knoxJwtRealm.cookieName = hadoop-jwt
knoxJwtRealm.publicKeyPath = /etc/zeppelin/conf/knox-sso.pem
-knoxJwtRealm.groupPrincipalMapping = group.principal.mapping
-knoxJwtRealm.principalMapping = principal.mapping
# This is required if KNOX SSO is enabled, to check if
"knoxJwtRealm.cookieName" cookie was expired/deleted.
authc = org.apache.zeppelin.realm.jwt.KnoxAuthenticationFilter
```
diff --git
a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/KnoxJwtRealm.java
b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/KnoxJwtRealm.java
index 3663174..6fedd12 100644
---
a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/KnoxJwtRealm.java
+++
b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/KnoxJwtRealm.java
@@ -65,11 +65,6 @@ public class KnoxJwtRealm extends AuthorizingRealm {
private String logout;
private Boolean logoutAPI;
- private String principalMapping;
- private String groupPrincipalMapping;
-
- private SimplePrincipalMapper mapper = new SimplePrincipalMapper();
-
/**
* Configuration object needed by for Hadoop classes.
*/
@@ -83,14 +78,6 @@ public class KnoxJwtRealm extends AuthorizingRealm {
@Override
protected void onInit() {
super.onInit();
- if (principalMapping != null && !principalMapping.isEmpty()
- || groupPrincipalMapping != null && !groupPrincipalMapping.isEmpty()) {
- try {
- mapper.loadMappingTable(principalMapping, groupPrincipalMapping);
- } catch (PrincipalMappingException e) {
- LOGGER.error("PrincipalMappingException in onInit", e);
- }
- }
try {
hadoopConfig = new Configuration();
@@ -241,7 +228,6 @@ public class KnoxJwtRealm extends AuthorizingRealm {
/* return the groups as seen by Hadoop */
Set<String> groups = null;
try {
- hadoopGroups.refresh();
final List<String> groupList = hadoopGroups
.getGroups(mappedPrincipalName);
@@ -261,7 +247,7 @@ public class KnoxJwtRealm extends AuthorizingRealm {
/* Log the error and return empty group */
LOGGER.info(String.format("errorGettingUserGroups for %s",
mappedPrincipalName));
}
- groups = new HashSet();
+ groups = new HashSet<String>();
}
return groups;
}
@@ -321,20 +307,4 @@ public class KnoxJwtRealm extends AuthorizingRealm {
public void setLogoutAPI(Boolean logoutAPI) {
this.logoutAPI = logoutAPI;
}
-
- public String getPrincipalMapping() {
- return principalMapping;
- }
-
- public void setPrincipalMapping(String principalMapping) {
- this.principalMapping = principalMapping;
- }
-
- public String getGroupPrincipalMapping() {
- return groupPrincipalMapping;
- }
-
- public void setGroupPrincipalMapping(String groupPrincipalMapping) {
- this.groupPrincipalMapping = groupPrincipalMapping;
- }
}
diff --git
a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/PrincipalMapper.java
b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/PrincipalMapper.java
deleted file mode 100644
index fec276c..0000000
---
a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/PrincipalMapper.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.realm.jwt;
-
-public interface PrincipalMapper {
- /**
- * Load the internal principal mapping table from the provided
- * string value which conforms to the following semicolon delimited format:
- * actual[,another-actual]=mapped;...
- * @param principalMapping
- */
- void loadMappingTable(String principalMapping, String groupMapping)
- throws PrincipalMappingException;
-
- /**
- * Acquire a mapped principal name from the mapping table
- * as appropriate. Otherwise, the provided principalName
- * will be used.
- * @param principalName
- * @return principal name to be used in the assertion
- */
- String mapUserPrincipal(String principalName);
-
- /**
- * Acquire array of group principal names from the mapping table
- * as appropriate. Otherwise, return null.
- * @param principalName
- * @return group principal names to be used in the assertion
- */
- String[] mapGroupPrincipal(String principalName);
-}
diff --git
a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/PrincipalMappingException.java
b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/PrincipalMappingException.java
deleted file mode 100644
index 50e5036..0000000
---
a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/PrincipalMappingException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * 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.realm.jwt;
-
-/***
- * {@link System}.
- */
-public class PrincipalMappingException extends Exception {
- public PrincipalMappingException(String message) {
- super(message);
- }
-
- public PrincipalMappingException(String message, Exception e) {
- super(message, e);
- }
-}
diff --git
a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/SimplePrincipalMapper.java
b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/SimplePrincipalMapper.java
deleted file mode 100644
index b194810..0000000
---
a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/jwt/SimplePrincipalMapper.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/**
- * 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.realm.jwt;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.StringTokenizer;
-
-
-/***
- *
- */
-public class SimplePrincipalMapper implements PrincipalMapper {
-
- public HashMap<String, String[]> principalMappings = null;
- public HashMap<String, String[]> groupMappings = null;
-
- public SimplePrincipalMapper() {
- }
-
- /* (non-Javadoc)
- * @see
org.apache.hadoop.gateway.filter.PrincipalMapper#loadMappingTable(java.lang.String)
- */
- @Override
- public void loadMappingTable(String principalMapping, String groupMapping)
- throws PrincipalMappingException {
- if (principalMapping != null) {
- principalMappings = parseMapping(principalMapping);
- groupMappings = parseMapping(groupMapping);
- }
- }
-
- private HashMap<String, String[]> parseMapping(String mappings)
- throws PrincipalMappingException {
- if (mappings == null) {
- return null;
- }
- HashMap<String, String[]> table = new HashMap<>();
- try {
- StringTokenizer t = new StringTokenizer(mappings, ";");
- if (t.hasMoreTokens()) {
- do {
- String mapping = t.nextToken();
- String principals = mapping.substring(0, mapping.indexOf('='));
- String value = mapping.substring(mapping.indexOf('=') + 1);
- String[] v = value.split(",");
- String[] p = principals.split(",");
- for (int i = 0; i < p.length; i++) {
- table.put(p[i], v);
- }
- } while (t.hasMoreTokens());
- }
- return table;
- } catch (Exception e) {
- // do not leave table in an unknown state - clear it instead
- // no principal mapping will occur
- table.clear();
- throw new PrincipalMappingException(
- "Unable to load mappings from provided string: " + mappings
- + " - no principal mapping will be provided.", e);
- }
- }
-
- /* (non-Javadoc)
- * @see
org.apache.hadoop.gateway.filter.PrincipalMapper#mapPrincipal(java.lang.String)
- */
- @Override
- public String mapUserPrincipal(String principalName) {
- String[] p = null;
- if (principalMappings != null) {
- p = principalMappings.get(principalName);
- }
- if (p == null) {
- return principalName;
- }
-
- return p[0];
- }
-
- /* (non-Javadoc)
- * @see
org.apache.hadoop.gateway.filter.PrincipalMapper#mapPrincipal(java.lang.String)
- */
- @Override
- public String[] mapGroupPrincipal(String principalName) {
- String[] groups = null;
- String[] wildCardGroups = null;
-
- if (groupMappings != null) {
- groups = groupMappings.get(principalName);
- wildCardGroups = groupMappings.get("*");
- if (groups != null && wildCardGroups != null) {
- groups = concat(groups, wildCardGroups);
- } else if (wildCardGroups != null) {
- return wildCardGroups;
- }
- }
-
- return groups;
- }
-
- /**
- * @param groups
- * @param wildCardGroups
- * @return
- */
- public static <T> T[] concat(T[] groups, T[] wildCardGroups) {
- T[] result = Arrays.copyOf(groups, groups.length + wildCardGroups.length);
- System.arraycopy(wildCardGroups, 0, result, groups.length,
wildCardGroups.length);
- return result;
- }
-}
diff --git
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/ShiroAuthenticationService.java
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/ShiroAuthenticationService.java
index 056eac7..d15b81e 100644
---
a/zeppelin-server/src/main/java/org/apache/zeppelin/service/ShiroAuthenticationService.java
+++
b/zeppelin-server/src/main/java/org/apache/zeppelin/service/ShiroAuthenticationService.java
@@ -39,6 +39,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.shiro.UnavailableSecurityManagerException;
import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.realm.ldap.DefaultLdapRealm;
@@ -48,10 +49,10 @@ import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.JdbcUtils;
import org.apache.shiro.util.ThreadContext;
-import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.realm.ActiveDirectoryGroupRealm;
import org.apache.zeppelin.realm.LdapRealm;
+import org.apache.zeppelin.realm.jwt.KnoxJwtRealm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -77,7 +78,7 @@ public class ShiroAuthenticationService implements
AuthenticationService {
if (conf.getShiroPath().length() > 0) {
try {
Collection<Realm> realms =
- ((DefaultWebSecurityManager)
org.apache.shiro.SecurityUtils.getSecurityManager())
+ ((DefaultSecurityManager)
org.apache.shiro.SecurityUtils.getSecurityManager())
.getRealms();
if (realms.size() > 1) {
boolean isIniRealmEnabled = false;
@@ -138,8 +139,8 @@ public class ShiroAuthenticationService implements
AuthenticationService {
@Override
public Collection<Realm> getRealmsList() {
String key = ThreadContext.SECURITY_MANAGER_KEY;
- DefaultWebSecurityManager defaultWebSecurityManager =
(DefaultWebSecurityManager) ThreadContext.get(key);
- return defaultWebSecurityManager.getRealms();
+ DefaultSecurityManager defaultSecurityManager = (DefaultSecurityManager)
ThreadContext.get(key);
+ return defaultSecurityManager.getRealms();
}
/** Checked if shiro enabled or not. */
@@ -220,7 +221,7 @@ public class ShiroAuthenticationService implements
AuthenticationService {
@Override
public Set<String> getAssociatedRoles() {
Subject subject = org.apache.shiro.SecurityUtils.getSubject();
- HashSet<String> roles = new HashSet<>();
+ Set<String> roles = new HashSet<>();
Map<String, String> allRoles = null;
if (subject.isAuthenticated()) {
@@ -247,6 +248,9 @@ public class ShiroAuthenticationService implements
AuthenticationService {
} else if (ACTIVE_DIRECTORY_GROUP_REALM.equals(name)) {
allRoles = ((ActiveDirectoryGroupRealm) realm).getListRoles();
break;
+ } else if (realm instanceof KnoxJwtRealm) {
+ roles = ((KnoxJwtRealm) realm).mapGroupPrincipals(getPrincipal());
+ break;
}
}
if (allRoles != null) {
diff --git
a/zeppelin-server/src/test/java/org/apache/zeppelin/service/ShiroAuthenticationServiceTest.java
b/zeppelin-server/src/test/java/org/apache/zeppelin/service/ShiroAuthenticationServiceTest.java
index 1311b2e..5ce009a 100644
---
a/zeppelin-server/src/test/java/org/apache/zeppelin/service/ShiroAuthenticationServiceTest.java
+++
b/zeppelin-server/src/test/java/org/apache/zeppelin/service/ShiroAuthenticationServiceTest.java
@@ -18,12 +18,20 @@ package org.apache.zeppelin.service;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.spy;
import java.io.IOException;
import java.security.Principal;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.shiro.mgt.DefaultSecurityManager;
+import org.apache.shiro.subject.SimplePrincipalCollection;
+import org.apache.shiro.util.LifecycleUtils;
+import org.apache.shiro.util.ThreadContext;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.apache.zeppelin.notebook.Notebook;
+import org.apache.zeppelin.realm.jwt.KnoxJwtRealm;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,7 +57,7 @@ public class ShiroAuthenticationServiceTest {
}
@Test
- public void canGetPrincipalName() {
+ public void canGetPrincipalName() {
String expectedName = "java.security.Principal.getName()";
setupPrincipalName(expectedName);
assertEquals(expectedName, shiroSecurityService.getPrincipal());
@@ -66,6 +74,25 @@ public class ShiroAuthenticationServiceTest {
.getVarName(), String.valueOf(false));
}
+ @Test
+ public void testKnoxGetRoles() {
+ setupPrincipalName("test");
+
+ KnoxJwtRealm realm = spy(new KnoxJwtRealm());
+ LifecycleUtils.init(realm);
+ Set<String> testRoles = new HashSet<String>(){{
+ add("role1");
+ add("role2");
+ }};
+ when(realm.mapGroupPrincipals("test")).thenReturn(testRoles);
+
+ DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);
+ ThreadContext.bind(securityManager);
+
+ Set<String> roles = shiroSecurityService.getAssociatedRoles();
+ assertEquals(testRoles, roles);
+ }
+
private void setupPrincipalName(String expectedName) {
PowerMockito.mockStatic(org.apache.shiro.SecurityUtils.class);
when(org.apache.shiro.SecurityUtils.getSubject()).thenReturn(subject);