Skip to content

Commit

Permalink
Merge pull request 'Improve SAML Logout' (#32) from feature/saml_logo…
Browse files Browse the repository at this point in the history
…ut into develop
  • Loading branch information
fmichielssen committed Feb 17, 2021
2 parents 0ed1af6 + dc7c9c9 commit 8a11f5f
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ public class SAMLAuthenticationBackend implements IAuthenticationBackend {

public static final String NAME = "saml";

private static final String PROP_LOGOUT_URL = "proxy.saml.logout-url";

private static final String PROP_SUCCESS_LOGOUT_URL = "proxy.saml.logout-url";
private static final String PROP_SAML_LOGOUT_METHOD = "proxy.saml.logout-method";

@Autowired(required = false)
private SAMLEntryPoint samlEntryPoint;

Expand Down Expand Up @@ -90,17 +91,29 @@ public void configureAuthenticationManagerBuilder(AuthenticationManagerBuilder a

@Override
public String getLogoutURL() {
if (environment.getProperty(PROP_LOGOUT_URL) != null) {
LogoutMethod logoutMethod = environment.getProperty(PROP_SAML_LOGOUT_METHOD, LogoutMethod.class, LogoutMethod.LOCAL);
if (logoutMethod == LogoutMethod.LOCAL) {
return "/logout";
}
return "/saml/logout";
return "/saml/logout"; // LogoutMethod.SAML
}

@Override
public String getLogoutSuccessURL() {
String logoutURL = environment.getProperty(PROP_LOGOUT_URL);
System.out.println("LogoutURL: " + logoutURL);
if (logoutURL == null || logoutURL.trim().isEmpty()) logoutURL = "/";
return determineLogoutSuccessURL(environment);
}

public static String determineLogoutSuccessURL(Environment environment) {
String logoutURL = environment.getProperty(PROP_SUCCESS_LOGOUT_URL);
if (logoutURL == null || logoutURL.trim().isEmpty()) {
logoutURL = "/";
}
return logoutURL;
}

private enum LogoutMethod {
LOCAL,
SAML
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* ContainerProxy
*
* Copyright (C) 2016-2020 Open Analytics
*
* ===========================================================================
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Apache License as published by
* The Apache Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Apache License for more details.
*
* You should have received a copy of the Apache License
* along with this program. If not, see <http://www.apache.org/licenses/>
*/
package eu.openanalytics.containerproxy.auth.impl.saml;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.saml.SAMLStatusException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {


if (exception.getCause() instanceof SAMLStatusException) {
SAMLStatusException samlException = (SAMLStatusException) exception.getCause();

if (samlException.getStatusCode().equals("urn:oasis:names:tc:SAML:2.0:status:RequestDenied")) {
response.sendRedirect(ServletUriComponentsBuilder.fromCurrentContextPath().path("/app-access-denied").build().toUriString());
return;
}
}

super.onAuthenticationFailure(request, response, exception);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import javax.inject.Inject;

import eu.openanalytics.containerproxy.auth.impl.SAMLAuthenticationBackend;
import org.apache.commons.httpclient.HttpClient;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -157,7 +158,7 @@ public SecurityContextLogoutHandler securityContextLogoutHandler() {
@Bean
public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler();
successLogoutHandler.setDefaultTargetUrl("/");
successLogoutHandler.setDefaultTargetUrl(SAMLAuthenticationBackend.determineLogoutSuccessURL(environment));
return successLogoutHandler;
}

Expand Down Expand Up @@ -310,7 +311,7 @@ public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() {

@Bean
public SimpleUrlAuthenticationFailureHandler authenticationFailureHandler() {
SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
AuthenticationFailureHandler failureHandler = new AuthenticationFailureHandler();
failureHandler.setUseForward(true);
failureHandler.setDefaultFailureUrl("/error");
return failureHandler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,10 @@
*/
package eu.openanalytics.containerproxy.security;

import java.util.List;

import javax.inject.Inject;

import eu.openanalytics.containerproxy.auth.IAuthenticationBackend;
import eu.openanalytics.containerproxy.auth.UserLogoutHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
Expand All @@ -43,8 +40,8 @@
import org.springframework.security.web.header.writers.StaticHeadersWriter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import eu.openanalytics.containerproxy.auth.IAuthenticationBackend;
import eu.openanalytics.containerproxy.auth.UserLogoutHandler;
import javax.inject.Inject;
import java.util.List;

@Configuration
@EnableWebSecurity
Expand All @@ -61,7 +58,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Inject
private Environment environment;

@Autowired(required=false)
private List<ICustomSecurityConfig> customConfigs;

Expand Down Expand Up @@ -128,8 +125,8 @@ protected void configure(HttpSecurity http) throws Exception {

if (auth.hasAuthorization()) {
http.authorizeRequests().antMatchers(
"/login", "/signin/**", "/auth-error",
"/favicon.ico", "/css/**", "/img/**", "/js/**", "/assets/**", "/webjars/**").permitAll();
"/login", "/signin/**", "/auth-error", "/app-access-denied", "/logout-success",
"/favicon.ico", "/css/**", "/img/**", "/js/**", "/assets/**", "/webjars/**").permitAll();
http
.formLogin()
.loginPage("/login")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ public void logout(Authentication auth) {
eventService.post(EventType.Logout.toString(), userId, null);
if (logoutStrategy != null) logoutStrategy.onLogout(userId);
log.info(String.format("User logged out [user: %s]", userId));

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import eu.openanalytics.containerproxy.api.BaseController;

@Controller
public class AuthErrorController extends BaseController {
public class AuthController extends BaseController {

@Inject
private Environment environment;
Expand All @@ -43,4 +43,9 @@ public String getAuthErrorPage(ModelMap map, HttpServletRequest request) {
return "auth-error";
}

@RequestMapping(value = "/app-access-denied", method = RequestMethod.GET)
public String getAppAccessDeniedPage(ModelMap map, HttpServletRequest request) {
return "app-access-denied";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* ContainerProxy
*
* Copyright (C) 2016-2020 Open Analytics
*
* ===========================================================================
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Apache License as published by
* The Apache Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Apache License for more details.
*
* You should have received a copy of the Apache License
* along with this program. If not, see <http://www.apache.org/licenses/>
*/
package eu.openanalytics.containerproxy.ui;

import eu.openanalytics.containerproxy.api.BaseController;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;

@Controller
public class LogoutSuccessController extends BaseController {

@Inject
private Environment environment;

@RequestMapping(value = "/logout-success", method = RequestMethod.GET)
public String getAuthErrorPage() {
return "logout-success";
}

}
55 changes: 55 additions & 0 deletions src/main/resources/templates/app-access-denied.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<!--
ContainerProxy
Copyright (C) 2016-2020 Open Analytics
===========================================================================
This program is free software: you can redistribute it and/or modify
it under the terms of the Apache License as published by
The Apache Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Apache License for more details.
You should have received a copy of the Apache License
along with this program. If not, see <http://www.apache.org/licenses/>
-->
<!DOCTYPE html>
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">

<head lang="en">
<title th:text="${title}"></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" media="screen" th:href="@{/webjars/bootstrap/3.4.1/css/bootstrap.min.css}"/>
<link rel="stylesheet" media="screen" th:href="@{/css/login.css}"/>
<link rel="stylesheet" media="screen" type="text/css" href="https://cdn.jsdelivr.net/bootstrap-social/5.1.1/bootstrap-social.css"/>
<link rel="stylesheet" media="screen" type="text/css" href="https://cdn.jsdelivr.net/fontawesome/4.7.0/css/font-awesome.min.css"/>
<script th:src="@{/webjars/jquery/3.5.0/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/3.4.1/js/bootstrap.min.js}"></script>
</head>

<body>
<div class="container">
<h2>You do not have access to this application.</h2>
<p>Your account does not have the required roles or groups required for accessing this application.</p>
<p>Please contact your administrator if you believe this is a mistake.</p>
</div>

<style>
h2 {
margin-bottom: 20px;
margin-top: 20px;
}
</style>

</body>

</html>
54 changes: 54 additions & 0 deletions src/main/resources/templates/logout-success.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!--
ContainerProxy
Copyright (C) 2016-2020 Open Analytics
===========================================================================
This program is free software: you can redistribute it and/or modify
it under the terms of the Apache License as published by
The Apache Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Apache License for more details.
You should have received a copy of the Apache License
along with this program. If not, see <http://www.apache.org/licenses/>
-->
<!DOCTYPE html>
<html
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">

<head lang="en">
<title th:text="${title}"></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" media="screen" th:href="@{/webjars/bootstrap/3.4.1/css/bootstrap.min.css}"/>
<link rel="stylesheet" media="screen" th:href="@{/css/login.css}"/>
<link rel="stylesheet" media="screen" type="text/css" href="https://cdn.jsdelivr.net/bootstrap-social/5.1.1/bootstrap-social.css"/>
<link rel="stylesheet" media="screen" type="text/css" href="https://cdn.jsdelivr.net/fontawesome/4.7.0/css/font-awesome.min.css"/>
<script th:src="@{/webjars/jquery/3.5.0/jquery.min.js}"></script>
<script th:src="@{/webjars/bootstrap/3.4.1/js/bootstrap.min.js}"></script>
</head>

<body>
<div class="container">
<h2>You have been logged out successfully.</h2>
<p><a th:href="@{/}"><b>Login again</b></a></p>
</div>

<style>
h2 {
margin-bottom: 20px;
margin-top: 20px;
}
</style>

</body>

</html>

0 comments on commit 8a11f5f

Please sign in to comment.