mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-10-24 19:28:45 +00:00
Merge branch 'authentication-factors'
Closes gh-17933
This commit is contained in:
commit
25e413127c
@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package org.springframework.security.cas.authentication;
|
package org.springframework.security.cas.authentication;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.apereo.cas.client.validation.Assertion;
|
import org.apereo.cas.client.validation.Assertion;
|
||||||
@ -35,7 +38,9 @@ import org.springframework.security.authentication.BadCredentialsException;
|
|||||||
import org.springframework.security.cas.ServiceProperties;
|
import org.springframework.security.cas.ServiceProperties;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
|
||||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||||
@ -64,6 +69,8 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
|
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "FACTOR_CAS";
|
||||||
|
|
||||||
@SuppressWarnings("NullAway.Init")
|
@SuppressWarnings("NullAway.Init")
|
||||||
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
|
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
|
||||||
|
|
||||||
@ -141,8 +148,10 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
|||||||
Assertion assertion = this.ticketValidator.validate(credentials.toString(), getServiceUrl(authentication));
|
Assertion assertion = this.ticketValidator.validate(credentials.toString(), getServiceUrl(authentication));
|
||||||
UserDetails userDetails = loadUserByAssertion(assertion);
|
UserDetails userDetails = loadUserByAssertion(assertion);
|
||||||
this.userDetailsChecker.check(userDetails);
|
this.userDetailsChecker.check(userDetails);
|
||||||
return new CasAuthenticationToken(this.key, userDetails, credentials,
|
Collection<GrantedAuthority> authorities = new ArrayList<>(
|
||||||
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
|
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()));
|
||||||
|
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
|
||||||
|
return new CasAuthenticationToken(this.key, userDetails, credentials, authorities, userDetails, assertion);
|
||||||
}
|
}
|
||||||
catch (TicketValidationException ex) {
|
catch (TicketValidationException ex) {
|
||||||
throw new BadCredentialsException(ex.getMessage(), ex);
|
throw new BadCredentialsException(ex.getMessage(), ex);
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.cas.ServiceProperties;
|
import org.springframework.security.cas.ServiceProperties;
|
||||||
@ -346,6 +347,22 @@ public class CasAuthenticationProviderTests {
|
|||||||
assertThat(checkCount.get()).isEqualTo(1);
|
assertThat(checkCount.get()).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticateWhenSuccessfulThenIssuesFactor() throws Exception {
|
||||||
|
CasAuthenticationProvider cap = new CasAuthenticationProvider();
|
||||||
|
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
|
||||||
|
cap.setKey("qwerty");
|
||||||
|
StatelessTicketCache cache = new MockStatelessTicketCache();
|
||||||
|
cap.setStatelessTicketCache(cache);
|
||||||
|
cap.setServiceProperties(makeServiceProperties());
|
||||||
|
cap.setTicketValidator(new MockTicketValidator(true));
|
||||||
|
cap.afterPropertiesSet();
|
||||||
|
CasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateful("ST-123");
|
||||||
|
token.setDetails("details");
|
||||||
|
Authentication result = cap.authenticate(token);
|
||||||
|
SecurityAssertions.assertThat(result).hasAuthority("FACTOR_CAS");
|
||||||
|
}
|
||||||
|
|
||||||
private class MockAuthoritiesPopulator implements AuthenticationUserDetailsService {
|
private class MockAuthoritiesPopulator implements AuthenticationUserDetailsService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import org.springframework.security.config.Customizer;
|
|||||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
@ -37,6 +38,7 @@ import org.springframework.security.web.authentication.preauth.x509.SubjectDnX50
|
|||||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
import org.springframework.security.web.authentication.preauth.x509.X509PrincipalExtractor;
|
||||||
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
|
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
|
||||||
|
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds X509 based pre authentication to an application. Since validating the certificate
|
* Adds X509 based pre authentication to an application. Since validating the certificate
|
||||||
@ -177,8 +179,13 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
|
|||||||
public void init(H http) {
|
public void init(H http) {
|
||||||
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
|
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
|
||||||
authenticationProvider.setPreAuthenticatedUserDetailsService(getAuthenticationUserDetailsService(http));
|
authenticationProvider.setPreAuthenticatedUserDetailsService(getAuthenticationUserDetailsService(http));
|
||||||
|
authenticationProvider.setGrantedAuthoritySupplier(() -> AuthorityUtils.createAuthorityList("FACTOR_X509"));
|
||||||
http.authenticationProvider(authenticationProvider)
|
http.authenticationProvider(authenticationProvider)
|
||||||
.setSharedObject(AuthenticationEntryPoint.class, new Http403ForbiddenEntryPoint());
|
.setSharedObject(AuthenticationEntryPoint.class, new Http403ForbiddenEntryPoint());
|
||||||
|
ExceptionHandlingConfigurer<H> exceptions = http.getConfigurer(ExceptionHandlingConfigurer.class);
|
||||||
|
if (exceptions != null) {
|
||||||
|
exceptions.defaultAuthenticationEntryPointFor(new Http403ForbiddenEntryPoint(), AnyRequestMatcher.INSTANCE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import org.springframework.http.MediaType;
|
|||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.mock.web.MockHttpSession;
|
import org.springframework.mock.web.MockHttpSession;
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
@ -322,8 +323,10 @@ public class OAuth2LoginBeanDefinitionParserTests {
|
|||||||
verify(this.authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture());
|
verify(this.authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), authenticationCaptor.capture());
|
||||||
Authentication authentication = authenticationCaptor.getValue();
|
Authentication authentication = authenticationCaptor.getValue();
|
||||||
assertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class);
|
assertThat(authentication.getPrincipal()).isInstanceOf(OAuth2User.class);
|
||||||
assertThat(authentication.getAuthorities()).hasSize(1);
|
SecurityAssertions.assertThat(authentication)
|
||||||
assertThat(authentication.getAuthorities()).first()
|
.roles()
|
||||||
|
.hasSize(1)
|
||||||
|
.first()
|
||||||
.isInstanceOf(SimpleGrantedAuthority.class)
|
.isInstanceOf(SimpleGrantedAuthority.class)
|
||||||
.hasToString("ROLE_OAUTH2_USER");
|
.hasToString("ROLE_OAUTH2_USER");
|
||||||
// re-setup for OIDC test
|
// re-setup for OIDC test
|
||||||
|
|||||||
@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package org.springframework.security.authentication.dao;
|
package org.springframework.security.authentication.dao;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
@ -33,7 +36,9 @@ import org.springframework.security.authentication.LockedException;
|
|||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
|
||||||
import org.springframework.security.core.userdetails.UserCache;
|
import org.springframework.security.core.userdetails.UserCache;
|
||||||
@ -94,6 +99,8 @@ public abstract class AbstractUserDetailsAuthenticationProvider
|
|||||||
|
|
||||||
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
|
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "FACTOR_PASSWORD";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows subclasses to perform any additional checks of a returned (or cached)
|
* Allows subclasses to perform any additional checks of a returned (or cached)
|
||||||
* <code>UserDetails</code> for a given authentication request. Generally a subclass
|
* <code>UserDetails</code> for a given authentication request. Generally a subclass
|
||||||
@ -197,8 +204,11 @@ public abstract class AbstractUserDetailsAuthenticationProvider
|
|||||||
// so subsequent attempts are successful even with encoded passwords.
|
// so subsequent attempts are successful even with encoded passwords.
|
||||||
// Also ensure we return the original getDetails(), so that future
|
// Also ensure we return the original getDetails(), so that future
|
||||||
// authentication events after cache expiry contain the details
|
// authentication events after cache expiry contain the details
|
||||||
|
Collection<GrantedAuthority> authorities = new LinkedHashSet<>(
|
||||||
|
this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
|
||||||
|
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
|
||||||
UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(principal,
|
UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(principal,
|
||||||
authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
|
authentication.getCredentials(), authorities);
|
||||||
result.setDetails(authentication.getDetails());
|
result.setDetails(authentication.getDetails());
|
||||||
this.logger.debug("Authenticated user");
|
this.logger.debug("Authenticated user");
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -45,6 +45,7 @@ import org.springframework.security.authentication.jaas.event.JaasAuthentication
|
|||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.session.SessionDestroyedEvent;
|
import org.springframework.security.core.session.SessionDestroyedEvent;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -120,6 +121,8 @@ import org.springframework.util.ObjectUtils;
|
|||||||
public abstract class AbstractJaasAuthenticationProvider implements AuthenticationProvider,
|
public abstract class AbstractJaasAuthenticationProvider implements AuthenticationProvider,
|
||||||
ApplicationEventPublisherAware, InitializingBean, ApplicationListener<SessionDestroyedEvent> {
|
ApplicationEventPublisherAware, InitializingBean, ApplicationListener<SessionDestroyedEvent> {
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "FACTOR_PASSWORD";
|
||||||
|
|
||||||
private ApplicationEventPublisher applicationEventPublisher = (event) -> {
|
private ApplicationEventPublisher applicationEventPublisher = (event) -> {
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -210,6 +213,7 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
|
||||||
return authorities;
|
return authorities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,10 +16,15 @@
|
|||||||
|
|
||||||
package org.springframework.security.authentication.ott;
|
package org.springframework.security.authentication.ott;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
@ -35,6 +40,8 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public final class OneTimeTokenAuthenticationProvider implements AuthenticationProvider {
|
public final class OneTimeTokenAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "FACTOR_OTT";
|
||||||
|
|
||||||
private final OneTimeTokenService oneTimeTokenService;
|
private final OneTimeTokenService oneTimeTokenService;
|
||||||
|
|
||||||
private final UserDetailsService userDetailsService;
|
private final UserDetailsService userDetailsService;
|
||||||
@ -56,7 +63,9 @@ public final class OneTimeTokenAuthenticationProvider implements AuthenticationP
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
UserDetails user = this.userDetailsService.loadUserByUsername(consumed.getUsername());
|
UserDetails user = this.userDetailsService.loadUserByUsername(consumed.getUsername());
|
||||||
OneTimeTokenAuthentication authenticated = new OneTimeTokenAuthentication(user, user.getAuthorities());
|
Collection<GrantedAuthority> authorities = new HashSet<>(user.getAuthorities());
|
||||||
|
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
|
||||||
|
OneTimeTokenAuthentication authenticated = new OneTimeTokenAuthentication(user, authorities);
|
||||||
authenticated.setDetails(otpAuthenticationToken.getDetails());
|
authenticated.setDetails(otpAuthenticationToken.getDetails());
|
||||||
return authenticated;
|
return authenticated;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,6 +75,10 @@ public final class SecurityAssertions {
|
|||||||
return authorities().has(new Condition<>(test, "contains %s", Arrays.toString(authorities)));
|
return authorities().has(new Condition<>(test, "contains %s", Arrays.toString(authorities)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CollectionAssert<GrantedAuthority> roles() {
|
||||||
|
return authorities().filteredOn((authority) -> authority.getAuthority().startsWith("ROLE_"));
|
||||||
|
}
|
||||||
|
|
||||||
public CollectionAssert<GrantedAuthority> authorities() {
|
public CollectionAssert<GrantedAuthority> authorities() {
|
||||||
return new CollectionAssert<>(this.authentication.getAuthorities());
|
return new CollectionAssert<>(this.authentication.getAuthorities());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import org.springframework.security.authentication.CredentialsExpiredException;
|
|||||||
import org.springframework.security.authentication.DisabledException;
|
import org.springframework.security.authentication.DisabledException;
|
||||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||||
import org.springframework.security.authentication.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.authentication.password.CompromisedPasswordChecker;
|
import org.springframework.security.authentication.password.CompromisedPasswordChecker;
|
||||||
@ -504,6 +505,15 @@ public class DaoAuthenticationProviderTests {
|
|||||||
assertThat(authentication).isNotNull();
|
assertThat(authentication).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void authenticateWhenSuccessThenIssuesFactor() {
|
||||||
|
UserDetails user = PasswordEncodedUser.user();
|
||||||
|
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(withUsers(user));
|
||||||
|
Authentication request = new UsernamePasswordAuthenticationToken("user", "password");
|
||||||
|
Authentication result = provider.authenticate(request);
|
||||||
|
SecurityAssertions.assertThat(result).hasAuthority("FACTOR_PASSWORD");
|
||||||
|
}
|
||||||
|
|
||||||
private UserDetailsService withUsers(UserDetails... users) {
|
private UserDetailsService withUsers(UserDetails... users) {
|
||||||
return new InMemoryUserDetailsManager(users);
|
return new InMemoryUserDetailsManager(users);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import org.springframework.context.ApplicationContext;
|
|||||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
import org.springframework.core.io.FileSystemResource;
|
import org.springframework.core.io.FileSystemResource;
|
||||||
import org.springframework.security.authentication.LockedException;
|
import org.springframework.security.authentication.LockedException;
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
@ -224,7 +225,9 @@ public class JaasAuthenticationProviderTests {
|
|||||||
"password");
|
"password");
|
||||||
assertThat(this.jaasProvider.supports(UsernamePasswordAuthenticationToken.class)).isTrue();
|
assertThat(this.jaasProvider.supports(UsernamePasswordAuthenticationToken.class)).isTrue();
|
||||||
Authentication auth = this.jaasProvider.authenticate(token);
|
Authentication auth = this.jaasProvider.authenticate(token);
|
||||||
assertThat(auth.getAuthorities()).withFailMessage("Only ROLE_TEST1 and ROLE_TEST2 should have been returned")
|
SecurityAssertions.assertThat(auth)
|
||||||
|
.roles()
|
||||||
|
.withFailMessage("Only ROLE_TEST1 and ROLE_TEST2 should have been returned")
|
||||||
.hasSize(2);
|
.hasSize(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,6 +237,13 @@ public class JaasAuthenticationProviderTests {
|
|||||||
.authenticate(new TestingAuthenticationToken("foo", "bar", AuthorityUtils.NO_AUTHORITIES))).isNull();
|
.authenticate(new TestingAuthenticationToken("foo", "bar", AuthorityUtils.NO_AUTHORITIES))).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticateWhenSuccessThenIssuesFactor() {
|
||||||
|
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("user", "password");
|
||||||
|
Authentication result = this.jaasProvider.authenticate(token);
|
||||||
|
SecurityAssertions.assertThat(result).hasAuthority("FACTOR_PASSWORD");
|
||||||
|
}
|
||||||
|
|
||||||
private static class MockLoginContext extends LoginContext {
|
private static class MockLoginContext extends LoginContext {
|
||||||
|
|
||||||
boolean loggedOut = false;
|
boolean loggedOut = false;
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import org.mockito.Mock;
|
|||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
@ -98,6 +99,18 @@ public class OneTimeTokenAuthenticationProviderTests {
|
|||||||
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(token));
|
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> this.provider.authenticate(token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void authenticateWhenSuccessThenIssuesFactor() {
|
||||||
|
given(this.oneTimeTokenService.consume(any()))
|
||||||
|
.willReturn(new DefaultOneTimeToken(TOKEN, USERNAME, Instant.now().plusSeconds(120)));
|
||||||
|
given(this.userDetailsService.loadUserByUsername(anyString()))
|
||||||
|
.willReturn(new User(USERNAME, PASSWORD, List.of()));
|
||||||
|
OneTimeTokenAuthenticationToken token = new OneTimeTokenAuthenticationToken(TOKEN);
|
||||||
|
|
||||||
|
Authentication authentication = this.provider.authenticate(token);
|
||||||
|
SecurityAssertions.assertThat(authentication).hasAuthority("FACTOR_OTT");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void constructorWhenOneTimeTokenServiceIsNullThenThrowIllegalArgumentException() {
|
void constructorWhenOneTimeTokenServiceIsNullThenThrowIllegalArgumentException() {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
|
|||||||
@ -79,7 +79,7 @@ The `CasProxyDecider` indicates whether the proxy list in the `TicketResponse` i
|
|||||||
Several implementations are provided with Spring Security: `RejectProxyTickets`, `AcceptAnyCasProxy` and `NamedCasProxyDecider`.
|
Several implementations are provided with Spring Security: `RejectProxyTickets`, `AcceptAnyCasProxy` and `NamedCasProxyDecider`.
|
||||||
These names are largely self-explanatory, except `NamedCasProxyDecider` which allows a `List` of trusted proxies to be provided.
|
These names are largely self-explanatory, except `NamedCasProxyDecider` which allows a `List` of trusted proxies to be provided.
|
||||||
* `CasAuthenticationProvider` will next request a `AuthenticationUserDetailsService` to load the `GrantedAuthority` objects that apply to the user contained in the `Assertion`.
|
* `CasAuthenticationProvider` will next request a `AuthenticationUserDetailsService` to load the `GrantedAuthority` objects that apply to the user contained in the `Assertion`.
|
||||||
* If there were no problems, `CasAuthenticationProvider` constructs a `CasAuthenticationToken` including the details contained in the `TicketResponse` and the ``GrantedAuthority``s.
|
* If there were no problems, `CasAuthenticationProvider` constructs a `CasAuthenticationToken` including the details contained in the `TicketResponse` and a set of ``GrantedAuthority``s that contains at least `FACTOR_BEARER`.
|
||||||
* Control then returns to `CasAuthenticationFilter`, which places the created `CasAuthenticationToken` in the security context.
|
* Control then returns to `CasAuthenticationFilter`, which places the created `CasAuthenticationToken` in the security context.
|
||||||
* The user's browser is redirected to the original page that caused the `AuthenticationException` (or a custom destination depending on the configuration).
|
* The user's browser is redirected to the original page that caused the `AuthenticationException` (or a custom destination depending on the configuration).
|
||||||
|
|
||||||
|
|||||||
@ -43,8 +43,12 @@ The `AbstractJaasAuthenticationProvider` obtains the JAAS principals by first su
|
|||||||
A call to `LoginContext.getSubject().getPrincipals()` is made, with each resulting principal passed to each `AuthorityGranter` defined against the `AbstractJaasAuthenticationProvider.setAuthorityGranters(List)` property.
|
A call to `LoginContext.getSubject().getPrincipals()` is made, with each resulting principal passed to each `AuthorityGranter` defined against the `AbstractJaasAuthenticationProvider.setAuthorityGranters(List)` property.
|
||||||
|
|
||||||
Spring Security does not include any production `AuthorityGranter` instances, given that every JAAS principal has an implementation-specific meaning.
|
Spring Security does not include any production `AuthorityGranter` instances, given that every JAAS principal has an implementation-specific meaning.
|
||||||
However, there is a `TestAuthorityGranter` in the unit tests that demonstrates a simple `AuthorityGranter` implementation.
|
However, Spring Security does issue the `FACTOR_PASSWORD` authority by default when authentication suceeds.
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
====
|
||||||
|
There is a `TestAuthorityGranter` in the unit tests that demonstrates a simple `AuthorityGranter` implementation.
|
||||||
|
====
|
||||||
|
|
||||||
[[jaas-defaultjaasauthenticationprovider]]
|
[[jaas-defaultjaasauthenticationprovider]]
|
||||||
== DefaultJaasAuthenticationProvider
|
== DefaultJaasAuthenticationProvider
|
||||||
|
|||||||
@ -19,5 +19,5 @@ image:{icondir}/number_3.png[] `DaoAuthenticationProvider` looks up the `UserDet
|
|||||||
|
|
||||||
image:{icondir}/number_4.png[] `DaoAuthenticationProvider` uses the xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to validate the password on the `UserDetails` returned in the previous step.
|
image:{icondir}/number_4.png[] `DaoAuthenticationProvider` uses the xref:servlet/authentication/passwords/password-encoder.adoc#servlet-authentication-password-storage[`PasswordEncoder`] to validate the password on the `UserDetails` returned in the previous step.
|
||||||
|
|
||||||
image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `UsernamePasswordAuthenticationToken` and has a principal that is the `UserDetails` returned by the configured `UserDetailsService`.
|
image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `UsernamePasswordAuthenticationToken` and has a principal that is the `UserDetails` returned by the configured `UserDetailsService` and a set of authorities containing at least `FACTOR_PASSWORD`.
|
||||||
Ultimately, the returned `UsernamePasswordAuthenticationToken` is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`.
|
Ultimately, the returned `UsernamePasswordAuthenticationToken` is set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`.
|
||||||
|
|||||||
@ -12,7 +12,7 @@ For example, if you use Tomcat, you should read the https://tomcat.apache.org/to
|
|||||||
You should get this working before trying it out with Spring Security.
|
You should get this working before trying it out with Spring Security.
|
||||||
|
|
||||||
The Spring Security X.509 module extracts the certificate by using a filter.
|
The Spring Security X.509 module extracts the certificate by using a filter.
|
||||||
It maps the certificate to an application user and loads that user's set of granted authorities for use with the standard Spring Security infrastructure.
|
It maps the certificate to an application user and loads that user's set of granted authorities for use with the standard Spring Security infrastructure, specifically including at least the `FACTOR_X509` authority when <<servlet-x509-config, using the `HttpSecurity` DSL>>.
|
||||||
|
|
||||||
[[servlet-x509-config]]
|
[[servlet-x509-config]]
|
||||||
== Adding X.509 Authentication to Your Web Application
|
== Adding X.509 Authentication to Your Web Application
|
||||||
|
|||||||
@ -613,6 +613,11 @@ class OAuth2LoginSecurityConfig {
|
|||||||
----
|
----
|
||||||
======
|
======
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
====
|
||||||
|
Once authentication completes, it also contains the `FACTOR_AUTHORIZATION_CODE` granted authority.
|
||||||
|
====
|
||||||
|
|
||||||
[[oauth2login-advanced-map-authorities-oauth2userservice]]
|
[[oauth2login-advanced-map-authorities-oauth2userservice]]
|
||||||
==== Delegation-based Strategy with OAuth2UserService
|
==== Delegation-based Strategy with OAuth2UserService
|
||||||
|
|
||||||
|
|||||||
@ -105,7 +105,7 @@ image:{icondir}/number_3.png[] `JwtAuthenticationProvider` decodes, verifies, an
|
|||||||
[[oauth2resourceserver-jwt-architecture-jwtauthenticationconverter]]
|
[[oauth2resourceserver-jwt-architecture-jwtauthenticationconverter]]
|
||||||
image:{icondir}/number_4.png[] `JwtAuthenticationProvider` then uses the <<oauth2resourceserver-jwt-authorization-extraction,`JwtAuthenticationConverter`>> to convert the `Jwt` into a `Collection` of granted authorities.
|
image:{icondir}/number_4.png[] `JwtAuthenticationProvider` then uses the <<oauth2resourceserver-jwt-authorization-extraction,`JwtAuthenticationConverter`>> to convert the `Jwt` into a `Collection` of granted authorities.
|
||||||
|
|
||||||
image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `JwtAuthenticationToken` and has a principal that is the `Jwt` returned by the configured `JwtDecoder`.
|
image:{icondir}/number_5.png[] When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `JwtAuthenticationToken` and has a principal that is the `Jwt` returned by the configured `JwtDecoder` and a set of authorities that contains at least `FACTOR_BEARER`.
|
||||||
Ultimately, the returned `JwtAuthenticationToken` will be set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`.
|
Ultimately, the returned `JwtAuthenticationToken` will be set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`.
|
||||||
|
|
||||||
[[oauth2resourceserver-jwt-jwkseturi]]
|
[[oauth2resourceserver-jwt-jwkseturi]]
|
||||||
|
|||||||
@ -96,7 +96,7 @@ image:{icondir}/number_2.png[] The `ProviderManager` is configured to use an xre
|
|||||||
|
|
||||||
[[oauth2resourceserver-opaque-architecture-introspector]]
|
[[oauth2resourceserver-opaque-architecture-introspector]]
|
||||||
image:{icondir}/number_3.png[] `OpaqueTokenAuthenticationProvider` introspects the opaque token and adds granted authorities using an <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>>.
|
image:{icondir}/number_3.png[] `OpaqueTokenAuthenticationProvider` introspects the opaque token and adds granted authorities using an <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>>.
|
||||||
When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `BearerTokenAuthentication` and has a principal that is the `OAuth2AuthenticatedPrincipal` returned by the configured <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>>.
|
When authentication is successful, the xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[`Authentication`] that is returned is of type `BearerTokenAuthentication` and has a principal that is the `OAuth2AuthenticatedPrincipal` returned by the configured <<oauth2resourceserver-opaque-introspector,`OpaqueTokenIntrospector`>> and a set of authorities that contains at least `FACTOR_BEARER`.
|
||||||
Ultimately, the returned `BearerTokenAuthentication` will be set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`.
|
Ultimately, the returned `BearerTokenAuthentication` will be set on the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[`SecurityContextHolder`] by the authentication `Filter`.
|
||||||
|
|
||||||
[[oauth2resourceserver-opaque-attributes]]
|
[[oauth2resourceserver-opaque-attributes]]
|
||||||
|
|||||||
@ -222,7 +222,7 @@ image:{icondir}/number_8.png[] Next, the provider validates each assertion's `Ex
|
|||||||
If any validations fail, authentication fails.
|
If any validations fail, authentication fails.
|
||||||
|
|
||||||
image:{icondir}/number_9.png[] Following that, the provider takes the first assertion's `AttributeStatement` and maps it to a `Map<String, List<Object>>`.
|
image:{icondir}/number_9.png[] Following that, the provider takes the first assertion's `AttributeStatement` and maps it to a `Map<String, List<Object>>`.
|
||||||
It also grants the `ROLE_USER` granted authority.
|
It also grants the `FACTOR_SAML_RESPONSE` and `ROLE_USER` granted authorities.
|
||||||
|
|
||||||
image:{icondir}/number_10.png[] And finally, it takes the `NameID` from the first assertion, the `Map` of attributes, and the `GrantedAuthority` and constructs a `Saml2AuthenticatedPrincipal`.
|
image:{icondir}/number_10.png[] And finally, it takes the `NameID` from the first assertion, the `Map` of attributes, and the `GrantedAuthority` and constructs a `Saml2AuthenticatedPrincipal`.
|
||||||
Then, it places that principal and the authorities into a `Saml2Authentication`.
|
Then, it places that principal and the authorities into a `Saml2Authentication`.
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
package org.springframework.security.ldap.authentication;
|
package org.springframework.security.ldap.authentication;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
@ -33,6 +34,7 @@ import org.springframework.security.core.Authentication;
|
|||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
@ -50,6 +52,8 @@ import org.springframework.util.StringUtils;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractLdapAuthenticationProvider implements AuthenticationProvider, MessageSourceAware {
|
public abstract class AbstractLdapAuthenticationProvider implements AuthenticationProvider, MessageSourceAware {
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "FACTOR_PASSWORD";
|
||||||
|
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||||
@ -100,8 +104,11 @@ public abstract class AbstractLdapAuthenticationProvider implements Authenticati
|
|||||||
UserDetails user) {
|
UserDetails user) {
|
||||||
Object password = this.useAuthenticationRequestCredentials ? authentication.getCredentials()
|
Object password = this.useAuthenticationRequestCredentials ? authentication.getCredentials()
|
||||||
: user.getPassword();
|
: user.getPassword();
|
||||||
UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(user, password,
|
Collection<GrantedAuthority> authorities = new LinkedHashSet<>(
|
||||||
this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
|
this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
|
||||||
|
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
|
||||||
|
UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken.authenticated(user, password,
|
||||||
|
authorities);
|
||||||
result.setDetails(authentication.getDetails());
|
result.setDetails(authentication.getDetails());
|
||||||
this.logger.debug("Authenticated user");
|
this.logger.debug("Authenticated user");
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import org.springframework.ldap.core.DirContextOperations;
|
|||||||
import org.springframework.ldap.support.LdapNameBuilder;
|
import org.springframework.ldap.support.LdapNameBuilder;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
@ -156,6 +157,16 @@ public class LdapAuthenticationProviderTests {
|
|||||||
.isSameAs(expectedCause);
|
.isSameAs(expectedCause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void authenticateWhenSuccessThenIssuesFactor() {
|
||||||
|
MockAuthenticator authenticator = new MockAuthenticator();
|
||||||
|
MockAuthoritiesPopulator populator = new MockAuthoritiesPopulator();
|
||||||
|
LdapAuthenticationProvider ldapProvider = new LdapAuthenticationProvider(authenticator, populator);
|
||||||
|
UsernamePasswordAuthenticationToken request = new UsernamePasswordAuthenticationToken("ben", "benspassword");
|
||||||
|
Authentication result = ldapProvider.authenticate(request);
|
||||||
|
SecurityAssertions.assertThat(result).hasAuthority("FACTOR_PASSWORD");
|
||||||
|
}
|
||||||
|
|
||||||
class MockAuthenticator implements LdapAuthenticator {
|
class MockAuthenticator implements LdapAuthenticator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -17,12 +17,15 @@
|
|||||||
package org.springframework.security.oauth2.client.authentication;
|
package org.springframework.security.oauth2.client.authentication;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||||
@ -66,6 +69,8 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider {
|
public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "FACTOR_AUTHORIZATION_CODE";
|
||||||
|
|
||||||
private final OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider;
|
private final OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider;
|
||||||
|
|
||||||
private final OAuth2UserService<OAuth2UserRequest, OAuth2User> userService;
|
private final OAuth2UserService<OAuth2UserRequest, OAuth2User> userService;
|
||||||
@ -118,8 +123,10 @@ public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider
|
|||||||
Map<String, Object> additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters();
|
Map<String, Object> additionalParameters = authorizationCodeAuthenticationToken.getAdditionalParameters();
|
||||||
OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(
|
OAuth2User oauth2User = this.userService.loadUser(new OAuth2UserRequest(
|
||||||
loginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters));
|
loginAuthenticationToken.getClientRegistration(), accessToken, additionalParameters));
|
||||||
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper
|
Collection<GrantedAuthority> authorities = new HashSet<>(oauth2User.getAuthorities());
|
||||||
.mapAuthorities(oauth2User.getAuthorities());
|
Collection<GrantedAuthority> mappedAuthorities = new LinkedHashSet<>(
|
||||||
|
this.authoritiesMapper.mapAuthorities(authorities));
|
||||||
|
mappedAuthorities.add(new SimpleGrantedAuthority(AUTHORITY));
|
||||||
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
|
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(
|
||||||
loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(),
|
loginAuthenticationToken.getClientRegistration(), loginAuthenticationToken.getAuthorizationExchange(),
|
||||||
oauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken());
|
oauth2User, mappedAuthorities, accessToken, authorizationCodeAuthenticationToken.getRefreshToken());
|
||||||
|
|||||||
@ -29,6 +29,8 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||||
@ -48,6 +50,7 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResp
|
|||||||
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;
|
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationRequests;
|
||||||
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;
|
import org.springframework.security.oauth2.core.endpoint.TestOAuth2AuthorizationResponses;
|
||||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||||
|
import org.springframework.security.oauth2.core.user.TestOAuth2Users;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
@ -56,6 +59,7 @@ import static org.mockito.ArgumentMatchers.any;
|
|||||||
import static org.mockito.ArgumentMatchers.anyCollection;
|
import static org.mockito.ArgumentMatchers.anyCollection;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link OAuth2LoginAuthenticationProvider}.
|
* Tests for {@link OAuth2LoginAuthenticationProvider}.
|
||||||
@ -187,7 +191,8 @@ public class OAuth2LoginAuthenticationProviderTests {
|
|||||||
this.authenticationProvider.setAuthoritiesMapper(authoritiesMapper);
|
this.authenticationProvider.setAuthoritiesMapper(authoritiesMapper);
|
||||||
OAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) this.authenticationProvider
|
OAuth2LoginAuthenticationToken authentication = (OAuth2LoginAuthenticationToken) this.authenticationProvider
|
||||||
.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange));
|
.authenticate(new OAuth2LoginAuthenticationToken(this.clientRegistration, this.authorizationExchange));
|
||||||
assertThat(authentication.getAuthorities()).isEqualTo(mappedAuthorities);
|
verify(authoritiesMapper).mapAuthorities(any());
|
||||||
|
SecurityAssertions.assertThat(authentication).authorities().containsAll(mappedAuthorities);
|
||||||
}
|
}
|
||||||
|
|
||||||
// gh-5368
|
// gh-5368
|
||||||
@ -206,6 +211,17 @@ public class OAuth2LoginAuthenticationProviderTests {
|
|||||||
.containsAllEntriesOf(accessTokenResponse.getAdditionalParameters());
|
.containsAllEntriesOf(accessTokenResponse.getAdditionalParameters());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticateWhenLoginSuccessThenIssuesFactor() {
|
||||||
|
OAuth2AccessTokenResponse accessTokenResponse = accessTokenSuccessResponse();
|
||||||
|
given(this.accessTokenResponseClient.getTokenResponse(any())).willReturn(accessTokenResponse);
|
||||||
|
given(this.userService.loadUser(any())).willReturn(TestOAuth2Users.create());
|
||||||
|
Authentication request = new OAuth2LoginAuthenticationToken(this.clientRegistration,
|
||||||
|
this.authorizationExchange);
|
||||||
|
Authentication result = this.authenticationProvider.authenticate(request);
|
||||||
|
SecurityAssertions.assertThat(result).hasAuthority("FACTOR_AUTHORIZATION_CODE");
|
||||||
|
}
|
||||||
|
|
||||||
private OAuth2AccessTokenResponse accessTokenSuccessResponse() {
|
private OAuth2AccessTokenResponse accessTokenSuccessResponse() {
|
||||||
Instant expiresAt = Instant.now().plusSeconds(5);
|
Instant expiresAt = Instant.now().plusSeconds(5);
|
||||||
Set<String> scopes = new LinkedHashSet<>(Arrays.asList("scope1", "scope2"));
|
Set<String> scopes = new LinkedHashSet<>(Arrays.asList("scope1", "scope2"));
|
||||||
|
|||||||
@ -17,10 +17,12 @@
|
|||||||
package org.springframework.security.oauth2.server.resource.authentication;
|
package org.springframework.security.oauth2.server.resource.authentication;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
import org.springframework.security.oauth2.jwt.JwtClaimNames;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -34,14 +36,16 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
|
public class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "FACTOR_BEARER";
|
||||||
|
|
||||||
private Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
private Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
|
|
||||||
private String principalClaimName = JwtClaimNames.SUB;
|
private String principalClaimName = JwtClaimNames.SUB;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final AbstractAuthenticationToken convert(Jwt jwt) {
|
public final AbstractAuthenticationToken convert(Jwt jwt) {
|
||||||
Collection<GrantedAuthority> authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt);
|
Collection<GrantedAuthority> authorities = new HashSet<>(this.jwtGrantedAuthoritiesConverter.convert(jwt));
|
||||||
|
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
|
||||||
String principalClaimValue = jwt.getClaimAsString(this.principalClaimName);
|
String principalClaimValue = jwt.getClaimAsString(this.principalClaimName);
|
||||||
return new JwtAuthenticationToken(jwt, authorities, principalClaimValue);
|
return new JwtAuthenticationToken(jwt, authorities, principalClaimValue);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ package org.springframework.security.oauth2.server.resource.authentication;
|
|||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
@ -28,6 +29,7 @@ import org.springframework.security.authentication.AuthenticationServiceExceptio
|
|||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
|
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
|
||||||
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
|
import org.springframework.security.oauth2.core.OAuth2TokenIntrospectionClaimNames;
|
||||||
@ -72,6 +74,8 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public final class OpaqueTokenAuthenticationProvider implements AuthenticationProvider {
|
public final class OpaqueTokenAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "FACTOR_BEARER";
|
||||||
|
|
||||||
private final Log logger = LogFactory.getLog(getClass());
|
private final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
private final OpaqueTokenIntrospector introspector;
|
private final OpaqueTokenIntrospector introspector;
|
||||||
@ -149,8 +153,9 @@ public final class OpaqueTokenAuthenticationProvider implements AuthenticationPr
|
|||||||
Instant exp = authenticatedPrincipal.getAttribute(OAuth2TokenIntrospectionClaimNames.EXP);
|
Instant exp = authenticatedPrincipal.getAttribute(OAuth2TokenIntrospectionClaimNames.EXP);
|
||||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, introspectedToken,
|
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, introspectedToken,
|
||||||
iat, exp);
|
iat, exp);
|
||||||
return new BearerTokenAuthentication(authenticatedPrincipal, accessToken,
|
Collection<GrantedAuthority> authorities = new HashSet<>(authenticatedPrincipal.getAuthorities());
|
||||||
authenticatedPrincipal.getAuthorities());
|
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
|
||||||
|
return new BearerTokenAuthentication(authenticatedPrincipal, accessToken, authorities);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.authentication.SecurityAssertions;
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
@ -110,4 +111,11 @@ public class JwtAuthenticationConverterTests {
|
|||||||
assertThat(authentication.getName()).isEqualTo("100");
|
assertThat(authentication.getName()).isEqualTo("100");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenDefaultsThenIssuesFactor() {
|
||||||
|
Jwt jwt = TestJwts.jwt().build();
|
||||||
|
Authentication result = this.jwtAuthenticationConverter.convert(jwt);
|
||||||
|
SecurityAssertions.assertThat(result).hasAuthority("FACTOR_BEARER");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -146,6 +146,17 @@ public class OpaqueTokenAuthenticationProviderTests {
|
|||||||
verifyNoMoreInteractions(introspector, authenticationConverter);
|
verifyNoMoreInteractions(introspector, authenticationConverter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void authenticateWhenSuccessThenIssuesFactor() {
|
||||||
|
OAuth2AuthenticatedPrincipal principal = TestOAuth2AuthenticatedPrincipals.active();
|
||||||
|
OpaqueTokenIntrospector introspector = mock(OpaqueTokenIntrospector.class);
|
||||||
|
given(introspector.introspect(any())).willReturn(principal);
|
||||||
|
OpaqueTokenAuthenticationProvider provider = new OpaqueTokenAuthenticationProvider(introspector);
|
||||||
|
Authentication request = new BearerTokenAuthenticationToken("token");
|
||||||
|
Authentication result = provider.authenticate(request);
|
||||||
|
SecurityAssertions.assertThat(result).hasAuthority("FACTOR_BEARER");
|
||||||
|
}
|
||||||
|
|
||||||
static Predicate<GrantedAuthority> isScope() {
|
static Predicate<GrantedAuthority> isScope() {
|
||||||
return (a) -> a.getAuthority().startsWith("SCOPE_");
|
return (a) -> a.getAuthority().startsWith("SCOPE_");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@ -59,6 +60,7 @@ import org.springframework.security.core.Authentication;
|
|||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.saml2.core.Saml2Error;
|
import org.springframework.security.saml2.core.Saml2Error;
|
||||||
import org.springframework.security.saml2.core.Saml2ErrorCodes;
|
import org.springframework.security.saml2.core.Saml2ErrorCodes;
|
||||||
import org.springframework.security.saml2.core.Saml2ResponseValidatorResult;
|
import org.springframework.security.saml2.core.Saml2ResponseValidatorResult;
|
||||||
@ -111,6 +113,8 @@ import org.springframework.util.StringUtils;
|
|||||||
*/
|
*/
|
||||||
public final class OpenSaml5AuthenticationProvider implements AuthenticationProvider {
|
public final class OpenSaml5AuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "FACTOR_SAML_RESPONSE";
|
||||||
|
|
||||||
private final BaseOpenSamlAuthenticationProvider delegate;
|
private final BaseOpenSamlAuthenticationProvider delegate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -899,7 +903,9 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
|||||||
.attributes(BaseOpenSamlAuthenticationProvider.getAssertionAttributes(assertion))
|
.attributes(BaseOpenSamlAuthenticationProvider.getAssertionAttributes(assertion))
|
||||||
.build();
|
.build();
|
||||||
Saml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, accessor);
|
Saml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, accessor);
|
||||||
Collection<GrantedAuthority> authorities = this.grantedAuthoritiesConverter.convert(assertion);
|
Collection<GrantedAuthority> authorities = new HashSet<>(
|
||||||
|
this.grantedAuthoritiesConverter.convert(assertion));
|
||||||
|
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
|
||||||
return new Saml2AssertionAuthentication(principal, accessor, authorities, registrationId);
|
return new Saml2AssertionAuthentication(principal, accessor, authorities, registrationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -985,6 +985,14 @@ public class OpenSaml5AuthenticationProviderTests {
|
|||||||
assertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> provider.authenticate(token));
|
assertThatExceptionOfType(Saml2AuthenticationException.class).isThrownBy(() -> provider.authenticate(token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticateWhenSuccessThenIssuesFactor() {
|
||||||
|
Response response = TestOpenSamlObjects.signedResponseWithOneAssertion();
|
||||||
|
Authentication request = token(response, verifying(registration()));
|
||||||
|
Authentication result = this.provider.authenticate(request);
|
||||||
|
SecurityAssertions.assertThat(result).hasAuthority("FACTOR_SAML_RESPONSE");
|
||||||
|
}
|
||||||
|
|
||||||
private <T extends XMLObject> T build(QName qName) {
|
private <T extends XMLObject> T build(QName qName) {
|
||||||
return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);
|
return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,7 @@ public class SecurityMockWithAuthoritiesMvcResultMatchersTests {
|
|||||||
List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>();
|
List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>();
|
||||||
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
|
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
|
||||||
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_SELLER"));
|
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_SELLER"));
|
||||||
|
grantedAuthorities.add(new SimpleGrantedAuthority("FACTOR_PASSWORD"));
|
||||||
this.mockMvc.perform(formLogin()).andExpect(authenticated().withAuthorities(grantedAuthorities));
|
this.mockMvc.perform(formLogin()).andExpect(authenticated().withAuthorities(grantedAuthorities));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.authentication.preauth;
|
package org.springframework.security.web.authentication.preauth;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
@ -28,6 +33,7 @@ import org.springframework.security.authentication.AuthenticationProvider;
|
|||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsChecker;
|
import org.springframework.security.core.userdetails.UserDetailsChecker;
|
||||||
@ -57,6 +63,8 @@ public class PreAuthenticatedAuthenticationProvider implements AuthenticationPro
|
|||||||
|
|
||||||
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
|
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
|
||||||
|
|
||||||
|
private Supplier<Collection<GrantedAuthority>> grantedAuthoritySupplier = List::of;
|
||||||
|
|
||||||
private boolean throwExceptionWhenTokenRejected;
|
private boolean throwExceptionWhenTokenRejected;
|
||||||
|
|
||||||
private int order = -1; // default: same as non-ordered
|
private int order = -1; // default: same as non-ordered
|
||||||
@ -98,8 +106,10 @@ public class PreAuthenticatedAuthenticationProvider implements AuthenticationPro
|
|||||||
UserDetails userDetails = this.preAuthenticatedUserDetailsService
|
UserDetails userDetails = this.preAuthenticatedUserDetailsService
|
||||||
.loadUserDetails((PreAuthenticatedAuthenticationToken) authentication);
|
.loadUserDetails((PreAuthenticatedAuthenticationToken) authentication);
|
||||||
this.userDetailsChecker.check(userDetails);
|
this.userDetailsChecker.check(userDetails);
|
||||||
|
Collection<GrantedAuthority> authorities = new LinkedHashSet<>(userDetails.getAuthorities());
|
||||||
|
authorities.addAll(this.grantedAuthoritySupplier.get());
|
||||||
PreAuthenticatedAuthenticationToken result = new PreAuthenticatedAuthenticationToken(userDetails,
|
PreAuthenticatedAuthenticationToken result = new PreAuthenticatedAuthenticationToken(userDetails,
|
||||||
authentication.getCredentials(), userDetails.getAuthorities());
|
authentication.getCredentials(), authorities);
|
||||||
result.setDetails(authentication.getDetails());
|
result.setDetails(authentication.getDetails());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -142,6 +152,14 @@ public class PreAuthenticatedAuthenticationProvider implements AuthenticationPro
|
|||||||
this.userDetailsChecker = userDetailsChecker;
|
this.userDetailsChecker = userDetailsChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets authorities that this provider should grant once authentication completes
|
||||||
|
* @param grantedAuthoritySupplier the supplier that grants authorities
|
||||||
|
*/
|
||||||
|
public void setGrantedAuthoritySupplier(Supplier<Collection<GrantedAuthority>> grantedAuthoritySupplier) {
|
||||||
|
this.grantedAuthoritySupplier = grantedAuthoritySupplier;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getOrder() {
|
public int getOrder() {
|
||||||
return this.order;
|
return this.order;
|
||||||
|
|||||||
@ -16,12 +16,18 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.authentication.preauth;
|
package org.springframework.security.web.authentication.preauth;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
@ -29,6 +35,9 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author TSARDD
|
* @author TSARDD
|
||||||
@ -89,6 +98,19 @@ public class PreAuthenticatedAuthenticationProviderTests {
|
|||||||
assertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> provider.authenticate(request));
|
assertThatExceptionOfType(UsernameNotFoundException.class).isThrownBy(() -> provider.authenticate(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void authenticateWhenSuccessThenIssuesFactor() {
|
||||||
|
UserDetails ud = PasswordEncodedUser.user();
|
||||||
|
PreAuthenticatedAuthenticationProvider provider = getProvider(ud);
|
||||||
|
Supplier<Collection<GrantedAuthority>> authorities = mock(Supplier.class);
|
||||||
|
given(authorities.get()).willReturn(AuthorityUtils.createAuthorityList("FACTOR"));
|
||||||
|
provider.setGrantedAuthoritySupplier(authorities);
|
||||||
|
Authentication request = new PreAuthenticatedAuthenticationToken(ud.getUsername(), ud.getPassword());
|
||||||
|
Authentication result = provider.authenticate(request);
|
||||||
|
SecurityAssertions.assertThat(result).hasAuthority("FACTOR");
|
||||||
|
verify(authorities).get();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public final void supportsArbitraryObject() throws Exception {
|
public final void supportsArbitraryObject() throws Exception {
|
||||||
PreAuthenticatedAuthenticationProvider provider = getProvider(null);
|
PreAuthenticatedAuthenticationProvider provider = getProvider(null);
|
||||||
|
|||||||
@ -16,10 +16,15 @@
|
|||||||
|
|
||||||
package org.springframework.security.web.webauthn.authentication;
|
package org.springframework.security.web.webauthn.authentication;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.AuthenticationException;
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
|
||||||
@ -39,6 +44,8 @@ import org.springframework.util.Assert;
|
|||||||
*/
|
*/
|
||||||
public class WebAuthnAuthenticationProvider implements AuthenticationProvider {
|
public class WebAuthnAuthenticationProvider implements AuthenticationProvider {
|
||||||
|
|
||||||
|
private static final String AUTHORITY = "FACTOR_WEBAUTHN";
|
||||||
|
|
||||||
private final WebAuthnRelyingPartyOperations relyingPartyOperations;
|
private final WebAuthnRelyingPartyOperations relyingPartyOperations;
|
||||||
|
|
||||||
private final UserDetailsService userDetailsService;
|
private final UserDetailsService userDetailsService;
|
||||||
@ -65,7 +72,9 @@ public class WebAuthnAuthenticationProvider implements AuthenticationProvider {
|
|||||||
.authenticate(webAuthnRequest.getWebAuthnRequest());
|
.authenticate(webAuthnRequest.getWebAuthnRequest());
|
||||||
String username = userEntity.getName();
|
String username = userEntity.getName();
|
||||||
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
|
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
|
||||||
return new WebAuthnAuthentication(userEntity, userDetails.getAuthorities());
|
Collection<GrantedAuthority> authorities = new HashSet<>(userDetails.getAuthorities());
|
||||||
|
authorities.add(new SimpleGrantedAuthority(AUTHORITY));
|
||||||
|
return new WebAuthnAuthentication(userEntity, authorities);
|
||||||
}
|
}
|
||||||
catch (RuntimeException ex) {
|
catch (RuntimeException ex) {
|
||||||
throw new BadCredentialsException(ex.getMessage(), ex);
|
throw new BadCredentialsException(ex.getMessage(), ex);
|
||||||
|
|||||||
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2004-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.web.webauthn.authentication;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.SecurityAssertions;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
|
||||||
|
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
|
||||||
|
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
|
||||||
|
import org.springframework.security.web.webauthn.api.TestAuthenticationAssertionResponses;
|
||||||
|
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions;
|
||||||
|
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities;
|
||||||
|
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentials;
|
||||||
|
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
|
||||||
|
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
class WebAuthnAuthenticationProviderTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void authenticateWhenSuccessThenIssuesFactor() {
|
||||||
|
WebAuthnRelyingPartyOperations operations = mock(WebAuthnRelyingPartyOperations.class);
|
||||||
|
UserDetailsService users = mock(UserDetailsService.class);
|
||||||
|
PublicKeyCredentialRequestOptions options = TestPublicKeyCredentialRequestOptions.create().build();
|
||||||
|
AuthenticatorAssertionResponse response = TestAuthenticationAssertionResponses
|
||||||
|
.createAuthenticatorAssertionResponse()
|
||||||
|
.build();
|
||||||
|
PublicKeyCredential<AuthenticatorAssertionResponse> credentials = TestPublicKeyCredentials
|
||||||
|
.createPublicKeyCredential(response)
|
||||||
|
.build();
|
||||||
|
Authentication request = new WebAuthnAuthenticationRequestToken(
|
||||||
|
new RelyingPartyAuthenticationRequest(options, credentials));
|
||||||
|
WebAuthnAuthenticationProvider provider = new WebAuthnAuthenticationProvider(operations, users);
|
||||||
|
given(users.loadUserByUsername(any())).willReturn(PasswordEncodedUser.user());
|
||||||
|
given(operations.authenticate(any())).willReturn(TestPublicKeyCredentialUserEntities.userEntity().build());
|
||||||
|
Authentication result = provider.authenticate(request);
|
||||||
|
SecurityAssertions.assertThat(result).hasAuthority("FACTOR_WEBAUTHN");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user