Oauth2 / Password / permission to verify the password for a specific object

The basic information and data information in my API is related to the project (Entity). What is a good approach for password flow: to manage the specific permission associated with the project using spring and OAuth2 security?

In this application you have 5 microservices :

  • Microservice UAA: authorization server
  • Microservice catalog
  • Microservice order
  • Microservice invoice
  • Client microservice

Scaling:

UUAMICROSERVICE

Each user can have many projects and can have permission for each project:

  • CAN_MANAGE_CATALOG
  • CAN_VIEW_CATALOG
  • CAN_MANAGE_ORDER
  • CAN_VIEW_ORDER
  • CAN_MANAGE_INVOICE
  • CAN_VIEW_INVOICE
  • ...

I have a lot of ideas, but I'm not sure if I have a good approach:

USE CASE: I want to pin an endpoint:

http://catalog-service/{project_key}/catalogs 

Only a user with VIEW_CATALOG or MANAGE_CATALOG permission for the project {project_key} can display the entire directory present in the project

My first idea: USE ProjectAccessExpression with pre-authorization

CatalogController.java

 @Controller public class CatalogController { @PreAuthorize("@projectAccessExpression.hasPermission(#projectKey, 'manageCatalog', principal)" + " or @projectAccessExpression.hasPermission(#projectKey, 'viewCatalog', principal)") @RequestMapping( value = "/{projectKey}/catalogs", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE ) public @ResponseBody List<Catalog> findByProject(@PathVariable("projectKey") String projectKey) { return catalogService.find(); } } 

ProjectAccessExpression.java

 @Component public class ProjectAccessExpression { private RestTemplate restTemplate; public boolean havePermission(String projectKey, String permission , String username) { Boolean havePermission = restTemplate.getForObject(String.format("http://uaa-service/permission/check?project=%1&permission=%2&username=%3", projectKey, permission, username ), Boolean.class); return havePermission; } } 

Inconvenient: you need to call the UAA service each time

Second idea: USE USER_ROLE

With user_role

  • username | Role
  • mylogin1 | SHOP1 .CAN_MANAGE_CATALOG
  • mylogin1 | SHOP1 .CAN_VIEW_CATALOG
  • mylogin1 | Shop2 .CAN_MANAGE_CATALOG
  • mylogin1 | Shop2 .CAN_VIEW_CATALOG
  • mylogin1 | Shop2 .CAN_MANAGE_ORDER
  • mylogin1 | Shop2 .CAN_VIEW_ORDER
  • ...

SHOP1 SHOP2 - projectKey

Inconvenient: I'm not sure, but if the user changes the permission, I need to cancel all associated tokens

Third idea: add custom permission in the authentication unit

I do not know how to do this for storage ...

