Skip to content

Commit

Permalink
Add CAS Gateway sample
Browse files Browse the repository at this point in the history
Closes gh-174
  • Loading branch information
marcusdacoregio committed Dec 20, 2023
1 parent e1565dc commit 7f919df
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 7 deletions.
2 changes: 2 additions & 0 deletions docker/cas/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ services:
--server.port=8080
--cas.service-registry.core.init-from-json=true
--cas.service-registry.json.location=file:/etc/cas/services
--cas.tgc.secure=false
--cas.tgc.sameSitePolicy=Lax
volumes:
- ./services/http-1.json:/etc/cas/services/http-1.json
networks:
Expand Down
2 changes: 2 additions & 0 deletions servlet/spring-boot/java/cas/login/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ plugins {
id 'java'
}

ext['spring-security.version'] = '6.3.0-SNAPSHOT'

repositories {
mavenCentral()
maven { url "https://repo.spring.io/milestone" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,9 @@ public String loggedout(Model model) {
return "loggedout";
}

@GetMapping("/public")
String publicPage() {
return "public";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,18 @@
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.cas.web.CasGatewayAuthenticationRedirectFilter;
import org.springframework.security.cas.web.CasGatewayResolverRequestMatcher;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

@Configuration
public class SecurityConfig {
Expand All @@ -52,14 +57,30 @@ public class SecurityConfig {
private ServletWebServerApplicationContext context;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http, UserDetailsService userDetailsService) throws Exception {
http.authorizeHttpRequests((authorize) -> authorize.requestMatchers(HttpMethod.GET, "/loggedout").permitAll()
.anyRequest().authenticated())
public SecurityFilterChain filterChain(HttpSecurity http, UserDetailsService userDetailsService,
MvcRequestMatcher.Builder builder) throws Exception {
// @formatter:off
CasGatewayAuthenticationRedirectFilter casGatewayAuthenticationRedirectFilter = new CasGatewayAuthenticationRedirectFilter(this.casLoginUrl, serviceProperties());
casGatewayAuthenticationRedirectFilter.setRequestMatcher(new AndRequestMatcher(
builder.pattern("/public"), new CasGatewayResolverRequestMatcher(serviceProperties())));
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(HttpMethod.GET, "/loggedout").permitAll()
.requestMatchers("/public").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling((exceptions) -> exceptions.authenticationEntryPoint(casAuthenticationEntryPoint()))
.logout((logout) -> logout.logoutSuccessUrl("/loggedout"))
.addFilter(casAuthenticationFilter(userDetailsService))
.addFilterBefore(new SingleSignOutFilter(), CasAuthenticationFilter.class);
.addFilterBefore(new SingleSignOutFilter(), CasAuthenticationFilter.class)
.addFilterAfter(casGatewayAuthenticationRedirectFilter, CasAuthenticationFilter.class);
return http.build();
// @formatter:on
}

@Bean
MvcRequestMatcher.Builder mvcRequestMatcherBuilder(HandlerMappingIntrospector introspector) {
return new MvcRequestMatcher.Builder(introspector);
}

public CasAuthenticationProvider casAuthenticationProvider(UserDetailsService userDetailsService) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
cas.base.url=http://localhost:8090/cas
server.port=8081
cas.base.url=http://localhost.example:8090/cas
cas.login.url=${cas.base.url}/login
cas.logout.url=${cas.base.url}/logout
service.base.url=http://localhost:8080
service.base.url=http://localhost:8081

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org" xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<title>Spring Security - CAS Login & Logout</title>
<meta charset="utf-8" />
<style>
span, dt {
font-weight: bold;
}
</style>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body>
<div class="container">
<main role="main" class="container-fluid">
<h1 class="mt-5">This is a public page that performs a CAS Gateway Authentication</h1>
<p class="lead">You are successfully logged in as <span sec:authentication="name"></span></p>

<h6>Visit the <a href="https://github.com/apereo/cas" target="_blank">Apereo CAS</a> documentation for more details.</h6>
</main>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.utility.DockerImageName;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.core.env.Environment;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

Expand All @@ -43,11 +45,15 @@ class CasLoginApplicationTests {
@LocalServerPort
int port;

@Autowired
Environment environment;

@Container
static GenericContainer<?> casServer = new GenericContainer<>(DockerImageName.parse("apereo/cas:6.6.6"))
.withCommand("--cas.standalone.configuration-directory=/etc/cas/config", "--server.ssl.enabled=false",
"--server.port=8080", "--cas.service-registry.core.init-from-json=true",
"--cas.service-registry.json.location=file:/etc/cas/services")
"--cas.service-registry.json.location=file:/etc/cas/services", "--cas.tgc.secure=false",
"--cas.tgc.sameSitePolicy=Lax")
.withExposedPorts(8080).withClasspathResourceMapping("cas/services/https-1.json",
"/etc/cas/services/https-1.json", BindMode.READ_WRITE)
.waitingFor(Wait.forLogMessage(".*Ready to process requests.*", 1));
Expand Down Expand Up @@ -92,4 +98,19 @@ void loginAndLogout() {
assertThat(logoutMsg).isEqualTo("You are successfully logged out of the app, but not CAS");
}

@Test
void publicPageWhenCasGatewayAuthenticationThenAuthenticated() {
doCasLogin();
Selenide.open("http://localhost:" + this.port + "/public");
String lead = Selenide.$(By.className("lead")).text();
assertThat(lead).isEqualTo("You are successfully logged in as casuser");
}

private void doCasLogin() {
Selenide.open(this.environment.getProperty("cas.login.url"));
Selenide.$(By.name("username")).setValue("casuser");
Selenide.$(By.name("password")).setValue("Mellon");
Selenide.$(By.name("submitBtn")).click();
}

}

0 comments on commit 7f919df

Please sign in to comment.