Protected admin section

This commit is contained in:
Andreas Svanberg 2024-09-12 17:40:50 +02:00
parent 3b52882037
commit 525d33ed01
Signed by: ansv7779
GPG Key ID: 729B051CFFD42F92
12 changed files with 139 additions and 1 deletions

1
.gitignore vendored

@ -4,6 +4,7 @@ target/
!**/src/main/**/target/ !**/src/main/**/target/
!**/src/test/**/target/ !**/src/test/**/target/
src/main/resources/application-local.yml src/main/resources/application-local.yml
jte-classes/
### STS ### ### STS ###
.apt_generated .apt_generated

17
pom.xml

@ -34,6 +34,18 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency>
<groupId>gg.jte</groupId>
<artifactId>jte-spring-boot-starter-3</artifactId>
<version>3.1.12</version>
</dependency>
<dependency>
<groupId>gg.jte</groupId>
<artifactId>jte</artifactId>
<version>3.1.12</version>
</dependency>
<dependency> <dependency>
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>
@ -73,6 +85,11 @@
<artifactId>spring-boot-testcontainers</artifactId> <artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.testcontainers</groupId> <groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>

@ -25,6 +25,7 @@ import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter; import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
import se.su.dsv.oauth2.shibboleth.Entitlement;
import se.su.dsv.oauth2.shibboleth.ShibbolethAuthenticationDetailsSource; import se.su.dsv.oauth2.shibboleth.ShibbolethAuthenticationDetailsSource;
import java.util.UUID; import java.util.UUID;
@ -84,14 +85,18 @@ public class AuthorizationServer extends SpringBootServletInitializer {
*/ */
@Bean @Bean
@Order(2) @Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) public SecurityFilterChain defaultSecurityFilterChain(
HttpSecurity http,
Config config)
throws Exception throws Exception
{ {
http.authorizeHttpRequests(authorize -> authorize http.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/admin/**").hasAuthority(Entitlement.asAuthority(config.adminEntitlement()))
.anyRequest().authenticated()); .anyRequest().authenticated());
http.exceptionHandling(exceptions -> exceptions http.exceptionHandling(exceptions -> exceptions
.accessDeniedPage("/forbidden")
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))); .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));
http.jee(jee -> jee http.jee(jee -> jee

@ -5,6 +5,10 @@ import org.springframework.security.core.GrantedAuthority;
public record Entitlement(String entitlement) implements GrantedAuthority { public record Entitlement(String entitlement) implements GrantedAuthority {
@Override @Override
public String getAuthority() { public String getAuthority() {
return asAuthority(entitlement);
}
public static String asAuthority(String entitlement) {
return "ENTITLEMENT_" + entitlement; return "ENTITLEMENT_" + entitlement;
} }
} }

@ -0,0 +1,14 @@
package se.su.dsv.oauth2.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/admin")
public class AdminController {
@GetMapping
public String index() {
return "admin/index";
}
}

@ -0,0 +1,12 @@
package se.su.dsv.oauth2.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ErrorController {
@GetMapping("/forbidden")
public String forbidden() {
return "error/forbidden";
}
}

@ -0,0 +1,15 @@
package se.su.dsv.oauth2.web;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class PublicController {
@GetMapping("/")
public String index(Model model, Authentication authentication) {
model.addAttribute("displayName", authentication.getName());
return "index";
}
}

@ -3,3 +3,7 @@ spring:
name: dsv-oauth2-authorization-server name: dsv-oauth2-authorization-server
profiles: profiles:
include: local include: local
gg:
jte:
templateLocation: src/main/resources/templates
developmentMode: true

@ -0,0 +1 @@
admin/index.jte

@ -0,0 +1 @@
Nuh uh

@ -0,0 +1,17 @@
@param String displayName
@param org.springframework.web.servlet.support.RequestContext springMacroRequestContext
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DSV OAuth 2.0</title>
</head>
<body>
<main>
<h1>DSV OAuth 2.0</h1>
<p>Welcome ${displayName} to DSV's OAuth 2.0 information page</p>
<a href="${springMacroRequestContext.getContextUrl("/admin")}">Admin</a>
</main>
</body>
</html>

@ -0,0 +1,47 @@
package se.su.dsv.oauth2.web;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.test.web.servlet.MockMvc;
import org.testcontainers.containers.MariaDBContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import se.su.dsv.oauth2.shibboleth.Entitlement;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@SpringBootTest(
properties = {
"se.su.dsv.oauth2.admin-entitlement=" + AdminControllerTest.ADMIN_ENTITLEMENT
}
)
@Testcontainers
@AutoConfigureMockMvc
class AdminControllerTest {
static final String ADMIN_ENTITLEMENT = "ADMIN";
@Container
@ServiceConnection
static MariaDBContainer<?> mariaDBContainer = new MariaDBContainer<>("mariadb:10.11");
@Autowired
MockMvc mockMvc;
@Test
void is_protected() throws Exception {
mockMvc.perform(get("/admin"))
.andExpect(redirectedUrl("http://localhost/login"));
}
@Test
void is_accessible_with_admin_authority() throws Exception {
mockMvc.perform(get("/admin")
.with(user("admin").authorities(new Entitlement(ADMIN_ENTITLEMENT))))
.andExpect(status().isOk());
}
}