This is a guide on how to create dynamic security control for a grails application, in effect to control the actual security of your controller and actions for them via your own customised domainClasses and its own auto generated controllers views for the new domain classes. In this example the application refers to two additional domain objects: Environment and Application class. It also includes these as part of the authentication process. So if url being:
/doSomething/somethingAction?appId=XX&envId=XX
where controller is doSomething and action is somethingAction in that controller. It does a further check against those specifics on that somethingAction of appId and envId also matching security checks.
So if all you wish to do is basic dynamic controller and its action control then simply remove any reference do environment/application.
The below code would work in conjunction with a customised shiro configuration found:
https://github.com/vahidhedayati/customshiro/
I have written some funky stuff to store the users departmentId manager etc that was coming back from LDAP which then get stored as part of the user credintial within the app:
So install above app or follow it then look at below and keep in mind the extra complexity I have added to show how you can customise your authentication further than just controller/action..
SecurityFilters.groovy
import grails.util.Holders
import javax.servlet.http.HttpSession
import myApp.PermissionGroups
import myApp.Permissions
import myApp.UserDetails
import myApp.UserPermissions
import org.apache.shiro.SecurityUtils
import org.springframework.web.context.request.RequestContextHolder
class SecurityFilters {
def filters = {
catchRememberMeCookie(url: "/**") {
before = {
def appid
if (controllerName == "servers") {
if (params.aid) {
appid=params.aid
}else{
appid = params.id
}
}
def subject = SecurityUtils.subject
if (((controllerName.equals('admin')) && (actionName.equals('denied')) )||
(controllerName.equals('contactUs'))||(controllerName.equals('simpleCaptcha'))||
(controllerName.equals('utils'))) {
checkSession(session,request)
request.accessAllowed = true
return
}else if ((controllerName.equals('auth'))&&(actionName.equals('signOut'))) {
request.cookies.find({ it.name == "rememberMe" }).each {
getSession()
//log.info "Removing rememberMe cookie: ${it.value}"
it.maxAge = 0
response.addCookie it
// def subject = SecurityUtils.subject
log.info "Logging user '${subject.principal}' out"
subject.logout()
}
request.accessAllowed = true
return
} else{
def originalRequestParams = [controller:controllerName, action:actionName]
originalRequestParams.putAll(params)
def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
def confirmurl= g.createLink(controller: controllerName, action: actionName, params:params, absolute: 'true' )
session.redirectit="no"
session.originalRequestParams = originalRequestParams
session.fromPlace=request.getHeader('referer')
session.currentPlace=confirmurl
if(!session.user) {
boolean not_found = checkSession(session,request)
boolean userAllowed=PermitUser(subject.principal, params, appid, actionName,controllerName)
if (userAllowed==false) {
request.accessAllowed = false
redirect(controller:'admin',action:'denied')
return false
}else{
request.accessAllowed = true
return
}
if (not_found) {
session.lastURL=request.getHeader('referer')
if (!session.lastURL.equals(confirmurl)) {
session.redirectit="yes"
session.lastPlace=confirmurl
}
redirect(controller:'admin',action:'denied')
request.accessAllowed = false
return false
}
}else{
boolean userAllowed=PermitUser(subject.principal, params, appid, actionName,controllerName)
if (userAllowed==false) {
request.accessAllowed = false
redirect(controller:'admin',action:'denied')
return false
}else{
request.accessAllowed = true
return
}
}
}
}
}
}
private Boolean checkSession(session,request) {
boolean not_found = true
request.cookies.find({ it.name == "rememberMe" }).each {
not_found = false
getSession() // Ensure a Session exists before we start the response
def userid = SecurityUtils.subject.principal
log.info "Found rememberMe cookie (reauthenticating user ${userid})"
//response.addCookie it
if (log.isDebugEnabled()) {
//log.debug "principal for ${cmd.username}=${userid}"
}
session.user=userid
if (!session.usersemail) {
def uemail=UserDetails.findByUserId(userid)
session.usersemail=uemail.emailAddress
session.usersname=uemail.usersname
}
}
return not_found
}
def checkUser() {
// Abstract classes i.e. what we require in this scenario
def config = Holders.config
def locations = config.grails.config.locations
def subject = SecurityUtils.subject
locations.each {
String configFileName = it.split("file:")[1]
config.merge(new ConfigSlurper().parse(new File(configFileName).text))
}
def originalRequestParams = [controller:controllerName, action:actionName]
originalRequestParams.putAll(params)
def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
def confirmurl= g.createLink(controller: controllerName, action: actionName, params:params, absolute: 'true' )
session.redirectit="no"
session.originalRequestParams = originalRequestParams
session.fromPlace=request.getHeader('referer')
session.currentPlace=confirmurl
if(!session.user) {
session.lastURL=request.getHeader('referer')
if (!session.lastURL.equals(confirmurl)) {
session.redirectit="yes"
session.lastPlace=confirmurl
}
redirect(controller:'admin',action:'denied')
return false
}else{
def appid = params.id ?: params.aid
boolean userAllowed=PermitUser(subject.principal, params, appid, actionName,controllerName)
if (userAllowed==false) {
redirect(controller:'admin',action:'denied')
return false
}
}
}
public Boolean PermitUser(String userId, def params,String appId, String action,String controller) {
boolean authorised=false
def usergroups=getSession().usersgroups
def userdepartment=getSession().usersdepartment
if (!appId) {
appId=params.aid
}
def Uperm=UserPermissions.where {
userId==userId
}
def Udept=UserPermissions.where {
departmentId==userdepartment
}
def foundperms=[]
int founduperm=0
founduperm=Uperm.list().size()
int foundudept=0
foundudept=Udept.list().size()
if ( (founduperm>0) || (foundudept>0)) {
Uperm.each{ foundu->
foundu.permissiongroups.each {
foundperms.add(it.id)
}
}
}
int foundugroup=0
usergroups.each { gr ->
def gg=UserPermissions.withCriteria {
or {
//like('ldapGroup', '%' + gr + '%')
eq('ldapGroup', gr)
}
}
gg.each{ foundu->
foundu.permissiongroups.each {
foundperms.add(it.id)
}
}
}
def permGroups
foundugroup=foundperms.size()
if ( (foundugroup>0)|| (founduperm>0) || (foundudept>0)) {
foundperms.each{ foundu->
def gid=foundu
permGroups=PermissionGroups.where {
id==gid as long
}
int foundugrp=0
foundugrp=permGroups.list().size()
if (foundugrp>0) {
permGroups.each{ foundg->
foundg.permissions.each { foundgp->
def pid=foundgp.id
def perms=Permissions.where {
id==pid
}
int foundper=0
foundper=perms.list().size()
if (foundper>0) {
perms.each{ founduper->
if (
(
( (founduper.envId==params.envid) || (founduper.envId=='*'))
&&
( (founduper.controllerAction==action) || (founduper.controllerAction=='*'))
&&
( (founduper.controllerName==controller) || (founduper.controllerName=='*'))
&&
( (founduper.appId==appId) || (founduper.appId=='*'))
) {
authorised=true
}
}
}
}
}
}
}
}
return authorised
}
private HttpSession getSession() {
return RequestContextHolder.currentRequestAttributes().getSession()
}
}
UserPermissions.groovy
package myApp
class UserPermissions {
String userId
String ldapGroup
String departmentId
String addedby
Date dateCreated
Date lastUpdated
static optionals = [ 'addedby', 'userid','ldapGroup']
static hasMany=[permissiongroups: PermissionGroups]
static mapping = {
cache true
permissiongroups cascade: 'lock'
userId defaultValue: ''
departmentId defaultValue: ''
ldapGroup defaultValue: ''
}
String toString() { "${ldapGroup}: ${userId}"}
static constraints = {
ldapGroup (nullable:true)
userId (nullable:true)
departmentId (nullable:true)
}
}
UserDetails.groovy
package myApp
class UserDetails {
String userId
String usersname
String emailAddress
String deptId
String manager
Date dateCreated
///Date lastUpdated
static mapping = {
cache true
}
static constraints = {
userId (maxLength: 100, blank: false, unique: true)
}
String toString() { "${userId}"}
}
Permissions.groovy
package myaApp
class Permissions {
String ruleName
String controllerName="*"
String controllerAction="*"
String envId="*"
String appId="*"
String addedby
static belongsTo=[PermissionGroups]
Date dateCreated
Date lastUpdated
static optionals = [ 'addedby']
String toString() { "${ruleName}"}
static constraints = { }
static mapping = {
cache true
}
}
PermissionGroups.groovy
package myApp
class PermissionGroups {
String name
static belongsTo=[UserPermissions]
static hasMany=[permissions:Permissions]
String addedby
Date dateCreated
Date lastUpdated
static optionals = [ 'addedby']
String toString() { "${name}"}
static mapping = {
cache true
permissions cache: true
permissions cascade: 'lock'
}
static constraints = {}
}
Now generate CRUD for all those domain classes:
permissions/form.gsp change to:
<%@ page import="myApp.Permissions" %>
<div class="fieldcontain ${hasErrors(bean: permissionsInstance, field: 'ruleName', 'error')} ">
<label for="ruleName">
<g:message code="permissions.ruleName.label" default="Rule Name" />
</label>
<g:textField name="ruleName" value="${permissionsInstance?.ruleName}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: permissionsInstance, field: 'controllerName', 'error')} ">
<label for="controllerName">
<g:message code="permissions.controllerName.label" default="Controller Name" />
</label>
<g:selectController id="controllerName" name="controllerName"
searchField='name'
collectField='name'
appendValue='*'
appendName='All Items'
noSelection="['*': 'All Controllers']"
setId="ControllerActions"
value="${permissionsInstance.controllerName}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: permissionsInstance, field: 'controllerAction', 'error')} ">
<label for="controllerAction">
<g:message code="permissions.controllerAction.label" default="Controller Action" />
</label>
<g:select name="controllerAction" id="ControllerActions"
optionKey="name" optionValue="name" value=""
from="[]" noSelection="['*': 'All Controller Actions']" />
</div>
<div class="fieldcontain ${hasErrors(bean: permissionsInstance, field: 'addedby', 'error')} ">
<g:hiddenField name="addedby" value="${session.user}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: permissionsInstance, field: 'envId', 'error')} ">
<label for="envId">
<g:message code="permissions.appId.label" default="Environment" />
</label>
<g:selectPrimary id="envId" name="envId"
domain='myApp.Environments'
domain2='myApp.Applications'
bindid="environments.id"
searchField='name'
collectField='id'
appendValue='*'
appendName='All Items'
noSelection="['*': 'All Environments']"
setId="appId"
value="${permissionsInstance.envId}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: permissionsInstance, field: 'appId', 'error')} ">
<label for="appId">
<g:message code="permissions.appId.label" default="App Id" />
</label>
<g:select name="appId" id="appId"
optionKey="id" optionValue="name" value="${permissionsInstance.appId}"
from="[]" noSelection="['*': 'All Applications']" />
</div>
using selectPrimary part of ajaxdependancy plugin