This is an automated email from the ASF dual-hosted git repository.
kumaab pushed a commit to branch ranger-2.10
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/ranger-2.10 by this push:
new 5fc403c44 RANGER-5767: Extend header based authn to support ROLEs in
Ranger Admin (#1206)
5fc403c44 is described below
commit 5fc403c4473d0540fa64f9d0c3f86ff2201a7b15
Author: Abhishek Kumar <[email protected]>
AuthorDate: Thu Sep 3 17:35:27 2026 -0700
RANGER-5767: Extend header based authn to support ROLEs in Ranger Admin
(#1206)
Co-authored-by: Madhan Neethiraj <[email protected]>
(cherry picked from commit d9d2c44d23b669b68b7cee14b1a959fa498a2a51)
---
.../java/org/apache/ranger/biz/SessionMgr.java | 37 +++-
.../main/java/org/apache/ranger/biz/XUserMgr.java | 90 +++++++++-
.../org/apache/ranger/common/RangerConstants.java | 4 -
.../web/filter/RangerHeaderPreAuthFilter.java | 99 ++++++++++-
.../main/resources/conf.dist/ranger-admin-site.xml | 5 +
.../web/filter/TestRangerHeaderPreAuthFilter.java | 198 +++++++++++++++++++++
6 files changed, 411 insertions(+), 22 deletions(-)
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java
b/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java
index c31ed4e51..d60e327d6 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/SessionMgr.java
@@ -31,6 +31,7 @@
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
+import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@@ -72,6 +73,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static
org.apache.ranger.security.web.filter.RangerHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED;
+
@Component
@Transactional
public class SessionMgr {
@@ -103,11 +106,20 @@ public class SessionMgr {
@Autowired
StringUtil stringUtil;
-
+
+ boolean ssoEnabled;
+ boolean headerAuthEnabled;
+
public SessionMgr() {
logger.debug("SessionManager created");
}
+ @PostConstruct
+ public void init() {
+ ssoEnabled =
PropertiesUtil.getBooleanProperty("ranger.sso.enabled", false);
+ headerAuthEnabled =
PropertiesUtil.getBooleanProperty(PROP_HEADER_AUTH_ENABLED, false);
+ }
+
private static final Long SESSION_UPDATE_INTERVAL_IN_MILLIS = 30 *
DateUtils.MILLIS_PER_MINUTE;
public UserSessionBase processSuccessLogin(int authType, String
userAgent,
@@ -246,17 +258,24 @@ else if
(!StringUtils.isEmpty(httpRequest.getRequestURI()) && !isPluginOrSecureD
}
private void getSSOSpnegoAuthCheckForAPI(String currentLoginId,
HttpServletRequest request) {
+ XXPortalUser gjUser =
daoManager.getXXPortalUser().findByLoginId(currentLoginId);
- RangerSecurityContext context =
RangerContextHolder.getSecurityContext();
- UserSessionBase session = context != null ?
context.getUserSession() : null;
- boolean ssoEnabled = session != null ? session.isSSOEnabled() :
PropertiesUtil.getBooleanProperty("ranger.sso.enabled", false);
+ if (gjUser == null) {
+ if (headerAuthEnabled) {
+ logger.debug("User : {} doesn't exist in Ranger
DB. Creating user as it's header-auth authenticated", currentLoginId);
- XXPortalUser gjUser =
daoManager.getXXPortalUser().findByLoginId(currentLoginId);
- if (gjUser == null && ((request.getAttribute("spnegoEnabled")
!= null && (boolean)request.getAttribute("spnegoEnabled")) || (ssoEnabled))) {
- if(logger.isDebugEnabled()){
- logger.debug("User : "+currentLoginId+" doesn't
exist in Ranger DB So creating user as it's SSO or Spnego authenticated");
+
xUserMgr.createServiceConfigUserSynchronously(currentLoginId);
+ } else {
+ RangerSecurityContext context =
RangerContextHolder.getSecurityContext();
+ UserSessionBase session = context !=
null ? context.getUserSession() : null;
+ boolean ssoEnabled = session !=
null ? session.isSSOEnabled() : this.ssoEnabled;
+
+ if (ssoEnabled ||
(request.getAttribute("spnegoEnabled") != null && (boolean)
request.getAttribute("spnegoEnabled"))) {
+ logger.debug("User : {} doesn't exist
in Ranger DB. Creating user as it's SSO or Spnego", currentLoginId);
+
+
xUserMgr.createServiceConfigUser(currentLoginId);
+ }
}
- xUserMgr.createServiceConfigUser(currentLoginId);
}
}
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
index 61c781090..13e614d65 100755
--- a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
@@ -67,6 +67,7 @@
import org.apache.ranger.db.XXUserDao;
import org.apache.ranger.db.XXUserPermissionDao;
import org.apache.ranger.entity.XXAuditMap;
+import org.apache.ranger.entity.XXAuthSession;
import org.apache.ranger.entity.XXGroup;
import org.apache.ranger.entity.XXGroupUser;
import org.apache.ranger.entity.XXPermMap;
@@ -81,10 +82,17 @@
import org.apache.ranger.entity.XXSecurityZoneRefUser;
import org.apache.ranger.entity.XXTrxLogV2;
import org.apache.ranger.entity.XXUser;
+import org.apache.ranger.security.context.RangerContextHolder;
+import org.apache.ranger.security.context.RangerSecurityContext;
+import org.apache.ranger.security.web.filter.RangerAuthenticationToken;
+import org.apache.ranger.security.web.filter.RangerHeaderPreAuthFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
@@ -3268,6 +3276,72 @@ public Map<String, Long> getUserCountByRole() {
return daoManager.getXXPortalUser().getCountByUserRole();
}
+ /**
+ * Resolves the roles to assign to a freshly auto-provisioned external
user. When header-based
+ * authentication is enabled and the current request was authenticated
through the trusted proxy,
+ * the (validated) roles carried in the {@link
RangerAuthenticationToken} authorities are used.
+ * Returns {@code null} otherwise, in which case the caller falls back
to the default ROLE_USER.
+ */
+ private List<String> getHeaderAuthRoles() {
+ List<String> ret = null;
+
+ if
(PropertiesUtil.getBooleanProperty(RangerHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED,
false)) {
+ Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
+
+ if (auth instanceof RangerAuthenticationToken &&
((RangerAuthenticationToken) auth).getAuthType() ==
XXAuthSession.AUTH_TYPE_TRUSTED_PROXY) {
+ List<String> roles = new ArrayList<>();
+
+ for (GrantedAuthority authority :
auth.getAuthorities()) {
+ String role = (authority != null) ?
StringUtils.trimToNull(authority.getAuthority()) : null;
+
+ if (role != null &&
RangerConstants.VALID_USER_ROLE_LIST.contains(role)) {
+ roles.add(role);
+ }
+ }
+
+ if (!roles.isEmpty()) {
+ ret = roles;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Creates the portal user with the supplied roles. When the roles
originate from the trusted
+ * roles header ({@code trustedHeaderRoles}), the creation runs under a
trusted system session
+ * (an admin session with no interactive user attached) so that
non-public roles such as
+ * ROLE_SYS_ADMIN carried in the header are permitted during
provisioning. No real user account
+ * is impersonated and the previous security context is always restored.
+ */
+ private XXPortalUser createExternalPortalUser(XXPortalUser
xXPortalUser, List<String> roleList, boolean trustedHeaderRoles) {
+ if (!trustedHeaderRoles) {
+ return userMgr.createUser(xXPortalUser,
RangerCommonEnums.STATUS_ENABLED, roleList);
+ }
+
+ RangerSecurityContext context =
RangerContextHolder.getSecurityContext();
+ UserSessionBase originalSession = context != null ?
context.getUserSession() : null;
+
+ if (context == null) {
+ context = new RangerSecurityContext();
+
+ RangerContextHolder.setSecurityContext(context);
+ }
+
+ try {
+ UserSessionBase systemSession = new UserSessionBase();
+
+ systemSession.setUserAdmin(true);
+
+ context.setUserSession(systemSession);
+
+ return userMgr.createUser(xXPortalUser,
RangerCommonEnums.STATUS_ENABLED, roleList);
+ } finally {
+ context.setUserSession(originalSession);
+ }
+ }
+
private class ExternalUserCreator implements Runnable {
private String userName;
@@ -3293,15 +3367,19 @@ private void createExternalUser() {
VXPortalUser vXPortalUser = new VXPortalUser();
vXPortalUser.setLoginId(userName);
vXPortalUser.setUserSource(RangerCommonEnums.USER_EXTERNAL);
- ArrayList<String> roleList = new
ArrayList<String>();
- roleList.add(RangerConstants.ROLE_USER);
+ List<String> headerRoles =
getHeaderAuthRoles();
+ boolean trustedHeaderRoles =
CollectionUtils.isNotEmpty(headerRoles);
+ ArrayList<String> roleList = new
ArrayList<String>();
+ if (trustedHeaderRoles) {
+ roleList.addAll(headerRoles);
+ } else {
+ roleList.add(RangerConstants.ROLE_USER);
+ }
vXPortalUser.setUserRoleList(roleList);
xXPortalUser =
userMgr.mapVXPortalUserToXXPortalUser(vXPortalUser);
try {
- xXPortalUser =
userMgr.createUser(xXPortalUser, RangerCommonEnums.STATUS_ENABLED, roleList);
- if (logger.isDebugEnabled()) {
-
logger.debug("createExternalUser(): Successfully created user in x_portal_user
table " + xXPortalUser.getLoginId());
- }
+ xXPortalUser =
createExternalPortalUser(xXPortalUser, roleList, trustedHeaderRoles);
+ logger.debug("createExternalUser():
Successfully created user {} in x_portal_user table with roles {}",
xXPortalUser.getLoginId(), roleList);
} catch (Exception ex) {
throw new RuntimeException("Failed to
create user " + userName + " in x_portal_user table. retrying", ex);
}
diff --git
a/security-admin/src/main/java/org/apache/ranger/common/RangerConstants.java
b/security-admin/src/main/java/org/apache/ranger/common/RangerConstants.java
index e4f05f51a..9f07e0520 100644
--- a/security-admin/src/main/java/org/apache/ranger/common/RangerConstants.java
+++ b/security-admin/src/main/java/org/apache/ranger/common/RangerConstants.java
@@ -32,12 +32,8 @@ public class RangerConstants extends RangerCommonEnums {
// Default Roles
public final static String ROLE_SYS_ADMIN = "ROLE_SYS_ADMIN";
public final static String ROLE_ADMIN = "ROLE_ADMIN";
- public final static String ROLE_INTEGRATOR = "ROLE_INTEGRATOR";
- public final static String ROLE_DATA_ANALYST = "ROLE_DATA_ANALYST";
- public final static String ROLE_BIZ_MGR = "ROLE_BIZ_MGR";
public final static String ROLE_KEY_ADMIN = "ROLE_KEY_ADMIN";
public final static String ROLE_USER = "ROLE_USER";
- public final static String ROLE_ANON = "ROLE_ANON";
public final static String ROLE_OTHER = "ROLE_OTHER";
public final static String GROUP_PUBLIC = "public";
public final static String ROLE_ADMIN_AUDITOR = "ROLE_ADMIN_AUDITOR";
diff --git
a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerHeaderPreAuthFilter.java
b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerHeaderPreAuthFilter.java
index 800bf92f5..4352519a1 100644
---
a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerHeaderPreAuthFilter.java
+++
b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerHeaderPreAuthFilter.java
@@ -21,6 +21,7 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.ranger.biz.UserMgr;
import org.apache.ranger.common.PropertiesUtil;
+import org.apache.ranger.common.RangerConstants;
import org.apache.ranger.entity.XXAuthSession;
import org.apache.ranger.plugin.util.SpiffeIdUtil;
import org.slf4j.Logger;
@@ -45,7 +46,10 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class RangerHeaderPreAuthFilter extends GenericFilterBean {
private static final Logger LOG =
LoggerFactory.getLogger(RangerHeaderPreAuthFilter.class);
@@ -54,10 +58,30 @@ public class RangerHeaderPreAuthFilter extends
GenericFilterBean {
public static final String PROP_USERNAME_HEADER_NAME =
"ranger.admin.authn.header.username";
public static final String PROP_SPIFFE_HEADER_NAME =
"ranger.admin.authn.header.spiffe";
public static final String PROP_REQUEST_ID_HEADER_NAME =
"ranger.admin.authn.header.requestid";
+ public static final String PROP_ROLES_HEADER_NAME =
"ranger.admin.authn.header.roles";
+
+ /**
+ * External-facing role names accepted in the configured roles header,
mapped to Ranger's
+ * internal role constants (see {@link
RangerConstants#VALID_USER_ROLE_LIST}).
+ */
+ private static final Map<String, String> EXTERNAL_ROLE_TO_RANGER_ROLE;
+
+ static {
+ Map<String, String> roleMap = new HashMap<>();
+
+ roleMap.put("RANGER_ROLE_ADMIN", RangerConstants.ROLE_SYS_ADMIN);
+ roleMap.put("RANGER_ROLE_AUDITOR", RangerConstants.ROLE_ADMIN_AUDITOR);
+ roleMap.put("RANGER_ROLE_USER", RangerConstants.ROLE_USER);
+ roleMap.put("RANGER_ROLE_KEY_ADMIN", RangerConstants.ROLE_KEY_ADMIN);
+ roleMap.put("RANGER_ROLE_KEY_ADMIN_AUDITOR",
RangerConstants.ROLE_KEY_ADMIN_AUDITOR);
+
+ EXTERNAL_ROLE_TO_RANGER_ROLE = Collections.unmodifiableMap(roleMap);
+ }
private boolean headerAuthEnabled;
private String userNameHeaderName;
private List<String> spiffeHeaderNames;
+ private String rolesHeaderName;
@Autowired
UserMgr userMgr;
@@ -69,6 +93,7 @@ protected void initialize() {
if (headerAuthEnabled) {
userNameHeaderName =
PropertiesUtil.getProperty(PROP_USERNAME_HEADER_NAME);
spiffeHeaderNames =
SpiffeIdUtil.parseHeaderNames(PropertiesUtil.getProperty(PROP_SPIFFE_HEADER_NAME));
+ rolesHeaderName =
PropertiesUtil.getProperty(PROP_ROLES_HEADER_NAME);
if (StringUtils.isBlank(userNameHeaderName) &&
spiffeHeaderNames.isEmpty()) {
LOG.warn("Disabling header-based authentication, as neither {}
nor {} is set", PROP_USERNAME_HEADER_NAME, PROP_SPIFFE_HEADER_NAME);
@@ -88,7 +113,7 @@ public void doFilter(ServletRequest request, ServletResponse
response, FilterCha
String username =
resolvePrincipal(httpRequest);
if (StringUtils.isNotBlank(username)) {
- List<GrantedAuthority> grantedAuthorities =
getAuthoritiesFromRanger(username);
+ List<GrantedAuthority> grantedAuthorities =
getAuthorities(httpRequest, username);
final UserDetails principal = new
User(username, "", grantedAuthorities);
RangerAuthenticationToken authToken = new
RangerAuthenticationToken(principal, grantedAuthorities,
XXAuthSession.AUTH_TYPE_TRUSTED_PROXY);
@@ -135,14 +160,82 @@ private String resolvePrincipal(HttpServletRequest
httpRequest) {
return null;
}
+ /**
+ * Resolves the authorities to assign to the authenticated user. When the
trusted proxy
+ * supplies roles via the configured roles header, those roles are
honored; otherwise the
+ * roles persisted for the user in the Ranger DB are used.
+ */
+ private List<GrantedAuthority> getAuthorities(HttpServletRequest
httpRequest, String username) {
+ List<GrantedAuthority> ret = getAuthoritiesFromHeader(httpRequest);
+
+ if (ret == null || ret.isEmpty()) {
+ ret = getAuthoritiesFromRanger(username);
+ }
+
+ return ret == null ? Collections.emptyList() : ret;
+ }
+
+ /**
+ * Loads authorities from the configured roles header. External-facing
role names
+ * ({@code RANGER_ROLE_ADMIN}, {@code RANGER_ROLE_AUDITOR}, etc.) are
mapped to Ranger's
+ * internal role constants before being added to the authentication token.
Internal role
+ * names from {@link RangerConstants#VALID_USER_ROLE_LIST} are also
accepted; any other
+ * value is ignored.
+ */
+ private List<GrantedAuthority> getAuthoritiesFromHeader(HttpServletRequest
httpRequest) {
+ List<GrantedAuthority> ret = null;
+
+ if (StringUtils.isNotBlank(rolesHeaderName)) {
+ String rolesHeaderValue = httpRequest.getHeader(rolesHeaderName);
+
+ if (StringUtils.isNotBlank(rolesHeaderValue)) {
+ ret = new ArrayList<>();
+
+ for (String role : rolesHeaderValue.split(",")) {
+ String trimmedRole = StringUtils.trimToNull(role);
+
+ if (trimmedRole != null) {
+ String rangerRole = resolveRoleFromHeader(trimmedRole);
+
+ if (rangerRole != null) {
+ ret.add(new SimpleGrantedAuthority(rangerRole));
+ } else {
+ LOG.warn("Ignoring unrecognized role '{}' received
in header '{}'", trimmedRole, rolesHeaderName);
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Maps an external-facing role name from the roles header to a Ranger
internal role constant,
+ * or returns the value unchanged when it is already a recognized internal
role name.
+ */
+ private String resolveRoleFromHeader(String headerRole) {
+ String ret = EXTERNAL_ROLE_TO_RANGER_ROLE.get(headerRole);
+
+ if (ret == null) {
+ if (RangerConstants.VALID_USER_ROLE_LIST.contains(headerRole)) {
+ ret = headerRole;
+ }
+ }
+
+ return ret;
+ }
+
/**
* Loads authorities from Ranger DB
*/
private List<GrantedAuthority> getAuthoritiesFromRanger(String username) {
- List<GrantedAuthority> ret = new ArrayList<>();
+ List<GrantedAuthority> ret = null;
Collection<String> roleList = userMgr.getRolesByLoginId(username);
- if (roleList != null) {
+ if (roleList != null && !roleList.isEmpty()) {
+ ret = new ArrayList<>();
+
for (String role : roleList) {
if (StringUtils.isNotBlank(role)) {
ret.add(new SimpleGrantedAuthority(role));
diff --git a/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
b/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
index 1a9c40caf..d8533051f 100644
--- a/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
+++ b/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
@@ -332,6 +332,11 @@
<name>ranger.admin.authn.header.requestid</name>
<value></value>
</property>
+ <property>
+ <name>ranger.admin.authn.header.roles</name>
+ <value></value>
+ <description>Name of the HTTP header (e.g. X-Forwarded-Roles)
carrying a comma-separated list of roles to assign to the authenticated user.
Accepted values are role names RANGER_ROLE_ADMIN, RANGER_ROLE_AUDITOR,
RANGER_ROLE_USER, RANGER_ROLE_KEY_ADMIN, and RANGER_ROLE_KEY_ADMIN_AUDITOR.
Values that are not recognized are ignored.</description>
+ </property>
<property>
<name>ranger.admin.spiffe.as.username.enabled</name>
<value>false</value>
diff --git
a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerHeaderPreAuthFilter.java
b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerHeaderPreAuthFilter.java
index 5103d836b..d70135035 100644
---
a/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerHeaderPreAuthFilter.java
+++
b/security-admin/src/test/java/org/apache/ranger/security/web/filter/TestRangerHeaderPreAuthFilter.java
@@ -68,6 +68,7 @@ public void tearDown() {
PropertiesUtil.getPropertiesMap().remove(RangerHeaderPreAuthFilter.PROP_USERNAME_HEADER_NAME);
PropertiesUtil.getPropertiesMap().remove(RangerHeaderPreAuthFilter.PROP_SPIFFE_HEADER_NAME);
PropertiesUtil.getPropertiesMap().remove(RangerHeaderPreAuthFilter.PROP_REQUEST_ID_HEADER_NAME);
+
PropertiesUtil.getPropertiesMap().remove(RangerHeaderPreAuthFilter.PROP_ROLES_HEADER_NAME);
}
@Test
@@ -372,4 +373,201 @@ public void
testDoFilter_enabled_existingAuthenticatedContext_doesNotOverrideAut
verify(userMgr, never()).getRolesByLoginId(anyString());
assertEquals(existingAuth,
SecurityContextHolder.getContext().getAuthentication());
}
+
+ @Test
+ public void
testDoFilter_enabled_withExternalRolesHeader_mapsToInternalRoles() throws
Exception {
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED,
"true");
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_USERNAME_HEADER_NAME,
"X-Forwarded-User");
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_ROLES_HEADER_NAME,
"X-Forwarded-Roles");
+
+ RangerHeaderPreAuthFilter filter = new RangerHeaderPreAuthFilter();
+ UserMgr userMgr = mock(UserMgr.class);
+
+ filter.userMgr = userMgr;
+ filter.initialize();
+
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+
+ when(request.getHeader("X-Forwarded-User")).thenReturn("joeuser");
+
when(request.getHeader("X-Forwarded-Roles")).thenReturn("RANGER_ROLE_ADMIN,
RANGER_ROLE_USER");
+
+ FilterChain chain = new FilterChain() {
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res) {
+ org.springframework.security.core.Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
+
+ assertNotNull(auth);
+ assertTrue(auth instanceof RangerAuthenticationToken);
+ RangerAuthenticationToken rangerAuth =
(RangerAuthenticationToken) auth;
+ assertEquals(XXAuthSession.AUTH_TYPE_TRUSTED_PROXY,
rangerAuth.getAuthType());
+ assertEquals("joeuser", auth.getName());
+
+ Collection<?> authorities = auth.getAuthorities();
+ assertEquals(2, authorities.size());
+ assertTrue(authorities.stream().anyMatch(a ->
"ROLE_SYS_ADMIN".equals(a.toString())));
+ assertTrue(authorities.stream().anyMatch(a ->
"ROLE_USER".equals(a.toString())));
+ }
+ };
+
+ filter.doFilter(request, response, chain);
+
+ verify(userMgr, never()).getRolesByLoginId(anyString());
+ }
+
+ @Test
+ public void
testDoFilter_enabled_withRolesHeader_setsAuthenticationFromHeaderRoles() throws
Exception {
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED,
"true");
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_USERNAME_HEADER_NAME,
"X-Forwarded-User");
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_ROLES_HEADER_NAME,
"X-Forwarded-Roles");
+
+ RangerHeaderPreAuthFilter filter = new RangerHeaderPreAuthFilter();
+ UserMgr userMgr = mock(UserMgr.class);
+
+ filter.userMgr = userMgr;
+ filter.initialize();
+
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+
+ when(request.getHeader("X-Forwarded-User")).thenReturn("joeuser");
+
when(request.getHeader("X-Forwarded-Roles")).thenReturn("ROLE_SYS_ADMIN,
ROLE_USER");
+
+ FilterChain chain = new FilterChain() {
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res) {
+ org.springframework.security.core.Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
+
+ assertNotNull(auth);
+ assertTrue(auth instanceof RangerAuthenticationToken);
+ RangerAuthenticationToken rangerAuth =
(RangerAuthenticationToken) auth;
+ assertEquals(XXAuthSession.AUTH_TYPE_TRUSTED_PROXY,
rangerAuth.getAuthType());
+ assertEquals("joeuser", auth.getName());
+
+ Collection<?> authorities = auth.getAuthorities();
+ assertEquals(2, authorities.size());
+ assertTrue(authorities.stream().anyMatch(a ->
"ROLE_SYS_ADMIN".equals(a.toString())));
+ assertTrue(authorities.stream().anyMatch(a ->
"ROLE_USER".equals(a.toString())));
+ }
+ };
+
+ filter.doFilter(request, response, chain);
+
+ // roles came from the trusted header, so the Ranger DB must not be
consulted
+ verify(userMgr, never()).getRolesByLoginId(anyString());
+ }
+
+ @Test
+ public void
testDoFilter_enabled_rolesHeaderWithUnknownRoles_ignoresInvalidAndKeepsValid()
throws Exception {
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED,
"true");
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_USERNAME_HEADER_NAME,
"X-Forwarded-User");
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_ROLES_HEADER_NAME,
"X-Forwarded-Roles");
+
+ RangerHeaderPreAuthFilter filter = new RangerHeaderPreAuthFilter();
+ UserMgr userMgr = mock(UserMgr.class);
+
+ filter.userMgr = userMgr;
+ filter.initialize();
+
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+
+ when(request.getHeader("X-Forwarded-User")).thenReturn("joeuser");
+
when(request.getHeader("X-Forwarded-Roles")).thenReturn("ROLE_ADMIN_AUDITOR,
ROLE_BOGUS, , ROLE_USER");
+
+ FilterChain chain = new FilterChain() {
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res) {
+ org.springframework.security.core.Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
+
+ assertNotNull(auth);
+
+ Collection<?> authorities = auth.getAuthorities();
+ assertEquals(2, authorities.size());
+ assertTrue(authorities.stream().anyMatch(a ->
"ROLE_ADMIN_AUDITOR".equals(a.toString())));
+ assertTrue(authorities.stream().anyMatch(a ->
"ROLE_USER".equals(a.toString())));
+ }
+ };
+
+ filter.doFilter(request, response, chain);
+
+ verify(userMgr, never()).getRolesByLoginId(anyString());
+ }
+
+ @Test
+ public void
testDoFilter_enabled_rolesHeaderWithNoValidRoles_fallsBackToRangerDbRoles()
throws Exception {
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED,
"true");
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_USERNAME_HEADER_NAME,
"X-Forwarded-User");
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_ROLES_HEADER_NAME,
"X-Forwarded-Roles");
+
+ RangerHeaderPreAuthFilter filter = new RangerHeaderPreAuthFilter();
+ UserMgr userMgr = mock(UserMgr.class);
+
+ filter.userMgr = userMgr;
+ filter.initialize();
+
+
when(userMgr.getRolesByLoginId("joeuser")).thenReturn(Collections.singletonList("ROLE_USER"));
+
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+
+ when(request.getHeader("X-Forwarded-User")).thenReturn("joeuser");
+ when(request.getHeader("X-Forwarded-Roles")).thenReturn("ROLE_BOGUS");
+
+ FilterChain chain = new FilterChain() {
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res) {
+ org.springframework.security.core.Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
+
+ assertNotNull(auth);
+
+ Collection<?> authorities = auth.getAuthorities();
+ assertEquals(1, authorities.size());
+ assertTrue(authorities.stream().anyMatch(a ->
"ROLE_USER".equals(a.toString())));
+ }
+ };
+
+ filter.doFilter(request, response, chain);
+
+ verify(userMgr).getRolesByLoginId("joeuser");
+ }
+
+ @Test
+ public void
testDoFilter_enabled_rolesHeaderConfiguredButAbsent_fallsBackToRangerDbRoles()
throws Exception {
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED,
"true");
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_USERNAME_HEADER_NAME,
"X-Forwarded-User");
+
PropertiesUtil.getPropertiesMap().put(RangerHeaderPreAuthFilter.PROP_ROLES_HEADER_NAME,
"X-Forwarded-Roles");
+
+ RangerHeaderPreAuthFilter filter = new RangerHeaderPreAuthFilter();
+ UserMgr userMgr = mock(UserMgr.class);
+
+ filter.userMgr = userMgr;
+ filter.initialize();
+
+
when(userMgr.getRolesByLoginId("joeuser")).thenReturn(Arrays.asList("ROLE_SYS_ADMIN",
"ROLE_USER"));
+
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ HttpServletResponse response = mock(HttpServletResponse.class);
+
+ when(request.getHeader("X-Forwarded-User")).thenReturn("joeuser");
+ // roles header configured but not present in the request
+
+ FilterChain chain = new FilterChain() {
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res) {
+ org.springframework.security.core.Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
+
+ assertNotNull(auth);
+
+ Collection<?> authorities = auth.getAuthorities();
+ assertEquals(2, authorities.size());
+ assertTrue(authorities.stream().anyMatch(a ->
"ROLE_SYS_ADMIN".equals(a.toString())));
+ assertTrue(authorities.stream().anyMatch(a ->
"ROLE_USER".equals(a.toString())));
+ }
+ };
+
+ filter.doFilter(request, response, chain);
+
+ verify(userMgr).getRolesByLoginId("joeuser");
+ }
}