This is an automated email from the ASF dual-hosted git repository.

chaitalicod pushed a commit to branch atlas-2.6
in repository https://gitbox.apache.org/repos/asf/atlas.git


The following commit(s) were added to refs/heads/atlas-2.6 by this push:
     new 3dc819970 ATLAS-5308: Add support for header based authentication 
(#664)
3dc819970 is described below

commit 3dc8199707d338971226a4b171103358725960d9
Author: chaitalicod <[email protected]>
AuthorDate: Tue Jun 9 19:14:56 2026 +0530

    ATLAS-5308: Add support for header based authentication (#664)
    
    Co-authored-by: chaitali.borole <[email protected]>
    (cherry picked from commit 0f9409d6261fc124d1be85fa740305960ac6d84a)
---
 distro/src/conf/atlas-application.properties       |   8 +-
 .../web/filters/AtlasAuthenticationToken.java      |  55 +++++++
 .../web/filters/AtlasHeaderPreAuthFilter.java      | 171 +++++++++++++++++++++
 .../org/apache/atlas/web/filters/AuditFilter.java  |  11 +-
 .../atlas/web/security/AtlasSecurityConfig.java    |  11 +-
 webapp/src/main/resources/spring-security.xml      |   4 +
 .../web/filters/AtlasHeaderPreAuthFilterTest.java  | 146 ++++++++++++++++++
 .../web/security/AtlasSecurityConfigTest.java      |  14 +-
 8 files changed, 414 insertions(+), 6 deletions(-)

diff --git a/distro/src/conf/atlas-application.properties 
b/distro/src/conf/atlas-application.properties
index b5734d7a8..126143a20 100755
--- a/distro/src/conf/atlas-application.properties
+++ b/distro/src/conf/atlas-application.properties
@@ -281,4 +281,10 @@ atlas.search.gremlin.enable=false
 
 ######### Skip check for the same attribute name in Parent type and Child type 
#########
 
-#atlas.skip.check.for.parent.child.attribute.name=true
\ No newline at end of file
+#atlas.skip.check.for.parent.child.attribute.name=true
+
+######### Header Based Authentication #########
+#atlas.authn.header.enabled=false
+#atlas.authn.header.username=x-awc-username
+#atlas.authn.header.roles=x-awc-roles
+#atlas.authn.header.requestid=x-awc-requestid
\ No newline at end of file
diff --git 
a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationToken.java
 
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationToken.java
new file mode 100644
index 000000000..15cd70b9a
--- /dev/null
+++ 
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationToken.java
@@ -0,0 +1,55 @@
+/*
+ * 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.atlas.web.filters;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+
+public class AtlasAuthenticationToken extends AbstractAuthenticationToken {
+    public static final int AUTH_TYPE_TRUSTED_PROXY = 3;
+
+    private final UserDetails principal;
+    private final int         authType;
+
+    public AtlasAuthenticationToken(UserDetails principal, Collection<? 
extends GrantedAuthority> authorities, int authType) {
+        super(authorities);
+
+        this.principal = principal;
+        this.authType  = authType;
+
+        super.setAuthenticated(true);
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return principal;
+    }
+
+    @Override
+    public Object getCredentials() {
+        return null;
+    }
+
+    public int getAuthType() {
+        return authType;
+    }
+}
diff --git 
a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilter.java
 
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilter.java
new file mode 100644
index 000000000..423995bc6
--- /dev/null
+++ 
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilter.java
@@ -0,0 +1,171 @@
+/*
+ * 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.atlas.web.filters;
+
+import org.apache.atlas.ApplicationProperties;
+import org.apache.atlas.web.model.User;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import 
org.springframework.security.web.authentication.WebAuthenticationDetails;
+import org.springframework.stereotype.Component;
+
+import javax.inject.Inject;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+@Component
+public class AtlasHeaderPreAuthFilter implements Filter {
+    private static final Logger LOG = 
LoggerFactory.getLogger(AtlasHeaderPreAuthFilter.class);
+
+    public static final String PROP_HEADER_AUTH_ENABLED = 
"atlas.authn.header.enabled";
+    public static final String PROP_USERNAME_HEADER     = 
"atlas.authn.header.username";
+    public static final String PROP_ROLES_HEADER        = 
"atlas.authn.header.roles";
+    public static final String PROP_REQUEST_ID_HEADER   = 
"atlas.authn.header.requestid";
+    public static final String REQUEST_ID_ATTRIBUTE     = "atlas.request.id";
+
+    private Configuration configuration;
+    private boolean       headerAuthEnabled;
+    private String        userNameHeaderName;
+    private String        rolesHeaderName;
+
+    @Inject
+    public AtlasHeaderPreAuthFilter() {
+        loadConfiguration();
+    }
+
+    private void loadConfiguration() {
+        try {
+            configuration = ApplicationProperties.get();
+        } catch (Exception e) {
+            LOG.error("Error loading application properties for header 
pre-auth", e);
+            configuration = null;
+        }
+        if (configuration == null) {
+            headerAuthEnabled  = false;
+            userNameHeaderName = null;
+            rolesHeaderName    = null;
+            return;
+        }
+        headerAuthEnabled  = 
configuration.getBoolean(PROP_HEADER_AUTH_ENABLED, false);
+        userNameHeaderName = 
StringUtils.trimToNull(configuration.getString(PROP_USERNAME_HEADER, ""));
+        rolesHeaderName    = 
StringUtils.trimToNull(configuration.getString(PROP_ROLES_HEADER, ""));
+    }
+
+    private List<GrantedAuthority> getAuthoritiesFromRolesHeader(String 
rolesHeader) {
+        List<GrantedAuthority> ret = new ArrayList<>();
+        if (StringUtils.isBlank(rolesHeader)) {
+            return ret;
+        }
+
+        for (String role : rolesHeader.split(",")) {
+            String trimmed = StringUtils.trimToNull(role);
+            if (trimmed != null) {
+                ret.add(new 
org.springframework.security.core.authority.SimpleGrantedAuthority(trimmed));
+            }
+        }
+
+        return ret;
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) {
+        loadConfiguration();
+    }
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse 
servletResponse, FilterChain chain)
+            throws IOException, ServletException {
+        HttpServletRequest  httpRequest  = (HttpServletRequest) servletRequest;
+        HttpServletResponse httpResponse = (HttpServletResponse) 
servletResponse;
+
+        AtlasResponseRequestWrapper responseWrapper = new 
AtlasResponseRequestWrapper(httpResponse);
+        HeadersUtil.setSecurityHeaders(responseWrapper);
+
+        try {
+            if (headerAuthEnabled) {
+                Authentication pre = 
SecurityContextHolder.getContext().getAuthentication();
+
+                if (pre == null || !pre.isAuthenticated()) {
+                    String username = 
StringUtils.trimToNull(httpRequest.getHeader(userNameHeaderName));
+
+                    if (username != null) {
+                        List<GrantedAuthority> grantedAuths =
+                                
getAuthoritiesFromRolesHeader(httpRequest.getHeader(rolesHeaderName));
+
+                        UserDetails principal = new User(username, "", 
grantedAuths);
+
+                        AtlasAuthenticationToken token =
+                                new AtlasAuthenticationToken(principal, 
grantedAuths,
+                                        
AtlasAuthenticationToken.AUTH_TYPE_TRUSTED_PROXY);
+
+                        token.setDetails(new 
WebAuthenticationDetails(httpRequest));
+
+                        
SecurityContextHolder.getContext().setAuthentication(token);
+                    }
+                }
+            }
+
+            Authentication auth     = 
SecurityContextHolder.getContext().getAuthentication();
+            String         requestId = getRequestId(auth, httpRequest);
+            httpRequest.setAttribute(REQUEST_ID_ATTRIBUTE, requestId);
+
+            chain.doFilter(servletRequest, responseWrapper);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } catch (ServletException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private String getRequestId(Authentication auth, HttpServletRequest 
request) {
+        String requestIdHeaderName = configuration != null ? 
configuration.getString(PROP_REQUEST_ID_HEADER) : null;
+
+        if (requestIdHeaderName != null &&
+                auth instanceof AtlasAuthenticationToken &&
+                ((AtlasAuthenticationToken) auth).getAuthType() == 
AtlasAuthenticationToken.AUTH_TYPE_TRUSTED_PROXY) {
+            String requestId = 
StringUtils.trimToNull(request.getHeader(requestIdHeaderName));
+            if (requestId != null) {
+                return requestId;
+            }
+        }
+
+        return UUID.randomUUID().toString();
+    }
+
+    @Override
+    public void destroy() {
+    }
+}
diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java 
b/webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java
index 207879c7a..0a64e6b16 100755
--- a/webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java
+++ b/webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java
@@ -83,7 +83,7 @@ public class AuditFilter implements Filter {
         final Date                requestTime        = new Date();
         final HttpServletRequest  httpRequest        = (HttpServletRequest) 
request;
         final HttpServletResponse httpResponse       = (HttpServletResponse) 
response;
-        final String              requestId          = 
UUID.randomUUID().toString();
+        final String              requestId          = 
getRequestId(httpRequest);
         final Thread              currentThread      = Thread.currentThread();
         final String              oldName            = currentThread.getName();
         final String              user               = 
AtlasAuthorizationUtils.getCurrentUserName();
@@ -140,6 +140,15 @@ public class AuditFilter implements Filter {
         return oldName + " - " + requestId;
     }
 
+    private String getRequestId(HttpServletRequest httpRequest) {
+        String requestId = (String) 
httpRequest.getAttribute(AtlasHeaderPreAuthFilter.REQUEST_ID_ATTRIBUTE);
+        if (StringUtils.isNotEmpty(requestId)) {
+            return requestId;
+        }
+
+        return UUID.randomUUID().toString();
+    }
+
     private void recordAudit(HttpServletRequest httpRequest, Date when, String 
who, int httpStatus, long timeTaken) {
         final String fromAddress = httpRequest.getRemoteAddr();
         final String whatRequest = httpRequest.getMethod();
diff --git 
a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java 
b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java
index 0f7ad76b4..fbcdf3d7e 100644
--- 
a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java
+++ 
b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java
@@ -22,6 +22,7 @@ import 
org.apache.atlas.web.filters.AtlasAuthenticationEntryPoint;
 import org.apache.atlas.web.filters.AtlasAuthenticationFilter;
 import org.apache.atlas.web.filters.AtlasCSRFPreventionFilter;
 import org.apache.atlas.web.filters.AtlasDelegatingAuthenticationEntryPoint;
+import org.apache.atlas.web.filters.AtlasHeaderPreAuthFilter;
 import org.apache.atlas.web.filters.AtlasKnoxSSOAuthenticationFilter;
 import org.apache.atlas.web.filters.HeadersUtil;
 import org.apache.atlas.web.filters.StaleTransactionCleanupFilter;
@@ -93,6 +94,7 @@ public class AtlasSecurityConfig extends 
WebSecurityConfigurerAdapter {
     private final AtlasAuthenticationProvider       authenticationProvider;
     private final AtlasAuthenticationSuccessHandler successHandler;
     private final AtlasAuthenticationFailureHandler failureHandler;
+    private final AtlasHeaderPreAuthFilter          headerPreAuthFilter;
     private final AtlasKnoxSSOAuthenticationFilter  ssoAuthenticationFilter;
     private final AtlasAuthenticationFilter         atlasAuthenticationFilter;
     private final AtlasCSRFPreventionFilter         csrfPreventionFilter;
@@ -111,7 +113,8 @@ public class AtlasSecurityConfig extends 
WebSecurityConfigurerAdapter {
     private KeycloakConfigResolver keycloakConfigResolver;
 
     @Inject
-    public AtlasSecurityConfig(AtlasKnoxSSOAuthenticationFilter 
ssoAuthenticationFilter,
+    public AtlasSecurityConfig(AtlasHeaderPreAuthFilter headerPreAuthFilter,
+            AtlasKnoxSSOAuthenticationFilter ssoAuthenticationFilter,
             AtlasCSRFPreventionFilter atlasCSRFPreventionFilter,
             AtlasAuthenticationFilter atlasAuthenticationFilter,
             AtlasAuthenticationProvider authenticationProvider,
@@ -121,6 +124,7 @@ public class AtlasSecurityConfig extends 
WebSecurityConfigurerAdapter {
             Configuration configuration,
             StaleTransactionCleanupFilter staleTransactionCleanupFilter,
             ActiveServerFilter activeServerFilter) {
+        this.headerPreAuthFilter           = headerPreAuthFilter;
         this.ssoAuthenticationFilter       = ssoAuthenticationFilter;
         this.csrfPreventionFilter          = atlasCSRFPreventionFilter;
         this.atlasAuthenticationFilter     = atlasAuthenticationFilter;
@@ -238,8 +242,9 @@ public class AtlasSecurityConfig extends 
WebSecurityConfigurerAdapter {
             httpSecurity.addFilterAfter(activeServerFilter, 
BasicAuthenticationFilter.class);
         }
 
-        httpSecurity.addFilterAfter(staleTransactionCleanupFilter, 
BasicAuthenticationFilter.class)
-                .addFilterBefore(ssoAuthenticationFilter, 
BasicAuthenticationFilter.class)
+        httpSecurity.addFilterBefore(headerPreAuthFilter, 
BasicAuthenticationFilter.class)
+                .addFilterAfter(ssoAuthenticationFilter, 
AtlasHeaderPreAuthFilter.class)
+                .addFilterAfter(staleTransactionCleanupFilter, 
BasicAuthenticationFilter.class)
                 .addFilterAfter(atlasAuthenticationFilter, 
SecurityContextHolderAwareRequestFilter.class)
                 .addFilterAfter(csrfPreventionFilter, 
AtlasAuthenticationFilter.class);
 
diff --git a/webapp/src/main/resources/spring-security.xml 
b/webapp/src/main/resources/spring-security.xml
index 3020690dc..ea41d46ab 100644
--- a/webapp/src/main/resources/spring-security.xml
+++ b/webapp/src/main/resources/spring-security.xml
@@ -34,6 +34,7 @@
         <security:session-management
                 session-fixation-protection="newSession" />
         <intercept-url pattern="/**" access="isAuthenticated()" />
+        <security:custom-filter position="PRE_AUTH_FILTER" 
ref="headerPreAuthFilter" />
         <custom-filter ref="ssoAuthenticationFilter" after="BASIC_AUTH_FILTER" 
/>
 
         <security:custom-filter ref="krbAuthenticationFilter" 
after="SERVLET_API_SUPPORT_FILTER" />
@@ -54,6 +55,9 @@
         <csrf disabled="true"/>
     </security:http>
 
+    <beans:bean id="headerPreAuthFilter" 
class="org.apache.atlas.web.filters.AtlasHeaderPreAuthFilter">
+    </beans:bean>
+
     <beans:bean id="krbAuthenticationFilter" 
class="org.apache.atlas.web.filters.AtlasAuthenticationFilter">
     </beans:bean>
 
diff --git 
a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilterTest.java
 
b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilterTest.java
new file mode 100644
index 000000000..cd959aaa6
--- /dev/null
+++ 
b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderPreAuthFilterTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.atlas.web.filters;
+
+import org.apache.atlas.ApplicationProperties;
+import org.apache.commons.configuration2.Configuration;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.MockitoAnnotations;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.IOException;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+public class AtlasHeaderPreAuthFilterTest {
+    @Mock
+    private HttpServletRequest request;
+
+    @Mock
+    private HttpServletResponse response;
+
+    @Mock
+    private FilterChain filterChain;
+
+    @Mock
+    private Configuration configuration;
+
+    @BeforeMethod
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        SecurityContextHolder.clearContext();
+    }
+
+    @AfterMethod
+    public void tearDown() {
+        SecurityContextHolder.clearContext();
+    }
+
+    @Test
+    public void testDoFilterEnabledWithUsernameAndRoles() throws Exception {
+        
when(configuration.getBoolean(AtlasHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED,
 false)).thenReturn(true);
+        
when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_USERNAME_HEADER, ""))
+                .thenReturn("x-user");
+        
when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_ROLES_HEADER, ""))
+                .thenReturn("x-roles");
+        when(request.getHeader("x-user")).thenReturn("alice");
+        when(request.getHeader("x-roles")).thenReturn("ROLE_ADMIN, ROLE_USER");
+
+        try (MockedStatic<ApplicationProperties> appProps = 
org.mockito.Mockito.mockStatic(ApplicationProperties.class)) {
+            
appProps.when(ApplicationProperties::get).thenReturn(configuration);
+
+            AtlasHeaderPreAuthFilter filter = new AtlasHeaderPreAuthFilter();
+            filter.doFilter(request, response, filterChain);
+        }
+
+        Authentication auth = 
SecurityContextHolder.getContext().getAuthentication();
+        assertNotNull(auth);
+        assertTrue(auth instanceof AtlasAuthenticationToken);
+
+        AtlasAuthenticationToken token = (AtlasAuthenticationToken) auth;
+        UserDetails principal          = (UserDetails) token.getPrincipal();
+
+        assertEquals(principal.getUsername(), "alice");
+        assertEquals(token.getAuthorities().size(), 2);
+        assertEquals(token.getAuthType(), 
AtlasAuthenticationToken.AUTH_TYPE_TRUSTED_PROXY);
+        verify(filterChain).doFilter(eq(request), 
any(HttpServletResponse.class));
+    }
+
+    @Test
+    public void testDoFilterEnabledWithoutUsernameDoesNotAuthenticate() throws 
IOException, ServletException {
+        
when(configuration.getBoolean(AtlasHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED,
 false)).thenReturn(true);
+        
when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_USERNAME_HEADER, ""))
+                .thenReturn("x-user");
+        
when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_ROLES_HEADER, ""))
+                .thenReturn("x-roles");
+        when(request.getHeader("x-user")).thenReturn("   ");
+
+        try (MockedStatic<ApplicationProperties> appProps = 
org.mockito.Mockito.mockStatic(ApplicationProperties.class)) {
+            
appProps.when(ApplicationProperties::get).thenReturn(configuration);
+
+            AtlasHeaderPreAuthFilter filter = new AtlasHeaderPreAuthFilter();
+            filter.doFilter(request, response, filterChain);
+        }
+
+        assertEquals(SecurityContextHolder.getContext().getAuthentication(), 
null);
+        verify(filterChain).doFilter(eq(request), 
any(HttpServletResponse.class));
+    }
+
+    @Test
+    public void testDoFilterEnabledKeepsExistingAuthentication() throws 
IOException, ServletException {
+        Authentication existing = 
org.mockito.Mockito.mock(Authentication.class);
+        when(existing.isAuthenticated()).thenReturn(true);
+        SecurityContextHolder.getContext().setAuthentication(existing);
+
+        
when(configuration.getBoolean(AtlasHeaderPreAuthFilter.PROP_HEADER_AUTH_ENABLED,
 false)).thenReturn(true);
+        
when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_USERNAME_HEADER, ""))
+                .thenReturn("x-user");
+        
when(configuration.getString(AtlasHeaderPreAuthFilter.PROP_ROLES_HEADER, ""))
+                .thenReturn("x-roles");
+        when(request.getHeader("x-user")).thenReturn("alice");
+
+        try (MockedStatic<ApplicationProperties> appProps = 
org.mockito.Mockito.mockStatic(ApplicationProperties.class)) {
+            
appProps.when(ApplicationProperties::get).thenReturn(configuration);
+
+            AtlasHeaderPreAuthFilter filter = new AtlasHeaderPreAuthFilter();
+            filter.doFilter(request, response, filterChain);
+        }
+
+        assertSame(SecurityContextHolder.getContext().getAuthentication(), 
existing);
+        verify(filterChain).doFilter(eq(request), 
any(HttpServletResponse.class));
+    }
+}
diff --git 
a/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java
 
b/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java
index a0d14f293..4828b4b92 100644
--- 
a/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java
+++ 
b/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java
@@ -24,6 +24,7 @@ import 
org.apache.atlas.web.filters.AtlasAuthenticationEntryPoint;
 import org.apache.atlas.web.filters.AtlasAuthenticationFilter;
 import org.apache.atlas.web.filters.AtlasCSRFPreventionFilter;
 import org.apache.atlas.web.filters.AtlasDelegatingAuthenticationEntryPoint;
+import org.apache.atlas.web.filters.AtlasHeaderPreAuthFilter;
 import org.apache.atlas.web.filters.AtlasKnoxSSOAuthenticationFilter;
 import org.apache.atlas.web.filters.StaleTransactionCleanupFilter;
 import org.apache.commons.configuration2.Configuration;
@@ -98,6 +99,9 @@ import static org.testng.Assert.assertTrue;
 
 @SuppressWarnings("deprecation")
 public class AtlasSecurityConfigTest {
+    @Mock
+    private AtlasHeaderPreAuthFilter mockHeaderPreAuthFilter;
+
     @Mock
     private AtlasKnoxSSOAuthenticationFilter mockSsoAuthenticationFilter;
 
@@ -160,6 +164,7 @@ public class AtlasSecurityConfigTest {
 
         // Execute
         atlasSecurityConfig = new AtlasSecurityConfig(
+                mockHeaderPreAuthFilter,
                 mockSsoAuthenticationFilter,
                 mockCsrfPreventionFilter,
                 mockAtlasAuthenticationFilter,
@@ -186,6 +191,7 @@ public class AtlasSecurityConfigTest {
 
         // Execute
         atlasSecurityConfig = new AtlasSecurityConfig(
+                mockHeaderPreAuthFilter,
                 mockSsoAuthenticationFilter,
                 mockCsrfPreventionFilter,
                 mockAtlasAuthenticationFilter,
@@ -346,6 +352,7 @@ public class AtlasSecurityConfigTest {
 
         // Create AtlasSecurityConfig instance
         AtlasSecurityConfig configInstance = new AtlasSecurityConfig(
+                mockHeaderPreAuthFilter,
                 mockSsoAuthenticationFilter,
                 mockCsrfPreventionFilter,
                 mockAtlasAuthenticationFilter,
@@ -430,6 +437,7 @@ public class AtlasSecurityConfigTest {
 
         // Create fresh AtlasSecurityConfig instance
         AtlasSecurityConfig configInstance = new AtlasSecurityConfig(
+                mockHeaderPreAuthFilter,
                 mockSsoAuthenticationFilter,
                 mockCsrfPreventionFilter,
                 mockAtlasAuthenticationFilter,
@@ -480,8 +488,9 @@ public class AtlasSecurityConfigTest {
         }
 
         // Verify standard filters are always added
+        verify(freshHttpSecurity, 
atLeastOnce()).addFilterBefore(eq(mockHeaderPreAuthFilter), any());
+        verify(freshHttpSecurity, 
atLeastOnce()).addFilterAfter(eq(mockSsoAuthenticationFilter), 
eq(AtlasHeaderPreAuthFilter.class));
         verify(freshHttpSecurity, 
atLeastOnce()).addFilterAfter(eq(mockStaleTransactionCleanupFilter), any());
-        verify(freshHttpSecurity, 
atLeastOnce()).addFilterBefore(eq(mockSsoAuthenticationFilter), any());
         verify(freshHttpSecurity, 
atLeastOnce()).addFilterAfter(eq(mockAtlasAuthenticationFilter), any());
         verify(freshHttpSecurity, 
atLeastOnce()).addFilterAfter(eq(mockCsrfPreventionFilter), any());
 
@@ -581,6 +590,7 @@ public class AtlasSecurityConfigTest {
         setupKeycloakConfiguration("/path/to/keycloak.json");
 
         atlasSecurityConfig = new AtlasSecurityConfig(
+                mockHeaderPreAuthFilter,
                 mockSsoAuthenticationFilter,
                 mockCsrfPreventionFilter,
                 mockAtlasAuthenticationFilter,
@@ -811,6 +821,7 @@ public class AtlasSecurityConfigTest {
     public void testInjectAnnotation() throws Exception {
         // Verify @Inject annotation on constructor
         Inject injectAnnotation = AtlasSecurityConfig.class.getConstructor(
+                AtlasHeaderPreAuthFilter.class,
                 AtlasKnoxSSOAuthenticationFilter.class,
                 AtlasCSRFPreventionFilter.class,
                 AtlasAuthenticationFilter.class,
@@ -836,6 +847,7 @@ public class AtlasSecurityConfigTest {
         }
 
         atlasSecurityConfig = new AtlasSecurityConfig(
+                mockHeaderPreAuthFilter,
                 mockSsoAuthenticationFilter,
                 mockCsrfPreventionFilter,
                 mockAtlasAuthenticationFilter,

Reply via email to