Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
ch4mpy committed Feb 9, 2024
1 parent fa15532 commit e222bf6
Show file tree
Hide file tree
Showing 31 changed files with 337 additions and 384 deletions.
2 changes: 2 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ I could forget to update README before releasing, so please refer to [maven cent
- Replace `AuthoritiesMappingPropertiesResolver` with `OpenidProviderPropertiesResolver`
- `OpenidProviderPropertiesResolver` makes multi-tenancy much simpler to implement, including in "dynamic" scenarios (see [spring-addons-starter-oidc README](https://github.com/ch4mpy/spring-addons/tree/master/spring-addons-starter-oidc#1-1-4))
- Fix names of `(Server)HttpSecurityPostProcessor` (synchronised impl where prefixed with `Server` which it shouldn't and reactive weren't when it should)
* renamed `HttpSecurityPostProcessor`, `ClientHttpSecurityPostProcessor` and `ResourceServerHttpSecurityPostProcessor` from `reactive` packages to `ReactiveHttpSecurityPostProcessor`, `ClientReactiveHttpSecurityPostProcessor` and `ResourceServerReactiveHttpSecurityPostProcessor`
* renamed `ServerHttpSecurityPostProcessor`, `ClientHttpSecurityPostProcessor` and `ResourceServerHttpSecurityPostProcessor` from `synchronized` packages to `SynchronizedHttpSecurityPostProcessor`, `ClientSynchronizedHttpSecurityPostProcessor` and `ResourceServerSynchronizedHttpSecurityPostProcessor`
### `7.4.1`
- [gh-183](https://github.com/ch4mpy/spring-addons/issues/183) Allow anonymous CORS preflight requests (`OPTIONS` requests to a path configured with CORS)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
package com.c4soft.springaddons.tutorials;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.c4_soft.springaddons.security.oidc.OAuthentication;
import com.c4_soft.springaddons.security.oidc.OpenidClaimSet;

@RestController
@PreAuthorize("isAuthenticated()")
public class GreetingController {

@GetMapping("/greet")
public MessageDto getGreeting(OAuthentication<OpenidClaimSet> auth) {
public MessageDto getGreeting(JwtAuthenticationToken auth) {
return new MessageDto(
"Hi %s! You are granted with: %s and your email is %s.".formatted(auth.getName(), auth.getAuthorities(), auth.getClaims().getEmail()));
"Hi %s! You are granted with: %s and your email is %s."
.formatted(auth.getName(), auth.getAuthorities(), auth.getTokenAttributes().get(StandardClaimNames.EMAIL)));
}

@GetMapping("/nice")
@PreAuthorize("hasAuthority('NICE')")
public MessageDto getNiceGreeting(OAuthentication<OpenidClaimSet> auth) {
public MessageDto getNiceGreeting(JwtAuthenticationToken auth) {
return new MessageDto("Dear %s! You are granted with: %s.".formatted(auth.getName(), auth.getAuthorities()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,25 @@
@EnableMethodSecurity
public class WebSecurityConfig {

@Component
public class IssuerStartsWithOpenidProviderPropertiesResolver implements OpenidProviderPropertiesResolver {
private final SpringAddonsOidcProperties properties;
@Component
public class IssuerStartsWithOpenidProviderPropertiesResolver implements OpenidProviderPropertiesResolver {
private final SpringAddonsOidcProperties properties;

public IssuerStartsWithOpenidProviderPropertiesResolver(SpringAddonsOidcProperties properties) {
this.properties = properties;
}
public IssuerStartsWithOpenidProviderPropertiesResolver(SpringAddonsOidcProperties properties) {
this.properties = properties;
}

@Override
public Optional<OpenidProviderProperties> resolve(Map<String, Object> claimSet) {
final var tokenIss = Optional
.ofNullable(claimSet.get(JwtClaimNames.ISS))
.map(Object::toString)
.orElseThrow(() -> new RuntimeException("Invalid token: missing issuer"));
return properties.getOps().stream().filter(opProps -> {
final var opBaseHref = Optional.ofNullable(opProps.getIss()).map(URI::toString).orElse(null);
if (StringUtils.isEmpty(opBaseHref)) {
return false;
}
return tokenIss.startsWith(opBaseHref);
}).findAny();
}
}
@Override
public Optional<OpenidProviderProperties> resolve(Map<String, Object> claimSet) {
final var tokenIss = Optional.ofNullable(claimSet.get(JwtClaimNames.ISS)).map(Object::toString)
.orElseThrow(() -> new RuntimeException("Invalid token: missing issuer"));
return properties.getOps().stream().filter(opProps -> {
final var opBaseHref = Optional.ofNullable(opProps.getIss()).map(URI::toString).orElse(null);
if (!StringUtils.hasText(opBaseHref)) {
return false;
}
return tokenIss.startsWith(opBaseHref);
}).findAny();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ com:
- path: $.realm_access.roles
- path: $.resource_access.*.roles
resourceserver:
enabled: false
permit-all:
- "/actuator/health/readiness"
- "/actuator/health/liveness"
Expand All @@ -34,6 +33,7 @@ logging:
level:
org:
springframework:
boot: INFO
security: INFO

management:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.test.context.support.WithAnonymousUser;

import com.c4_soft.springaddons.security.oauth2.test.annotations.WithJwt;
import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.ParameterizedAuthentication;
import com.c4_soft.springaddons.security.oauth2.test.webmvc.AutoConfigureAddonsWebmvcResourceServerSecurity;
import com.c4_soft.springaddons.security.oauth2.test.webmvc.MockMvcSupport;
import com.c4_soft.springaddons.security.oidc.OAuthentication;
import com.c4_soft.springaddons.security.oidc.OpenidClaimSet;

@WebMvcTest(controllers = GreetingController.class)
@Import(WebSecurityConfig.class)
Expand All @@ -35,13 +36,13 @@ class GreetingControllerTest {
WithJwt.AuthenticationFactory jwtAuthFactory;

@ParameterizedTest
@MethodSource("auth0users") // see below for the factory
@MethodSource("users") // see below for the factory
void givenUserIsAuthenticated_whenGreet_thenOk(@ParameterizedAuthentication Authentication auth) throws Exception {
@SuppressWarnings("unchecked")
final var oauth = (OAuthentication<OpenidClaimSet>) auth;
final var oauth = (JwtAuthenticationToken) auth;
final var actual = api.get("/greet").andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
assertThat(actual).contains(
"Hi %s! You are granted with: %s and your email is %s.".formatted(auth.getName(), auth.getAuthorities(), oauth.getAttributes().getEmail()));
"Hi %s! You are granted with: %s and your email is %s."
.formatted(auth.getName(), auth.getAuthorities(), oauth.getTokenAttributes().get(StandardClaimNames.EMAIL)));
}

@Test
Expand All @@ -51,13 +52,14 @@ void givenRequestIsAnonymous_whenGreet_thenUnauthorized() throws Exception {
}

@Test
@WithJwt("auth0_nice.json")
@WithJwt("keycloak_nice.json")
void givenUserIsNice_whenGetNice_thenOk() throws Exception {
api.get("/nice").andExpect(status().isOk()).andExpect(jsonPath("$.body").value("Dear ch4mp! You are granted with: [USER_ROLES_EDITOR, NICE, AUTHOR]."));
api.get("/nice").andExpect(status().isOk()).andExpect(
jsonPath("$.body").value("Dear oauth2|c4-soft|4dd56dbb-71ef-4fe2-9358-3ae3240a9e94! You are granted with: [USER_ROLES_EDITOR, NICE, AUTHOR]."));
}

@Test
@WithJwt("auth0_badboy.json")
@WithJwt("keycloak_badboy.json")
void givenUserIsNotGrantedWithNice_whenGetNice_thenForbidden() throws Exception {
api.get("/nice").andExpect(status().isForbidden());
}
Expand All @@ -71,11 +73,11 @@ void givenRequestIsAnonymous_whenGetNice_thenUnauthorized() throws Exception {
/**
* &#64;MethodSource for &#64;ParameterizedTest
*
* @return a stream of {@link OAuthentication OAuthentication&lt;OpenidClaimSet&gt;} as defined by the authentication converter in the security
* configuration
* @return a stream of {@link OAuthentication OAuthentication&lt;OpenidClaimSet&gt;} as defined by the authentication converter in the
* security configuration
*/
Stream<AbstractAuthenticationToken> auth0users() {
return jwtAuthFactory.authenticationsFrom("auth0_nice.json", "auth0_badboy.json");
Stream<AbstractAuthenticationToken> users() {
return jwtAuthFactory.authenticationsFrom("keycloak_nice.json", "keycloak_badboy.json");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.test.context.support.WithAnonymousUser;

import com.c4_soft.springaddons.security.oauth2.test.annotations.WithJwt;
import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.ParameterizedAuthentication;
import com.c4_soft.springaddons.security.oauth2.test.webmvc.AddonsWebmvcTestConf;
import com.c4_soft.springaddons.security.oauth2.test.webmvc.MockMvcSupport;
import com.c4_soft.springaddons.security.oidc.OAuthentication;
import com.c4_soft.springaddons.security.oidc.OpenidClaimSet;

@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
@AutoConfigureMockMvc
Expand All @@ -39,13 +40,13 @@ class ResourceServerMultitenantDynamicApplicationTests {
WithJwt.AuthenticationFactory jwtAuthFactory;

@ParameterizedTest
@MethodSource("auth0users") // see below for the factory
@MethodSource("users") // see below for the factory
void givenUserIsAuthenticated_whenGreet_thenOk(@ParameterizedAuthentication Authentication auth) throws Exception {
@SuppressWarnings("unchecked")
final var oauth = (OAuthentication<OpenidClaimSet>) auth;
final var oauth = (JwtAuthenticationToken) auth;
final var actual = api.get("/greet").andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
assertThat(actual).contains(
"Hi %s! You are granted with: %s and your email is %s.".formatted(auth.getName(), auth.getAuthorities(), oauth.getAttributes().getEmail()));
"Hi %s! You are granted with: %s and your email is %s."
.formatted(auth.getName(), auth.getAuthorities(), oauth.getTokenAttributes().get(StandardClaimNames.EMAIL)));
}

@Test
Expand All @@ -54,13 +55,14 @@ void givenRequestIsAnonymous_whenGreet_thenUnauthorized() throws Exception {
}

@Test
@WithJwt("auth0_nice.json")
@WithJwt("keycloak_nice.json")
void givenUserIsNice_whenGetNice_thenOk() throws Exception {
api.get("/nice").andExpect(status().isOk()).andExpect(jsonPath("$.body").value("Dear ch4mp! You are granted with: [USER_ROLES_EDITOR, NICE, AUTHOR]."));
api.get("/nice").andExpect(status().isOk()).andExpect(
jsonPath("$.body").value("Dear oauth2|c4-soft|4dd56dbb-71ef-4fe2-9358-3ae3240a9e94! You are granted with: [USER_ROLES_EDITOR, NICE, AUTHOR]."));
}

@Test
@WithJwt("auth0_badboy.json")
@WithJwt("keycloak_badboy.json")
void givenUserIsNotGrantedWithNice_whenGetNice_thenForbidden() throws Exception {
api.get("/nice").andExpect(status().isForbidden());
}
Expand All @@ -74,11 +76,11 @@ void givenRequestIsAnonymous_whenGetNice_thenUnauthorized() throws Exception {
/**
* &#64;MethodSource for &#64;ParameterizedTest
*
* @return a stream of {@link OAuthentication OAuthentication&lt;OpenidClaimSet&gt;} as defined by the Authentication converter in the security
* configuration
* @return a stream of {@link OAuthentication OAuthentication&lt;OpenidClaimSet&gt;} as defined by the Authentication converter in the
* security configuration
*/
Stream<AbstractAuthenticationToken> auth0users() {
return jwtAuthFactory.authenticationsFrom("auth0_nice.json", "auth0_badboy.json");
Stream<AbstractAuthenticationToken> users() {
return jwtAuthFactory.authenticationsFrom("keycloak_nice.json", "keycloak_badboy.json");
}

}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"email": "[email protected]",
"email_verified": true,
"realm_access": {
"roles": [
"SKIPPER", "UNCLE"
]
},
"iss": "http://localhost:8080/realms/master",
"sub": "oauth2|c4-soft|5ff56dbb-71ef-4fe2-9358-3ae3240a9fff"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"email": "[email protected]",
"email_verified": true,
"realm_access": {
"roles": [
"USER_ROLES_EDITOR", "NICE", "AUTHOR"
]
},
"iss": "http://localhost:8080/realms/master",
"sub": "oauth2|c4-soft|4dd56dbb-71ef-4fe2-9358-3ae3240a9e94"
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.core.GrantedAuthority;

import com.c4_soft.springaddons.security.oidc.spring.C4MethodSecurityExpressionHandler;
import com.c4_soft.springaddons.security.oidc.spring.C4MethodSecurityExpressionRoot;
import com.c4_soft.springaddons.security.oidc.spring.SpringAddonsMethodSecurityExpressionHandler;
import com.c4_soft.springaddons.security.oidc.spring.SpringAddonsMethodSecurityExpressionRoot;
import com.c4_soft.springaddons.security.oidc.starter.synchronised.resourceserver.JwtAbstractAuthenticationTokenConverter;

@Configuration
Expand All @@ -31,10 +31,10 @@ public class SecurityConfig {

@Bean
static MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
return new C4MethodSecurityExpressionHandler(ProxiesMethodSecurityExpressionRoot::new);
return new SpringAddonsMethodSecurityExpressionHandler(ProxiesMethodSecurityExpressionRoot::new);
}

static final class ProxiesMethodSecurityExpressionRoot extends C4MethodSecurityExpressionRoot {
static final class ProxiesMethodSecurityExpressionRoot extends SpringAddonsMethodSecurityExpressionRoot {

public boolean is(String preferredUsername) {
return Objects.equals(preferredUsername, getAuthentication().getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ logging:
level:
org:
springframework:
security: INFO
security: DEBUG

management:
endpoint:
Expand Down
Loading

0 comments on commit e222bf6

Please sign in to comment.