Skip to content

Mfa in steps #17702

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

Closed
wants to merge 12 commits into from
Closed
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 @@ -22,6 +22,7 @@
import org.apereo.cas.client.validation.Assertion;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.AuthenticationResult;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
Expand All @@ -33,7 +34,7 @@
* @author Ben Alex
* @author Scott Battaglia
*/
public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable, AuthenticationResult {

private static final long serialVersionUID = 620L;

Expand Down Expand Up @@ -104,6 +105,12 @@ private CasAuthenticationToken(final Integer keyHash, final Object principal, fi
setAuthenticated(true);
}

@Override
public CasAuthenticationToken withGrantedAuthorities(Collection<GrantedAuthority> authorities) {
return new CasAuthenticationToken(this.keyHash, getPrincipal(), getCredentials(), authorities, this.userDetails,
this.assertion);
}

private static Integer extractKeyHash(String key) {
Assert.hasLength(key, "key cannot be null or empty");
return key.hashCode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ public void configure(B http) throws Exception {
if (requestCache != null) {
this.defaultSuccessHandler.setRequestCache(requestCache);
}
this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
this.authFilter.setAuthenticationManager(postProcess(http.getSharedObject(AuthenticationManager.class)));
this.authFilter.setAuthenticationSuccessHandler(this.successHandler);
this.authFilter.setAuthenticationFailureHandler(this.failureHandler);
if (this.authenticationDetailsSource != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
Expand All @@ -28,11 +30,13 @@
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationManagers;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.SingleResultAuthorizationManager;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.config.ObjectPostProcessor;
Expand Down Expand Up @@ -139,6 +143,8 @@ public final class AuthorizationManagerRequestMatcherRegistry
private final RequestMatcherDelegatingAuthorizationManager.Builder managerBuilder = RequestMatcherDelegatingAuthorizationManager
.builder();

private final HasAllAuthoritiesAuthorizationManager<RequestAuthorizationContext> hasAuthority = new HasAllAuthoritiesAuthorizationManager<>();

private List<RequestMatcher> unmappedMatchers;

private int mappingCount;
Expand All @@ -165,6 +171,7 @@ private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
+ ". Try completing it with something like requestUrls().<something>.hasRole('USER')");
Assert.state(this.mappingCount > 0,
"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())");
this.hasAuthority.setRoleHierarchy(AuthorizeHttpRequestsConfigurer.this.roleHierarchy.get());
AuthorizationManager<HttpServletRequest> manager = postProcess(
(AuthorizationManager<HttpServletRequest>) this.managerBuilder.build());
return AuthorizeHttpRequestsConfigurer.this.postProcessor.postProcess(manager);
Expand All @@ -173,7 +180,7 @@ private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
@Override
protected AuthorizedUrl chainRequestMatchers(List<RequestMatcher> requestMatchers) {
this.unmappedMatchers = requestMatchers;
return new AuthorizedUrl(requestMatchers);
return new AuthorizedUrl(this, requestMatchers);
}

/**
Expand All @@ -188,6 +195,10 @@ public AuthorizationManagerRequestMatcherRegistry withObjectPostProcessor(
return this;
}

void hasAuthority(String authority) {
this.hasAuthority.add(authority);
}

}

/**
Expand All @@ -199,6 +210,8 @@ public AuthorizationManagerRequestMatcherRegistry withObjectPostProcessor(
*/
public class AuthorizedUrl {

private final AuthorizationManagerRequestMatcherRegistry registry;

private final List<? extends RequestMatcher> matchers;

private boolean not;
Expand All @@ -207,7 +220,8 @@ public class AuthorizedUrl {
* Creates an instance.
* @param matchers the {@link RequestMatcher} instances to map
*/
AuthorizedUrl(List<? extends RequestMatcher> matchers) {
AuthorizedUrl(AuthorizationManagerRequestMatcherRegistry registry, List<? extends RequestMatcher> matchers) {
this.registry = registry;
this.matchers = matchers;
}

Expand Down Expand Up @@ -289,10 +303,10 @@ public AuthorizationManagerRequestMatcherRegistry hasAnyAuthority(String... auth
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities)));
}

private AuthorityAuthorizationManager<RequestAuthorizationContext> withRoleHierarchy(
private AuthorizationManager<RequestAuthorizationContext> withRoleHierarchy(
AuthorityAuthorizationManager<RequestAuthorizationContext> manager) {
manager.setRoleHierarchy(AuthorizeHttpRequestsConfigurer.this.roleHierarchy.get());
return manager;
return withAuthentication(manager);
}

/**
Expand All @@ -301,7 +315,7 @@ private AuthorityAuthorizationManager<RequestAuthorizationContext> withRoleHiera
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry authenticated() {
return access(AuthenticatedAuthorizationManager.authenticated());
return access(withAuthentication(AuthenticatedAuthorizationManager.authenticated()));
}

/**
Expand All @@ -313,7 +327,7 @@ public AuthorizationManagerRequestMatcherRegistry authenticated() {
* @see RememberMeConfigurer
*/
public AuthorizationManagerRequestMatcherRegistry fullyAuthenticated() {
return access(AuthenticatedAuthorizationManager.fullyAuthenticated());
return access(withAuthentication(AuthenticatedAuthorizationManager.fullyAuthenticated()));
}

/**
Expand All @@ -324,7 +338,7 @@ public AuthorizationManagerRequestMatcherRegistry fullyAuthenticated() {
* @see RememberMeConfigurer
*/
public AuthorizationManagerRequestMatcherRegistry rememberMe() {
return access(AuthenticatedAuthorizationManager.rememberMe());
return access(withAuthentication(AuthenticatedAuthorizationManager.rememberMe()));
}

/**
Expand Down Expand Up @@ -366,6 +380,11 @@ public AuthorizationManagerRequestMatcherRegistry access(
: AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
}

private AuthorizationManager<RequestAuthorizationContext> withAuthentication(
AuthorizationManager<RequestAuthorizationContext> manager) {
return AuthorizationManagers.allOf(this.registry.hasAuthority, manager);
}

/**
* An object that allows configuring {@link RequestMatcher}s with URI path
* variables
Expand Down Expand Up @@ -403,4 +422,25 @@ public AuthorizationManagerRequestMatcherRegistry equalTo(Function<Authenticatio

}

private static final class HasAllAuthoritiesAuthorizationManager<T> implements AuthorizationManager<T> {

private final AuthoritiesAuthorizationManager delegate = AuthoritiesAuthorizationManager.hasAllAuthorities();

private final Collection<String> authorities = new ArrayList<>();

@Override
public AuthorizationResult authorize(Supplier<Authentication> authentication, T object) {
return this.delegate.authorize(authentication, this.authorities);
}

private void setRoleHierarchy(RoleHierarchy hierarchy) {
this.delegate.setRoleHierarchy(hierarchy);
}

private void add(String authority) {
this.authorities.add(authority);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@

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

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Consumer;

import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.AuthorizationEntryPoint;
import org.springframework.security.web.AuthorizationRequestingAccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.access.ExceptionTranslationFilter;
Expand Down Expand Up @@ -75,13 +80,21 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>>

private LinkedHashMap<RequestMatcher, AccessDeniedHandler> defaultDeniedHandlerMappings = new LinkedHashMap<>();

private final List<AuthorizationEntryPoint> authorizationRequestEntries = new ArrayList<>();

/**
* Creates a new instance
* @see HttpSecurity#exceptionHandling(Customizer)
*/
public ExceptionHandlingConfigurer() {
}

public ExceptionHandlingConfigurer<H> authorizationEntryPoint(
Consumer<List<AuthorizationEntryPoint>> entriesConsumer) {
entriesConsumer.accept(this.authorizationRequestEntries);
return this;
}

/**
* Shortcut to specify the {@link AccessDeniedHandler} to be used is a specific error
* page
Expand Down Expand Up @@ -203,7 +216,8 @@ public void configure(H http) {
AccessDeniedHandler getAccessDeniedHandler(H http) {
AccessDeniedHandler deniedHandler = this.accessDeniedHandler;
if (deniedHandler == null) {
deniedHandler = createDefaultDeniedHandler(http);
deniedHandler = createAccessDeniedHandler(http);

}
return deniedHandler;
}
Expand All @@ -223,15 +237,26 @@ AuthenticationEntryPoint getAuthenticationEntryPoint(H http) {
return entryPoint;
}

private AccessDeniedHandler createDefaultDeniedHandler(H http) {
private AccessDeniedHandler createAccessDeniedHandler(H http) {
AccessDeniedHandler defaultAccessDeniedHandler = createDefaultAccessDeniedHandler();
if (this.defaultDeniedHandlerMappings.isEmpty()) {
return new AccessDeniedHandlerImpl();
return defaultAccessDeniedHandler;
}
if (this.defaultDeniedHandlerMappings.size() == 1) {
return this.defaultDeniedHandlerMappings.values().iterator().next();
return defaultAccessDeniedHandler;
}
return new RequestMatcherDelegatingAccessDeniedHandler(this.defaultDeniedHandlerMappings,
new AccessDeniedHandlerImpl());
defaultAccessDeniedHandler);
}

private AccessDeniedHandler createDefaultAccessDeniedHandler() {
if (!this.authorizationRequestEntries.isEmpty()) {
return new AuthorizationRequestingAccessDeniedHandler(this.authorizationRequestEntries);
}
if (this.defaultDeniedHandlerMappings.isEmpty()) {
return new AccessDeniedHandlerImpl();
}
return this.defaultDeniedHandlerMappings.values().iterator().next();
}

private AuthenticationEntryPoint createDefaultEntryPoint(H http) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {

private MfaConfigurer<H> mfa;

/**
* Creates a new instance
* @see HttpSecurity#formLogin(Customizer)
Expand Down Expand Up @@ -227,8 +229,20 @@ public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {
return this;
}

public FormLoginConfigurer<H> factor(Customizer<MfaConfigurer<H>> customizer) {
if (this.mfa == null) {
this.mfa = new MfaConfigurer<>("AUTHN_FORM", this);
this.mfa.authenticationEntryPoint(this::getAuthenticationEntryPoint);
}
customizer.customize(this.mfa);
return this;
}

@Override
public void init(H http) throws Exception {
if (this.mfa != null) {
this.mfa.init(http);
}
super.init(http);
initDefaultLoginFilter(http);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>

private static final String DEFAULT_REALM = "Realm";

private MfaConfigurer<B> mfa;

private AuthenticationEntryPoint authenticationEntryPoint;

private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
Expand Down Expand Up @@ -161,8 +163,20 @@ public HttpBasicConfigurer<B> securityContextRepository(SecurityContextRepositor
return this;
}

public HttpBasicConfigurer<B> factor(Customizer<MfaConfigurer<B>> customizer) {
if (this.mfa == null) {
this.mfa = new MfaConfigurer<>("AUTHN_BASIC", this);
this.mfa.authenticationEntryPoint(() -> this.authenticationEntryPoint);
}
customizer.customize(this.mfa);
return this;
}

@Override
public void init(B http) {
if (this.mfa != null) {
this.mfa.init(http);
}
registerDefaults(http);
}

Expand Down Expand Up @@ -207,7 +221,7 @@ private void registerDefaultLogoutSuccessHandler(B http, RequestMatcher preferre

@Override
public void configure(B http) {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
AuthenticationManager authenticationManager = postProcess(http.getSharedObject(AuthenticationManager.class));
BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager,
this.authenticationEntryPoint);
if (this.authenticationDetailsSource != null) {
Expand Down
Loading