04.11
The Spring-Security plugins for Grails are awesome! They got me 90% of the way there for pretty much everything I needed to do from a security standpoint for Mural Mate. One requirement that I needed to code my own solution for though was the desire to only allow the particular Owner (i.e. person who created it) update that League, Team, Classified, etc. If it was just Admins it would have been incredibly easy by just using a new Role (ROLE_ADMIN), but obviously I want the Team Owner, League Owner, Classified Owner, etc to be able to update their own stuff, but not anybody else. I couldn’t use a new role (i.e. ROLE_OWNER) for this because a user might be an owner for one League, but obviously not all of them. Also, Admins need to be able to update anything in the system. So, I dove in and this is what I came up with!
I knew I was going to need two things, a taglib and a service. The taglib is needed so that I can hide the edit and delete buttons from everybody except the particular owner and admins in GSPs and the service is used to abstract the logic for controllers. Let’s start with the service first:
/**
* Determines whether the current user
* has the authority to edit the instance
*
* @attr instance the instance to evaluate
**/
boolean hasModifyAuth(instance) {
if (SpringSecurityUtils.ifAnyGranted("ROLE_ADMIN")
|| instance.owner.id == springSecurityService.getCurrentUser().id) {
return true
} else {
return false
}
}
This service method is pretty simple, but does the trick! It checks if the current user is an admin or the owner for whatever instance is passed to it (all my domain classes that I want to do this with have an owner property of type User). Onto the taglib:
static namespace = "mm"
/**
* Renders the body if user has authority
*
* @attr value REQUIRED the field value
*/
def ifModifyAuth = { attrs, body ->
def instance = assertAttribute("value", attrs, "ifModifyAuth")
if (SpringSecurityUtils.ifAnyGranted("ROLE_ADMIN")
|| instance.owner.id == springSecurityService.getCurrentUser().id) {
out << body()
}
}
protected assertAttribute(String name, attrs, String tag) {
if (!attrs.containsKey(name)) {
throwTagError "Tag [$tag] is missing required attribute [$name]"
}
attrs.remove name
}
Once again pretty simple, but works perfectly! I copied the assertAttribute method idea from the Spring-Security-Core plugin and with the REQUIRED in the JavaDocs, SpringSource Tool Suite auto-completes the value attribute for the tags! Then I just do something like this in my GSPs:
<mm:ifModifyAuth value="${teamInstance}">
... edit and delete links ...
</mm:ifModifyAuth>
And this in my controller that I wanted to restrict modify access:
def beforeInterceptor =
[action:this.&auth,only:['update', 'delete', 'edit']]
def auth() {
def instance = Team.get(params.id)
if (instance) {
if (!muralMateSecurityService.hasModifyAuth(instance)) {
flash.message = "You do not have access to modify this instance"
redirect(action: "list")
return false
}
} else {
flash.message = "Instance not found"
redirect(action: "list")
return false
}
}
And there we have it! A reusable taglib for my views and service for my controllers implementing my custom authorization requirements!