spring boot ldap group and restricted endpoints - java

I want to restrict certain rest endpoints to be only for LDAP users in a certain group.
I followed the guide https://spring.io/guides/gs/authenticating-ldap/ to setup LDAP authentication which is working perfectly. So how do I restrict certain rest endpoints?
I tried
#PreAuthorize("hasRole('developers')")
#RequestMapping("/foo")
public String foo(HttpServletRequest request) {
return "Welcome to FOO " + request.getRemoteUser();
}
but it still lets users not in the developers group access that endpoint

You can modify your WebSecurityConfigurerAdapter configuration to something like:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.antMatchers("/foo").hasRole("developers")
.and()
.formLogin();
}
I am not exactly sure of the syntax and if that first rule will override your second rule, but it will be similar to that.
Or, you can try configuring security on a method by method basis like this sample.

#EnableGlobalMethodSecurity(securedEnabled=true) needed to be added to the webSecurityConfig. Once I did that I was able to use #Secured("ROLE_DEVELOPERS") and that method was then restricted to that role.

Related

How to secure some URLS while keeping others without authentication?

I have a Spring Boot based application. I want the URL /camunda/app/welcome/default/#!/login to be accessible without any authentication, while the URLs
/camunda/app/welcome/default/#!/welcome,
/camunda/app/welcome/default/#!/dashboard,
/camunda/app/tasklist/**, and
/camunda/app/admin/**
must be secured (i. e. only authenticated users should be able to access them).
To achieve this, I wrote the following configuration:
#Configuration
#EnableWebSecurity
public class MyConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.and()
.authorizeRequests()
.antMatchers("/camunda/app/welcome/default/#!/login").permitAll()
.antMatchers("/camunda/app/welcome/default/#!/welcome",
"/camunda/app/welcome/default/#!/dashboard",
"/camunda/app/tasklist/**",
"/camunda/app/admin/**",
"/oauth2/authorization/**",
"/oauth2/code/myredirecturl")
.authenticated()
.and()
.oauth2Login(...)
.logout()
.logoutRequestMatcher(...)
.logoutSuccessHandler(...);
}
}
However with this configuration unauthenticated users can access URLs that are supposed to be protected (/camunda/app/welcome/default/#!/welcome, /camunda/app/welcome/default/#!/dashboard, /camunda/app/tasklist/**, /camunda/app/admin/**).
What is wrong with my configuration and how can I fix it?
Sadly to say, but that will not work, because there is actually only one url:
/camunda/app/welcome/default/
and parts after '#' symbol are called 'anchors':
#!/welcome,
#!/dashboard,
Anchors are not processed on backend, because they point to some place in html document that was loaded on client side.
https://www.w3docs.com/snippets/html/how-to-create-an-anchor-link-to-jump-to-a-specific-part-of-a-page.html
So you cant solve it by Spring only, there must be some frontend logic.
Also these two masks:
/camunda/app/tasklist/, and
/camunda/app/admin/
could be covered by Spring Boot, because point to different urls, not anchors.
Make sure you use the URL encoding of #, which is %23 when calling the endpoints. Otherwise, the characters after the # will not be considered.
Making a request to /camunda/app/welcome/default/#!/welcome without properly encoding will be interpreted as a request to /camunda/app/welcome/default/. Since that endpoint doesn't require authentication then anyone will be allowed to access it.
Since all endpoints except /camunda/app/welcome/default/#!/login require authentication you condense your HttpSecurity configuration. I'll rewrite it below using the lambda style configuration to make it more readable:
http
// no need to add requestMatchers since you aren't changing the default configuration
.authorizeRequests(authz -> authz
.antMatchers("/camunda/app/welcome/default/#!/login").permitAll()
.anyRequest().authenticated() // any request that does not match the above rule ^ will require an authenticated user
)
.oauth2Login(...)
.logout(...)

Java Spring Security hasAnyAuthority doesn't works

I have a spring restful application, backend - Spring 2.4.3, frontend - Angular, when I trying to restrict access to individual pages, I get 401 code. I've tried all variations of hasRole () and hasAuthority () nothing helps. What am I doing wrong?
SecurityConfig.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/login", "/registration").permitAll()
.antMatchers("/profile","/profile/*").hasAnyAuthority("USER","ADMIN","INTERVIEWER")
.antMatchers("/getAllUsers").permitAll()
.anyRequest().authenticated();
http
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
/*.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)*/
.cors();
}
Role.java
#XmlType
#XmlEnum
public enum Role implements GrantedAuthority {
ADMIN,
USER,
INTERVIEWER;
#Override
public String getAuthority() {
return this.name();
}
}
Result:
something wrong :(
Based on your provided code, the line .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class) is commented out. I can't speak to what happens when you un-comment that line (since it is a custom filter), but without that line, you have no means of authenticating. This results in your entry point (which is not provided in your example) being invoked, and seems to be returning your 401 status code.
You can test this by commenting out the lines:
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
and adding .formLogin().and() instead. Form Login will provide a default authentication entry point, default authentication filter, and (if using spring boot) default user details service with a randomly generated password printed to your console, which you can use to test logging in. See the docs for more info on this.
A note on testing with hello world (out of the box) configuration: It is a very useful technique to use formLogin() for testing authorization rules (e.g. .antMatchers("/profile","/profile/*").hasAnyAuthority("USER","ADMIN","INTERVIEWER")) in Spring Security. It allows you to eliminate your authentication mechanism from being the problem. Once you are confident your authorization rules are working, you can move on to configuring your own authentication scheme. When possible, seek to utilize an existing scheme provided by Spring Security, and only create a custom filter when you cannot use an out of the box scheme. You can read about JWT authentication in the docs.

