hasRole() and denyAll() method don't restrict access to resources - spring-boot

I'm developing Spring Boot and Spring Security web application with authorization and resource servers enabled. I have defined a set of users with roles assigned to them and trying to implement roles based access to REST endpoints. I was able to implement token based access to endpoints, but can't restrict access to end users, that would be based on their roles.
I have done two endpoints: /rest/products/list and /rest/products/add and trying to restrict access to /rest/products/add endpoint with the user that is of ADMIN role.
My WebSecurityConfigurerAdapter is as follows:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("user1")
.password(passwordEncoder.encode("user1Pass"))
.roles("USER")
.and()
.withUser("user2")
.password(passwordEncoder.encode("user2Pass"))
.roles("USER")
.and()
.withUser("admin")
.password(passwordEncoder.encode("adminPass"))
.roles("ADMIN");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/rest/products/add").hasAnyRole("ADMIN")
.antMatchers("/rest/products/list").denyAll();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Therefore, resource /rest/products/add should be accessible to admin / adminPass user only as far as that user has ADMIN role. But if to try to it with user1 / user1Pass, it is still accessible:
Get access token for user1 postman screen
Accessing ADMIN only related endpoint with user1 Postman screen
Also I added (in the testing purpose) in the configuration method the following rule .antMatchers("/products/list").denyAll(); Here is indicated that /products/list shouldn't be accessible to any user. But it still keeps on responding (provided access correct token).
In the similar question here How to fix role in Spring Security? the order of matchers should be from the more specific to the less. But in my case there are two matchers and no matchers that can overlap them.
I'm using Spring Boot with spring-boot-starter-security plugin version 2.5.2.
What additional configuration should be done to make .hasRole("ADMIN") and .denyAll() work as expected?

Finally was able to find the solution with the following:
Here there is an example of ResourceServerConfigurerAdapter class. From this and from your comment, dur, I realized that I confused ResourceServerConfigurerAdapter and WebSecurityConfigurerAdapter, trying to define access restriction matchers in WebSecurityConfigurerAdapter. I changed resource server configuration in the following way:
Method that was in WebSecurityConfigurerAdapter
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/products/add").hasAnyRole("ADMIN")
.antMatchers("/rest/products/list").denyAll();
}
was moved to
#Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/products/add").hasAnyRole("ADMIN")
.antMatchers("/rest/products/list").denyAll();
}
}
Now restrictions defined by matchers above are working as expected.

Related

Spring security - Specific session creation policy per matchers

