Skip to content

Commit f9a0709

Browse files
Atlas[Backend] Fix for improving logout mechanism in Atlas Backend code base
1 parent 66a8e53 commit f9a0709

File tree

8 files changed

+227
-31
lines changed

8 files changed

+227
-31
lines changed

webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -163,22 +163,13 @@ public void init(FilterConfig filterConfig) throws ServletException {
163163
*/
164164
@Override
165165
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
166-
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
167-
AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse);
168-
169-
HeadersUtil.setSecurityHeaders(responseWrapper);
170-
171-
if (!ssoEnabled) {
172-
filterChain.doFilter(servletRequest, servletResponse);
173-
174-
return;
175-
}
176-
177166
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
167+
httpRequest.setAttribute("ssoEnabled",false);
178168

179-
if (LOG.isDebugEnabled()) {
180-
LOG.debug("Knox doFilter {}", httpRequest.getRequestURI());
181-
}
169+
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
170+
AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpServletResponse);
171+
172+
HeadersUtil.setSecurityHeaders(responseWrapper);
182173

183174
if (httpRequest.getSession() != null && httpRequest.getSession().getAttribute("locallogin") != null) {
184175
servletRequest.setAttribute("ssoEnabled", false);
@@ -187,19 +178,8 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
187178
return;
188179
}
189180

190-
if (jwtProperties == null || isAuthenticated()) {
191-
filterChain.doFilter(servletRequest, servletResponse);
192-
193-
return;
194-
}
195-
196-
if (LOG.isDebugEnabled()) {
197-
LOG.debug("Knox ssoEnabled {} {}", ssoEnabled, httpRequest.getRequestURI());
198-
}
199-
200181
//if jwt properties are loaded and is current not authenticated then it will go for sso authentication
201182
//Note : Need to remove !isAuthenticated() after knoxsso solve the bug from cross-origin script
202-
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
203183
String serializedJWT = getJWTFromCookie(httpRequest);
204184

