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

Add Multi-factor Authentication Support #17775

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

Draft
jzheaux wants to merge 10 commits into spring-projects:main
base: main
Choose a base branch
Loading
from jzheaux:mfa
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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<? extends GrantedAuthority>) 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");
}

}
Loading
Loading

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