I want to create a custom permission handler using grails spring security plugin.
Imagine I have a User class and a company class with a many-to-many association.
I want only allow users to call a method called "delete company" when they belong to the company. Example:
class User {
static hasMany= [companies:Company]
static belongsTo = [Company]
}
class Company {
static hasMany = [users:User]
}
the controller action looks like to following:
def deleteCompany(Long id) {
}
I only want to allow users to call the method that are part of the company. So when
assert Company.get(id}.users.find { it == currentUser }
This is just a simplified example. The actual structure is much more complex. That's why I want to use the power of spring security for this.
I already played around with spring security ACL but it seems that I can only use custom permissions in services but not in controllers
You could use beforeInterceptor in your controller:
def springSecurityService
def beforeInterceptor=[action:this.&auth]
private auth = {
def toBeCheckedId=params.id
if(toBeCheckedId
&& Company.get(toBeCheckedId}.users.find { it == springSecurityService.currentUser }){
redirect action:someHandlingAction
return false
}
}
}
Related
I am creating a restful service from Grails and would like to add a user field on every table which on insert or on update automatically changed to the corresponding user making the change.
Is there any way to create a variable like version or dateCreated which are automatically bound to each domain where defined and added automatically updated on creation or update?
1. Define an abstract domain class, put your common fields on it (on src/groovy)
2. Extend it by each domain object that needs the common fields
So your abstract domain class will look like this (also note how the updateByUser and createdByUser are automatically determined):
abstract class AbstractDomain {
transient securityService
User createdByUser;
User updatedByUser;
def beforeInsert() {
if(null != securityService) {
User currentUser = securityService.getCurrentUser()
if(null != currentUser){
this.createdByUser = currentUser
}
}
}
def beforeUpdate() {
if(null != securityService) {
User currentUser = securityService.getCurrentUser()
if(null != currentUser){
this.updatedByUser = currentUser
}
}
}
}
Is there any way to define the users that can use my application in a list in Config.groovy? This will be using Grails 2.2.3 and the latest versions of Spring Security Core and Spring Security LDAP.
We use Active Directory for authentication, and only 2 or 3 people will use this little application, so it doesn't seem worthy of making an AD Group for just this app. It would be simpler to define a list, and any time there is a new hire instead of adding them to the AD group all I have to do is add their name to the external Grails config.
I would like to do something like the following:
SomeController.groovy
#Secured("authentication.name in grailsApplication.config.my.app.usersList")
class SomeController {
}
Then in Config.groovy put this code:
my.app.usersList = ['Bill', 'Tom', 'Rick']
Is this possible? If so, is this a terrible idea? Thanks a lot.
That seems really silly. Why not have the list of users in a table? Then you can add/remove from that table without have to modify the application.
I currently do this and in my UserDetailsContextMapper I make sure the username already exists in the Users table.
You need a custom authenticator that will try to access your Active Directory and if authenticated, will look into Grails properties to check if the username is allowed to login.
This is the class that I use. I changed the code to validate the config:
class ActiveDirectoryAuthenticator {
private DefaultSpringSecurityContextSource contextFactory
private String principalSuffix = ""
def grailsApplication
public DirContextOperations authenticate(Authentication authentication) {
// Grab the username and password out of the authentication object.
String principal = authentication.getName() + "#" + principalSuffix
String password = ""
if (authentication.getCredentials() != null) {
password = authentication.getCredentials().toString()
}
// If we have a valid username and password, try to authenticate.
if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
try {
String provider = contextFactory.getUrls()[0]
Hashtable authEnv = new Hashtable(11)
authEnv.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory")
authEnv.put(Context.PROVIDER_URL, provider)
authEnv.put(Context.SECURITY_AUTHENTICATION, "simple")
authEnv.put(Context.SECURITY_PRINCIPAL, principal)
authEnv.put(Context.SECURITY_CREDENTIALS, password)
javax.naming.directory.DirContext authContext = new InitialDirContext(authEnv)
//here validate the user against your config.
if(!authentication.getName() in grailsApplication.config.adUsersAllowed) {
throw new BadCredentialsException("User not allowed.")
}
DirContextOperations authAdapter = new DirContextAdapter()
authAdapter.addAttributeValue("ldapContext", authContext)
return authAdapter
} catch ( NamingException ex ) {
throw new BadCredentialsException(ex.message)
}
} else {
throw new BadCredentialsException("Incorrect username or password")
}
}
public DefaultSpringSecurityContextSource getContextFactory() {
return contextFactory
}
/**
* Set the context factory to use for generating a new LDAP context.
*
* #param contextFactory
*/
public void setContextFactory(DefaultSpringSecurityContextSource contextFactory) {
this.contextFactory = contextFactory
}
public String getPrincipalSuffix() {
return principalSuffix
}
/**
* Set the string to be prepended to all principal names prior to attempting authentication
* against the LDAP server. (For example, if the Active Directory wants the domain-name-plus
* backslash prepended, use this.)
*
* #param principalPrefix
*/
public void setPrincipalSuffix(String principalSuffix) {
if (principalSuffix != null) {
this.principalSuffix = principalSuffix
} else {
this.principalSuffix = ""
}
}
}
Declare it as your ldapAuthenticator in resources.groovy:
ldapAuthenticator(ActiveDirectoryAuthenticator) {
contextFactory = ref('contextSource')
principalSuffix = 'domain.local' //your domain suffix
grailsApplication = ref('grailsApplication')
}
The downside is that you need to restart your context when you change config.groovy
In your controllers just use #Secured('IS_AUTHENTICATED_FULLY')
I do not think you can do that because annotations are resolved at compile time and not in runtime. Config properties will be read during the application runtime so you I fear you have to end up doing:
#Secured(["authentication.name in ['Bill', 'Tom', 'Rick']"])
class SomeController {
}
If I remember correctly the #Secured annotation cannot be used for other things than comparing roles. But you should be able to do this with spring securities #PreAuthorize and #PostAuthorize annotations. When using grails the easiest way to setup these annotations is installing the spring security ACL plugin.
Within #PreAuthorize and #PostAuthorize you can use SPEL expressions which are more flexible. Unfortunatelly SPEL does not provide an in operator. However you can delegate the security check to a service:
#PreAuthorize('#securityService.canAccess(authentication)')
public void test() {
println "test?"
}
With the # symbol you can reference other beans like services within expression. Here the method securityService.canAccess() is called to evaluate if the logged in user can access this method.
To use this you have to configure a BeanResolver. I wrote some more details about configuring a BeanResolver here.
Within securityService you can now do:
class SecurityService {
def grailsApplication
public boolean canAccess(Authentication auth) {
return grailsApplication.config.myList.contains(auth.name)
}
}
In general I would not recommend to use a configuration value for validating the user in security checks. The groovy configuration will be compiled so you cannot easily add a new user without redeploying your application.
I'm trying to apply annotation-based security to my Grails app.
Peter says here that we should be using Shiro's annotations instead of the quasi-deprecated grails-shiro plugin annotations.
How does one get that working?
I'm finding the grails-shiro plugin strays from how Shiro does things, what with the Realm-converting and all. Has anyone tried implementing Shiro directly rather than using the grails plugin? Any success?
Thanks,
Graham.
G'day I have taken over the Grails-Shiro plugin project. I'm currently re-writing the functional tests and can confirm that the shiro annotations do work, with a couple of caveats:
You can use them in controllers on method actions in grails 2.x
You can use them on Service methods
They don't currently work on service classes (I'm investigating this)
e.g. this works on a service
class SecuredMethodsService {
def methodOne() {
return 'one'
}
#RequiresGuest
def methodTwo() {
return 'two'
}
#RequiresUser
def methodThree() {
return 'three'
}
#RequiresAuthentication
def methodFour() {
return 'four'
}
#RequiresRoles('User')
def methodFive() {
return 'five'
}
#RequiresPermissions("book:view")
def methodSix() {
return 'six'
}
}
or in a controller on an action method like this:
#RequiresAuthentication
def unrestricted() {
render(view: 'simple', model: [msg: "secure action"])
}
When using annotations you may need to add an "afterView" filter to catch the AuthorizationException thrown by the annotation e.g.
class ShiroSecurityFilters {
def filters = {
all(uri: "/**") {
before = {
// Ignore direct views (e.g. the default main index page).
if (!controllerName) return true
// Access control by convention.
accessControl()
}
afterView = { e ->
while (e && !(e instanceof AuthorizationException)) {
e = e.cause
}
if (e instanceof AuthorizationException) {
if (e instanceof UnauthenticatedException) {
// User is not authenticated, so redirect to the login page.
flash.message = "You need to be logged in to continue."
redirect(
controller: 'auth',
action: 'login',
params: [targetUri: request.forwardURI - request.contextPath])
} else {
redirect(controller: 'auth', action: 'unauthorized')
}
}
}
}
}
}
I hope that helps. A new version of the plugin should be released RSN.
Cheers,
Peter.
Since nobody is anwering...
I don't know how you get the annotations to work, but I've used shiro in several Grails projects and didn't miss them... So why do you need them?
When you need to permission explicit roles, you can just create some ShiroRoles and assign star-permissions to them: 'book:*' allows a role to execute all actions on the book controller. 'book:list,show' allows a role to only list or show books.
When you need implicit permissions, use a filter. So if you want to give someone access if she is (for instance) the boss of someone, just fetch the object on which you want to decide in a filter and make a decision.
When you need switches in you gsp-code (e.g. show this only if it's an admin), use the shiro tags. Just unzip the shiro plugin and look for the taglibrary. It is well documented.
HTH
I am trying to set a variable for the current user (a POJO) in all views so I can get things like the user name and check their role on every view (including the default layout). How can I setup something (e.g. currentUser) in grails so that it is accessible in every grails view like so:
<div>${currentUser.name}</div>
or like this:
<g:if test="${currentUser.admin}">ADMIN</g:if>
You want to use a grails filter. Using a filter, you can specify which controllers and methods (using wild cards) you want to intercept using before/after and afterView methods.
This makes it easy to stuff a new variable into the model so it's available in a view. Here's an example that uses the acegi plugin authenticateService:
class SecurityFilters {
def authenticateService
def filters = {
all(controller:'*', action:'*') {
after = { model ->
def principal = authenticateService.principal()
if (principal != null && principal != 'anonymousUser') {
model?.loggedInUser = principal?.domainClass
log.debug("SecurityFilter: adding current user to model = $model")
} else {
log.debug("SecurityFilter: anonymous user, model = $model")
}
}
}
}
}
You can use the session scope to store the variable. Your calls would change to:
<div>${session.currentUser.name}</div>
and
<g:if test="${session.currentUser.admin}">ADMIN</g:if>
And you would set the variable like so in a controller:
session.currentUser = XXXXX
I recently moved from the JSecurity plugin to Spring Security. How do I get the authenticated user from within my controllers?
It's not currently documented, but in the plugin installation file, there are 3 methods that it adds to every controller so that you don't actually have to inject the authenticationService:
private void addControllerMethods(MetaClass mc) {
mc.getAuthUserDomain = {
def principal = SCH.context?.authentication?.principal
if (principal != null && principal != 'anonymousUser') {
return principal?.domainClass
}
return null
}
mc.getPrincipalInfo = {
return SCH.context?.authentication?.principal
}
mc.isUserLogon = {
def principal = SCH.context?.authentication?.principal
return principal != null && principal != 'anonymousUser'
}
}
This means that you can just call
principalInfo
To get the principal object. It also has "isUserLogin" to see if the user is logged and "authUserDomain" to get the actual domain class instance (the Person/User) associated with the principal of the logged in user.
The following code is from the Spring Security Core Plugin (Version: 1.1.2) - Reference Documentation - Section 6.2
grails.plugins.springsecurity.SpringSecurityService provides security utility functions. It is a regular Grails service, so you use dependency injection to inject it into a controller, service, taglib, and so on:
class SomeController {
def springSecurityService
def someAction = {
def user = springSecurityService.currentUser
…
}
}
I'm using 0.5.1 and the following works for me:
class EventController {
def authenticateService
def list = {
def user = authenticateService.principal()
def username = user?.getUsername()
.....
.....
}
}
Nowadays, I think the way to do it is:
def user = getAuthenticatedUser()
You can get current User by this way also
class AnyController {
def springSecurityService
def someAction = {
def user = User.get(springSecurityService.principal.id)
}
}
Use this code:
if (springSecurityService.isLoggedIn()){
println "Logged In"
}