Role-based access control with Spring MVC - authentication

I would like to know the best practices for the role based access control with spring.
My requirements are,
I will have set of roles assigned to users say,
user1=admin, user2=expert
user1 will have the accesses write like
/admin/member-management
/admin/project-management
......
for user2....
/myproject1/*
so if user2 tries to access the url
/admin/member-management
will be redirect to authorization failure page.

The standard framework to use with Spring MVC is Spring Security. While it can be very complex, here's a minimal version of what you need: 4.2.2 A Minimal Configuration
In your case, the config would be something like this:
<http auto-config='true'>
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
</http>

Spring Security has the concept of roles but out of the box it does not have a concept of permissions. It does have a concept of ACLs but this ACLs are a lot more complicated than permissions, and they are tied to acting on specific objects, versus authorizing actions in general.
Take a look at Apache Shiro. It has roles and permissions that look very similar to what you gave as an example (using wildcards). It is also easy to use with Spring.

public class DashBoardController {
#Autowired
UserService userService;
private static final Logger logger = LoggerFactory.getLogger(DashBoardController.class);
#SuppressWarnings("unchecked")
#RequestMapping(value = PathProxy.DashBoardUrls.SHOW_DASHBOARD, method = RequestMethod.GET)
public String role(Locale locale, Model model) {
String userRole = null;
logger.info("dashboard Controller");
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder
.getContext().getAuthentication().getAuthorities();
for (SimpleGrantedAuthority simpleGrantedAuthority : authorities) {
userRole = simpleGrantedAuthority.toString();
}
switch (userRole) {
case "ROLE_ADMIN":
return "dashboard/admin";
case "ROLE_HR_MANAGER":
return "dashboard/hr_manager";
case "ROLE_MANAGER":
return "dashboard/manager";
case "ROLE_EMPLOYEE":
return "dashboard/employee";
case "ROLE_COMPANY_ADMIN":
return "dashboard/admin";
default:
break;
}
return userRole;
}
}

Related

list the api in swagger based on user roles

I have a requirement where I want to list the api methods in swagger based on user roles.
For example :-
User A with basic access can use limited api methods.
User B with Admin access can use all the listed api methods.
I don't know how to achieve this.
I am using Swashbuckle.AspNetCore Version="1.0.0"
Possible solution:
Define several dockets in your swagger config with different group names
#Bean
public Docket api1() {
...
return new Docket(DocumentationType.SWAGGER_2)
...
.groupName("api1")
...
.paths(PathSelectors.ant("/api/api1Url/**"))
.build().apiInfo(metaData());
}
#Bean
public Docket api2() {
...
return new Docket(DocumentationType.SWAGGER_2)
...
.groupName("api2")
...
.paths(PathSelectors.ant("/api/api2Url/**"))
.build().apiInfo(metaData());
}
Define your own DocumentationCache which overrides Swagger's one
#Primary
#Component
public class RolesAwareDocumentationCache extends DocumentationCache {
private final Map<String, Set<String>> allowedResourcesPerRole =
Map.of(SecurityConfig.API1_ROLE, Collections.singleton("api1"),
SecurityConfig.API2_ROLE, Collections.singleton("api2"),
SecurityConfig.SOME_ADMIN_ROLE, Set.of("api1", "api2"));
#Override
public Map<String, Documentation> all() {
var documentationMap = super.all();
return documentationMap.entrySet().stream()
.filter(e -> isAllowedForRole(e.getKey())) // check if has access to this group
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
private boolean isAllowedForRole(String groupName) {
var userAuthorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities().stream()
.map(Object::toString)
.collect(Collectors.toUnmodifiableSet());
return userAuthorities.stream()
.map(allowedResourcesPerRole::get) // get allowed resources for all of the user roles
.filter(Objects::nonNull)
.flatMap(Collection::stream) // flatMap to collection
.anyMatch(s -> s.contains(groupName)); // check if result collection has specified group name
}
}
So this cache will return groups based on the current user's role from the security context. You can actually use any rules to restrict access to different groups.
Also do not forget to define proper permissions for HttpSecurity to restrict the invocation of an API for not allowed roles.
Try using an IDocumentFilter, you can limit what the user gets in the SwaggerDocument and the swagger-ui feeds from that.
Here are some examples https://github.com/heldersepu/SwashbuckleTest/blob/master/Swagger_Test/App_Start/SwaggerConfig.cs#L261

Web API 2 does not set Thread.CurrentPrincipal in certain scenarios

While trying to debug why custom authentication filter does not work in our in-memory integration tests, I found that there is a difference in the way different subclasses of HttpRequestContext set Principal.
Most of them ( OwinHttpRequestContext, SelfHostHttpRequestContext, WebHostHttpRequestContext) set Thread.Principal like this
WebHostHttpRequestContext.cs:
public override IPrincipal Principal
{
get
{
return _contextBase.User;
}
set
{
_contextBase.User = value;
Thread.CurrentPrincipal = value;
}
}
But some do not set it at all (BatchHttpRequestContext and RequestBackedHttpRequestContext).
Is this a design decision and I need to rewrite parts of application to not use Thread.Principal) or is it a bug that needs to be/will be fixed?

Implementing user session in Sencha and SpringBoot

I am trying to make a web app in Sencha Touch with Springboot as my back-end. My app is going to have users and each one of them is going to have their own separate activity. How do I make my app "know" what user is logged in so it can display their specific details? I am a newbie and don't know exactly how this needs to be done, especially on the server side (Springboot). If somebody could throw some light, that would be awesome! Thanks!
Assuming you are planning to use Spring Security, the current-user data can be obtained through its principal. There are a few ways to get the principal. One way is to have a principal parameter in the controller method, and Spring will inject it. Like this:
#RequestMapping(value = "/user", method = RequestMethod.GET)
#ResponseBody
public String currentUserName(Principal principal) {
return principal;
}
Another way would be to have a utility method like this:
public static User getUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
Object principal = auth.getPrincipal();
if (principal instanceof User) {
return (U) principal;
}
}
return null;
}
This can then be called from the controller method.

Extending Restlet 2.3 ClientInfo

Is it possible to extend org.restlet.data.ClientInfo? I need a convenient way of adding a List<String> permissions to complement the existing List<Role> roles. In a perfect world I would be able to add List<Permission> permissions but the former is perfectly acceptable.
I need to be able to get this from the request: org.restlet.resource.Resource.getRequest().getClientInfo().getPermissions()
I don't think that it's possible to add something within the class ClientInfo since it's a class that is managed by the Restlet engine. You can't subclass it to add a field permissions (you don't have the hand on the client info instantiation).
That said, you can leverage the context attributes. I mean that you can fill within your Enroler implementation an attribute permissions for the request, as described below:
public class MyEnroler implements Enroler {
private Application application;
public MyEnroler(Application application) {
this.application = application;
}
public void enrole(ClientInfo clientInfo) {
// Roles
Role role = new Role(application, "roleId",
"Role name");
clientInfo.getRoles().add(role);
// Permissions
Request request = Request.getCurrent();
List<Permission> permissions = new ArrayList<Permission>();
request.getAttributes().put("permissions", permissions);
Permission permission = (...)
permissions.add(permission);
}
Hope it helps you,
Thierry

spring-security how ACL grants permissions

I'm currently integrating springs-security into our new web application stack. We will need to be able to grant permissions for a user or role to access a specific object or all objects of a certain type. However that's one thing I didn't really get when working through documentations and examples:
Does an ACL only grant permissions to a user/role for a single object or does it do that for the entire type? As I understand it, domain object means the type but the examples and tutorials seem like they assign permissions to specific objects. Am I just confused or can I do both? If not, how do I do the other?
Thanks!
With spring-security you can do both. It's possible because spring-security supports the so called permission rules - within the spring-security terminology they call it permission evaluators. Permission rules encompass ACL, but also you can secure instances of objects when they're in a certain state...etc.
This is how it works:
You need to extend the PermissionEvaluator - this allows you to have super custom logic for determining access rights - you can check the type of the object or check for a particular id, or check if the user invoking the method is the user that created the object, etc.:
public class SomePermissionsEvaluator implements PermissionEvaluator {
#Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
if (permission.equals("do_something") &&
/*authentication authorities has the role A*/) {
return true
} else if (permission.equals("do_something_else") &&
/*authentication authorities has the role B*/) {
return /*true if targetDomainObject satisfies certain condition*/;
}
return false;
}
#Override
public boolean hasPermission(Authentication authentication,
Serializable targetId, String targetType, Object permission) {
throw new UnsupportedOperationException();
}
}
Now that you have a security rule, you need to apply it through annotations:
#PreAuthorize("hasRole('SOME_ROLE_OR_RIGHT') and" +
" hasPermission(#someDomainObject, 'do_something')")
public void updateSomeDomainObject(SomeDomainObject someDomainObject) {
// before updating the object spring-security will check the security rules
}
In order for this to work the security annotations should be enabled in the applicationContext.xml:
<global-method-security secured-annotations="enabled" pre-post-annotations="enabled">
<expression-handler ref="expressionHandler"/>
</global-method-security>
<beans:bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name="permissionEvaluator">
<beans:bean id="permissionEvaluator" class="com.npacemo.permissions.SomePermissionsEvaluator"/>
</beans:property>
</beans:bean>