Skip to content

Commit ac98b57

Browse files
committed
fix: address PR review feedback from RegistrationGuard SPI (#271)
- Include denial reason in OAuth2Error description field so AuthenticationFailureHandlers can access it via getError().getDescription() in both DSOAuth2UserService and DSOidcUserService - Replace String.trim().isEmpty() with String.isBlank() in RegistrationDecision.deny() (Java 17+ idiomatic) - Extract DEFAULT_DENIAL_REASON constant in RegistrationDecision - Add @FunctionalInterface to RegistrationGuard interface - Add JavaDoc clarifying RegistrationDecision reason is only meaningful when allowed is false - Add RegistrationContextTest covering null source validation
1 parent 11428da commit ac98b57

5 files changed

Lines changed: 53 additions & 5 deletions

File tree

src/main/java/com/digitalsanctuary/spring/user/registration/RegistrationDecision.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,19 @@
44
* The result of a {@link RegistrationGuard} evaluation indicating whether
55
* a registration attempt should be allowed or denied.
66
*
7+
* <p>Use the static factory methods {@link #allow()} and {@link #deny(String)}
8+
* rather than the canonical constructor. The {@code reason} parameter is only
9+
* meaningful when {@code allowed} is {@code false}.</p>
10+
*
711
* @param allowed whether the registration is permitted
8-
* @param reason a human-readable denial reason (may be {@code null} when allowed)
12+
* @param reason a human-readable denial reason; only meaningful when {@code allowed}
13+
* is {@code false}, may be {@code null} when allowed
914
*/
1015
public record RegistrationDecision(boolean allowed, String reason) {
1116

17+
/** Default reason used when {@link #deny(String)} is called with a blank or null reason. */
18+
private static final String DEFAULT_DENIAL_REASON = "Registration denied.";
19+
1220
/**
1321
* Creates a decision that allows the registration to proceed.
1422
*
@@ -25,8 +33,8 @@ public static RegistrationDecision allow() {
2533
* @return a denying decision with the given reason
2634
*/
2735
public static RegistrationDecision deny(String reason) {
28-
if (reason == null || reason.trim().isEmpty()) {
29-
reason = "Registration denied.";
36+
if (reason == null || reason.isBlank()) {
37+
reason = DEFAULT_DENIAL_REASON;
3038
}
3139
return new RegistrationDecision(false, reason);
3240
}

src/main/java/com/digitalsanctuary/spring/user/registration/RegistrationGuard.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* @see RegistrationDecision
4141
* @see DefaultRegistrationGuard
4242
*/
43+
@FunctionalInterface
4344
public interface RegistrationGuard {
4445

4546
/**

src/main/java/com/digitalsanctuary/spring/user/service/DSOAuth2UserService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public User handleOAuthLoginSuccess(String registrationId, OAuth2User oAuth2User
110110
log.info("Registration denied for email: {} source: OAUTH2 provider: {} reason: {}",
111111
user.getEmail(), registrationId, decision.reason());
112112
throw new OAuth2AuthenticationException(
113-
new OAuth2Error("registration_denied"), decision.reason());
113+
new OAuth2Error("registration_denied", decision.reason(), null), decision.reason());
114114
}
115115
user = registerNewOAuthUser(registrationId, user);
116116
return user;

src/main/java/com/digitalsanctuary/spring/user/service/DSOidcUserService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public User handleOidcLoginSuccess(String registrationId, OidcUser oidcUser) {
109109
log.info("Registration denied for email: {} source: OIDC provider: {} reason: {}",
110110
user.getEmail(), registrationId, decision.reason());
111111
throw new OAuth2AuthenticationException(
112-
new OAuth2Error("registration_denied"), decision.reason());
112+
new OAuth2Error("registration_denied", decision.reason(), null), decision.reason());
113113
}
114114
user = registerNewOidcUser(registrationId, user);
115115
return user;
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.digitalsanctuary.spring.user.registration;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
5+
6+
import org.junit.jupiter.api.DisplayName;
7+
import org.junit.jupiter.api.Test;
8+
9+
@DisplayName("RegistrationContext Tests")
10+
class RegistrationContextTest {
11+
12+
@Test
13+
@DisplayName("Should reject null source in RegistrationContext")
14+
void shouldRejectNullSource() {
15+
assertThatThrownBy(() -> new RegistrationContext("user@example.com", null, null))
16+
.isInstanceOf(NullPointerException.class)
17+
.hasMessageContaining("source must not be null");
18+
}
19+
20+
@Test
21+
@DisplayName("Should accept valid context with all fields")
22+
void shouldAcceptValidContext() {
23+
RegistrationContext context = new RegistrationContext("user@example.com", RegistrationSource.OAUTH2, "google");
24+
25+
assertThat(context.email()).isEqualTo("user@example.com");
26+
assertThat(context.source()).isEqualTo(RegistrationSource.OAUTH2);
27+
assertThat(context.providerName()).isEqualTo("google");
28+
}
29+
30+
@Test
31+
@DisplayName("Should accept null email and null providerName")
32+
void shouldAcceptNullEmailAndProvider() {
33+
RegistrationContext context = new RegistrationContext(null, RegistrationSource.FORM, null);
34+
35+
assertThat(context.email()).isNull();
36+
assertThat(context.source()).isEqualTo(RegistrationSource.FORM);
37+
assertThat(context.providerName()).isNull();
38+
}
39+
}

0 commit comments

Comments
 (0)