205185
// if we get the hadoop-jwt token from the cookies then will process it further
@@ -308,7 +288,7 @@ protected String getJWTFromCookie(HttpServletRequest req) {
308288
for (Cookie cookie : cookies) {
309289
if (cookieName.equals(cookie.getName())) {
310290
LOG.debug("{} cookie has been found and is being processed", cookieName);
311-
291+
req.setAttribute("ssoEnabled",true);
312292
serializedJWT = cookie.getValue();
313293
break;
314294
}

webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ public class HeadersUtil {
5050
public static final String X_REQUESTED_WITH_VALUE = "XMLHttpRequest";
5151
public static final int SC_AUTHENTICATION_TIMEOUT = 419;
5252
public static final String CONFIG_PREFIX_HTTP_RESPONSE_HEADER = "atlas.headers";
53+
public static final String CACHE_CONTROL = "Cache-Control";
54+
public static final String CACHE_CONTROL_VAL = "no-cache";
5355

5456
private static final Map<String, String> HEADER_MAP = new HashMap<>();
5557

webapp/src/main/java/org/apache/atlas/web/filters/RestUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class RestUtil {
3030
private static final Logger LOG = LoggerFactory.getLogger(RestUtil.class);
3131

3232
public static final String TIMEOUT_ACTION = "timeout";
33-
public static final String LOGOUT_URL = "/logout.html";
33+
public static final String LOGOUT_URL = "/logout";
3434
public static final String DELIMITTER = "://";
3535

3636
private static final String PROXY_ATLAS_URL_PATH = "/atlas";

webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
import org.apache.atlas.utils.AtlasJson;
7777
import org.apache.atlas.utils.AtlasPerfTracer;
7878
import org.apache.atlas.web.filters.AtlasCSRFPreventionFilter;
79+
import org.apache.atlas.web.filters.AtlasKnoxSSOAuthenticationFilter;
7980
import org.apache.atlas.web.model.DebugMetrics;
8081
import org.apache.atlas.web.service.AtlasDebugMetricsSink;
8182
import org.apache.atlas.web.service.ServiceState;
@@ -1098,6 +1099,15 @@ public Response serviceReadiness() throws AtlasBaseException {
10981099
}
10991100
}
11001101

1102+
@GET
1103+
@Path("/checksso")
1104+
@Produces(MediaType.TEXT_PLAIN)
1105+
public String checkSSO(@Context HttpServletRequest httpServletRequest) {
1106+
Object ssoFlag = httpServletRequest.getAttribute("ssoEnabled");
1107+
LOG.debug("SSO attribute Value: {}", ssoFlag);
1108+
return String.valueOf(ssoFlag);
1109+
}
1110+
11011111
private void updateCriteriaWithDefaultValues(AuditReductionCriteria auditReductionCriteria) {
11021112
if (auditReductionCriteria.getDefaultAgeoutTTLInDays() <= 0) {
11031113
auditReductionCriteria.setDefaultAgeoutTTLInDays(AtlasConfiguration.ATLAS_AUDIT_DEFAULT_AGEOUT_TTL.getInt());
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
20+
package org.apache.atlas.web.security;
21+
22+
import com.fasterxml.jackson.databind.ObjectMapper;
23+
import org.springframework.context.annotation.Bean;
24+
import org.springframework.context.annotation.Configuration;
25+
26+
@Configuration
27+
public class AtlasSecurityCommonConfig {
28+
@Bean
29+
public ObjectMapper objectMapper() {
30+
return new ObjectMapper();
31+
}
32+
}

webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public class AtlasSecurityConfig extends WebSecurityConfigurerAdapter {
103103
private final StaleTransactionCleanupFilter staleTransactionCleanupFilter;
104104
private final ActiveServerFilter activeServerFilter;
105105
private final boolean keycloakEnabled;
106+
private final CustomLogoutSuccessHandler customLogoutSuccessHandler;
106107

107108
@Value("${keycloak.configurationFile:WEB-INF/keycloak.json}")
108109
private Resource keycloakConfigFileResource;
@@ -120,7 +121,7 @@ public AtlasSecurityConfig(AtlasKnoxSSOAuthenticationFilter ssoAuthenticationFil
120121
AtlasAuthenticationEntryPoint atlasAuthenticationEntryPoint,
121122
Configuration configuration,
122123
StaleTransactionCleanupFilter staleTransactionCleanupFilter,
123-
ActiveServerFilter activeServerFilter) {
124+
ActiveServerFilter activeServerFilter, CustomLogoutSuccessHandler customLogoutSuccessHandler) {
124125
this.ssoAuthenticationFilter = ssoAuthenticationFilter;
125126
this.csrfPreventionFilter = atlasCSRFPreventionFilter;
126127
this.atlasAuthenticationFilter = atlasAuthenticationFilter;
@@ -131,6 +132,7 @@ public AtlasSecurityConfig(AtlasKnoxSSOAuthenticationFilter ssoAuthenticationFil
131132
this.configuration = configuration;
132133
this.staleTransactionCleanupFilter = staleTransactionCleanupFilter;
133134
this.activeServerFilter = activeServerFilter;
135+
this.customLogoutSuccessHandler = customLogoutSuccessHandler;
134136

135137
this.keycloakEnabled = configuration.getBoolean(AtlasAuthenticationProvider.KEYCLOAK_AUTH_METHOD, false);
136138
}
@@ -220,10 +222,9 @@ protected void configure(HttpSecurity httpSecurity) throws Exception {
220222
.passwordParameter("j_password")
221223
.and()
222224
.logout()
223-
.logoutSuccessUrl("/login.jsp")
225+
.logoutSuccessHandler(customLogoutSuccessHandler)
224226
.deleteCookies("ATLASSESSIONID")
225-
.logoutUrl("/logout.html");
226-
227+
.logoutUrl("/logout");
227228
//@formatter:on
228229

229230
boolean configMigrationEnabled = !StringUtils.isEmpty(configuration.getString(ATLAS_MIGRATION_MODE_FILENAME));
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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+
20+
package org.apache.atlas.web.security;
21+
22+
import com.fasterxml.jackson.databind.ObjectMapper;
23+
import org.apache.atlas.web.filters.HeadersUtil;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
import org.springframework.security.core.Authentication;
27+
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
28+
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
29+
import org.springframework.stereotype.Component;
30+
31+
import javax.inject.Inject;
32+
import javax.servlet.http.HttpServletRequest;
33+
import javax.servlet.http.HttpServletResponse;
34+
35+
import java.io.IOException;
36+
import java.util.HashMap;
37+
import java.util.Map;
38+
39+
@Component
40+
public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler {
41+
private final ObjectMapper mapper;
42+
private static final Logger LOG = LoggerFactory.getLogger(CustomLogoutSuccessHandler.class);
43+
44+
@Inject
45+
public CustomLogoutSuccessHandler(ObjectMapper mapper) {
46+
this.mapper = mapper;
47+
}
48+
49+
@Override
50+
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
51+
request.getServletContext().removeAttribute(request.getRequestedSessionId());
52+
response.setContentType("application/json;charset=UTF-8");
53+
response.setHeader(HeadersUtil.CACHE_CONTROL, HeadersUtil.CACHE_CONTROL_VAL);
54+
response.setHeader(HeadersUtil.X_FRAME_OPTIONS_KEY, HeadersUtil.X_FRAME_OPTIONS_VAL);
55+
56+
try {
57+
Map<String, Object> responseMap = new HashMap<>();
58+
responseMap.put("statusCode", HttpServletResponse.SC_OK);
59+
responseMap.put("msgDesc", "Logout Successful");
60+
String jsonStr = mapper.writeValueAsString(responseMap);
61+
62+
response.setStatus(HttpServletResponse.SC_OK);
63+
response.getWriter().write(jsonStr);
64+
65+
LOG.debug("Log-out Successfully done. Returning Json : {}", jsonStr);
66+
} catch (IOException e) {
67+
LOG.debug("Error while writing JSON in HttpServletResponse");
68+
}
69+
}
70+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.atlas.web.filters;
19+
20+
import org.apache.atlas.web.resources.AdminResource;
21+
import org.mockito.InjectMocks;
22+
import org.mockito.Mock;
23+
import org.mockito.MockitoAnnotations;
24+
import org.springframework.mock.web.MockHttpServletRequest;
25+
import org.springframework.mock.web.MockHttpServletResponse;
26+
import org.springframework.mock.web.MockHttpSession;
27+
import org.testng.annotations.BeforeMethod;
28+
import org.testng.annotations.Test;
29+
30+
import javax.servlet.FilterChain;
31+
import javax.servlet.ServletException;
32+
import javax.servlet.http.Cookie;
33+
import java.io.IOException;
34+
import java.util.Arrays;
35+
36+
import static org.mockito.Mockito.verify;
37+
import static org.testng.Assert.*;
38+
39+
public class AtlasKnoxSSOAuthenticationFilterTest {
40+
@Mock
41+
private FilterChain filterChain;
42+
43+
@InjectMocks
44+
private AtlasKnoxSSOAuthenticationFilter filter;
45+
46+
private MockHttpServletRequest request;
47+
private MockHttpServletResponse response;
48+
private MockHttpSession session;
49+
50+
@InjectMocks
51+
AdminResource adminResource;
52+
53+
@BeforeMethod
54+
public void testSetup() {
55+
MockitoAnnotations.openMocks(this);
56+
request = new MockHttpServletRequest();
57+
response = new MockHttpServletResponse();
58+
session = new MockHttpSession();
59+
request.setSession(session);
60+
61+
request.setRequestURI("/api/atlas/admin/checksso");
62+
request.addHeader("User-Agent", "Chrome");
63+
}
64+
65+
@Test
66+
public void testDoFilter_withoutJwt_setsSsoDisabled() throws IOException, ServletException {
67+
request.setCookies(); // clear cookies for this test
68+
filter.doFilter(request, response, filterChain);
69+
70+
assertNull(filter.getJWTFromCookie(request));
71+
72+
verify(filterChain).doFilter(request, response);
73+
}
74+
75+
@Test
76+
public void testDoFilter_withJwt_setsSsoEnabledTrue() throws IOException, ServletException {
77+
request.setCookies(new Cookie("hadoop-jwt", "dummy-jwt-token"));
78+
filter.doFilter(request, response, filterChain);
79+
80+
assertNotNull(filter.getJWTFromCookie(request));
81+
assertNotNull(request.getCookies());
82+
assertTrue(Arrays.stream(request.getCookies())
83+
.anyMatch(cookie -> cookie.getValue().equals("dummy-jwt-token")));
84+
85+
verify(filterChain).doFilter(request, response);
86+
}
87+
88+
@Test
89+
public void test_CheckSSO_API_true() {
90+
request.setAttribute("ssoEnabled",true);
91+
String result = adminResource.checkSSO(request);
92+
assertEquals(result, "true");
93+
}
94+
95+
@Test
96+
public void test_CheckSSO_API_false() {
97+
request.setAttribute("ssoEnabled",false);
98+
String result = adminResource.checkSSO(request);
99+
assertEquals(result, "false");
100+
}
101+
}

0 commit comments

Comments
 (0)