Acegi ACLs are a daunting subject. However I have recently integrated Acegi ACL into the Grails Acegi Plugin, and with that made it much easier to implement fine-grained ACLs in your Grails projects.
Prerequisites
Please refer to the Resources section at the end of this article for downloads
There are primarily two use-cases for the current Acegi Plugin's ACL extensions:
- Protecting Method Invocations
- Protecting Domain object instances
1. Protecting Method Invocations
You can protect method invocations by requiring logged in user to be in certain roles(ROLE_xxx). This check ignores all method parameters, and will throw an exception if the current user is not in the required Role.
e.g. method signature:
1:public ReportLog getReportLog(Integer reportId){
2: return Report.get(reportId).log
3:}
You can protect domain object instances (tuples in database) using a variety of fine-grained access controls. Currently Acegi supports three (3) distinct methods out of the box, two (2) of which are currently directly supported by the Grails plugin.
2.1. Applying ACLs to method parameters (CURRENTLY UNSUPPORTED!)
This ACL checks the first parameter of a method for a matching domain class to see if current user has ACL on that object instance (ACL_ENTRY).
e.g. method signature:
1:public void saveReport(Report report){
2: report.save()
3:}
to see if the current user has an ACL granting permission for the domain object
instance in the parameter.
2.2 Filtering collections using ACLs
You can filter the domain objects in a collection returned from a method to see if the user has appropriate ACLs on each object instance in the collection. Items in collection to which the current user does not have access to are automatically removed from the list. (AFTER_ACL_COLLECTION_READ)
e.g. method signature:
1:public List getAllReports(){
2: return Report.list()
3:}
4:
You can check the domain object returned from a method invocation to see if the current user as an appropriate ACL granting permission to that domain object. (AFTER_ACL_READ)
e.g. method signature:
1:public Report getReport(Integer id){
2: return Report.get(id)
3:}
4:
Protecting a Service Class
Let us now look at a service class which has ACL protection turned on:
1:class ReportService{
2:
3: static acegiACL = {
4: Report{
5: getReportLog(['ROLE_USER', 'ROLE_ADMIN'])
6: getAllReports(['ROLE_USER', 'AFTER_ACL_COLLECTION_READ'])
7: getReport(['ROLE_USER', 'AFTER_ACL_READ'])
8: saveReport(['ACL_REPORT_WRITE']) //CURRENTLY UNSUPPORTED!
9: }
10: }
11:
12: boolean transactional = true
13:
14: def getReport(Integer reportId) {
15: return Report.get( reportId )
16: }
17:
18: def getAllReports(){
19: return Report.list()
20: }
21:
22: def getReportLog(Integer reportId){
23: return Report.get(reportId).log
24: }
25:
26: def saveReport(Report report){
27: report.save()
28: }
29:
30:}
NOTE: The 'acegiACL' closure is not currently supported on domain objects.
The 'acegiACL' closure can be explained as follows:
- line 3 - "Report {... "
- refers to the domain object we are protecting. This name is used when applying ACL controls for AFTER_ACL_READ and AFTER_ACL_COLLECTION_READ.
- line 5 - getReportLog(['ROLE_USER', 'ROLE_ADMIN'])
- Only allow users in the ROLE_USER and ROLE_ADMIN roles to call the getReportLog() method
- line 6 - getAllReports(['ROLE_USER', 'AFTER_ACL_COLLECTION_READ'])
- Only allow users in the ROLE_USER role to call the getAllReports() method.
- Additionally filter all returned results checking that user has READ permission on the domain objects.
- line 7 - getReport(['ROLE_USER', 'AFTER_ACL_READ'])
- Only allow users in the ROLE_USER role to call this method.
- Additionally check the returned domain instance to see if the current user has the READ permission on the object.
- line 8 - saveReport(['ACL_REPORT_WRITE']) - (UNSUPPORTED!)
- Check the custom permission to verify that the current user has WRITE permission on domain object instance passed into the method.
- Allow fine-grained ACLs of the type ACL_REPORT_SAVE, by automatically creating instances of AclEntryVoter
- Create a mechanism allowing users to easily hook up their own custom instances of AbstractAclVoter into the ACL mechanism
Resources:
- Acegi Security - http://www.acegisecurity.org/
- Download example application demonstrating ACL integration here.
- Download development version of Grails Acegi Plugin with ACL here.
Update: [7May2008] - This plugin download is NOT the same as the 0.3 version of the plugin available for download from grails.codehaus.org!