Skip to content

Commit 2345c1d

Browse files
kumaabmneethirajCursor AI (assisted)
authored
RANGER-5499: Add support for header based authentication (#873)
Co-authored-by: Madhan Neethiraj <madhan@apache.org> Co-authored-by: Cursor AI (assisted) <noreply@cursor.com>
1 parent 2702a2f commit 2345c1d

8 files changed

Lines changed: 498 additions & 93 deletions

File tree

mkdocs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.cache/
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.ranger.security.web.filter;
20+
21+
import org.springframework.security.authentication.AbstractAuthenticationToken;
22+
import org.springframework.security.core.GrantedAuthority;
23+
import org.springframework.security.core.userdetails.UserDetails;
24+
25+
import java.util.Collection;
26+
27+
public class RangerAuthenticationToken extends AbstractAuthenticationToken {
28+
private static final long serialVersionUID = 1L;
29+
30+
private final Object principal;
31+
private final int authType;
32+
33+
public RangerAuthenticationToken(UserDetails principal, Collection<? extends GrantedAuthority> authorities, int authType) {
34+
super(authorities);
35+
36+
this.principal = principal;
37+
this.authType = authType;
38+
39+
super.setAuthenticated(true);
40+
}
41+
42+
@Override
43+
public Object getPrincipal() {
44+
return principal;
45+
}
46+
47+
@Override
48+
public Object getCredentials() {
49+
return null;
50+
}
51+
52+
public int getAuthType() {
53+
return authType;
54+
}
55+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.ranger.security.web.filter;
20+
21+
import org.apache.commons.lang3.StringUtils;
22+
import org.apache.ranger.biz.UserMgr;
23+
import org.apache.ranger.common.PropertiesUtil;
24+
import org.apache.ranger.entity.XXAuthSession;
25+
import org.slf4j.Logger;
26+
import org.slf4j.LoggerFactory;
27+
import org.springframework.beans.factory.annotation.Autowired;
28+
import org.springframework.security.core.Authentication;
29+
import org.springframework.security.core.GrantedAuthority;
30+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
31+
import org.springframework.security.core.context.SecurityContextHolder;
32+
import org.springframework.security.core.userdetails.User;
33+
import org.springframework.security.core.userdetails.UserDetails;
34+
import org.springframework.security.web.authentication.WebAuthenticationDetails;
35+
import org.springframework.web.filter.GenericFilterBean;
36+
37+
import javax.annotation.PostConstruct;
38+
import javax.servlet.FilterChain;
39+
import javax.servlet.FilterConfig;
40+
import javax.servlet.ServletException;
41+
import javax.servlet.ServletRequest;
42+
import javax.servlet.ServletResponse;
43+
import javax.servlet.http.HttpServletRequest;
44+
45+
import java.io.IOException;
46+
import java.util.ArrayList;
47+
import java.util.Collection;
48+
import java.util.List;
49+
50+
public class RangerHeaderPreAuthFilter extends GenericFilterBean {
51+
private static final Logger LOG = LoggerFactory.getLogger(RangerHeaderPreAuthFilter.class);
52+
53+
public static final String PROP_HEADER_AUTH_ENABLED = "ranger.admin.authn.header.enabled";
54+
public static final String PROP_USERNAME_HEADER_NAME = "ranger.admin.authn.header.username";
55+
public static final String PROP_REQUEST_ID_HEADER_NAME = "ranger.admin.authn.header.requestid";
56+
57+
private boolean headerAuthEnabled;
58+
private String userNameHeaderName;
59+
60+
@Autowired
61+
UserMgr userMgr;
62+
63+
@PostConstruct
64+
public void initialize(FilterConfig filterConfig) throws ServletException {
65+
headerAuthEnabled = PropertiesUtil.getBooleanProperty(PROP_HEADER_AUTH_ENABLED, false);
66+
userNameHeaderName = PropertiesUtil.getProperty(PROP_USERNAME_HEADER_NAME);
67+
}
68+
69+
@Override
70+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
71+
HttpServletRequest httpRequest = (HttpServletRequest) request;
72+
73+
if (headerAuthEnabled) {
74+
Authentication existingAuthn = SecurityContextHolder.getContext().getAuthentication();
75+
76+
if (existingAuthn == null || !existingAuthn.isAuthenticated()) {
77+
String username = StringUtils.trimToNull(httpRequest.getHeader(userNameHeaderName));
78+
79+
if (StringUtils.isNotBlank(username)) {
80+
List<GrantedAuthority> grantedAuthorities = getAuthoritiesFromRanger(username);
81+
final UserDetails principal = new User(username, "", grantedAuthorities);
82+
RangerAuthenticationToken authToken = new RangerAuthenticationToken(principal, grantedAuthorities, XXAuthSession.AUTH_TYPE_TRUSTED_PROXY);
83+
84+
authToken.setDetails(new WebAuthenticationDetails(httpRequest));
85+
86+
SecurityContextHolder.getContext().setAuthentication(authToken);
87+
88+
LOG.debug("Authenticated request using trusted headers for user={}", username);
89+
} else {
90+
LOG.debug("Username header '{}' is missing or empty in the request!", userNameHeaderName);
91+
}
92+
}
93+
} else {
94+
LOG.debug("Header-based authentication is disabled!");
95+
}
96+
97+
chain.doFilter(request, response);
98+
}
99+
100+
/**
101+
* Loads authorities from Ranger DB
102+
*/
103+
private List<GrantedAuthority> getAuthoritiesFromRanger(String username) {
104+
List<GrantedAuthority> ret = new ArrayList<>();
105+
Collection<String> roleList = userMgr.getRolesByLoginId(username);
106+
107+
if (roleList != null) {
108+
for (String role : roleList) {
109+
if (StringUtils.isNotBlank(role)) {
110+
ret.add(new SimpleGrantedAuthority(role));
111+
}
112+
}
113+
}
114+
115+
return ret;
116+
}
117+
}

security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerSecurityContextFormationFilter.java

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
*/
2323
package org.apache.ranger.security.web.filter;
2424

25+
import org.apache.commons.lang3.StringUtils;
2526
import org.apache.ranger.biz.SessionMgr;
2627
import org.apache.ranger.biz.XUserMgr;
2728
import org.apache.ranger.common.GUIDUtil;
@@ -33,6 +34,8 @@
3334
import org.apache.ranger.security.context.RangerContextHolder;
3435
import org.apache.ranger.security.context.RangerSecurityContext;
3536
import org.apache.ranger.util.RestUtil;
37+
import org.slf4j.Logger;
38+
import org.slf4j.LoggerFactory;
3639
import org.springframework.beans.factory.annotation.Autowired;
3740
import org.springframework.security.authentication.AnonymousAuthenticationToken;
3841
import org.springframework.security.core.Authentication;
@@ -50,6 +53,8 @@
5053
import java.io.IOException;
5154

5255
public class RangerSecurityContextFormationFilter extends GenericFilterBean {
56+
private static final Logger LOG = LoggerFactory.getLogger(RangerSecurityContextFormationFilter.class);
57+
5358
public static final String AKA_SC_SESSION_KEY = "AKA_SECURITY_CONTEXT";
5459
public static final String USER_AGENT = "User-Agent";
5560

@@ -65,10 +70,12 @@ public class RangerSecurityContextFormationFilter extends GenericFilterBean {
6570
@Autowired
6671
GUIDUtil guidUtil;
6772

68-
String testIP;
73+
private final String testIP;
74+
private final String requestIdHeaderName;
6975

7076
public RangerSecurityContextFormationFilter() {
71-
testIP = PropertiesUtil.getProperty("xa.env.ip");
77+
this.testIP = PropertiesUtil.getProperty("xa.env.ip");
78+
this.requestIdHeaderName = PropertiesUtil.getProperty(RangerHeaderPreAuthFilter.PROP_REQUEST_ID_HEADER_NAME);
7279
}
7380

7481
/*
@@ -113,15 +120,15 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
113120
requestContext.setIpAddress(reqIP);
114121
requestContext.setUserAgent(userAgent);
115122
requestContext.setDeviceType(httpUtil.getDeviceType(httpRequest));
116-
requestContext.setServerRequestId(guidUtil.genGUID());
123+
requestContext.setServerRequestId(getRequestId(auth, httpRequest));
117124
requestContext.setRequestURL(httpRequest.getRequestURI());
118125
requestContext.setClientTimeOffsetInMinute(clientTimeOffset);
119126

120127
context.setRequestContext(requestContext);
121128

122129
RangerContextHolder.setSecurityContext(context);
123130

124-
int authType = getAuthType(httpRequest);
131+
int authType = getAuthType(auth, httpRequest);
125132
UserSessionBase userSession = sessionMgr.processSuccessLogin(authType, userAgent, httpRequest);
126133

127134
if (userSession != null) {
@@ -160,25 +167,36 @@ private void setupAdminOpContext(ServletRequest request) {
160167
}
161168
}
162169

163-
private int getAuthType(HttpServletRequest request) {
164-
int authType;
170+
private int getAuthType(Authentication auth, HttpServletRequest request) {
171+
if (auth instanceof RangerAuthenticationToken) {
172+
return ((RangerAuthenticationToken) auth).getAuthType();
173+
}
174+
165175
Object ssoEnabledObj = request.getAttribute("ssoEnabled");
166176
boolean ssoEnabled = ssoEnabledObj != null ? Boolean.parseBoolean(String.valueOf(ssoEnabledObj)) : PropertiesUtil.getBooleanProperty("ranger.sso.enabled", false);
167177

168178
if (ssoEnabled) {
169-
authType = XXAuthSession.AUTH_TYPE_SSO;
179+
return XXAuthSession.AUTH_TYPE_SSO;
170180
} else if (request.getAttribute("spnegoEnabled") != null && Boolean.parseBoolean(String.valueOf(request.getAttribute("spnegoEnabled")))) {
171181
if (request.getAttribute("trustedProxyEnabled") != null && Boolean.parseBoolean(String.valueOf(request.getAttribute("trustedProxyEnabled")))) {
172-
logger.debug("Setting auth type as trusted proxy");
182+
LOG.debug("Setting auth type as trusted proxy");
173183

174-
authType = XXAuthSession.AUTH_TYPE_TRUSTED_PROXY;
184+
return XXAuthSession.AUTH_TYPE_TRUSTED_PROXY;
175185
} else {
176-
authType = XXAuthSession.AUTH_TYPE_KERBEROS;
186+
return XXAuthSession.AUTH_TYPE_KERBEROS;
177187
}
178-
} else {
179-
authType = XXAuthSession.AUTH_TYPE_PASSWORD;
180188
}
181189

182-
return authType;
190+
return XXAuthSession.AUTH_TYPE_PASSWORD;
191+
}
192+
193+
private String getRequestId(Authentication auth, HttpServletRequest request) {
194+
String ret = null;
195+
196+
if (requestIdHeaderName != null && auth instanceof RangerAuthenticationToken && ((RangerAuthenticationToken) auth).getAuthType() == XXAuthSession.AUTH_TYPE_TRUSTED_PROXY) {
197+
ret = StringUtils.trimToNull(request.getHeader(requestIdHeaderName));
198+
}
199+
200+
return ret != null ? ret : guidUtil.genGUID();
183201
}
184202
}

security-admin/src/main/resources/conf.dist/ranger-admin-site.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,20 @@
271271
<value>Mozilla,chrome</value>
272272
</property>
273273
<!-- SSO Properties Ends-->
274+
<!-- Trusted header auth properties start -->
275+
<property>
276+
<name>ranger.admin.authn.header.enabled</name>
277+
<value>false</value>
278+
</property>
279+
<property>
280+
<name>ranger.admin.authn.header.username</name>
281+
<value>x-awc-username</value>
282+
</property>
283+
<property>
284+
<name>ranger.admin.authn.header.requestid</name>
285+
<value>x-awc-requestid</value>
286+
</property>
287+
<!-- Trusted header auth properties end -->
274288
<!-- Kerberos Properties starts-->
275289
<property>
276290
<name>ranger.admin.kerberos.token.valid.seconds</name>

security-admin/src/main/resources/conf.dist/security-applicationContext.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd">
6464
</security:headers>
6565
<security:session-management session-fixation-protection="newSession" />
6666
<intercept-url pattern="/**" access="isAuthenticated()"/>
67+
<security:custom-filter position="PRE_AUTH_FILTER" ref="headerPreAuthFilter" />
6768
<custom-filter ref="ssoAuthenticationFilter" after="BASIC_AUTH_FILTER" />
6869
<security:custom-filter ref="rangerJwtAuthWrapper" before="SERVLET_API_SUPPORT_FILTER" />
6970
<security:custom-filter ref="krbAuthenticationFilter" after="SERVLET_API_SUPPORT_FILTER" />
@@ -115,6 +116,9 @@ http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd">
115116
<beans:bean id="mdcFilter" class="org.apache.ranger.security.web.filter.RangerMDCFilter">
116117
</beans:bean>
117118

119+
<beans:bean id="headerPreAuthFilter" class="org.apache.ranger.security.web.filter.RangerHeaderPreAuthFilter">
120+
</beans:bean>
121+
118122
<beans:bean id="ssoAuthenticationFilter" class="org.apache.ranger.security.web.filter.RangerSSOAuthenticationFilter">
119123
</beans:bean>
120124

0 commit comments

Comments
 (0)