Spring Boot Security configuring HttpSecurity

To authorize the requests we override the configure(HttpSecurity) method where we mention for accessing API we want which role. But the APIs that we don't mention can be accessible without login. Why is this behaviour?
I didn't write permitAll() for the APIs why is this default behaviour?
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/user").hasAnyRole("ADMIN", "USER")
.antMatchers("/").hasRole("USER")
.and()
.formLogin();
}
}
In this code admin API can be accessed by admin role, user API can be accessed by admin as well as user role, / API can be accessed by user role and there is one more API /student which I didn't mention and I can access that without login.
Question is how even I didn't write permitAll() method for student API.
Change this line: .antMatchers("/").hasRole("USER") to this: .anyRequest().hasRole("USER").
Only configured URLs are checked. If you doesn't configure an URL, it will not be checked at all.
Therefore, you should add a configuration, that checks all other URLs, see Spring Security Reference:
11.2. Authorize HttpServletRequest with FilterSecurityInterceptor
[...]
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.authorizeRequests(authorize -> authorize 1
.mvcMatchers("/resources/**", "/signup", "/about").permitAll() 2
.mvcMatchers("/admin/**").hasRole("ADMIN") 3
.mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") 4
.anyRequest().denyAll() 5
);
}
1 There are multiple authorization rules specified.
Each rule is considered in the order they were declared.
2 We specified multiple URL patterns that any user can access.
Specifically, any user can access a request if the URL starts with "/resources/", equals "/signup", or equals "/about".
3 Any URL that starts with "/admin/" will be restricted to users who have the role "ROLE_ADMIN".
You will notice that since we are invoking the hasRole method we do not need to specify the "ROLE_" prefix.
4 Any URL that starts with "/db/" requires the user to have both "ROLE_ADMIN" and "ROLE_DBA".
You will notice that since we are using the hasRole expression we do not need to specify the "ROLE_" prefix.
5 Any URL that has not already been matched on is denied access.
This is a good strategy if you do not want to accidentally forget to update your authorization rules.
To protect the API which is not configured in configure method , have to add line .anyRequest().denyAll()
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/user").hasAnyRole("ADMIN", "USER")
.antMatchers("/").hasRole("USER")
.anyRequest().denyAll()
.and()
.formLogin();
}
}
For every request to be protected without configuring it in configure method
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
);
}

Spring security requests authorization

I am new to spring security and was checking how to authorize requests to URLs in my application.
According to the documentation here, we add authorization as follow:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
// ...
.formLogin();
}
As this method worked fine for me, I was wondering if there's another dynamic way to specify this configuration. By using some sort of annotations for our REST controllers for example?
I have a solution in mind that would be really practical, but I wanted to make sure that there's no other way to do this before starting to develop my own code.
Thank you for your help.
Yes there is an annotations as #Secured/#PreAuthorize/#PostAuthorize. this annotations are preferred way for applying method-level security, and supports Spring Expression Language out of the box, and provide expression-based access control.
for e.g
#PreAuthorize("hasRole('ADMIN')")
public String yourControllerMethod() {
return response;
}
for detail check here.
The only other way is to use the #Secured/#PreAuthorize/#PostAuthorize annotations. But you must put them on all webservices you want to secure.
Usually, when I build a webservices application, I like to authorize all requests on the WebSecurityConfigurerAdapter, and then secure requests one by one with these annotations.

Spring security application of antMatcher() vs. antMatchers()

Just want to see whether I'm interpreting the answer to this question the right way.
If we only need to secure one path like this:
http.antMatcher("/api/**").authorizeRequests()....
Then use antMatcher().
If we need to secure multiple URL paths like this:
http
.authorizeRequests()
.antMatchers("/high_level_url_A/sub_level_1").hasRole('USER')
.antMatchers("/high_level_url_A/sub_level_2").hasRole('USER2')
...
Then use antMatchers().
There are two answers in this question, but the example provided in each of them contradicts example given in the other. The first answer says that the author does not need antMatcher() and the second says to always start with `antMatcher() IIUC.
HttpSecurity.antMatcher() changes the default request matcher for the HttpSecurity instance to an AntPathRequestMatcher from AnyRequestMatcher. ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry.antMatchers() is used for applying authorization rules to a subset of endpoints associated with the current HttpSecurity instance.
Example code:
http
.antMatcher("/api/**")
.httpBasic()
.disable()
.authorizeRequests()
.antMatchers("/api/user/**", "/api/ticket/**", "/index")
.hasRole("USER");
In the example above, basic authorization is disabled for all endpoints matching /api/**. Additionally, endpoints matching /api/user/** or /api/ticket/** will require the request's Authentication to contain ROLE_USER. However, when a user attempts to access /index, they will be met with a basic auth prompt. Upon entering credentials, the user will be granted access to the endpoint regardless of whether or not the request's Authentication contains ROLE_USER. This is because .antMatcher("/api/**") is limiting the scope of the entire HttpSecurity instance to that specific AntMatcher.
The example below would ensure that the HttpSecurity's scope includes the three previous AntMatchers and nothing else:
http
.requestMatchers()
.antMatchers("/api/user/**", "/api/ticket/**", "/index")
.and()
.httpBasic()
.disable()
.authorizeRequests()
.any()
.hasRole("USER");
EDIT
If you use #hasRole(), then your role should not start with "ROLE_" as this is automatically inserted.
antMatcher() allows configuring the HttpSecurity to only be invoked when matching the provided ant pattern.
If more advanced configuration is necessary, consider using requestMatchers() or requestMatcher(RequestMatcher).
Invoking antMatcher() will override previous invocations of antMatcher(), mvcMatcher(), requestMatchers(), regexMatcher(), and requestMatcher()
See the example bellow for using requestMatchers
#Configuration
#EnableWebSecurity
public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers((requestMatchers) ->
requestMatchers
.antMatchers("/api/**")
.antMatchers("/oauth/**")
)
.authorizeRequests((authorizeRequests) ->
authorizeRequests
.antMatchers("/**").hasRole("USER")
)
.httpBasic(withDefaults());
}
}
The configuration below is also the same as the above configuration.
#Configuration
#EnableWebSecurity
public class RequestMatchersSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers((requestMatchers) ->
requestMatchers
.antMatchers("/api/**")
)
.requestMatchers((requestMatchers) ->
requestMatchers
.antMatchers("/oauth/**")
)
.authorizeRequests((authorizeRequests) ->
authorizeRequests
.antMatchers("/**").hasRole("USER")
)
.httpBasic(withDefaults());
}
}

Categories