Skip to content

Commit d46ce34

Browse files
committed
Polish Builders
- Added remaining properties - Removed apply method since Spring Security isn't using it right now - Made builders extensible since the authentications are extensible
1 parent 662fe72 commit d46ce34

File tree

37 files changed

+611
-321
lines changed

37 files changed

+611
-321
lines changed

cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationToken.java

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import java.util.Collection;
2121

2222
import org.apereo.cas.client.validation.Assertion;
23-
import org.jspecify.annotations.NonNull;
23+
import org.jspecify.annotations.Nullable;
2424

2525
import org.springframework.security.authentication.AbstractAuthenticationToken;
2626
import org.springframework.security.core.Authentication;
@@ -106,6 +106,20 @@ private CasAuthenticationToken(final Integer keyHash, final Object principal, fi
106106
setAuthenticated(true);
107107
}
108108

109+
protected CasAuthenticationToken(Builder<?> builder) {
110+
super(builder);
111+
Assert.isTrue(builder.principal != null && !"".equals(builder.principal), "principal cannot be null or empty");
112+
Assert.notNull(builder.credentials != null && !"".equals(builder.credentials),
113+
"credentials cannot be null or empty");
114+
Assert.notNull(builder.userDetails, "userDetails cannot be null");
115+
Assert.notNull(builder.assertion, "assertion cannot be null");
116+
this.keyHash = builder.keyHash;
117+
this.principal = builder.principal;
118+
this.credentials = builder.credentials;
119+
this.userDetails = builder.userDetails;
120+
this.assertion = builder.assertion;
121+
}
122+
109123
private static Integer extractKeyHash(String key) {
110124
Assert.hasLength(key, "key cannot be null or empty");
111125
return key.hashCode();
@@ -156,8 +170,8 @@ public UserDetails getUserDetails() {
156170
}
157171

158172
@Override
159-
public Builder toBuilder() {
160-
return new Builder().apply(this);
173+
public Builder<?> toBuilder() {
174+
return new Builder<>(this);
161175
}
162176

163177
@Override
@@ -174,7 +188,7 @@ public String toString() {
174188
*
175189
* @since 7.0
176190
*/
177-
public static final class Builder extends AbstractAuthenticationBuilder<@NonNull CasAuthenticationToken, Builder> {
191+
public static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<Object, Object, B> {
178192

179193
private Integer keyHash;
180194

@@ -186,47 +200,47 @@ public static final class Builder extends AbstractAuthenticationBuilder<@NonNull
186200

187201
private Assertion assertion;
188202

189-
private Builder() {
190-
203+
protected Builder(CasAuthenticationToken token) {
204+
super(token);
205+
this.keyHash = token.keyHash;
206+
this.principal = token.principal;
207+
this.credentials = token.credentials;
208+
this.userDetails = token.userDetails;
209+
this.assertion = token.assertion;
191210
}
192211

193-
public Builder apply(CasAuthenticationToken authentication) {
194-
return super.apply(authentication).keyHash(authentication.keyHash)
195-
.principal(authentication.principal)
196-
.credentials(authentication.credentials)
197-
.userDetails(authentication.userDetails)
198-
.assertion(authentication.assertion);
199-
}
200-
201-
public Builder keyHash(Integer keyHash) {
212+
public B keyHash(Integer keyHash) {
202213
this.keyHash = keyHash;
203-
return this;
214+
return (B) this;
204215
}
205216

206-
public Builder principal(Object principal) {
217+
@Override
218+
public B principal(@Nullable Object principal) {
219+
Assert.notNull(principal, "principal cannot be null");
207220
this.principal = principal;
208-
return this;
221+
return (B) this;
209222
}
210223

211-
public Builder credentials(Object credentials) {
224+
@Override
225+
public B credentials(@Nullable Object credentials) {
226+
Assert.notNull(credentials, "credentials cannot be null");
212227
this.credentials = credentials;
213-
return this;
228+
return (B) this;
214229
}
215230

216-
public Builder userDetails(UserDetails userDetails) {
231+
public B userDetails(UserDetails userDetails) {
217232
this.userDetails = userDetails;
218-
return this;
233+
return (B) this;
219234
}
220235

221-
public Builder assertion(Assertion assertion) {
236+
public B assertion(Assertion assertion) {
222237
this.assertion = assertion;
223-
return this;
238+
return (B) this;
224239
}
225240

226241
@Override
227-
protected @NonNull CasAuthenticationToken build(Collection<GrantedAuthority> authorities) {
228-
return new CasAuthenticationToken(this.keyHash, this.principal, this.credentials, authorities,
229-
this.userDetails, this.assertion);
242+
public CasAuthenticationToken build() {
243+
return new CasAuthenticationToken(this);
230244
}
231245

232246
}

cas/src/main/java/org/springframework/security/cas/authentication/CasServiceTicketAuthenticationToken.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
import java.io.Serial;
2020
import java.util.Collection;
2121

22+
import org.jspecify.annotations.Nullable;
23+
2224
import org.springframework.security.authentication.AbstractAuthenticationToken;
25+
import org.springframework.security.core.Authentication;
2326
import org.springframework.security.core.GrantedAuthority;
2427
import org.springframework.util.Assert;
2528

@@ -50,7 +53,7 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
5053
*
5154
*/
5255
public CasServiceTicketAuthenticationToken(String identifier, Object credentials) {
53-
super(null);
56+
super((Collection<? extends GrantedAuthority>) null);
5457
this.identifier = identifier;
5558
this.credentials = credentials;
5659
setAuthenticated(false);
@@ -73,6 +76,12 @@ public CasServiceTicketAuthenticationToken(String identifier, Object credentials
7376
super.setAuthenticated(true);
7477
}
7578

79+
protected CasServiceTicketAuthenticationToken(Builder<?> builder) {
80+
super(builder);
81+
this.identifier = builder.principal;
82+
this.credentials = builder.credentials;
83+
}
84+
7685
public static CasServiceTicketAuthenticationToken stateful(Object credentials) {
7786
return new CasServiceTicketAuthenticationToken(CAS_STATEFUL_IDENTIFIER, credentials);
7887
}
@@ -108,4 +117,46 @@ public void eraseCredentials() {
108117
this.credentials = null;
109118
}
110119

120+
public Builder<?> toBuilder() {
121+
return new Builder<>(this);
122+
}
123+
124+
/**
125+
* A builder preserving the concrete {@link Authentication} type
126+
*
127+
* @since 7.0
128+
*/
129+
public static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<String, Object, B> {
130+
131+
private String principal;
132+
133+
private Object credentials;
134+
135+
protected Builder(CasServiceTicketAuthenticationToken token) {
136+
super(token);
137+
this.principal = token.identifier;
138+
this.credentials = token.credentials;
139+
}
140+
141+
@Override
142+
public B principal(@Nullable String principal) {
143+
Assert.notNull(principal, "principal cannot be null");
144+
this.principal = principal;
145+
return (B) this;
146+
}
147+
148+
@Override
149+
public B credentials(@Nullable Object credentials) {
150+
Assert.notNull(credentials, "credentials cannot be null");
151+
this.credentials = credentials;
152+
return (B) this;
153+
}
154+
155+
@Override
156+
public CasServiceTicketAuthenticationToken build() {
157+
return new CasServiceTicketAuthenticationToken(this);
158+
}
159+
160+
}
161+
111162
}

cas/src/test/java/org/springframework/security/cas/authentication/CasAuthenticationTokenTests.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,14 @@ public void toBuilderWhenApplyThenCopies() {
165165
Assertion assertionTwo = new AssertionImpl("test");
166166
CasAuthenticationToken factorTwo = new CasAuthenticationToken("yek", "bob", "ssap",
167167
AuthorityUtils.createAuthorityList("FACTOR_TWO"), PasswordEncodedUser.admin(), assertionTwo);
168-
CasAuthenticationToken authentication = factorOne.toBuilder().apply(factorTwo).build();
168+
CasAuthenticationToken authentication = factorOne.toBuilder()
169+
.authorities((a) -> a.addAll(factorTwo.getAuthorities()))
170+
.keyHash(factorTwo.getKeyHash())
171+
.principal(factorTwo.getPrincipal())
172+
.credentials(factorTwo.getCredentials())
173+
.userDetails(factorTwo.getUserDetails())
174+
.assertion(factorTwo.getAssertion())
175+
.build();
169176
Set<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
170177
assertThat(authentication.getKeyHash()).isEqualTo(factorTwo.getKeyHash());
171178
assertThat(authentication.getPrincipal()).isEqualTo(factorTwo.getPrincipal());

config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTransientAuthenticationTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.config.annotation.web.configurers;
1818

19+
import java.util.Collection;
20+
1921
import org.junit.jupiter.api.Test;
2022
import org.junit.jupiter.api.extension.ExtendWith;
2123

@@ -31,6 +33,7 @@
3133
import org.springframework.security.config.test.SpringTestContextExtension;
3234
import org.springframework.security.core.Authentication;
3335
import org.springframework.security.core.AuthenticationException;
36+
import org.springframework.security.core.GrantedAuthority;
3437
import org.springframework.security.core.Transient;
3538
import org.springframework.security.web.SecurityFilterChain;
3639
import org.springframework.test.web.servlet.MockMvc;
@@ -113,7 +116,7 @@ public boolean supports(Class<?> authentication) {
113116
static class SomeTransientAuthentication extends AbstractAuthenticationToken {
114117

115118
SomeTransientAuthentication() {
116-
super(null);
119+
super((Collection<? extends GrantedAuthority>) null);
117120
}
118121

119122
@Override

config/src/test/java/org/springframework/security/config/http/SessionManagementConfigTransientAuthenticationTests.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.config.http;
1818

19+
import java.util.Collection;
20+
1921
import org.junit.jupiter.api.Test;
2022
import org.junit.jupiter.api.extension.ExtendWith;
2123

@@ -26,6 +28,7 @@
2628
import org.springframework.security.config.test.SpringTestContextExtension;
2729
import org.springframework.security.core.Authentication;
2830
import org.springframework.security.core.AuthenticationException;
31+
import org.springframework.security.core.GrantedAuthority;
2932
import org.springframework.security.core.Transient;
3033
import org.springframework.test.web.servlet.MockMvc;
3134
import org.springframework.test.web.servlet.MvcResult;
@@ -82,7 +85,7 @@ public boolean supports(Class<?> authentication) {
8285
static class SomeTransientAuthentication extends AbstractAuthenticationToken {
8386

8487
SomeTransientAuthentication() {
85-
super(null);
88+
super((Collection<? extends GrantedAuthority>) null);
8689
}
8790

8891
@Override

core/src/main/java/org/springframework/security/authentication/AbstractAuthenticationToken.java

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import java.util.ArrayList;
2121
import java.util.Collection;
2222
import java.util.Collections;
23-
import java.util.HashSet;
23+
import java.util.LinkedHashSet;
2424
import java.util.function.Consumer;
2525

2626
import org.jspecify.annotations.Nullable;
@@ -43,6 +43,8 @@
4343
*/
4444
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
4545

46+
private static final long serialVersionUID = -3194696462184782834L;
47+
4648
private final Collection<GrantedAuthority> authorities;
4749

4850
private @Nullable Object details;
@@ -65,6 +67,12 @@ public AbstractAuthenticationToken(@Nullable Collection<? extends GrantedAuthori
6567
this.authorities = Collections.unmodifiableList(new ArrayList<>(authorities));
6668
}
6769

70+
protected AbstractAuthenticationToken(AbstractAuthenticationBuilder<?, ?, ?> builder) {
71+
this(builder.authorities);
72+
this.authenticated = builder.authenticated;
73+
this.details = builder.details;
74+
}
75+
6876
@Override
6977
public Collection<GrantedAuthority> getAuthorities() {
7078
return this.authorities;
@@ -187,36 +195,40 @@ public String toString() {
187195
return sb.toString();
188196
}
189197

190-
protected abstract static class AbstractAuthenticationBuilder<A extends Authentication, B extends AbstractAuthenticationBuilder<A, B>>
191-
implements Builder<A, B> {
198+
protected abstract static class AbstractAuthenticationBuilder<P, C, B extends AbstractAuthenticationBuilder<P, C, B>>
199+
implements Authentication.Builder<P, C, B> {
192200

193-
private final Collection<GrantedAuthority> authorities = new HashSet<>();
201+
protected boolean authenticated;
194202

195-
protected AbstractAuthenticationBuilder() {
203+
protected @Nullable Object details;
196204

205+
protected final Collection<GrantedAuthority> authorities;
206+
207+
protected AbstractAuthenticationBuilder(AbstractAuthenticationToken token) {
208+
this.authorities = new LinkedHashSet<>(token.getAuthorities());
209+
this.authenticated = token.isAuthenticated();
210+
this.details = token.getDetails();
197211
}
198212

199213
@Override
200-
public B authorities(Consumer<Collection<GrantedAuthority>> authorities) {
201-
authorities.accept(this.authorities);
214+
public B authenticated(boolean authenticated) {
215+
this.authenticated = authenticated;
202216
return (B) this;
203217
}
204218

205219
@Override
206-
public A build() {
207-
return build(this.authorities);
220+
public B details(@Nullable Object details) {
221+
this.details = details;
222+
return (B) this;
208223
}
209224

210225
@Override
211-
public B apply(Authentication token) {
212-
Assert.isTrue(token.isAuthenticated(), "cannot mutate an unauthenticated token");
213-
Assert.notNull(token.getPrincipal(), "principal cannot be null");
214-
this.authorities.addAll(token.getAuthorities());
226+
public B authorities(Consumer<Collection<GrantedAuthority>> authorities) {
227+
authorities.accept(this.authorities);
228+
this.authenticated = true;
215229
return (B) this;
216230
}
217231

218-
protected abstract A build(Collection<GrantedAuthority> authorities);
219-
220232
}
221233

222234
}

0 commit comments

Comments
 (0)