Skip to content

Commit 6689798

Browse files
committed
Merge remote-tracking branch 'jzheaux/authentication-builder'
Issue gh-17861 Issue gh-17862
2 parents eeb4574 + b09afb3 commit 6689798

File tree

53 files changed

+1739
-26
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1739
-26
lines changed

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

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

2222
import org.apereo.cas.client.validation.Assertion;
23+
import org.jspecify.annotations.Nullable;
2324

2425
import org.springframework.security.authentication.AbstractAuthenticationToken;
2526
import org.springframework.security.core.GrantedAuthority;
@@ -104,6 +105,19 @@ private CasAuthenticationToken(final Integer keyHash, final Object principal, fi
104105
setAuthenticated(true);
105106
}
106107

108+
protected CasAuthenticationToken(Builder<?> builder) {
109+
super(builder);
110+
Assert.isTrue(!"".equals(builder.principal), "principal cannot be null or empty");
111+
Assert.notNull(!"".equals(builder.credentials), "credentials cannot be null or empty");
112+
Assert.notNull(builder.userDetails, "userDetails cannot be null");
113+
Assert.notNull(builder.assertion, "assertion cannot be null");
114+
this.keyHash = builder.keyHash;
115+
this.principal = builder.principal;
116+
this.credentials = builder.credentials;
117+
this.userDetails = builder.userDetails;
118+
this.assertion = builder.assertion;
119+
}
120+
107121
private static Integer extractKeyHash(String key) {
108122
Assert.hasLength(key, "key cannot be null or empty");
109123
return key.hashCode();
@@ -153,6 +167,11 @@ public UserDetails getUserDetails() {
153167
return this.userDetails;
154168
}
155169

170+
@Override
171+
public Builder<?> toBuilder() {
172+
return new Builder<>(this);
173+
}
174+
156175
@Override
157176
public String toString() {
158177
StringBuilder sb = new StringBuilder();
@@ -162,4 +181,81 @@ public String toString() {
162181
return (sb.toString());
163182
}
164183

184+
/**
185+
* A builder of {@link CasAuthenticationToken} instances
186+
*
187+
* @since 7.0
188+
*/
189+
public static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {
190+
191+
private Integer keyHash;
192+
193+
private Object principal;
194+
195+
private Object credentials;
196+
197+
private UserDetails userDetails;
198+
199+
private Assertion assertion;
200+
201+
protected Builder(CasAuthenticationToken token) {
202+
super(token);
203+
this.keyHash = token.keyHash;
204+
this.principal = token.principal;
205+
this.credentials = token.credentials;
206+
this.userDetails = token.userDetails;
207+
this.assertion = token.assertion;
208+
}
209+
210+
/**
211+
* Use this key
212+
* @param key the key to use
213+
* @return the {@link Builder} for further configurations
214+
*/
215+
public B key(String key) {
216+
this.keyHash = key.hashCode();
217+
return (B) this;
218+
}
219+
220+
@Override
221+
public B principal(@Nullable Object principal) {
222+
Assert.notNull(principal, "principal cannot be null");
223+
this.principal = principal;
224+
return (B) this;
225+
}
226+
227+
@Override
228+
public B credentials(@Nullable Object credentials) {
229+
Assert.notNull(credentials, "credentials cannot be null");
230+
this.credentials = credentials;
231+
return (B) this;
232+
}
233+
234+
/**
235+
* Use this {@link UserDetails}
236+
* @param userDetails the {@link UserDetails} to use
237+
* @return the {@link Builder} for further configurations
238+
*/
239+
public B userDetails(UserDetails userDetails) {
240+
this.userDetails = userDetails;
241+
return (B) this;
242+
}
243+
244+
/**
245+
* Use this {@link Assertion}
246+
* @param assertion the {@link Assertion} to use
247+
* @return the {@link Builder} for further configurations
248+
*/
249+
public B assertion(Assertion assertion) {
250+
this.assertion = assertion;
251+
return (B) this;
252+
}
253+
254+
@Override
255+
public CasAuthenticationToken build() {
256+
return new CasAuthenticationToken(this);
257+
}
258+
259+
}
260+
165261
}

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

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
5252
*
5353
*/
5454
public CasServiceTicketAuthenticationToken(String identifier, Object credentials) {
55-
super(null);
55+
super((Collection<? extends GrantedAuthority>) null);
5656
this.identifier = identifier;
5757
this.credentials = credentials;
5858
setAuthenticated(false);
@@ -75,6 +75,12 @@ public CasServiceTicketAuthenticationToken(String identifier, Object credentials
7575
super.setAuthenticated(true);
7676
}
7777

78+
protected CasServiceTicketAuthenticationToken(Builder<?> builder) {
79+
super(builder);
80+
this.identifier = builder.principal;
81+
this.credentials = builder.credentials;
82+
}
83+
7884
public static CasServiceTicketAuthenticationToken stateful(Object credentials) {
7985
return new CasServiceTicketAuthenticationToken(CAS_STATEFUL_IDENTIFIER, credentials);
8086
}
@@ -110,4 +116,46 @@ public void eraseCredentials() {
110116
this.credentials = null;
111117
}
112118

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

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.Collections;
2020
import java.util.List;
21+
import java.util.Set;
2122

2223
import org.apereo.cas.client.validation.Assertion;
2324
import org.apereo.cas.client.validation.AssertionImpl;
@@ -26,6 +27,7 @@
2627
import org.springframework.security.core.GrantedAuthority;
2728
import org.springframework.security.core.authority.AuthorityUtils;
2829
import org.springframework.security.core.authority.SimpleGrantedAuthority;
30+
import org.springframework.security.core.userdetails.PasswordEncodedUser;
2931
import org.springframework.security.core.userdetails.User;
3032
import org.springframework.security.core.userdetails.UserDetails;
3133

@@ -155,4 +157,29 @@ public void testToString() {
155157
assertThat(result.lastIndexOf("Credentials (Service/Proxy Ticket):") != -1).isTrue();
156158
}
157159

160+
@Test
161+
public void toBuilderWhenApplyThenCopies() {
162+
Assertion assertionOne = new AssertionImpl("test");
163+
CasAuthenticationToken factorOne = new CasAuthenticationToken("key", "alice", "pass",
164+
AuthorityUtils.createAuthorityList("FACTOR_ONE"), PasswordEncodedUser.user(), assertionOne);
165+
Assertion assertionTwo = new AssertionImpl("test");
166+
CasAuthenticationToken factorTwo = new CasAuthenticationToken("yek", "bob", "ssap",
167+
AuthorityUtils.createAuthorityList("FACTOR_TWO"), PasswordEncodedUser.admin(), assertionTwo);
168+
CasAuthenticationToken authentication = factorOne.toBuilder()
169+
.authorities((a) -> a.addAll(factorTwo.getAuthorities()))
170+
.key("yek")
171+
.principal(factorTwo.getPrincipal())
172+
.credentials(factorTwo.getCredentials())
173+
.userDetails(factorTwo.getUserDetails())
174+
.assertion(factorTwo.getAssertion())
175+
.build();
176+
Set<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
177+
assertThat(authentication.getKeyHash()).isEqualTo(factorTwo.getKeyHash());
178+
assertThat(authentication.getPrincipal()).isEqualTo(factorTwo.getPrincipal());
179+
assertThat(authentication.getCredentials()).isEqualTo(factorTwo.getCredentials());
180+
assertThat(authentication.getUserDetails()).isEqualTo(factorTwo.getUserDetails());
181+
assertThat(authentication.getAssertion()).isEqualTo(factorTwo.getAssertion());
182+
assertThat(authorities).containsExactlyInAnyOrder("FACTOR_ONE", "FACTOR_TWO");
183+
}
184+
158185
}

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: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616

1717
package org.springframework.security.authentication;
1818

19+
import java.io.Serial;
1920
import java.security.Principal;
2021
import java.util.ArrayList;
2122
import java.util.Collection;
2223
import java.util.Collections;
24+
import java.util.LinkedHashSet;
25+
import java.util.function.Consumer;
2326

2427
import org.jspecify.annotations.Nullable;
2528

@@ -41,6 +44,9 @@
4144
*/
4245
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
4346

47+
@Serial
48+
private static final long serialVersionUID = -3194696462184782834L;
49+
4450
private final Collection<GrantedAuthority> authorities;
4551

4652
private @Nullable Object details;
@@ -63,6 +69,12 @@ public AbstractAuthenticationToken(@Nullable Collection<? extends GrantedAuthori
6369
this.authorities = Collections.unmodifiableList(new ArrayList<>(authorities));
6470
}
6571

72+
protected AbstractAuthenticationToken(AbstractAuthenticationBuilder<?> builder) {
73+
this(builder.authorities);
74+
this.authenticated = builder.authenticated;
75+
this.details = builder.details;
76+
}
77+
6678
@Override
6779
public Collection<GrantedAuthority> getAuthorities() {
6880
return this.authorities;
@@ -185,4 +197,48 @@ public String toString() {
185197
return sb.toString();
186198
}
187199

200+
/**
201+
* A common abstract implementation of {@link Authentication.Builder}. It implements
202+
* the builder methods that correspond to the {@link Authentication} methods that
203+
* {@link AbstractAuthenticationToken} implements
204+
*
205+
* @param <B>
206+
* @since 7.0
207+
*/
208+
protected abstract static class AbstractAuthenticationBuilder<B extends AbstractAuthenticationBuilder<B>>
209+
implements Authentication.Builder<B> {
210+
211+
private boolean authenticated;
212+
213+
private @Nullable Object details;
214+
215+
private final Collection<GrantedAuthority> authorities;
216+
217+
protected AbstractAuthenticationBuilder(AbstractAuthenticationToken token) {
218+
this.authorities = new LinkedHashSet<>(token.getAuthorities());
219+
this.authenticated = token.isAuthenticated();
220+
this.details = token.getDetails();
221+
}
222+
223+
@Override
224+
public B authenticated(boolean authenticated) {
225+
this.authenticated = authenticated;
226+
return (B) this;
227+
}
228+
229+
@Override
230+
public B details(@Nullable Object details) {
231+
this.details = details;
232+
return (B) this;
233+
}
234+
235+
@Override
236+
public B authorities(Consumer<Collection<GrantedAuthority>> authorities) {
237+
authorities.accept(this.authorities);
238+
this.authenticated = true;
239+
return (B) this;
240+
}
241+
242+
}
243+
188244
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ public Mono<Authentication> authenticate(Authentication authentication) {
6161
Function<ReactiveAuthenticationManager, Mono<Authentication>> logging = (m) -> m.authenticate(authentication)
6262
.doOnError(AuthenticationException.class, (ex) -> ex.setAuthenticationRequest(authentication))
6363
.doOnError(this.logger::debug);
64-
6564
return ((this.continueOnError) ? result.concatMapDelayError(logging) : result.concatMap(logging)).next();
6665
}
6766

0 commit comments

Comments
 (0)