Skip to content

Commit

Permalink
Add Request Path Extraction Support
Browse files Browse the repository at this point in the history
Closes gh-13256
  • Loading branch information
kth496 authored and jzheaux committed Dec 20, 2023
1 parent b82bd47 commit ec02c22
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.springframework.security.config.annotation.web.configurers;

import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;

import io.micrometer.observation.ObservationRegistry;
Expand All @@ -37,6 +38,7 @@
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
Expand Down Expand Up @@ -387,6 +389,21 @@ public AuthorizationManagerRequestMatcherRegistry anonymous() {
return access(AuthenticatedAuthorizationManager.anonymous());
}

/**
* Specify that a path variable in URL to be compared.
*
* <p>
* For example, <pre>
* requestMatchers("/user/{username}").hasVariable("username").equalTo(Authentication::getName)
* </pre>
* @param variable the variable in URL template to compare.
* @return {@link AuthorizedUrlVariable} for further customization.
* @since 6.3
*/
public AuthorizedUrlVariable hasVariable(String variable) {
return new AuthorizedUrlVariable(variable);
}

/**
* Allows specifying a custom {@link AuthorizationManager}.
* @param manager the {@link AuthorizationManager} to use
Expand All @@ -401,6 +418,41 @@ public AuthorizationManagerRequestMatcherRegistry access(
: AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
}

/**
* An object that allows configuring {@link RequestMatcher}s with URI path
* variables
*
* @author Taehong Kim
* @since 6.3
*/
public final class AuthorizedUrlVariable {

private final String variable;

private AuthorizedUrlVariable(String variable) {
this.variable = variable;
}

/**
* Compares the value of a path variable in the URI with an `Authentication`
* attribute
* <p>
* For example, <pre>
* requestMatchers("/user/{username}").hasVariable("username").equalTo(Authentication::getName));
* </pre>
* @param function a function to get value from {@link Authentication}.
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
* customization.
*/
public AuthorizationManagerRequestMatcherRegistry equalTo(Function<Authentication, String> function) {
return access((auth, requestContext) -> {
String value = requestContext.getVariables().get(this.variable);
return new AuthorizationDecision(function.apply(auth.get()).equals(value));
});
}

}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
Expand Down Expand Up @@ -543,6 +545,17 @@ public void requestWhenMvcMatcherPathVariablesThenMatchesOnPathVariables() throw
this.mvc.perform(request).andExpect(status().isOk());
request = get("/user/deny");
this.mvc.perform(request).andExpect(status().isUnauthorized());

UserDetails user = TestAuthentication.withUsername("taehong").build();
Authentication authentication = TestAuthentication.authenticated(user);
request = get("/v2/user/{username}", user.getUsername()).with(authentication(authentication));
this.mvc.perform(request).andExpect(status().isOk());

request = get("/v2/user/{username}", "withNoAuthentication");
this.mvc.perform(request).andExpect(status().isUnauthorized());

request = get("/v2/user/{username}", "another").with(authentication(authentication));
this.mvc.perform(request).andExpect(status().isForbidden());
}

private static RequestPostProcessor remoteAddress(String remoteAddress) {
Expand Down Expand Up @@ -1067,6 +1080,7 @@ SecurityFilterChain chain(HttpSecurity http) throws Exception {
.httpBasic(withDefaults())
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/user/{username}").access(new WebExpressionAuthorizationManager("#username == 'user'"))
.requestMatchers("/v2/user/{username}").hasVariable("username").equalTo(Authentication::getName)
);
// @formatter:on
return http.build();
Expand All @@ -1080,6 +1094,11 @@ String path(@PathVariable("username") String username) {
return username;
}

@RequestMapping("/v2/user/{username}")
String pathV2(@PathVariable("username") String username) {
return username;
}

}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ public class TestAuthentication extends PasswordEncodedUser {
AuthorityUtils.createAuthorityList("ROLE_USER"));

public static Authentication authenticatedAdmin() {
return autheticated(admin());
return authenticated(admin());
}

public static Authentication authenticatedUser() {
return autheticated(user());
return authenticated(user());
}

public static Authentication autheticated(UserDetails user) {
public static Authentication authenticated(UserDetails user) {
return UsernamePasswordAuthenticationToken.authenticated(user, null, user.getAuthorities());
}

Expand Down

0 comments on commit ec02c22

Please sign in to comment.