Skip to content

Commit 95644fb

Browse files
committed
Merge branch 'builder-enhancements'
Issue gh-18052 Issue gh-18053
2 parents fbf7bb3 + 21ff768 commit 95644fb

File tree

24 files changed

+144
-277
lines changed

24 files changed

+144
-277
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.jspecify.annotations.Nullable;
2424

2525
import org.springframework.security.authentication.AbstractAuthenticationToken;
26+
import org.springframework.security.core.BuildableAuthentication;
2627
import org.springframework.security.core.GrantedAuthority;
2728
import org.springframework.security.core.userdetails.UserDetails;
2829
import org.springframework.util.Assert;
@@ -34,7 +35,8 @@
3435
* @author Ben Alex
3536
* @author Scott Battaglia
3637
*/
37-
public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
38+
public class CasAuthenticationToken extends AbstractAuthenticationToken
39+
implements BuildableAuthentication, Serializable {
3840

3941
private static final long serialVersionUID = 620L;
4042

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.jspecify.annotations.Nullable;
2323

2424
import org.springframework.security.authentication.AbstractAuthenticationToken;
25+
import org.springframework.security.core.BuildableAuthentication;
2526
import org.springframework.security.core.GrantedAuthority;
2627
import org.springframework.util.Assert;
2728

@@ -32,7 +33,8 @@
3233
* @author Hal Deadman
3334
* @since 6.1
3435
*/
35-
public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationToken {
36+
public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationToken
37+
implements BuildableAuthentication {
3638

3739
static final String CAS_STATELESS_IDENTIFIER = "_cas_stateless_";
3840

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.jspecify.annotations.Nullable;
2222

23+
import org.springframework.security.core.BuildableAuthentication;
2324
import org.springframework.security.core.GrantedAuthority;
2425
import org.springframework.util.Assert;
2526

@@ -32,7 +33,7 @@
3233
* @author Ben Alex
3334
* @author Luke Taylor
3435
*/
35-
public class RememberMeAuthenticationToken extends AbstractAuthenticationToken {
36+
public class RememberMeAuthenticationToken extends AbstractAuthenticationToken implements BuildableAuthentication {
3637

3738
private static final long serialVersionUID = 620L;
3839

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.jspecify.annotations.Nullable;
2424

25+
import org.springframework.security.core.BuildableAuthentication;
2526
import org.springframework.security.core.GrantedAuthority;
2627
import org.springframework.security.core.authority.AuthorityUtils;
2728
import org.springframework.util.Assert;
@@ -34,7 +35,7 @@
3435
*
3536
* @author Ben Alex
3637
*/
37-
public class TestingAuthenticationToken extends AbstractAuthenticationToken {
38+
public class TestingAuthenticationToken extends AbstractAuthenticationToken implements BuildableAuthentication {
3839

3940
private static final long serialVersionUID = 1L;
4041

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.jspecify.annotations.Nullable;
2222

23+
import org.springframework.security.core.BuildableAuthentication;
2324
import org.springframework.security.core.GrantedAuthority;
2425
import org.springframework.util.Assert;
2526

@@ -35,7 +36,8 @@
3536
* @author Ben Alex
3637
* @author Norbert Nowak
3738
*/
38-
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
39+
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken
40+
implements BuildableAuthentication {
3941

4042
private static final long serialVersionUID = 620L;
4143

core/src/main/java/org/springframework/security/authentication/ott/OneTimeTokenAuthentication.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.jspecify.annotations.Nullable;
2323

2424
import org.springframework.security.authentication.AbstractAuthenticationToken;
25+
import org.springframework.security.core.BuildableAuthentication;
2526
import org.springframework.security.core.GrantedAuthority;
2627
import org.springframework.util.Assert;
2728

@@ -31,7 +32,7 @@
3132
* @author Josh Cummings
3233
* @since 7.0
3334
*/
34-
public class OneTimeTokenAuthentication extends AbstractAuthenticationToken {
35+
public class OneTimeTokenAuthentication extends AbstractAuthenticationToken implements BuildableAuthentication {
3536

3637
@Serial
3738
private static final long serialVersionUID = 1195893764725073959L;

core/src/main/java/org/springframework/security/core/Authentication.java

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import java.io.Serializable;
2020
import java.security.Principal;
2121
import java.util.Collection;
22+
import java.util.Set;
2223
import java.util.function.Consumer;
24+
import java.util.stream.Collectors;
2325

2426
import org.jspecify.annotations.Nullable;
2527

@@ -138,35 +140,37 @@ public interface Authentication extends Principal, Serializable {
138140
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
139141

140142
/**
141-
* Return an {@link Builder} based on this instance. By default, returns a builder
142-
* that builds a {@link SimpleAuthentication}.
143-
* <p>
144-
* Although a {@code default} method, all {@link Authentication} implementations
145-
* should implement this. The reason is to ensure that the {@link Authentication} type
146-
* is preserved when {@link Builder#build} is invoked. This is especially important in
147-
* the event that your authentication implementation contains custom fields.
148-
* </p>
149-
* <p>
150-
* This isn't strictly necessary since it is recommended that applications code to the
151-
* {@link Authentication} interface and that custom information is often contained in
152-
* the {@link Authentication#getPrincipal} value.
153-
* </p>
154-
* @return an {@link Builder} for building a new {@link Authentication} based on this
155-
* instance
156-
* @since 7.0
157-
*/
158-
default Builder<?> toBuilder() {
159-
return new SimpleAuthentication.Builder(this);
160-
}
161-
162-
/**
163-
* A builder based on a given {@link Authentication} instance
143+
* A builder based on a given {@link BuildableAuthentication} instance
164144
*
165145
* @author Josh Cummings
166146
* @since 7.0
167147
*/
168148
interface Builder<B extends Builder<B>> {
169149

150+
/**
151+
* Apply this authentication instance
152+
* <p>
153+
* By default, merges the authorities in the provided {@code authentication} with
154+
* the authentication being built. Only those authorities that haven't already
155+
* been specified to the builder will be added.
156+
* </p>
157+
* @param authentication the {@link Authentication} to appluy
158+
* @return the {@link Builder} for additional configuration
159+
* @see BuildableAuthentication#getAuthorities
160+
*/
161+
default B authentication(Authentication authentication) {
162+
return authorities((a) -> {
163+
Set<String> newAuthorities = a.stream()
164+
.map(GrantedAuthority::getAuthority)
165+
.collect(Collectors.toUnmodifiableSet());
166+
for (GrantedAuthority currentAuthority : authentication.getAuthorities()) {
167+
if (!newAuthorities.contains(currentAuthority.getAuthority())) {
168+
a.add(currentAuthority);
169+
}
170+
}
171+
});
172+
}
173+
170174
/**
171175
* Mutate the authorities with this {@link Consumer}.
172176
* <p>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.core;
18+
19+
/**
20+
* An {@link Authentication} that is also buildable.
21+
*
22+
* @author Josh Cummings
23+
* @since 7.0
24+
*/
25+
public interface BuildableAuthentication extends Authentication {
26+
27+
/**
28+
* Return an {@link Builder} based on this instance.
29+
* <p>
30+
* Although a {@code default} method, all {@link BuildableAuthentication}
31+
* implementations should implement this. The reason is to ensure that the
32+
* {@link BuildableAuthentication} type is preserved when {@link Builder#build} is
33+
* invoked. This is especially important in the event that your authentication
34+
* implementation contains custom fields.
35+
* </p>
36+
* <p>
37+
* This isn't strictly necessary since it is recommended that applications code to the
38+
* {@link Authentication} interface and that custom information is often contained in
39+
* the {@link Authentication#getPrincipal} value.
40+
* </p>
41+
* @return an {@link Builder} for building a new {@link Authentication} based on this
42+
* instance
43+
*/
44+
Builder<?> toBuilder();
45+
46+
}

core/src/main/java/org/springframework/security/core/SimpleAuthentication.java

Lines changed: 0 additions & 151 deletions
This file was deleted.

core/src/test/java/org/springframework/security/authentication/AbstractAuthenticationBuilderTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ void applyWhenAuthoritiesThenAdds() {
3737
assertThat(authorities).containsExactlyInAnyOrder("FACTOR_ONE", "FACTOR_TWO");
3838
}
3939

40+
@Test
41+
void authenticationWhenAuthoritiesThenAdds() {
42+
TestingAuthenticationToken factorOne = new TestingAuthenticationToken("user", "pass", "FACTOR_ONE");
43+
TestingAuthenticationToken factorTwo = new TestingAuthenticationToken("user", "pass", "FACTOR_TWO");
44+
TestAbstractAuthenticationBuilder builder = new TestAbstractAuthenticationBuilder(factorOne);
45+
Authentication result = builder.authentication(factorTwo).build();
46+
Set<String> authorities = AuthorityUtils.authorityListToSet(result.getAuthorities());
47+
assertThat(authorities).containsExactlyInAnyOrder("FACTOR_ONE", "FACTOR_TWO");
48+
}
49+
4050
private static final class TestAbstractAuthenticationBuilder
4151
extends TestingAuthenticationToken.Builder<TestAbstractAuthenticationBuilder> {
4252

0 commit comments

Comments
 (0)