Skip to content

Instantly share code, notes, and snippets.

@vahidhedayati
Last active August 29, 2015 14:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vahidhedayati/71d92f8153ade5d732b3 to your computer and use it in GitHub Desktop.
Save vahidhedayati/71d92f8153ade5d732b3 to your computer and use it in GitHub Desktop.
Apache-Shiro-LDAP-Dynamic-Permissions for grails

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:

https://github.com/vahidhedayati/customshiro/blob/master/grails-app/services/customshiro/security/LdapService.groovy#L56-L137

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment