Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Propagate Authorities From Previous Factors #17790

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jzheaux wants to merge 11 commits into spring-projects:main
base: main
Choose a base branch
Loading
from jzheaux:authentication-builder
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
11 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import java.util.Collection;

import org.apereo.cas.client.validation.Assertion;
import org.jspecify.annotations.Nullable;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -104,6 +106,19 @@ private CasAuthenticationToken(final Integer keyHash, final Object principal, fi
setAuthenticated(true);
}

protected CasAuthenticationToken(Builder<?> builder) {
super(builder);
Assert.isTrue(!"".equals(builder.principal), "principal cannot be null or empty");
Assert.notNull(!"".equals(builder.credentials), "credentials cannot be null or empty");
Assert.notNull(builder.userDetails, "userDetails cannot be null");
Assert.notNull(builder.assertion, "assertion cannot be null");
this.keyHash = builder.keyHash;
this.principal = builder.principal;
this.credentials = builder.credentials;
this.userDetails = builder.userDetails;
this.assertion = builder.assertion;
}

private static Integer extractKeyHash(String key) {
Assert.hasLength(key, "key cannot be null or empty");
return key.hashCode();
Expand Down Expand Up @@ -153,6 +168,11 @@ public UserDetails getUserDetails() {
return this.userDetails;
}

@Override
public Builder<?> toBuilder() {
return new Builder<>(this);
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Expand All @@ -162,4 +182,66 @@ public String toString() {
return (sb.toString());
}

/**
* A builder preserving the concrete {@link Authentication} type
*
* @since 7.0
*/
public static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<Object, Object, B> {

private Integer keyHash;

private Object principal;

private Object credentials;

private UserDetails userDetails;

private Assertion assertion;

protected Builder(CasAuthenticationToken token) {
super(token);
this.keyHash = token.keyHash;
this.principal = token.principal;
this.credentials = token.credentials;
this.userDetails = token.userDetails;
this.assertion = token.assertion;
}

public B keyHash(Integer keyHash) {
this.keyHash = keyHash;
return (B) this;
}

@Override
public B principal(@Nullable Object principal) {
Assert.notNull(principal, "principal cannot be null");
this.principal = principal;
return (B) this;
}

@Override
public B credentials(@Nullable Object credentials) {
Assert.notNull(credentials, "credentials cannot be null");
this.credentials = credentials;
return (B) this;
}

public B userDetails(UserDetails userDetails) {
this.userDetails = userDetails;
return (B) this;
}

public B assertion(Assertion assertion) {
this.assertion = assertion;
return (B) this;
}

@Override
public CasAuthenticationToken build() {
return new CasAuthenticationToken(this);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.jspecify.annotations.Nullable;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;

Expand Down Expand Up @@ -52,7 +53,7 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
*
*/
public CasServiceTicketAuthenticationToken(String identifier, Object credentials) {
super(null);
super((Collection<? extendsGrantedAuthority>) null);
this.identifier = identifier;
this.credentials = credentials;
setAuthenticated(false);
Expand All @@ -75,6 +76,12 @@ public CasServiceTicketAuthenticationToken(String identifier, Object credentials
super.setAuthenticated(true);
}

protected CasServiceTicketAuthenticationToken(Builder<?> builder) {
super(builder);
this.identifier = builder.principal;
this.credentials = builder.credentials;
}

public static CasServiceTicketAuthenticationToken stateful(Object credentials) {
return new CasServiceTicketAuthenticationToken(CAS_STATEFUL_IDENTIFIER, credentials);
}
Expand Down Expand Up @@ -110,4 +117,46 @@ public void eraseCredentials() {
this.credentials = null;
}

public Builder<?> toBuilder() {
return new Builder<>(this);
}

/**
* A builder preserving the concrete {@link Authentication} type
*
* @since 7.0
*/
public static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<String, Object, B> {

private String principal;

private @Nullable Object credentials;

protected Builder(CasServiceTicketAuthenticationToken token) {
super(token);
this.principal = token.identifier;
this.credentials = token.credentials;
}

@Override
public B principal(@Nullable String principal) {
Assert.notNull(principal, "principal cannot be null");
this.principal = principal;
return (B) this;
}

@Override
public B credentials(@Nullable Object credentials) {
Assert.notNull(credentials, "credentials cannot be null");
this.credentials = credentials;
return (B) this;
}

@Override
public CasServiceTicketAuthenticationToken build() {
return new CasServiceTicketAuthenticationToken(this);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.AssertionImpl;
Expand All @@ -26,6 +27,7 @@
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;

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

@Test
public void toBuilderWhenApplyThenCopies() {
Assertion assertionOne = new AssertionImpl("test");
CasAuthenticationToken factorOne = new CasAuthenticationToken("key", "alice", "pass",
AuthorityUtils.createAuthorityList("FACTOR_ONE"), PasswordEncodedUser.user(), assertionOne);
Assertion assertionTwo = new AssertionImpl("test");
CasAuthenticationToken factorTwo = new CasAuthenticationToken("yek", "bob", "ssap",
AuthorityUtils.createAuthorityList("FACTOR_TWO"), PasswordEncodedUser.admin(), assertionTwo);
CasAuthenticationToken authentication = factorOne.toBuilder()
.authorities((a) -> a.addAll(factorTwo.getAuthorities()))
.keyHash(factorTwo.getKeyHash())
.principal(factorTwo.getPrincipal())
.credentials(factorTwo.getCredentials())
.userDetails(factorTwo.getUserDetails())
.assertion(factorTwo.getAssertion())
.build();
Set<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
assertThat(authentication.getKeyHash()).isEqualTo(factorTwo.getKeyHash());
assertThat(authentication.getPrincipal()).isEqualTo(factorTwo.getPrincipal());
assertThat(authentication.getCredentials()).isEqualTo(factorTwo.getCredentials());
assertThat(authentication.getUserDetails()).isEqualTo(factorTwo.getUserDetails());
assertThat(authentication.getAssertion()).isEqualTo(factorTwo.getAssertion());
assertThat(authorities).containsExactlyInAnyOrder("FACTOR_ONE", "FACTOR_TWO");
}

}
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.authentication.AuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
Expand Down Expand Up @@ -456,7 +457,7 @@ final class SerializationSamples {
generatorByClassName.put(AuthenticationSuccessEvent.class,
(r) -> new AuthenticationSuccessEvent(authentication));
generatorByClassName.put(InteractiveAuthenticationSuccessEvent.class,
(r) -> new InteractiveAuthenticationSuccessEvent(authentication, Authentication.class));
(r) -> new InteractiveAuthenticationSuccessEvent(authentication, AuthenticationFilter.class));
generatorByClassName.put(LogoutSuccessEvent.class, (r) -> new LogoutSuccessEvent(authentication));
generatorByClassName.put(JaasAuthenticationFailedEvent.class,
(r) -> new JaasAuthenticationFailedEvent(authentication, new RuntimeException("message")));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

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

import java.util.Collection;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

Expand All @@ -31,6 +33,7 @@
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.Transient;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.MockMvc;
Expand Down Expand Up @@ -113,7 +116,7 @@ public boolean supports(Class<?> authentication) {
static class SomeTransientAuthentication extends AbstractAuthenticationToken {

SomeTransientAuthentication() {
super(null);
super((Collection<? extends GrantedAuthority>) null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.springframework.security.config.http;

import java.util.Collection;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

Expand All @@ -26,6 +28,7 @@
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.Transient;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
Expand Down Expand Up @@ -82,7 +85,7 @@ public boolean supports(Class<?> authentication) {
static class SomeTransientAuthentication extends AbstractAuthenticationToken {

SomeTransientAuthentication() {
super(null);
super((Collection<? extends GrantedAuthority>) null);
}

@Override
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@

package org.springframework.security.authentication;

import java.io.Serial;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.function.Consumer;

import org.jspecify.annotations.Nullable;

Expand All @@ -41,6 +44,9 @@
*/
public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {

@Serial
private static final long serialVersionUID = -3194696462184782834L;

private final Collection<GrantedAuthority> authorities;

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

protected AbstractAuthenticationToken(AbstractAuthenticationBuilder<?, ?, ?> builder) {
this(builder.authorities);
this.authenticated = builder.authenticated;
this.details = builder.details;
}

@Override
public Collection<GrantedAuthority> getAuthorities() {
return this.authorities;
Expand Down Expand Up @@ -185,4 +197,40 @@ public String toString() {
return sb.toString();
}

protected abstract static class AbstractAuthenticationBuilder<P, C, B extends AbstractAuthenticationBuilder<P, C, B>>
implements Authentication.Builder<P, C, B> {

protected boolean authenticated;

protected @Nullable Object details;

protected final Collection<GrantedAuthority> authorities;

protected AbstractAuthenticationBuilder(AbstractAuthenticationToken token) {
this.authorities = new LinkedHashSet<>(token.getAuthorities());
this.authenticated = token.isAuthenticated();
this.details = token.getDetails();
}

@Override
public B authenticated(boolean authenticated) {
this.authenticated = authenticated;
return (B) this;
}

@Override
public B details(@Nullable Object details) {
this.details = details;
return (B) this;
}

@Override
public B authorities(Consumer<Collection<GrantedAuthority>> authorities) {
authorities.accept(this.authorities);
this.authenticated = true;
return (B) this;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ public Mono<Authentication> authenticate(Authentication authentication) {
Function<ReactiveAuthenticationManager, Mono<Authentication>> logging = (m) -> m.authenticate(authentication)
.doOnError(AuthenticationException.class, (ex) -> ex.setAuthenticationRequest(authentication))
.doOnError(this.logger::debug);

return ((this.continueOnError) ? result.concatMapDelayError(logging) : result.concatMap(logging)).next();
}

Expand Down
Loading

AltStyle によって変換されたページ (->オリジナル) /