Skip to content

Simplify RequestMatcherDelegatingAuthorizationManager.Builder matcher registration #13110

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

Merged
merged 1 commit into from
Jun 22, 2023
Merged
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
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
Expand All @@ -17,15 +17,23 @@
package org.springframework.security.authentication;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.core.userdetails.UserDetails;

/**
* @author Rob Winch
* @author Evgeniy Cheban
* @since 5.0
*/
public class TestAuthentication extends PasswordEncodedUser {

private static final Authentication ANONYMOUS = new AnonymousAuthenticationToken("key", "anonymous",
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));

private static final RememberMeAuthenticationToken REMEMBER_ME = new RememberMeAuthenticationToken("key", "user",
AuthorityUtils.createAuthorityList("ROLE_USER"));

public static Authentication authenticatedAdmin() {
return autheticated(admin());
}
Expand All @@ -38,4 +46,12 @@ public static Authentication autheticated(UserDetails user) {
return UsernamePasswordAuthenticationToken.authenticated(user, null, user.getAuthorities());
}

public static Authentication anonymousUser() {
return ANONYMOUS;
}

public static Authentication rememberMeUser() {
return REMEMBER_ME;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
Expand All @@ -26,9 +26,12 @@
import org.apache.commons.logging.LogFactory;

import org.springframework.core.log.LogMessage;
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
Expand Down Expand Up @@ -102,6 +105,8 @@ public static Builder builder() {
*/
public static final class Builder {

private boolean anyRequestConfigured;

private final List<RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>>> mappings = new ArrayList<>();

/**
Expand All @@ -111,6 +116,7 @@ public static final class Builder {
* @return the {@link Builder} for further customizations
*/
public Builder add(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
Assert.state(!this.anyRequestConfigured, "Can't add mappings after anyRequest");
Assert.notNull(matcher, "matcher cannot be null");
Assert.notNull(manager, "manager cannot be null");
this.mappings.add(new RequestMatcherEntry<>(matcher, manager));
Expand All @@ -127,11 +133,34 @@ public Builder add(RequestMatcher matcher, AuthorizationManager<RequestAuthoriza
*/
public Builder mappings(
Consumer<List<RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>>>> mappingsConsumer) {
Assert.state(!this.anyRequestConfigured, "Can't configure mappings after anyRequest");
Assert.notNull(mappingsConsumer, "mappingsConsumer cannot be null");
mappingsConsumer.accept(this.mappings);
return this;
}

/**
* Maps any request.
* @return the {@link AuthorizedUrl} for further customizations
* @since 6.2
*/
public AuthorizedUrl anyRequest() {
Assert.state(!this.anyRequestConfigured, "Can't configure anyRequest after itself");
this.anyRequestConfigured = true;
return new AuthorizedUrl(AnyRequestMatcher.INSTANCE);
}

/**
* Maps {@link RequestMatcher}s to {@link AuthorizationManager}.
* @param matchers the {@link RequestMatcher}s to map
* @return the {@link AuthorizedUrl} for further customizations
* @since 6.2
*/
public AuthorizedUrl requestMatchers(RequestMatcher... matchers) {
Assert.state(!this.anyRequestConfigured, "Can't configure requestMatchers after anyRequest");
return new AuthorizedUrl(matchers);
}

/**
* Creates a {@link RequestMatcherDelegatingAuthorizationManager} instance.
* @return the {@link RequestMatcherDelegatingAuthorizationManager} instance
Expand All @@ -140,6 +169,123 @@ public RequestMatcherDelegatingAuthorizationManager build() {
return new RequestMatcherDelegatingAuthorizationManager(this.mappings);
}

/**
* An object that allows configuring the {@link AuthorizationManager} for
* {@link RequestMatcher}s.
*
* @author Evgeniy Cheban
* @since 6.2
*/
public final class AuthorizedUrl {

private final List<RequestMatcher> matchers;

private AuthorizedUrl(RequestMatcher... matchers) {
this(List.of(matchers));
}

private AuthorizedUrl(List<RequestMatcher> matchers) {
this.matchers = matchers;
}

/**
* Specify that URLs are allowed by anyone.
* @return the {@link Builder} for further customizations
*/
public Builder permitAll() {
return access((a, o) -> new AuthorizationDecision(true));
}

/**
* Specify that URLs are not allowed by anyone.
* @return the {@link Builder} for further customizations
*/
public Builder denyAll() {
return access((a, o) -> new AuthorizationDecision(false));
}

/**
* Specify that URLs are allowed by any authenticated user.
* @return the {@link Builder} for further customizations
*/
public Builder authenticated() {
return access(AuthenticatedAuthorizationManager.authenticated());
}

/**
* Specify that URLs are allowed by users who have authenticated and were not
* "remembered".
* @return the {@link Builder} for further customization
*/
public Builder fullyAuthenticated() {
return access(AuthenticatedAuthorizationManager.fullyAuthenticated());
}

/**
* Specify that URLs are allowed by users that have been remembered.
* @return the {@link Builder} for further customization
*/
public Builder rememberMe() {
return access(AuthenticatedAuthorizationManager.rememberMe());
}

/**
* Specify that URLs are allowed by anonymous users.
* @return the {@link Builder} for further customization
*/
public Builder anonymous() {
return access(AuthenticatedAuthorizationManager.anonymous());
}

/**
* Specifies a user requires a role.
* @param role the role that should be required which is prepended with ROLE_
* automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_
* @return {@link Builder} for further customizations
*/
public Builder hasRole(String role) {
return access(AuthorityAuthorizationManager.hasRole(role));
}

/**
* Specifies that a user requires one of many roles.
* @param roles the roles that the user should have at least one of (i.e.
* ADMIN, USER, etc). Each role should not start with ROLE_ since it is
* automatically prepended already
* @return the {@link Builder} for further customizations
*/
public Builder hasAnyRole(String... roles) {
return access(AuthorityAuthorizationManager.hasAnyRole(roles));
}

/**
* Specifies a user requires an authority.
* @param authority the authority that should be required
* @return the {@link Builder} for further customizations
*/
public Builder hasAuthority(String authority) {
return access(AuthorityAuthorizationManager.hasAuthority(authority));
}

/**
* Specifies that a user requires one of many authorities.
* @param authorities the authorities that the user should have at least one
* of (i.e. ROLE_USER, ROLE_ADMIN, etc)
* @return the {@link Builder} for further customizations
*/
public Builder hasAnyAuthority(String... authorities) {
return access(AuthorityAuthorizationManager.hasAnyAuthority(authorities));
}

private Builder access(AuthorizationManager<RequestAuthorizationContext> manager) {
for (RequestMatcher matcher : this.matchers) {
Builder.this.mappings.add(new RequestMatcherEntry<>(matcher, manager));
}
return Builder.this;
}

}

}

}
Loading