I'm trying to implement SessionCreationPolicy.ALWAYS for the /testMVCController/** endpoint and SessionCreationPolicy.STATELESS for rest of endpoints (/**).
Expected scenario:
When accessing to /testMVCController/displayUsers the user logs in once and the log I have implemented in UserDetailsService logs the authorities associated to that user.
After that, all the requests to /testMVCController/displayUsers or other URL under /testMVCController/** will not log the authorities again because the session creation policy is always and the user is already logged in.
This works when I don't specify the 2nd security configuration (X509ClientSessionCreationPolicyStateless) but when I add it, all the requests become session stateless.
It is not working with the current security configuration because after I log in with my client certificate, at any request executed under /testMVCController/** endpoint (e.g. /testMVCController/displayUsers), the authenticationUserDetailsService is consulted and the list of authorities is logged for each file request the browser makes (.js file, .css files, ...), even after the initial login.
So, if there are 3 requests (/testMVCController/displayUsers, displayUsers.js, displayUsers.css) the list of authorities log present in authenticationUserDetailsService is logged 3 times.
I configured SecurityConfiguration as shown below but it is not working:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfiguration {
#Configuration
#Order(1)
public static class X509ClientSessionCreationPolicyAlways extends WebSecurityConfigurerAdapter {
#Autowired
private X509CUDService x509CUDService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/testMVCController/**")
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.x509()
.authenticationUserDetailsService(x509CUDService)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
}
}
#Configuration
#Order(2)
public static class X509ClientSessionCreationPolicyStateless extends WebSecurityConfigurerAdapter {
#Autowired
private X509CUDService X509CUDService ;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.x509()
.authenticationUserDetailsService(X509CUDService);
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
}
I've searched this issue and I found various links (e.g. Spring session creation policy per-request?, Spring Session: How to create separate session management policies for different URLs and Multiple HttpSecurity) but none of them worked.
Thanks in advance!
I was missing some details on my configuration. I was catching all the requests to /testMVCController/** and that was working, but in addition to catch the requests to any endpoint of the type /testMVCController/** (e.g.: /testMVCController/usersList), I also have to catch the requests that these pages make to get their scripts (.js files, .css files, .png files).
What was happening was: the request to /testMVCController/usersList), was configured with SessionCreationPolicy.ALWAYS, but the subsequent requests such as usersList.js, usersList.css, etc were configured with SessionCreationPolicy.STATELESS, and in these cases the X509CustomUserDetailsService was always consulted.
Example:
GET request to /testMVCController/usersList works, but there also requests in this usersList page to usersList.js, usersList.css, etc.
So, once I included these resource paths in the antMatchers all worked perfectly.

While protecting the app with OAuth2, I'd like to expose some URLs accessible to anyone

After I created a small Spring Boot 2.2.6 application and I configured AWS Cognito as authentication provider, everything work well. When accessing any of application's URLs, I am redirected to Cognito and, after login, the application worked well.
I try to add some public pages (/api/**), which do not require any authentication.
First I tried this:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").permitAll(); // This should be permitted for anyone
but, now, everything is open. No security at all. Ooops.
I change it to:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").permitAll() // This should be permitted for anyone
.anyRequest().authenticated(); // Everything else should be protected
Now, the whitelisted URL (/api/**) work well, no password. But all other URL (eg. /private), instead of redirecting me to the login page, produce a 403 error:
There was an unexpected error (type=Forbidden, status=403). Access Denied
Does anybody have any idea how to keep the original behaviour (password) but with few URLs accessible anonymously?
The WebSecurityConfigurerAdapter has another method that can be used to ignore certain urls:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/**");
}

Role based access control for REST APIs

How do I do role validation for REST APIs?
I have 2 roles called admin and manager. How do I limit the access of REST APIs using RBAC (Role-based Access Control)? For example, /users POST can be accessed by admin role and /users GET can be accessed by manager role.
You can achieve it by using Spring Security.
Spring Security
A highly customizable framework, Spring Security is widely used to handle the authentication and access control (authorization) issues arising in any Enterprise based application developed in Java.
Ex :
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/user/login").permitAll().antMatchers(HttpMethod.OPTIONS)
.permitAll()
.antMatchers(HttpMethod.GET, "/user").hasRole("ADMIN")
.antMatchers(HttpMethod.GET, "/user/list").hasAnyRole("MANAGER", "ADMIN")
.authenticated();
}
}

Spring Security with OAuth2 and anonymous access

I have my Spring REST API secured with Spring Security and OAuth2, I can successfully retrieve a token and access my APIs. My App defines the OAuth2 client itsself.
Now I want users to have anonymous access on some resources. The use case is really simple: I want my app to be usable without login - but if they are logged in, I want to have access to that principal.
Here is my WebSecurityConfigurerAdapter so far:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api1").anonymous().and()
.authorizeRequests().antMatchers("/ap2**").permitAll();
}
As soon as I add a second antMatcher/anonymous, it fails to work though, and it doesn't really express my intent either - e.g. I wan't to have anonymous access on api1 GETs, but authenticated on POSTs (easy to do with #PreAuthorize).
How can I make the OAuth2 authentication optional?
I dropped my #EnableWebSecurity and used a ResourceServerConfigurerAdapter like so:
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/api1", "/api/api2").permitAll()
.and().authorizeRequests()
.anyRequest().authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("my-resource-id");
}
}
/api/api1 may now be called with or without authentication.

Controlling access to Spring Security OAuth2 endpoints

I'm trying to control what clients can generate access tokens in Spring Security OAuth2.0.
I'd like to allow only one client to be able to generate access tokens (access /oauth/authorize, /oauth/token) and all the other ones to validate them.
The documentation says that I should use the standard Spring Security WebSecurityConfigurer to achieve such an access granularity. However, all the configuration I do does not affect access to the end-points.
I tried the following configuration to allow only client mgmt to generate tokens:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("mgmt")
.secret("pass")
.authorities("ROLE_WRITE")
.and()
.withClient("resource")
.secret("pass")
.authorities("ROLE_READ");
}
}
#Configuration
public class EndpointAuthorizationConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/oauth/token")
.and()
.authorizeRequests()
.antMatchers("/oauth/token")
.hasAuthority("ROLE_WRITE")
.and()
.httpBasic();
}
}
I also tried to define users one more time in the EndpointAuthorizationConfig class, but that did not help. Client resource still can access those endpoints.
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("mgmt")
.password("pass")
.roles("WRITE");
}

Resources