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

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

commit 9a752f125617a71e0c85f68aab907b48a8fb3ac7
Author: pareshd <[email protected]>
AuthorDate: Fri Apr 25 01:59:39 2025 +0530

    ATLAS-5019: Add header in atlas login.jsp page (#335)
    
    (cherry picked from commit 008f7548752dafa25a7f4ba336b89be2557e0926)
---
 .../web/filters/AtlasAuthenticationFilter.java     |  15 +--
 .../web/filters/AtlasCSRFPreventionFilter.java     |   2 +-
 .../filters/AtlasKnoxSSOAuthenticationFilter.java  |   5 +-
 .../org/apache/atlas/web/filters/HeadersUtil.java  |  77 +++++++++++----
 .../atlas/web/servlets/AtlasHttpServlet.java       |  22 ++---
 .../apache/atlas/web/filters/HeaderUtilsTest.java  | 108 +++++++++++++++++++++
 6 files changed, 178 insertions(+), 51 deletions(-)

diff --git 
a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java
 
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java
index e95828f4a..652af3a86 100644
--- 
a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java
+++ 
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java
@@ -155,10 +155,6 @@ public class AtlasAuthenticationFilter extends 
AuthenticationFilter {
             throw new ServletException(e);
         }
 
-        if (configuration != null) {
-            headerProperties = 
ConfigurationConverter.getProperties(configuration.subset("atlas.headers"));
-        }
-
         String tokenValidityStr = null;
 
         if (configuration != null) {
@@ -416,16 +412,7 @@ public class AtlasAuthenticationFilter extends 
AuthenticationFilter {
             String                      action          = 
httpRequest.getParameter("action");
             String                      doAsUser        = 
request.getParameter("doAs");
 
-            HeadersUtil.setHeaderMapAttributes(responseWrapper, 
HeadersUtil.X_FRAME_OPTIONS_KEY);
-            HeadersUtil.setHeaderMapAttributes(responseWrapper, 
HeadersUtil.X_CONTENT_TYPE_OPTIONS_KEY);
-            HeadersUtil.setHeaderMapAttributes(responseWrapper, 
HeadersUtil.X_XSS_PROTECTION_KEY);
-            HeadersUtil.setHeaderMapAttributes(responseWrapper, 
HeadersUtil.STRICT_TRANSPORT_SEC_KEY);
-
-            if (headerProperties != null) {
-                for (String headerKey : 
headerProperties.stringPropertyNames()) {
-                    responseWrapper.setHeader(headerKey, 
headerProperties.getProperty(headerKey));
-                }
-            }
+            HeadersUtil.setSecurityHeaders(responseWrapper);
 
             if (logoutHandler != null && supportTrustedProxy && 
StringUtils.isNotEmpty(doAsUser) && StringUtils.equals(action, 
RestUtil.TIMEOUT_ACTION)) {
                 if (existingAuth != null) {
diff --git 
a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java
 
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java
index e62cde382..7dc12acc3 100644
--- 
a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java
+++ 
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java
@@ -106,7 +106,7 @@ public class AtlasCSRFPreventionFilter implements Filter {
         final HttpServletResponse   httpResponse    = (HttpServletResponse) 
response;
         AtlasResponseRequestWrapper responseWrapper = new 
AtlasResponseRequestWrapper(httpResponse);
 
-        HeadersUtil.setHeaderMapAttributes(responseWrapper, 
HeadersUtil.X_FRAME_OPTIONS_KEY);
+        HeadersUtil.setSecurityHeaders(responseWrapper);
 
         if (isCSRF_ENABLED) {
             handleHttpInteraction(new 
ServletFilterHttpInteraction(httpRequest, httpResponse, chain));
diff --git 
a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java
 
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java
index ac8de40c8..a5dca7aae 100644
--- 
a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java
+++ 
b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java
@@ -166,10 +166,7 @@ public class AtlasKnoxSSOAuthenticationFilter implements 
Filter {
         HttpServletResponse         httpResponse    = (HttpServletResponse) 
servletResponse;
         AtlasResponseRequestWrapper responseWrapper = new 
AtlasResponseRequestWrapper(httpResponse);
 
-        HeadersUtil.setHeaderMapAttributes(responseWrapper, 
HeadersUtil.X_FRAME_OPTIONS_KEY);
-        HeadersUtil.setHeaderMapAttributes(responseWrapper, 
HeadersUtil.X_CONTENT_TYPE_OPTIONS_KEY);
-        HeadersUtil.setHeaderMapAttributes(responseWrapper, 
HeadersUtil.X_XSS_PROTECTION_KEY);
-        HeadersUtil.setHeaderMapAttributes(responseWrapper, 
HeadersUtil.STRICT_TRANSPORT_SEC_KEY);
+        HeadersUtil.setSecurityHeaders(responseWrapper);
 
         if (!ssoEnabled) {
             filterChain.doFilter(servletRequest, servletResponse);
diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.java 
b/webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.java
index cc9863112..dbec3cdbf 100644
--- a/webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.java
+++ b/webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.java
@@ -17,28 +17,39 @@
  */
 package org.apache.atlas.web.filters;
 
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.atlas.ApplicationProperties;
 import org.apache.atlas.AtlasConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
+import java.util.Properties;
 
 public class HeadersUtil {
-    public static final String X_FRAME_OPTIONS_KEY        = "X-Frame-Options";
-    public static final String X_CONTENT_TYPE_OPTIONS_KEY = 
"X-Content-Type-Options";
-    public static final String X_XSS_PROTECTION_KEY       = "X-XSS-Protection";
-    public static final String STRICT_TRANSPORT_SEC_KEY   = 
"Strict-Transport-Security";
-    public static final String CONTENT_SEC_POLICY_KEY     = 
"Content-Security-Policy";
-    public static final String X_FRAME_OPTIONS_VAL        = "DENY";
-    public static final String X_CONTENT_TYPE_OPTIONS_VAL = "nosniff";
-    public static final String X_XSS_PROTECTION_VAL       = "1; mode=block";
-    public static final String STRICT_TRANSPORT_SEC_VAL   = "max-age=31536000; 
includeSubDomains";
-    public static final String CONTENT_SEC_POLICY_VAL     = "default-src 
'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data:; 
connect-src 'self'; img-src 'self' blob: data:; style-src 'self' 
'unsafe-inline';font-src 'self' data:";
-    public static final String SERVER_KEY                 = "Server";
-    public static final String USER_AGENT_KEY             = "User-Agent";
-    public static final String USER_AGENT_VALUE           = "Mozilla";
-    public static final String X_REQUESTED_WITH_KEY       = "X-REQUESTED-WITH";
-    public static final String X_REQUESTED_WITH_VALUE     = "XMLHttpRequest";
-    public static final int    SC_AUTHENTICATION_TIMEOUT  = 419;
+    public static final Logger LOG = 
LoggerFactory.getLogger(HeadersUtil.class);
+
+    public static final String X_FRAME_OPTIONS_KEY                 = 
"X-Frame-Options";
+    public static final String X_CONTENT_TYPE_OPTIONS_KEY          = 
"X-Content-Type-Options";
+    public static final String X_XSS_PROTECTION_KEY                = 
"X-XSS-Protection";
+    public static final String STRICT_TRANSPORT_SEC_KEY            = 
"Strict-Transport-Security";
+    public static final String CONTENT_SEC_POLICY_KEY              = 
"Content-Security-Policy";
+    public static final String X_FRAME_OPTIONS_VAL                 = "DENY";
+    public static final String X_CONTENT_TYPE_OPTIONS_VAL          = "nosniff";
+    public static final String X_XSS_PROTECTION_VAL                = "1; 
mode=block";
+    public static final String STRICT_TRANSPORT_SEC_VAL            = 
"max-age=31536000; includeSubDomains";
+    public static final String CONTENT_SEC_POLICY_VAL              = 
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: 
data:; connect-src 'self'; img-src 'self' blob: data:; style-src 'self' 
'unsafe-inline';font-src 'self' data:";
+    public static final String SERVER_KEY                          = "Server";
+    public static final String USER_AGENT_KEY                      = 
"User-Agent";
+    public static final String USER_AGENT_VALUE                    = "Mozilla";
+    public static final String X_REQUESTED_WITH_KEY                = 
"X-REQUESTED-WITH";
+    public static final String X_REQUESTED_WITH_VALUE              = 
"XMLHttpRequest";
+    public static final int    SC_AUTHENTICATION_TIMEOUT           = 419;
+    public static final String CONFIG_PREFIX_HTTP_RESPONSE_HEADER  = 
"atlas.headers";
 
     private static final Map<String, String> HEADER_MAP = new HashMap<>();
 
@@ -50,22 +61,48 @@ public class HeadersUtil {
         return HEADER_MAP.get(header);
     }
 
+    public static Map<String, String> getAllHeaders() {
+        return new HashMap<>(HEADER_MAP);
+    }
+
     public static void setHeaderMapAttributes(AtlasResponseRequestWrapper 
responseWrapper, String headerKey) {
         responseWrapper.setHeader(headerKey, HEADER_MAP.get(headerKey));
     }
 
     public static void setSecurityHeaders(AtlasResponseRequestWrapper 
responseWrapper) {
-        for (Map.Entry<String, String> entry : HEADER_MAP.entrySet()) {
-            responseWrapper.setHeader(entry.getKey(), entry.getValue());
-        }
+        HEADER_MAP.forEach((key, value) -> responseWrapper.setHeader(key, 
value));
     }
 
-    static {
+    @VisibleForTesting
+    public static void initializeHttpResponseHeaders(Properties 
configuredHeaders) {
+        HEADER_MAP.clear();
+
         HEADER_MAP.put(X_FRAME_OPTIONS_KEY, X_FRAME_OPTIONS_VAL);
         HEADER_MAP.put(X_CONTENT_TYPE_OPTIONS_KEY, X_CONTENT_TYPE_OPTIONS_VAL);
         HEADER_MAP.put(X_XSS_PROTECTION_KEY, X_XSS_PROTECTION_VAL);
         HEADER_MAP.put(STRICT_TRANSPORT_SEC_KEY, STRICT_TRANSPORT_SEC_VAL);
         HEADER_MAP.put(CONTENT_SEC_POLICY_KEY, CONTENT_SEC_POLICY_VAL);
         HEADER_MAP.put(SERVER_KEY, 
AtlasConfiguration.HTTP_HEADER_SERVER_VALUE.getString());
+
+        if (configuredHeaders != null) {
+            configuredHeaders.stringPropertyNames().forEach(name -> 
HEADER_MAP.put(name, configuredHeaders.getProperty(name)));
+        }
+    }
+
+    static {
+        Properties configuredHeaders = null;
+
+        try {
+            Configuration baseConfig   = ApplicationProperties.get();
+            Configuration headerConfig = 
ApplicationProperties.getSubsetConfiguration(baseConfig, 
CONFIG_PREFIX_HTTP_RESPONSE_HEADER);
+
+            configuredHeaders = Optional.ofNullable(headerConfig)
+                    .map(ConfigurationConverter::getProperties)
+                    .orElseGet(Properties::new);
+        } catch (Exception e) {
+            LOG.info("Failed to load custom headers: {}", e.getMessage());
+        }
+
+        initializeHttpResponseHeaders(configuredHeaders);
     }
 }
diff --git 
a/webapp/src/main/java/org/apache/atlas/web/servlets/AtlasHttpServlet.java 
b/webapp/src/main/java/org/apache/atlas/web/servlets/AtlasHttpServlet.java
index e42e6d37d..04566a378 100644
--- a/webapp/src/main/java/org/apache/atlas/web/servlets/AtlasHttpServlet.java
+++ b/webapp/src/main/java/org/apache/atlas/web/servlets/AtlasHttpServlet.java
@@ -17,11 +17,11 @@
  */
 package org.apache.atlas.web.servlets;
 
+import org.apache.atlas.web.filters.AtlasResponseRequestWrapper;
+import org.apache.atlas.web.filters.HeadersUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -29,22 +29,20 @@ import javax.servlet.http.HttpServletResponse;
 public class AtlasHttpServlet extends HttpServlet {
     public static final Logger LOG = 
LoggerFactory.getLogger(AtlasHttpServlet.class);
 
-    public static final String TEXT_HTML     = "text/html";
-    public static final String XFRAME_OPTION = "X-Frame-Options";
-    public static final String DENY          = "DENY";
-    public static final String ALLOW         = "ALLOW";
+    public static final String TEXT_HTML = "text/html";
+    public static final String ALLOW     = "ALLOW";
 
     protected void includeResponse(HttpServletRequest request, 
HttpServletResponse response, String template) {
         try {
             response.setContentType(TEXT_HTML);
-            response.setHeader(XFRAME_OPTION, DENY);
+            AtlasResponseRequestWrapper responseWrapper = new 
AtlasResponseRequestWrapper(response);
+            HeadersUtil.setSecurityHeaders(responseWrapper);
 
-            ServletContext    context = getServletContext();
-            RequestDispatcher rd      = context.getRequestDispatcher(template);
-
-            rd.include(request, response);
+            getServletContext()
+                    .getRequestDispatcher(template)
+                    .include(request, response);
         } catch (Exception e) {
-            LOG.error("Error in AtlasHttpServlet {}", template, e);
+            LOG.error("Failed to include template [{}] in AtlasHttpServlet", 
template, e);
         }
     }
 }
diff --git 
a/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java 
b/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java
new file mode 100644
index 000000000..a6113a52f
--- /dev/null
+++ b/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java
@@ -0,0 +1,108 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class HeaderUtilsTest {
+    private Map<String, String> originalHeaders;
+
+    @Before
+    public void setUp() throws Exception {
+        originalHeaders = new HashMap<>(HeadersUtil.getAllHeaders());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        
HeadersUtil.initializeHttpResponseHeaders(convertMapToProperties(originalHeaders));
+    }
+
+    private Properties convertMapToProperties(Map<String, String> map) {
+        Properties props = new Properties();
+
+        map.forEach(props::setProperty);
+
+        return props;
+    }
+
+    @Test
+    public void testLoadHeadersFromProperties() {
+        
HeadersUtil.initializeHttpResponseHeaders(createPropertiesWithHeaders("X-Custom-Header-One",
 "ValueOne", "X-Custom-Header-Two", "ValueTwo"));
+
+        assertEquals("ValueOne", 
HeadersUtil.getHeaderMap("X-Custom-Header-One"));
+        assertEquals("ValueTwo", 
HeadersUtil.getHeaderMap("X-Custom-Header-Two"));
+    }
+
+    @Test
+    public void testGetHeaderMapReturnsNullForMissingKey() {
+        
HeadersUtil.initializeHttpResponseHeaders(createPropertiesWithHeaders("X-Exists",
 "ExistsValue"));
+
+        assertNull(HeadersUtil.getHeaderMap("X-Does-Not-Exist"));
+    }
+
+    @Test
+    public void testSetSecurityHeadersSetsAllHeaders() {
+        
HeadersUtil.initializeHttpResponseHeaders(createPropertiesWithHeaders("X-One", 
"Val1", "X-Two", "Val2"));
+
+        AtlasResponseRequestWrapper mockWrapper = 
mock(AtlasResponseRequestWrapper.class);
+        HeadersUtil.setSecurityHeaders(mockWrapper);
+
+        verify(mockWrapper).setHeader("X-One", "Val1");
+        verify(mockWrapper).setHeader("X-Two", "Val2");
+    }
+
+    @Test
+    public void testSetHeaderMapAttributes() {
+        
HeadersUtil.initializeHttpResponseHeaders(createPropertiesWithHeaders("X-Test", 
"HeaderTestValue"));
+
+        AtlasResponseRequestWrapper mockWrapper = 
mock(AtlasResponseRequestWrapper.class);
+        HeadersUtil.setHeaderMapAttributes(mockWrapper, "X-Test");
+
+        verify(mockWrapper).setHeader("X-Test", "HeaderTestValue");
+    }
+
+    @Test
+    public void testDefaultHeadersArePresent() {
+        HeadersUtil.initializeHttpResponseHeaders(null);
+
+        assertEquals("DENY", 
HeadersUtil.getHeaderMap(HeadersUtil.X_FRAME_OPTIONS_KEY));
+        assertEquals("nosniff", 
HeadersUtil.getHeaderMap(HeadersUtil.X_CONTENT_TYPE_OPTIONS_KEY));
+        assertEquals("1; mode=block", 
HeadersUtil.getHeaderMap(HeadersUtil.X_XSS_PROTECTION_KEY));
+    }
+
+    private Properties createPropertiesWithHeaders(String... headers) {
+        Properties props = new Properties();
+
+        for (int i = 0; i < headers.length / 2; i++) {
+            props.setProperty(headers[i * 2], headers[(i *2) + 1]);
+        }
+
+        return props;
+    }
+}

Reply via email to