And in the controller annotation:

 @PreAuthorize("@ProjectAccessExpression.hasPermission(authentication, 'manageCatalog||viewCatalog', #projectKey) 

Inconvenient: the same inconvenient with the second idea

+6
source share
2 answers

This is the solution that I use and works great.

** 1 - Download business logic Security when user signs **

In this example, find the user with the role saved in the database and add the entire project depending on the role. After the operation, I have an authentication token with GrantedAuthority: ROLE_USER, ROLE_MANAGE_CATALOG: project1, ROLE_VIEW_PROFILE: project1, ROLE_MANAGE_PROJECT: project2, ...

 @Service public class CustomUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Optional<User> user = userService.findByLogin(username); if (!user.isPresent()) { Object args[] = {username}; throw new UsernameNotFoundException( messageSource.getMessage("user.notexist", args, "User {0} doesn't not exist", LocaleContextHolder.getLocale()) ); } if (!user.get().isActivated()) { //throw new UserNotActivatedException(String.format("User %s was not activated!", username)); Object args[] = {username}; throw new UserNotActivatedException( messageSource.getMessage("user.notactivated", args, "User {0} was not activated", LocaleContextHolder.getLocale())); } //Here implement your proper logic //Add busness logic security Roles // eg ROLE_MANAGE_PROJECT:{project_key}, ROLE_MANAGE_CATALOG:{project_key} List<Role> bRoles = projectService.getRolesForUser(username) user.get().getRoles().addAll( bRoles ); UserRepositoryUserDetails userDetails = new UserRepositoryUserDetails(user.get()); return userDetails; } } 

** 2 check security using pre-authorization expression **

In this example, only a user who has this permission can perform this operation:

  • ROLE_ADMIN OR
  • ROLE_MANAGE_PROJECT: {projectKey}

    @PreAuthorize ("@ oauthUserAccess.hasPermission (authentication, '" + Constants.PP_MANAGE_PROJECT + "', #projectKey)") @RequestMapping (value = "/ projects / {projectKey}", method = RequestMethod.PUT, produces = MediaType. APPLICATION_JSON_VALUE) public ResponseEntity updateProject (@PathVariable ("projectKey") String projectKey, @Valid @RequestBody Project project)

OauthUserAccess Class:

 @Component("oauthUserAccess") public class OauthUserAccess { /** * Check if it is the administrator of the application IMASTER * @param authentication * @param projectKey * @return */ public boolean hasAdminPermission(OAuth2Authentication authentication, String projectKey) { if(authentication.getOAuth2Request().getAuthorities().contains("ROLE_ADMIN")) return true; return false; } /** * * @param authentication * @param permissionType * @param projectKey * @return */ public boolean hasPermission(OAuth2Authentication authentication, String permissionType, String projectKey) { if (!ProjectPermissionType.exist(permissionType) || projectKey.isEmpty() || !projectKey.matches(Constants.PROJECT_REGEX)) return false; if (authentication.isClientOnly()) { //TODO check scope permission if(authentication.getOAuth2Request().getScope().contains(permissionType+":"+projectKey)) return true; } if (hasAdminPermission(authentication, projectKey)) return true; String projectPermission = "ROLE_" + permissionType + ":" + projectKey; String projectPermissionManage = "ROLE_" + permissionType.replace("VIEW", "MANAGE") + ":" + projectKey; String manageProject = "ROLE_" + Constants.PP_MANAGE_PROJECT + ":" + projectKey; Predicate<GrantedAuthority> p = r -> r.getAuthority().equals(projectPermission) || r.getAuthority().equals(projectPermissionManage) || r.getAuthority().equals(manageProject); if (authentication.getAuthorities().stream().anyMatch(p)) { return true; }; return false; } } 

3 - Advantage / Disadvantage

Advantage

business logic resolution is only loaded when a user logs into the application and not every time, therefore it is a powerful solution for microservice architecture.

Inconvenience

It is necessary to update the authentication token or cancel the token when changing rights to another when updating the permission for the user, the user needs to log out and log in. But you have the same problem without this security logic, for example, when the user is disconnected or turned on.

I use my solution, for example, in the controller:

 newAuthorities = projectService.getRolesForUser(username); UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), newAuthorities); OAuth2Authentication authentication = (OAuth2Authentication)SecurityContextHolder.getContext().getAuthentication(); Collection<OAuth2AccessToken> accessTokens = tokenStore.findTokensByUserName(principal.getName()); OAuth2Authentication auth2 = new OAuth2Authentication(authentication.getOAuth2Request(), newAuth); accessTokens.forEach(token -> { if (!token.isExpired()) { tokenStore.storeAccessToken(token, auth2); } }); 
0
source

It basically looks like you're just trying to use roles with OAuth 2.0 for your project. Here is a snippet of some spring documentation on OAuth 2.0

Mapping user roles to realms: http://projects.spring.io/spring-security-oauth/docs/oauth2.html

Sometimes it is useful to limit the amount of tokens not only to areas assigned to the client, but also in accordance with the user's own permissions. If you use DefaultOAuth2RequestFactory in AuthorizationEndpoint , you can set the checkUserScopes = true flag to restrict the allowed areas to only those that correspond to user roles. You can also enter OAuth2RequestFactory in TokenEndpoint , but this only works (for example with a password), if you also set TokenEndpointAuthenticationFilter - you just need to add this filter after the HTTP BasicAuthenticationFilter . Of course, you can also implement your own rules for attaching scopes to roles and set your own version of OAuth2RequestFactory . AuthorizationServerEndpointsConfigurer allows you to add a custom OAuth2RequestFactory so you can use this function to set up a factory if you use @EnableAuthorizationServer .

All of this basically comes down to the fact that you can protect your endpoints with different areas by mapping areas to your own custom roles. This will allow you to get very good protection with your safety.

I found a pretty good walk that you can use as a reference: (Obviously, you will need to adjust the settings in your own case)

https://raymondhlee.wordpress.com/2014/12/21/implementing-oauth2-with-spring-security/

0
source

All Articles