Is it possible at all for me to use a wildcard in the access property of the <sec:authorize /> tag.
Currently I have
<sec:authorize access="hasRole('TICKET_VIEW') or hasRole('TICKET_EDIT')">
but I would like to be able to use
<sec:authorize access="hasRole('TICKET_*')">
Is this possible or does anyone know a work-around that would accomplish the same thing?
Thanks
It's possible in Spring EL starting from Spring 3.x. The expression you're looking for is hasAnyRole(..).
So it should look like this:
<sec:authorize access="hasAnyRole('TICKET_VIEW', 'TICKET_EDIT')">
...
</sec:authorize>
Here's a link for some more Spring EL expressions:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/el-access.html
I realize that this is an old question, but this answer might help future searchers.
1) Allow Single Role from a Fixed Set: This is the simple base case.
<security:authorize access="hasRole('ROLE_ADMIN_ABC')">
You are allowed to see these admin links.
</security:authorize>
2) Allow Any Role from a Fixed Set: For cases where you want to allow "any role that starts with ADMIN", you know all of the role names in advance, and you just have a few roles, jzelenkov's answer is perfectly correct. However, if you have too many roles to deal with, you will probably want to create a custom method call that can make the access decision, and insert it into the access attribute with SpEL. This solution is closer to the wildcard question that was originally asked.
<bean id="mySecurityBean" class="com.sample.MySecurityBean" />
<security:authorize access="#mySecurityBean.roleStartsWith(principal, 'ROLE_ADMIN_')">
You are allowed to see these admin links.
</security:authorize>
public class MySecurityBean {
/**
* Returns true if any role starts with some prefix.
*/
public boolean roleStartsWith(UserDetails user, String rolePrefix) {
for (GrantedAuthority auth : user.getAuthorities()) {
if (auth.getAuthority().startsWith(rolePrefix)
return (true);
}
return (false);
}
}
3) Allow Single Role from a Dynamic Set: For cases where you want to allow "a specific role that starts with ADMIN", but you don't necessarily know all of the allowed role suffixes, you can insert the role name at render time with JSTL. As an example, consider an app with many workspaces, each with a unique code. You want to create a ROLE_ADMIN_workspaceName role for each workspace. When someone is visiting the ABC workspace page, you only want the admin links to appear if the user has the ROLE_ADMIN_ABC role. Let us assume that every workspace uses the same JSP view, and the name is passed into the model as ${workspaceName}.
<sec:authorize access="hasRole('ROLE_ADMIN_${workspaceName}')">
You are allowed to see these admin links.
</sec:authorize>
4) Allow Any Role from a Dynamic Set: This is identical to the solution for #2.
Related
I am building an Application where alongside general public pages there will be a login page and 3 Dashboard Versions for each 3 role levels.
Table
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('firstname');
$table->string('surname');
$table->string('username');
$table->string('email')->unique();
$table->string('password', 60);
$table->string('role');
$table->rememberToken();
$table->datetime('created_at');
$table->datetime('updated_at');
});
With that i have another table Roles:
Schema::create('roles', function (Blueprint $table) {
$table->increments('id');
$table->string('role_type');
});
Relationship
Users::hasOne('roles');
Roles::hasMany('users');
Essentially on login function i want to check the users role_type and then redirect them based on the role.
I would like to know if this table structure and idea is correctly thought out.
Additionally when a user accesses a specific route i will have the route either display login if no user or 404/503 if user is logged in But does not match a specific role_type.
Thanks guys
On your users table instead of the role column you have, it should be these two lines:
$table->integer('role_id')->unsigned()->nullable();
$table->foreign('role_id')->references('id')->on('roles')->onDelete('set null');
The downside to this is it allows a null value for the 'role_id' column on users. The reason it needs this is to deal with what would happen if a Role that is assigned to a user is deleted.
But a better solution would be to use a many-to-many relationship, I personally would suggest using Entrust, since it has what you want built in, plus more options that you might want later.
I am working on a grails 2.3.8 project and trying to customize the Role Hierarchy. I am trying to change the default value of rolePrefix = 'ROLE_' in resources.groovy with rolePrefix = 'PERM_'. I understand that to make this work, I need to make the following changes in my Config.groovy into something like:
grails.plugins.springsecurity.userLookup.userDomainClassName = 'tpo.core.acl.AdminAccount'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'tpo.core.acl.AdminAccountPermission'
grails.plugins.springsecurity.authority.className = 'tpo.core.acl.Permission'
And to establish hierarchy, I need to add this too in my Config.groovy
grails.plugins.springsecurity.roleHierarchy = '''
PERM_ACCOUNT_ALL > PERM_ACCOUNT_CREATE
PERM_ACCOUNT_ALL > PERM_ACCOUNT_READ
PERM_ACCOUNT_ALL > PERM_ACCOUNT_UPDATE
PERM_ACCOUNT_ALL > PERM_ACCOUNT_DELETE
'''
So in my Controller, it is something like,
#Secured(['PERM_ACCOUNT_ALL'])
def index() {
redirect(action: "list", params: params)
}
When I try to run my application, and access my controller's index() action, I was prompted to log in, this is expected because of the presence of #Secured(), but having successfully logged in, I was not yet able to access the index() action, and it displayed, Sorry, you're not authorized to view this page. The permission was assigned to the user that I used to logged in, but still, I was not able to access it.
Where am I missing?
There's a lot more to it than that :)
The reason that the plugin doesn't allow this change is to support the standard voters. Currently there are three styles of strings that can be used to specify access rules - role names, SPeL expressions, and the funky "IS_AUTHENTICATED" ones - IS_AUTHENTICATED_ANONYMOUSLY, IS_AUTHENTICATED_FULLY, and IS_AUTHENTICATED_REMEMBERED. Additionally there's a new way that was added for the 2.0 release - using a Closure and any arbitrary Groovy code inside of it, but that's unrelated to role names.
Each of the registered voters is queried to determine if they "support" (i.e. can vote on) each of these tokens. The logic is currently rather straightforward - the "IS_AUTHENTICATED_..." strings are handled by one voter, strings starting with "ROLE_" are handled by another, and everything else is assumed to be a SPeL expression.
To be honest, I think since roles would have to have been "registered" at startup anyway (to specify what access rules are allowed for each role in annotations, Config.groovy, etc.) that the role voter could do more than just check that the string starts with some prefix - it could look at its collection of known role names. So it probably wouldn't be too much work to add support for custom role prefixes for the 2.0 release, and I'll look into that. But for now, the plugin is as customizable as much as possible in every way except for this one exception.
in our app, we have the following:
<intercept-url pattern="/app/**" access="ROLE_ADMIN,ROLE_USER,ROLE_CUST_ADMIN" />
but, in our app, we can create custom roles too, and, when a user of a custom role, for ex, ROLE_LIMITED_USER tries login, the access is denied.
how can I secure the app without listing the roles ? or how can I make it accept a pattern ROLE_* ?
I tried the following :
<intercept-url pattern="/app/**" access="IS_AUTHENTICATED_FULLY" />
but, this is causing the session time out and user is required to login. Before the change, the user login was remembered.
Appreciate any solution
thanks
A couple things:
The roles that you are requiring to access functionality in your app should not change - think of them more as "permissions" instead of "roles". (The Spring Security default here may be a misnomer.) Then you can map sets of permissions to roles (via your own code), allowing custom roles to be created as a different bag of permissions, but the actual permissions that you are coding/checking against are static - they don't change. When a user is authenticated, the set of GrantedAuthoritys you populate in the UserDetails should be a merged collection of permissions based on the user's assigned roles.
That said, I think you can still do what you want without changing the security model by using Expression-Based Access Control. Assuming you're using the security namespace (i.e. xmlns="http://www.springframework.org/schema/security"), then you need to set use-expressions="true" on the <http> element and change your access attribute values to SpEL expressions, such as:
<http use-expressions="true">
<intercept-url pattern="/app/**" access="hasAnyRole('ROLE_ADMIN','ROLE_USER','ROLE_CUST_ADMIN')" />
<intercept-url pattern="/other1/**" access="isAuthenticated()" />
<intercept-url pattern="/other2/**" access="authentication.authorities.?[authority.startsWith('ROLE_')].size() != 0" />
</http>
Note that this code snippet has not been tested, and I'm pretty sure the 3rd intercept-url example won't work as is but should be pretty close. (It's attempting to filter the Collection<GrantedAuthority> to authorities that start with ROLE_ and make sure the filtered list isn't empty.)
My guess is that it will be easier to use the 2nd intercept-url and do any further custom checking in code, where you can get access to the current SecurityContext / Authentication / principal via:
SecurityContextHolder.getContext()
Hope this helps.
Note similar question here as well:
Spring Security Authorize Access Role with a Wildcard
Use Spring's #PreAuthorize annotations in your classes, and reference a custom permission evaluator via SpEL. Don't worry about building an expression parser, as you can simply reference a boolean method from within the annotation:
#PreAuthorize("#myCustomClass.booleanMethod(param1, param2, #passedParam) and isAuthenticated()")
myMethod(passedParam) {
//do something
}
Then, in the custom class, define the boolean method, and it can handle your roles (permissions) however you like -- including accessing a database to discover your newly added roles.
You won't be able to add new roles to the XML configuration on the fly, so if your question pertains to new role creation (which seems to be the case), you'll need something like the above to make it happen. Using SpEL and a custom boolean method gives you access to a potentially limitless set of restrictions and requirements you can add to your application.
Is there a way to retrieve all access rules from a specific Role?
As roles are just flagged at the top of an action or on top of the whole class I canĀ“t find a way to retrieve this information unless I read and parse the whole file and after that find a way to link this [authorization] tag to a group.
Thanks
No there is no builtin way. Its even impossible, because you might check for Roles in your code (actions/views) as well.
And how should the list of access rules be returned?
For example, how should an algorithm return / name this access rule in a view:
#if(User.IsInRole("SomeRole") {
<div>
Show some html only visible for users in SomeRole
</div>
}
You have to administer the list of your application defined access rules by yourself - i list will be very specific for your app.
Of cause, when you just use the Authorize attribute, you could generate a list of action methods accessible for a given role by reflecting over all controller classes.
I'm currently developing a menu for my application that should be able to display only the controllers that the current user can access (requestmap defined in the database).
How can I check if the current user has access to a specific controller and action?
To check roles in view :
Spring security plugin provides ifAllGranted, ifAnyGranted, ifNoneGranted etc., tags to check roles
For example, to check Admin Role of logged in User :
<sec:ifLoggedIn>
<sec:ifAllGranted roles="ROLE_ADMIN">
Admin resource
</sec:ifAllGranted>
</sec:ifLoggedIn>
(tested in grails-2.2.2 and springSecurityCorePlugin-1.2.7.3)
org.grails.plugins.springsecurity.service.AuthenticateService authenticateService = new org.grails.plugins.springsecurity.service.AuthenticateService()
def isAdmin = authenticateService.ifAllGranted('ROLE_ADMIN')
if(isAdmin) {
println 'I am Admin'
}
This question is pretty old, but I thought I'd post at least an answer that seems to work with Grails 2.0. If you are using the spring security plugin, there's a tag lib included called grails.plugins.springsecurity.SecurityTagLib.
The tag-lib has a protected method, hasAccess() which can take the same params map that you give the g:link tag. So, if you extend SecurityTagLib, you can call hasAccess() and get the behavior you want. Why this isn't externalized into a service that can be injected is beyond me as the functionality seems to fulfill an obvious need.
We use this to wrap the g:link tag and only generate a link of the user has access to the target page:
def link = { attrs, body ->
if( hasAccess(attrs.clone(), "link") ) {
out << g.link(attrs, body)
}
else {
out << body()
}
}
When dealing with permissions in views and taglibs, you can use the AuthorizeTagLib that's provided by the plugin.
For example, if you don't want a menu item to appear in your list for unauthenticated users, you might use:
<g:isLoggedIn>
<li>Restricted Link</li>
</g:isLoggedIn>
If you have more specific roles defined and those roles are tied to your controller/action request mapping, you can use other tags, such as:
<g:ifAllGranted role="ROLE_ADMINISTRATOR">
<li>Administrator Link</li>
</g:ifAllGranted>
In my experience, there's not yet a good way to tie the request mapping to your markup - I think you're going to have to use some of the above tags to limit access to content within a particular GSP.
I think that Burt Beckwith has a future modification (and is currently providing a beta version) to the plugin that integrates some ACL stuff that might solve this problem better in the future, but for now, I think the best approach is a hybrid request map + GSP tags one.
Not sure of the situation when this question was originally asked, but now you can check to see if a user is in a specific role by using SpringSecurityUtils.ifAllGranted() which takes a single String which is a comma delimited list of roles. It will return true if the current user belongs to all of them.
if(SpringSecurityUtils.ifAllGranted('ROLE_ADMIN,ROLE_USER')) {
Obviously, you can simply pass one role to the function if that is all you need. SpringSecurityUtils also has methods like ifAnyGranted, ifNotGranted, etc, so it should work for whatever it is you are trying to accomplish.
SpringSecurityUtils is a static API, so you don't need to create a private member named springSecurityUtils or anything like that.
You have to configure the file config/SecurityConfig.groovy (if it does not exists, create it, this overrides the default Security Configuration)
Add this entry:
requestMapString = """\
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/=IS_AUTHENTICATED_REMEMBERED
/login/auth=IS_AUTHENTICATED_ANONYMOUSLY
/login/authajax=IS_AUTHENTICATED_ANONYMOUSLY
/login/authfail=IS_AUTHENTICATED_ANONYMOUSLY
/js/**=IS_AUTHENTICATED_ANONYMOUSLY
/css/**=IS_AUTHENTICATED_ANONYMOUSLY
/images/**=IS_AUTHENTICATED_ANONYMOUSLY
/plugins/**=IS_AUTHENTICATED_ANONYMOUSLY
/**=IS_AUTHENTICATED_REMEMBERED
"""
This is means that you have to log in to enter the site. But all the resources (css, js, images, etc) is accessed without authentification.
If you want specific role only enter specific controller:
For example, for UserController:
/user/**=ROLE_ADMIN
/role/**=ROLE_ADMIN
For more information: http://www.grails.org/AcegiSecurity+Plugin+-+Securing+URLs
Regards
As far as I can tell, there doesn't look like there's an easy way to do it.
You can inject an instance of the grails AuthenticatedVetoableDecisionManager which is a concrete class of spring's AbstractAccessDecisionManager by doing this:
def accessDecisionManager
This has a "decide" method on it that takes 3 parameters
decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
This is probably the method that you'd need to call and pass in the right things to figure out if the user with the auth creds can access that "object" (which looks like it's normally a request/response). Some additional digging around might prove out something workable here.
Short term, it's probably easier to use the ifAnyGranted taglib as another poster mentions.
I'm not sure about in Groovy, but in Java (so I assume Groovy too...) you could do (minus NPE checks):
GrantedAuthority[] authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
boolean isAdmin = false;
for(GrantedAuthority authority : authorities) {
String role = authority.getAuthority();
if(role != null && role.equals("ROLE_ADMIN")) {
isAdmin = true;
break;
}
}
As for knowing whether or not the action is supported, you'd have to call the RequestMap service to get the roles for the mapping and see if it contains the found user role.