Integration test for API that has #Secured - spring

I have a spring REST API that is secured by spring security and Keycloak, and I am using keycloak testcontainers for the integration test. I can add a user to keycloak and get a token to use while testing most of the APIs.
The question is, If I have an API that is annotated with #Secured to limit its access to only users with a specific role, how to assign that role to the created user or mock this role to run the integration test

You may simply want to add the role to the user, e.g. like this:
RoleRepresentation role = new RoleRepresentation("rolename", "role desc", false);
KeycloakContainer container = new KeycloakContainer();
RealmResource realm = container.getKeycloakAdminClient().realm("realm");
realm.roles().create(role);
realm
.users()
.get("user")
.roles()
.realmLevel()
.add(List.of(role));

Related

Spring Boot Security Oauth2 - adding dynamic OIDC parameters

How do I add OIDC token request parameters dynamically in my app code? I want to add domain_hint based on some data received by my controller from as yet un-authenticated user.
You can implement custom OAuth2AuthorizationRequestResolver
and then add to your spring security configuration
.oauth2Login(req->
req.authorizationEndpoint()
.authorizationRequestResolver(new YourCustomAuthorizationRequestResolver)
)

how to implement role-based security in microservices architecture [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have a spring-boot application with 4 microservices, eureka server and a centralized API gateway.
All external traffic is coming via my API gateway to my microservices.
My API gateway (Zuul) is validating and verifying JWT token.
the JWT token is generated by one of my microservices after user login (the users microservice), the token contain the user Id and his roles/authorities.
Now I want to implement role-based security on methods that are present in microservices other than the gateway.
I have tried to use #PreAuthorize but it's not working out of the gateway (obviously in order to make it work I have to set a Spring Security authentication object in the SecurityContextHolder in my microservices and populate it with authorities).
So is there any solution to achieve this type of security?
What is the best design to set up security in microservice architecture?
Authentication at API gateway level and authorization at microservices level?
Do I need to use spring security within the microservices or just pass down the roles (append them to the request) after validating the JWT at API gateway level and for example create my own annotations and use Spring AOP to handle authorization?
In Spring5 microservices you will be able to find a base to develop a microservice architecture with several of the requisites you are looking for:
Registry server using Eureka.
Gateway server with Zuul.
Regarding to security, I have developed two different microservices:
Spring Oauth2 with Jwt
Spring Jwt multi-application security service to work with access and refresh Jwt tokens, with several customizations like: definition of the content of every one, work with JWS or JWE, etc
Most important ones are well documented using Swagger, as you can see here, and all documented APIs are accessible using an unique gateway Url.
For all classes of every microservice, Junit tests were developed.
Security
At this point, I took several decisions:
1. Is not the gateway the microservice that verifies the security.
Because use the gateway as "firewall" is a less flexible approach. I wanted to decide which microservices need security and every one should manage internally the roles can access to every endpoint. In summary, every microservice has to work with the authorization/authentication but it don't need to know how that functionality is done.
2. Specific microservice to deal with the security
As I told you, I developed 2 different ones, because I wanted to "play" with different options/approaches. The most important advantage is the encapsulation, if "tomorrow" I decide to change Jwt by any other option, I will only need to modify those ones, the microservices that use them will keep the same code (I will explain you soon how the integration was done)
Security integration example
I will explain how the security functionality was integrated between:
Pizza service easy microservice developed as part of the architecture.
Spring Jwt
1. Every application that manages user and roles, will include in the security microservice a folder similar to the next one, to define its models, repositories to get the required information, etc
2. Global endpoints of the security microservice are defined here. As you can see, they work basically with 2 Dtos:
AuthenticationInformationDto
UsernameAuthoritiesDto
The main advantage, only the security microservice knows the details about how that functionality was done, the other ones that use it will receive a well known Dtos with the required information.
3. In pizza-service, the security integration is mainly defined in the next 3 classes:
SecurityContextRepository to get authorization token from the header and send it to the SecurityManager.
SecurityManager call to security-jwt-service with the provided "authorization token" (it doesn't know if it is Jwt or any other thing) and receives a well know UsernameAuthoritiesDto (transforming it into an object of the Spring class UsernamePasswordAuthenticationToken)
WebSecurityConfiguration global security configuration.
Now you can include in your endpoints the required role based security:
Controller example
Custom PreAuthorize annotation
Final considerations
pizza-service was developed using Webflux, you can see an equivalent integration based on a MVC microservice one in order-service here (in this case I used the "other security service" but is easy to adapt it).
To improve the security and follow the "Oauth approach", the requests to security-jwt-service need to include the Basic authentication too. As you can see in SecurityManager class:
private String buildAuthorizationHeader(String username, String password) {
String auth = username + ":" + password;
byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes());
return "Basic " + new String(encodedAuth);
}
The table in database to store that information is the same one used to manage the security configuration of every application: security.jwt_client_details
Question is wide at the moment as the nature of the traffic to your microservices is not clear.
Assuming that all external traffic coming via your API gateway to your microservices.
You don't need to validate the JWT twice once in API gateway and then again in your internal microservice. If the JWT is invalid , the request will never reach your microservice
Then API gateway propagate the roles. In your microservice, you initialise the spring security context using the roles passed in the header. It will allow you to use #PreAuthorize etc
Assuming that external traffic can come via your API gateway as well as directly to your microservices.
Now you need to verify it in both API gateway and in your microservices
Update
I don't have knowledge about Zuul API gateway. This is just addressing the following:
I have tried to use #PreAuthorize but it's not working out of the gateway (obviously in order to make it work I have to set a Spring Security authentication object in the SecurityContextHolder in my microservices and populate it with authorities).
public class PreAuthenticatedUserRoleHeaderFilter
extends GenericFilterBean {
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String rolesString = //extract the roles
String userName = // extract the username
List<GrantedAuthority> authorities
= AuthorityUtils.commaSeparatedStringToAuthorityList(rolesString);
PreAuthenticatedAuthenticationToken authentication
= new PreAuthenticatedAuthenticationToken(
userName, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(servletRequest, servletResponse);
}
}
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true,
jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
PreAuthenticatedUserRoleHeaderFilter authFilter
= new PreAuthenticatedUserRoleHeaderFilter();
http.
antMatcher("/**")
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(authFilter,
BasicAuthenticationFilter.class)
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
If you'r API gateway is also the one who create's and sign JWT token's with private key's and to authenticate you use public key's from API gateway then you are the one who specifies structure of that JWT token and you should be able to encode roles into that JWT (it could be scope parameter for example but all possible scope's are usually accessible by all users). Then you can configure spring boot to automaticaly resolve group role from that JWT (set SecurityContextHolder role's right) and #PreAuthorize annotation can be used without any modification.
If you'r API gateway is only verifying JWT token's against 3rd party authorization server (the server which signed and structured that JWT) with public key's from this server you must implement some custom mechanism for role-based access. One that come's to my mind is to implement second level Oauth2 authentication which would be only used with request's between your microservice's and API gateway using some kind of 'inner' JWT. For example see following image:
Since you define how structure of inner JWT should look by your API gateway code you can set custom attribute's like role: (admin, user etc..). This can be resolved for example from user name, id, email which you are provided from outer JWT from 3rd party authorization server. Therefore you would need to keep some mapping inside API gateway code like:
(userId: 12563) => Admin group
(userId: 45451) => User group
Since your micro-services use JWT for authentication you can use spring boot resource server to setup authentication and configure it to resolve group's (object you mentioned inside SecurityContextHolder) automatically from your custom structured inner JWT. This way you could simply use #PreAuthorize annotation inside your micro-service's and therefore you would not have to create custom annotation's. Note that this is only supposed to solve second case i have specified in first case you are in control of JWT token already.
Role based authorization is avoided these days and scope based authorization is preferred with something like service1.read, service2.full_access scopes. You could either: move role based authorization into each service and away from identity server, convert to scope based authorization, move role based authorization job to respective service rather than relay on identity server.
You can user reference token flow and invalidate token when changes occurs in your role/rights, this will help explaining it

Spring oauth2 Remotetokenservice

I have 2 microservices that I have created with spring boot. 1 microservice has a oauth2 authentication service and the other is an oauth2 resource server.
The resource server uses RemoteTokenService to check if the access token is valid. This works and when I create a rest endpoint and supply a Principal parameter the principal of the logged in user is supplied. Example:
#RequestMapping(method = RequestMethod.GET, value = "/api/user/{id:[0-9]+}")
#PreAuthorize("hasAnyAuthority('ROLE_USER')")
public User getUser(#PathVariable("id") long id, Principal principal) {
}
The thing is that the Principal contains the username and authorities of the logged in user and I also need the user info like id of the user.
I don't want to do an extra rest call to get the user data so I was wandering is there anyway to get the remotetokenservice to return more information?
The RemoteTokenServices makes nothing but calling CheckTokenEndpoint of Spring Security and return back the map values to the resources server.
If you want to get more information then you have to implement CheckTokenEndpoint::checkToken method in the Authentication server.
We managed to solve your problem by having spring-oauth2 with JWT integration. So the Authentication Server generates an access token as a claim (after authentication the user) which can hold more information that could be useful at the Resource server. The resource server in this case didn't need to have a remote call to check the token, but it verify the signature of the JWT to accept the claim.

Spring #PreAuthorize not working in RestController

I have GrantedAuthorities as [admin, player, user]
To test this I have injected Authentication object in method and invoked authentication.getAuthorities().
but when at REST Controller Method I put #PreAuthorize("hasRole('ROLE_player')")I am getting response for my REST web service as 403 forbidden.
I have custom roles defined which I am picking from database. I want to authorize REST call before execution of any business logic.
Tried with #Secured but still not working.
The default prefix for hasRole is ROLE_. If a prefix isn't supplied, spring will automatically add it. Since your roles in your database aren't prefixed with ROLE_ they will not match with hasRole.
// will be checking for ROLE_admin, your role in DB is admin
#PreAuthorize("hasRole('admin')")
You can update your roles in your db to prefix them with ROLE_ or you can alter the prefix spring uses on DefaultWebSecurityExpressionHandler. You should also be able to use hasAuthority rather than hasRole. The hasAuthority will not add any prefix to the supplied parameter.
#PreAuthorize("hasAuthority('admin')")
http://docs.spring.io/spring-security/site/docs/current/reference/html/el-access.html

Unit Testing Spring Data JPA Repositories with Spring Security

I have a Spring Data JPA repository, and the unit tests work fine, as long as the Spring Security dependency (spring-boot-starter-security) is not added, with corresponding method authorization annotations on the repository. Once added I get an AuthenticationCredentialsNotFound exception when running the unit test.
How do I "Authenticate" calls to repository methods in the unit test?
I found an example of what to do here:
https://github.com/spring-projects/spring-data-examples/blob/master/jpa/security/src/test/java/example/springdata/jpa/security/SecurityIntegrationTests.java
My tests run fine now as long as prior to calling methods on the repository, I first authenticate the user that has the required role like this:
SecurityContextHolder.getContext()
.setAuthentication(new UsernamePasswordAuthenticationToken(
"admin",
"password",
Collections.singleton(new SimpleGrantedAuthority("ROLE_ADMIN"))));
// Make calls to repository methods that require the admin role
Also in case you need to authenticate set more than one role (Courtesy of Rob Winch - Spring Security Lead):
SecurityContextHolder.getContext()
.setAuthentication(new UsernamePasswordAuthenticationToken(
ADMIN,
PASSWORD,
AuthorityUtils.createAuthorityList("ROLE_USER", "ROLE_ADMIN")
));

Resources