From 8d81f9a24eafe99bf7d07fbbb8a13314a76b9611 Mon Sep 17 00:00:00 2001 From: Brian Koehmstedt <1261658+bkoehm@users.noreply.github.com> Date: Wed, 4 Dec 2024 07:44:03 -0800 Subject: [PATCH 1/6] Grails 7: (#46) add notable changes to README.md for the 5.0.x version of the plugin --- README.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 63a82d9..d21e4e6 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,45 @@ See [documentation](https://grails-plugins.github.io/grails-spring-security-acl/ To run the tests exeucte: -`./gradlew -Dgeb.env=chromeHeadless check` \ No newline at end of file +`./gradlew -Dgeb.env=chromeHeadless check` + +## v5.0.0 changes + +### Caching + +The default cache manager has changed to +[JCacheCacheManager](https://docs.spring.io/spring-framework/docs/6.2.0/javadoc-api/org/springframework/cache/jcache/JCacheCacheManager.html). + +### Method parameter discovery + +The behavior of parameter discovery has changed to align with +[Spring Security 6 default](https://docs.spring.io/spring-security/site/docs/6.4.1/api//org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscoverer.html) +behavior. This may require code changes if you are utilizing ACL +annotations that reference method parameters. You will need to add the +[P](https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/core/parameters/P.html) +annotation to reference method parameters. This is documented in the +Spring Security reference doc under the +[Using Method Parameters](https://docs.spring.io/spring-security/reference/servlet/authorization/method-security.html#using_method_parameters) +section. + +Previously if you had code similar to: +``` +@PreAuthorize("hasPermission(#contract, 'write')") +public void updateContact(Contact contact) { + ... +} +``` + +This should be changed to: + +``` +import org.springframework.security.core.parameters.P + +@PreAuthorize("hasPermission(#contract, 'write')") +public void updateContact(@P("contract") Contact contact) { + ... +} +``` + +Since parameter `contract` is referenced in the `@PreAuthorize` annotation, it +should now be annotated with `@P`. From ce29a75a5f70de362156dfb9735f3ae4c30303ad Mon Sep 17 00:00:00 2001 From: Brian Koehmstedt <1261658+bkoehm@users.noreply.github.com> Date: Wed, 4 Dec 2024 07:46:35 -0800 Subject: [PATCH 2/6] Grails 7: (#46) Update documentation with updated links and add @P annotation to examples --- docs/src/docs/installing.adoc | 19 +++++++++++++++---- docs/src/docs/installing/distribution.adoc | 2 +- docs/src/docs/installing/snapshots.adoc | 4 ++-- docs/src/docs/introduction.adoc | 4 ++-- docs/src/docs/tutorial.adoc | 13 +++++++------ docs/src/docs/usage/acls.adoc | 2 +- docs/src/docs/usage/configuration.adoc | 2 +- docs/src/docs/usage/customPermissions.adoc | 2 +- docs/src/docs/usage/domainClasses.adoc | 2 +- docs/src/docs/usage/runAs.adoc | 2 +- docs/src/docs/usage/serviceMethods.adoc | 17 +++++++++-------- 11 files changed, 41 insertions(+), 28 deletions(-) diff --git a/docs/src/docs/installing.adoc b/docs/src/docs/installing.adoc index d75970a..e25adf8 100644 --- a/docs/src/docs/installing.adoc +++ b/docs/src/docs/installing.adoc @@ -15,14 +15,25 @@ dependencies { Different Branches are built for different versions of Grails -* master: Grails 4 -* 3.3.x: Grails 3.3+ +* 5.0.x: Grails 7 +* 4.0: Grails 4 through Grails 6 +* 3.3.x: Grails 3.3 * grails_32: Grails 3.2 * 2.x: Grails 2 -Current version (master) is for Grails 4 only. -Previous version (3.3.x) is only compatible with Grails 3.3.x or higher. +Current version (5.0.x) is for Grails 7 only. + +For Grails 3.3.x use: + +[source, groovy] +.build.gradle +---- +dependencies { + ... + compile 'org.grails.plugins:spring-security-acl:3.2.1' + ... +---- For previous Grails 3 versions ( 3.0.x, 3.1.x and 3.2.x ) use: diff --git a/docs/src/docs/installing/distribution.adoc b/docs/src/docs/installing/distribution.adoc index f1b1a3f..f15e6a2 100644 --- a/docs/src/docs/installing/distribution.adoc +++ b/docs/src/docs/installing/distribution.adoc @@ -1,5 +1,5 @@ [[Distribution]] === Distribution -Grails Spring Security ACL plugin is https://bintray.com/grails/plugins/spring-security-acl[distributed in bintray]. +Grails Spring Security ACL plugin is https://repo.grails.org/ui/native/core/org/grails/plugins/spring-security-acl/[distributed in repo.grails.org]. diff --git a/docs/src/docs/installing/snapshots.adoc b/docs/src/docs/installing/snapshots.adoc index ff63307..61026fa 100644 --- a/docs/src/docs/installing/snapshots.adoc +++ b/docs/src/docs/installing/snapshots.adoc @@ -1,11 +1,11 @@ [[Snapshots]] === Snapshots -Snapshots are published automatically to https://oss.jfrog.org/[Artifactory OSS] on every successful build. +Snapshots are published automatically to https://repo.grails.org/ui/native/core/org/grails/plugins/spring-security-acl/[repo.grails.org] on every successful build. You can use them by defining this Maven repository inside the `repositories` block in your `build.gradle`: [source, groovy] ---- -maven { url "https://oss.jfrog.org/artifactory/oss-snapshot-local" } +maven { url "https://repo.grails.org/grails/core" } ---- \ No newline at end of file diff --git a/docs/src/docs/introduction.adoc b/docs/src/docs/introduction.adoc index af096a8..6330109 100644 --- a/docs/src/docs/introduction.adoc +++ b/docs/src/docs/introduction.adoc @@ -1,10 +1,10 @@ [[introduction]] == Introduction to the Spring Security ACL Plugin -The ACL plugin adds Domain Object Security support to a Grails application that uses Spring Security. It depends on the http://grails.org/plugin/spring-security-core[Spring Security Core plugin]. +The ACL plugin adds Domain Object Security support to a Grails application that uses Spring Security. It depends on the https://github.com/grails/grails-spring-security-core[Spring Security Core plugin]. The core plugin and other extension plugins support restricting access to URLs via rules that include checking a user's authentication status, roles, etc. and the ACL plugin extends this by adding support for restricting access to individual domain class instances. The access can be very fine-grained and can define which actions can be taken on an object - these typically include Read, Create, Write, Delete, and Administer but you're free to define whatever actions you like. To learn about using ACLs in Grails, you can follow the <> and in addition you can download and run a complete Grails application that uses the plugin. Installing and running the application are described in <>. -In addition to this document, you should read the http://docs.spring.io/spring-security/site/docs/5.0.x/reference/htmlsingle/#domain-acls[Spring Security documentation]. \ No newline at end of file +In addition to this document, you should read the https://docs.spring.io/spring-security/reference/servlet/authorization/acls.html[Spring Security documentation]. \ No newline at end of file diff --git a/docs/src/docs/tutorial.adoc b/docs/src/docs/tutorial.adoc index 6fa2a5d..c49d114 100644 --- a/docs/src/docs/tutorial.adoc +++ b/docs/src/docs/tutorial.adoc @@ -26,7 +26,7 @@ and run the `compile` command to resolve the dependencies: $ grails compile .... -This will transitively install the http://grails.org/plugin/spring-security-core[Spring Security Core] plugin, so you'll need to configure that by running the `s2-quickstart` script: +This will transitively install the https://github.com/grails/grails-spring-security-core[Spring Security Core] plugin, so you'll need to configure that by running the `s2-quickstart` script: .... $ grails s2-quickstart com.testacl User Role @@ -76,6 +76,7 @@ import org.springframework.security.acls.model.AccessControlEntry import org.springframework.security.acls.model.MutableAcl import org.springframework.security.acls.model.Permission import org.springframework.security.acls.model.Sid +import org.springframework.security.core.parameters.P import grails.compiler.GrailsCompileStatic import grails.plugin.springsecurity.SpringSecurityService @@ -97,7 +98,7 @@ class ReportService { @PreAuthorize('hasPermission(#report, admin)') @Transactional - void addPermission(Report report, String username, Permission permission) { + void addPermission(@P("report") Report report, String username, Permission permission) { aclUtilService.addPermission report, username, permission } @@ -113,7 +114,7 @@ class ReportService { } @PreAuthorize('hasPermission(#id, "com.testacl.Report", read) or hasPermission(#id, "com.testacl.Report", admin)') - Report get(long id) { + Report get(@P("id") long id) { Report.get id } @@ -129,13 +130,13 @@ class ReportService { @Transactional @PreAuthorize('hasPermission(#report, write) or hasPermission(#report, admin)') - void update(Report report, String name) { + void update(@P("report") Report report, String name) { report.name = name } @Transactional @PreAuthorize('hasPermission(#report, delete) or hasPermission(#report, admin)') - void delete(Report report) { + void delete(@P("report") Report report) { report.delete() // Delete the ACL information as well @@ -144,7 +145,7 @@ class ReportService { @Transactional @PreAuthorize('hasPermission(#report, admin)') - void deletePermission(Report report, Sid recipient, Permission permission) { + void deletePermission(@P("report") Report report, Sid recipient, Permission permission) { MutableAcl acl = (MutableAcl)aclUtilService.readAcl(report) // Remove all permissions associated with this particular diff --git a/docs/src/docs/usage/acls.adoc b/docs/src/docs/usage/acls.adoc index 54a4082..41a3cf2 100644 --- a/docs/src/docs/usage/acls.adoc +++ b/docs/src/docs/usage/acls.adoc @@ -3,7 +3,7 @@ ==== Suggested application changes -To properly display access denied exceptions (e.g. when a user tries to perform an action but doesn't have a grant authorizing it), you should create a mapping in `grails-app/controllers/UrlMappings.groovy` for error code 403. In addition, it's possible to trigger a http://docs.spring.io/spring-security/site/docs/5.0.x/apidocs/org/springframework/security/acls/model/NotFoundException.html[NotFoundException] which will create an error 500, but should be treated like a 403 error, so you should add mappings for these conditions: +To properly display access denied exceptions (e.g. when a user tries to perform an action but doesn't have a grant authorizing it), you should create a mapping in `grails-app/controllers/UrlMappings.groovy` for error code 403. In addition, it's possible to trigger a https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/acls/model/NotFoundException.html[NotFoundException] which will create an error 500, but should be treated like a 403 error, so you should add mappings for these conditions: [source,java] ---- diff --git a/docs/src/docs/usage/configuration.adoc b/docs/src/docs/usage/configuration.adoc index f8bd84a..2045903 100644 --- a/docs/src/docs/usage/configuration.adoc +++ b/docs/src/docs/usage/configuration.adoc @@ -27,7 +27,7 @@ grails.plugin.springsecurity.acl.authority. ==== Run-As Authentication Replacement -There are also two options to configure http://docs.spring.io/spring-security/site/docs/5.0.x/reference/htmlsingle/#runas[Run-As Authentication Replacement]: +There are also two options to configure https://docs.spring.io/spring-security/reference/servlet/authentication/runas.html[Run-As Authentication Replacement]: [width="100%",options="header"] |==================== diff --git a/docs/src/docs/usage/customPermissions.adoc b/docs/src/docs/usage/customPermissions.adoc index c352c54..c72ecff 100644 --- a/docs/src/docs/usage/customPermissions.adoc +++ b/docs/src/docs/usage/customPermissions.adoc @@ -61,7 +61,7 @@ Once this is done you can use the permission like any other, specifying its quot [source,java] ---- @PreAuthorize("hasPermission(#id, 'com.testacl.Report', 'approve')") -Report get(long id) { +Report get(@P("id") long id) { Report.get id } ---- diff --git a/docs/src/docs/usage/domainClasses.adoc b/docs/src/docs/usage/domainClasses.adoc index 8f9676e..945097e 100644 --- a/docs/src/docs/usage/domainClasses.adoc +++ b/docs/src/docs/usage/domainClasses.adoc @@ -57,7 +57,7 @@ By default it's assumed that domain classes have a numeric primary key, but that Finally, the `AclEntry` domain class contains entries representing grants (or denials) of a permission on an object instance to a recipient. The `aclObjectIdentity` field references the domain class instance (since an instance can have many granted permissions). The `sid` field references the recipient. The `granting` field determines whether the entry grants the permission (`true`) or denies it (`false`). The `aceOrder` field specifies the position of the entry, which is important because the entries are evaluated in order and the first matching entry determines whether access is allowed. `auditSuccess` and `auditFailure` determine whether to log success and/or failure events (these both default to `false`). -The `mask` field holds the permission. This can be a source of confusion because the name (and the Spring Security documentation) indicates that it's a bit mask. A value of 1 indicates permission A, a value of 2 indicates permission B, a value of 4 indicates permission C, a value of 8 indicates permission D, etc. So you would think that a value of 5 would indicate a grant of both permission A and C. Unfortunately this is not the case. There is a http://docs.spring.io/spring-security/site/docs/5.0.x/apidocs/org/springframework/security/acls/domain/CumulativePermission.html[CumulativePermission] class that supports this, but the standard classes don't support it (`AclImpl.isGranted()` checks for == rather than using | (bitwise or) so a combined entry would never match). So rather than grouping all permissions for one recipient on one instances into a bit mask, you must create individual records for each. +The `mask` field holds the permission. This can be a source of confusion because the name (and the Spring Security documentation) indicates that it's a bit mask. A value of 1 indicates permission A, a value of 2 indicates permission B, a value of 4 indicates permission C, a value of 8 indicates permission D, etc. So you would think that a value of 5 would indicate a grant of both permission A and C. Unfortunately this is not the case. There is a https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/acls/domain/CumulativePermission.html[CumulativePermission] class that supports this, but the standard classes don't support it (`AclImpl.isGranted()` checks for == rather than using | (bitwise or) so a combined entry would never match). So rather than grouping all permissions for one recipient on one instances into a bit mask, you must create individual records for each. [source,groovy] ---- diff --git a/docs/src/docs/usage/runAs.adoc b/docs/src/docs/usage/runAs.adoc index e570a9c..3a202d6 100644 --- a/docs/src/docs/usage/runAs.adoc +++ b/docs/src/docs/usage/runAs.adoc @@ -1,7 +1,7 @@ [[runAs]] === Run-As Authentication Replacement -Although not strictly related to ACLs, the plugin implements http://docs.spring.io/spring-security/site/docs/5.0.x/reference/htmlsingle/#runas[Run-As Authentication Replacement] since it's related to method security in general. This feature is similar to the Switch User feature of the Spring Security Core plugin, but instead of running as another user until you choose to revert to your original `Authentication`, the temporary authentication switch only lasts for one method invocation. +Although not strictly related to ACLs, the plugin implements https://docs.spring.io/spring-security/reference/servlet/authentication/runas.html[Run-As Authentication Replacement] since it's related to method security in general. This feature is similar to the Switch User feature of the Spring Security Core plugin, but instead of running as another user until you choose to revert to your original `Authentication`, the temporary authentication switch only lasts for one method invocation. For example, in this service `someMethod()` requires that the authenticated user have `ROLE_ADMIN` and will also be granted `ROLE_RUN_AS_SUPERUSER` for the duration of the method only: diff --git a/docs/src/docs/usage/serviceMethods.adoc b/docs/src/docs/usage/serviceMethods.adoc index 3b2d273..78c1666 100644 --- a/docs/src/docs/usage/serviceMethods.adoc +++ b/docs/src/docs/usage/serviceMethods.adoc @@ -5,12 +5,12 @@ There are two primary use cases for ACL security: determining whether a user is There are four annotations: -* http://docs.spring.io/spring-security/site/docs/5.0.x/apidocs/org/springframework/security/access/prepost/PreAuthorize.html[@PreAuthorize] -* http://docs.spring.io/spring-security/site/docs/5.0.x/apidocs/org/springframework/security/access/prepost/PreFilter.html[@PreFilter] -* http://docs.spring.io/spring-security/site/docs/5.0.x/apidocs/org/springframework/security/access/prepost/PostAuthorize.html[@PostAuthorize] -* http://docs.spring.io/spring-security/site/docs/5.0.x/apidocs/org/springframework/security/access/prepost/PostFilter.html[@PostFilter] +* https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/access/prepost/PreAuthorize.html[@PreAuthorize] +* https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/access/prepost/PreFilter.html[@PreFilter] +* https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/access/prepost/PostAuthorize.html[@PostAuthorize] +* https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/access/prepost/PostFilter.html[@PostFilter] -The annotations use security-specific Spring expression language (SpEL) expressions - see http://docs.spring.io/spring-security/site/docs/5.0.x/reference/htmlsingle/#el-access[the documentation] for the available standard and method expressions. +The annotations use security-specific Spring expression language (SpEL) expressions - see https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html#authorization-expressions[the documentation] for the available standard and method expressions. Here's an example service that manages a `Report` domain class and uses these annotations and expressions: @@ -18,6 +18,7 @@ Here's an example service that manages a `Report` domain class and uses these an ---- import org.springframework.security.access.prepost.PostFilter import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.security.core.parameters.P import grails.gorm.transactions.Transactional import com.yourapp.Report @@ -26,7 +27,7 @@ class ReportService { @PreAuthorize("hasPermission(#id, 'com.yourapp.Report', read) or " + "hasPermission(#id, 'com.yourapp.Report', admin)") - Report getReport(long id) { + Report getReport(@P("id") long id) { Report.get(id) } @@ -53,7 +54,7 @@ class ReportService { @Transactional @PreAuthorize("hasPermission(#report, write) or " + "hasPermission(#report, admin)") - Report updateReport(Report report, params) { + Report updateReport(@P("report") Report report, params) { report.properties = params report.save() report @@ -62,7 +63,7 @@ class ReportService { @Transactional @PreAuthorize("hasPermission(#report, delete) or " + "hasPermission(#report, admin)") - void deleteReport(Report report) { + void deleteReport(@P("report") Report report) { report.delete() } } From e05481a08ff15e8136ab5e271bdb60d0b0e21d4a Mon Sep 17 00:00:00 2001 From: Brian Koehmstedt <1261658+bkoehm@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:03:40 -0800 Subject: [PATCH 3/6] Grails 7: Per PR review, change 'compile' in Gradle examples to 'implementation' --- docs/src/docs/installing.adoc | 6 +++--- docs/src/docs/tutorial.adoc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/docs/installing.adoc b/docs/src/docs/installing.adoc index e25adf8..d504755 100644 --- a/docs/src/docs/installing.adoc +++ b/docs/src/docs/installing.adoc @@ -9,7 +9,7 @@ Add an entry in the dependencies block of your `build.gradle file, changing the ---- dependencies { ... - compile 'org.grails.plugins:spring-security-acl:{projectVersion}' + implementation 'org.grails.plugins:spring-security-acl:{projectVersion}' ... ---- @@ -31,7 +31,7 @@ For Grails 3.3.x use: ---- dependencies { ... - compile 'org.grails.plugins:spring-security-acl:3.2.1' + implementation 'org.grails.plugins:spring-security-acl:3.2.1' ... ---- @@ -42,7 +42,7 @@ For previous Grails 3 versions ( 3.0.x, 3.1.x and 3.2.x ) use: ---- dependencies { ... - compile 'org.grails.plugins:spring-security-acl:3.1.1' + implementation 'org.grails.plugins:spring-security-acl:3.1.1' ... ---- diff --git a/docs/src/docs/tutorial.adoc b/docs/src/docs/tutorial.adoc index c49d114..1d9f087 100644 --- a/docs/src/docs/tutorial.adoc +++ b/docs/src/docs/tutorial.adoc @@ -15,7 +15,7 @@ Add a dependency for the plugin by adding it to the `dependencies` block in buil ---- dependencies { ... - compile 'org.grails.plugins:spring-security-acl:{project-version}' + implementation 'org.grails.plugins:spring-security-acl:{project-version}' ... } ---- @@ -570,7 +570,7 @@ and open http://localhost:8080/report. [NOTE] ==== -If you see the error `NoSuchMethodError: org.springframework.cache.ehcache.EhCacheFactoryBean.setMaxEntriesLocalHeap(J)V` when starting the app, comment out the `compile 'org.hibernate:hibernate-ehcache'` dependency and disable the second-level cache in `application.yml` with `use_second_level_cache: false`, and restart. +If you see the error `NoSuchMethodError: org.springframework.cache.ehcache.EhCacheFactoryBean.setMaxEntriesLocalHeap(J)V` when starting the app, comment out the `implementation 'org.hibernate:hibernate-ehcache'` dependency and disable the second-level cache in `application.yml` with `use_second_level_cache: false`, and restart. ==== Login as user1/password1 and you should see the first page of results. But if you click on page 7 or higher, you'll see that you can only see a subset of the ``Report``s. This illustrates one issue with using ACLs to restrict view access to instances; you would have to add joins in your query to the ACL database tables to get an accurate count of the total number of visible instances. From 52a62c4c5a3161c760248afe5a1e49bcf4fb2c42 Mon Sep 17 00:00:00 2001 From: Brian Koehmstedt <1261658+bkoehm@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:11:26 -0800 Subject: [PATCH 4/6] Grails 7: Per PR review, change source,java to source,groovy and source,html to source,xml --- .../docs/AclUtilService/addPermission.adoc | 4 +-- docs/src/docs/AclUtilService/changeOwner.adoc | 4 +-- docs/src/docs/AclUtilService/deleteAcl.adoc | 4 +-- .../docs/AclUtilService/deletePermission.adoc | 4 +-- .../docs/AclUtilService/hasPermission.adoc | 4 +-- docs/src/docs/AclUtilService/readAcl.adoc | 4 +-- docs/src/docs/TagLibraries/notPermitted.adoc | 12 ++++----- docs/src/docs/TagLibraries/permitted.adoc | 12 ++++----- docs/src/docs/tutorial.adoc | 26 +++++++++---------- docs/src/docs/usage/acls.adoc | 12 ++++----- docs/src/docs/usage/configuration.adoc | 4 +-- docs/src/docs/usage/customPermissions.adoc | 8 +++--- docs/src/docs/usage/domainClasses.adoc | 2 +- docs/src/docs/usage/runAs.adoc | 2 +- docs/src/docs/usage/serviceMethods.adoc | 2 +- 15 files changed, 52 insertions(+), 52 deletions(-) diff --git a/docs/src/docs/AclUtilService/addPermission.adoc b/docs/src/docs/AclUtilService/addPermission.adoc index 8525208..73f69dc 100644 --- a/docs/src/docs/AclUtilService/addPermission.adoc +++ b/docs/src/docs/AclUtilService/addPermission.adoc @@ -7,7 +7,7 @@ Grant a permission on a domain object instance to a recipient. .Examples -[source,java] +[source,groovy] ---- aclUtilService.addPermission Report, 1124, 'user123', BasePermission.WRITE @@ -18,7 +18,7 @@ aclUtilService.addPermission reportInstance, 'user123', BasePermission.WRITE `addPermission` has three signatures: -[source,java] +[source,groovy] ---- void addPermission(Class domainClass, long id, recipient, Permission permission) diff --git a/docs/src/docs/AclUtilService/changeOwner.adoc b/docs/src/docs/AclUtilService/changeOwner.adoc index 441cc80..7a3b1de 100644 --- a/docs/src/docs/AclUtilService/changeOwner.adoc +++ b/docs/src/docs/AclUtilService/changeOwner.adoc @@ -7,14 +7,14 @@ Change the ACL owner for a domain class instance. .Examples -[source,java] +[source,groovy] ---- aclUtilService.changeOwner reportInstance, 'user123' ---- .Description -[source,java] +[source,groovy] ---- void changeOwner(domainObject, String newUsername) ---- diff --git a/docs/src/docs/AclUtilService/deleteAcl.adoc b/docs/src/docs/AclUtilService/deleteAcl.adoc index 6bdc5b3..5922936 100644 --- a/docs/src/docs/AclUtilService/deleteAcl.adoc +++ b/docs/src/docs/AclUtilService/deleteAcl.adoc @@ -7,14 +7,14 @@ Deletes the ACL for a domain class instance. .Examples -[source,java] +[source,groovy] ---- aclUtilService.deleteAcl reportInstance ---- .Description -[source,java] +[source,groovy] ---- void deleteAcl(domainObject) ---- diff --git a/docs/src/docs/AclUtilService/deletePermission.adoc b/docs/src/docs/AclUtilService/deletePermission.adoc index ffc35d6..09895dc 100644 --- a/docs/src/docs/AclUtilService/deletePermission.adoc +++ b/docs/src/docs/AclUtilService/deletePermission.adoc @@ -7,7 +7,7 @@ Removes a granted permission. .Examples -[source,java] +[source,groovy] ---- aclUtilService.deletePermission Report, 42, 'user123', BasePermission.WRITE @@ -16,7 +16,7 @@ aclUtilService.deletePermission reportInstance, 'user123', BasePermission.WRITE .Description -[source,java] +[source,groovy] ---- void deletePermission(domainObject, recipient, Permission permission) diff --git a/docs/src/docs/AclUtilService/hasPermission.adoc b/docs/src/docs/AclUtilService/hasPermission.adoc index 81ca046..177715e 100644 --- a/docs/src/docs/AclUtilService/hasPermission.adoc +++ b/docs/src/docs/AclUtilService/hasPermission.adoc @@ -7,7 +7,7 @@ Check if the authentication has grants for the specified permission(s) on the do .Examples -[source,java] +[source,groovy] ---- if (aclUtilService.hasPermission(auth, reportInstance, BasePermission.WRITE)) { ... @@ -16,7 +16,7 @@ if (aclUtilService.hasPermission(auth, reportInstance, BasePermission.WRITE)) { .Description -[source,java] +[source,groovy] ---- boolean hasPermission(Authentication authentication, domainObject, Permission... permissions) diff --git a/docs/src/docs/AclUtilService/readAcl.adoc b/docs/src/docs/AclUtilService/readAcl.adoc index 7fb19bd..04cbfde 100644 --- a/docs/src/docs/AclUtilService/readAcl.adoc +++ b/docs/src/docs/AclUtilService/readAcl.adoc @@ -7,7 +7,7 @@ Retrieves the ACL for a domain class instance. .Examples -[source,java] +[source,groovy] ---- def acl = aclUtilService.readAcl(reportInstance) @@ -16,7 +16,7 @@ def acl = aclUtilService.readAcl(Report, 42) .Description -[source,java] +[source,groovy] ---- Acl readAcl(domainObject) diff --git a/docs/src/docs/TagLibraries/notPermitted.adoc b/docs/src/docs/TagLibraries/notPermitted.adoc index 38a0ee4..cd86378 100644 --- a/docs/src/docs/TagLibraries/notPermitted.adoc +++ b/docs/src/docs/TagLibraries/notPermitted.adoc @@ -9,7 +9,7 @@ Renders the body if the user is not granted the specified permission(s) Single String: -[source,html] +[source,xml] ---- @@ -20,7 +20,7 @@ the body content Multiple String: -[source,html] +[source,xml] ---- @@ -31,7 +31,7 @@ the body content Single Permission: -[source,html] +[source,xml] ---- <%@ page import="org.springframework.security.acls.domain.BasePermission" %> @@ -44,7 +44,7 @@ the body content List of Permission: -[source,html] +[source,xml] ---- <%@ page import="org.springframework.security.acls.domain.BasePermission" %> @@ -57,7 +57,7 @@ the body content Single mask int: -[source,html] +[source,xml] ---- @@ -68,7 +68,7 @@ the body content Multiple mask int: -[source,html] +[source,xml] ---- diff --git a/docs/src/docs/TagLibraries/permitted.adoc b/docs/src/docs/TagLibraries/permitted.adoc index 49fc8ad..02ef97c 100644 --- a/docs/src/docs/TagLibraries/permitted.adoc +++ b/docs/src/docs/TagLibraries/permitted.adoc @@ -9,7 +9,7 @@ Renders the body if the user is granted the specified permission(s) Single String: -[source,html] +[source,xml] ---- @@ -20,7 +20,7 @@ the body content Multiple String: -[source,html] +[source,xml] ---- @@ -31,7 +31,7 @@ the body content Single Permission: -[source,html] +[source,xml] ---- <%@ page import="org.springframework.security.acls.domain.BasePermission" %> @@ -44,7 +44,7 @@ the body content List of Permission: -[source,html] +[source,xml] ---- <%@ page import="org.springframework.security.acls.domain.BasePermission" %> @@ -57,7 +57,7 @@ the body content Single mask int: -[source,html] +[source,xml] ---- @@ -68,7 +68,7 @@ the body content Multiple mask int: -[source,html] +[source,xml] ---- diff --git a/docs/src/docs/tutorial.adoc b/docs/src/docs/tutorial.adoc index 1d9f087..efbdb29 100644 --- a/docs/src/docs/tutorial.adoc +++ b/docs/src/docs/tutorial.adoc @@ -10,7 +10,7 @@ $ cd acltest Add a dependency for the plugin by adding it to the `dependencies` block in build.gradle: -[source,java] +[source,groovy] [subs="attributes"] ---- dependencies { @@ -46,7 +46,7 @@ $ grails create-domain-class com.testacl.Report and add a `name` property for testing: -[source,java] +[source,groovy] ---- package com.testacl @@ -63,7 +63,7 @@ $ grails create-service com.testacl.Report and add some methods that work with ``Report``s: -[source,java] +[source,groovy] ---- package com.testacl @@ -180,7 +180,7 @@ $ grails create-service com.testacl.SampleData and add this code: -[source,java] +[source,groovy] ---- package com.testacl @@ -272,7 +272,7 @@ class SampleDataService { and configure BootStrap.groovy to call the service at startup: -[source,java] +[source,groovy] ---- class BootStrap { @@ -292,7 +292,7 @@ $ grails generate-all com.testacl.Report But to use the controller, it will have to be reworked to use `ReportService`. It's a good idea to put all create/edit/delete code in a transactional service, but in this case we need to move all database access to the service to ensure that appropriate access checks are made: -[source,java] +[source,groovy] ---- package com.testacl @@ -406,7 +406,7 @@ Finally, we'll make a few adjustments so errors are handled gracefully. First, edit `grails-app/controllers/UrlMappings.groovy` and add some error code mappings: -[source,java] +[source,groovy] ---- import org.springframework.security.access.AccessDeniedException import org.springframework.security.acls.model.NotFoundException @@ -436,7 +436,7 @@ $ grails create-controller com.testacl.Errors and add this code: -[source,java] +[source,groovy] ---- package com.testacl @@ -486,7 +486,7 @@ Note that this is not an issue when using the generated GSPs in Grails 3 because It's simple to adjust the `actionSubmit` buttons and you'll need to change them in `show.gsp` and `edit.gsp`; `list.gsp` (or `index.gsp`) and `show.gsp` don't need any changes. In `show.gsp`, replace the two actionSubmit buttons with these two forms (maintain the g:message tags; the strings are hard-coded here to reduce clutter): -[source,html] +[source,xml] ----
@@ -507,14 +507,14 @@ It's simple to adjust the `actionSubmit` buttons and you'll need to change them In `grails-app/views/report/edit.gsp`, change the `
` tag to -[source,html] +[source,xml] ---- ---- and convert the update button to a regular submit button: -[source,html] +[source,xml] ----
@@ -523,7 +523,7 @@ and convert the update button to a regular submit button: and move the delete button out of the form into its own form just below the main form: -[source,html] +[source,xml] ---- @@ -540,7 +540,7 @@ and move the delete button out of the form into its own form just below the main Finally, to make it easier to log out (by default POST is required, so typical link that uses GET won't work), add this to `grails-app/views/layouts/main.gsp` before the `` tag: -[source,html] +[source,xml] ---- diff --git a/docs/src/docs/usage/acls.adoc b/docs/src/docs/usage/acls.adoc index 41a3cf2..804ce30 100644 --- a/docs/src/docs/usage/acls.adoc +++ b/docs/src/docs/usage/acls.adoc @@ -5,7 +5,7 @@ To properly display access denied exceptions (e.g. when a user tries to perform an action but doesn't have a grant authorizing it), you should create a mapping in `grails-app/controllers/UrlMappings.groovy` for error code 403. In addition, it's possible to trigger a https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/acls/model/NotFoundException.html[NotFoundException] which will create an error 500, but should be treated like a 403 error, so you should add mappings for these conditions: -[source,java] +[source,groovy] ---- import org.springframework.security.access.AccessDeniedException import org.springframework.security.acls.model.NotFoundException @@ -28,7 +28,7 @@ class UrlMappings { These depend on an `ErrorsController`: -[source,java] +[source,groovy] ---- package com.yourcompany.yourapp @@ -74,7 +74,7 @@ Note that this is not an issue when using the generated GSPs in Grails 3 because It's simple to adjust the `actionSubmit` buttons and you'll need to change them in `show.gsp` and `edit.gsp`; `list.gsp` (or `index.gsp`) and `show.gsp` don't need any changes. In `show.gsp`, replace the two actionSubmit buttons with these two forms (maintain the g:message tags; the strings are hard-coded here to reduce clutter): -[source,html] +[source,xml] ----
@@ -95,14 +95,14 @@ It's simple to adjust the `actionSubmit` buttons and you'll need to change them In `edit.gsp`, change the `` tag to -[source,html] +[source,xml] ---- ---- and convert the update button to a regular submit button: -[source,html] +[source,xml] ----
@@ -113,7 +113,7 @@ and convert the update button to a regular submit button: and move the delete button out of the form into its own form just below the main form: -[source,html] +[source,xml] ---- diff --git a/docs/src/docs/usage/configuration.adoc b/docs/src/docs/usage/configuration.adoc index 2045903..8a48655 100644 --- a/docs/src/docs/usage/configuration.adoc +++ b/docs/src/docs/usage/configuration.adoc @@ -13,7 +13,7 @@ Creating, editing, or deleting permissions requires an authenticated user. In mo You can leave the attributes set to `ROLE_ADMIN` or change them to have separate values, e.g. -[source,java] +[source,groovy] ---- grails.plugin.springsecurity.acl.authority. modifyAuditingDetails = 'ROLE_ACL_MODIFY_AUDITING' @@ -38,7 +38,7 @@ There are also two options to configure https://docs.spring.io/spring-security/r Example: -[source,java] +[source,groovy] ---- grails.plugin.springsecurity.useRunAs = true grails.plugin.springsecurity.runAs.key = 'your run-as key' diff --git a/docs/src/docs/usage/customPermissions.adoc b/docs/src/docs/usage/customPermissions.adoc index c72ecff..979be2e 100644 --- a/docs/src/docs/usage/customPermissions.adoc +++ b/docs/src/docs/usage/customPermissions.adoc @@ -30,7 +30,7 @@ It sets the mask value to 32 (1 << 5) since the values up to 16 are defined in t To use your class instead of the default, specify it in with the `grails.plugin.springsecurity.acl.permissionClass` attribute either as a Class or a String, for example -[source,java] +[source,groovy] ---- import com.mycompany.myapp.MyPermissions ... @@ -39,14 +39,14 @@ grails.plugin.springsecurity.acl. permissionClass = MyPermissions or -[source,java] +[source,groovy] ---- grails.plugin.springsecurity.acl.permissionClass = 'com.mycompany.myapp.MyPermissions' ---- You can also override the `aclPermissionFactory` bean in `grails-app/conf/spring/resources.groovy`, keeping the `org.springframework.security.acls.domain. DefaultPermissionFactory` class but passing your class as the constructor argument to keep it from defaulting to `BasePermission`, or do a more complex override to more fully reconfigure the behavior: -[source,java] +[source,groovy] ---- import org.springframework.security.acls.domain.DefaultPermissionFactory import com.mycompany.myapp.MyPermission @@ -58,7 +58,7 @@ beans = { Once this is done you can use the permission like any other, specifying its quoted lowercase name in an expression, e.g. -[source,java] +[source,groovy] ---- @PreAuthorize("hasPermission(#id, 'com.testacl.Report', 'approve')") Report get(@P("id") long id) { diff --git a/docs/src/docs/usage/domainClasses.adoc b/docs/src/docs/usage/domainClasses.adoc index 945097e..fe1aec4 100644 --- a/docs/src/docs/usage/domainClasses.adoc +++ b/docs/src/docs/usage/domainClasses.adoc @@ -35,7 +35,7 @@ include::../code/grails-app/domain/grails/plugin/springsecurity/acl/AclObjectIde `AclObjectIdentity` actually extends a base class, `AbstractAclObjectIdentity`: -[source,java] +[source,groovy] ---- abstract class AbstractAclObjectIdentity { diff --git a/docs/src/docs/usage/runAs.adoc b/docs/src/docs/usage/runAs.adoc index 3a202d6..7ae5d8e 100644 --- a/docs/src/docs/usage/runAs.adoc +++ b/docs/src/docs/usage/runAs.adoc @@ -5,7 +5,7 @@ Although not strictly related to ACLs, the plugin implements https://docs.spring For example, in this service `someMethod()` requires that the authenticated user have `ROLE_ADMIN` and will also be granted `ROLE_RUN_AS_SUPERUSER` for the duration of the method only: -[source,java] +[source,groovy] ---- class SecureService { diff --git a/docs/src/docs/usage/serviceMethods.adoc b/docs/src/docs/usage/serviceMethods.adoc index 78c1666..f94e0cc 100644 --- a/docs/src/docs/usage/serviceMethods.adoc +++ b/docs/src/docs/usage/serviceMethods.adoc @@ -14,7 +14,7 @@ The annotations use security-specific Spring expression language (SpEL) expressi Here's an example service that manages a `Report` domain class and uses these annotations and expressions: -[source,java] +[source,groovy] ---- import org.springframework.security.access.prepost.PostFilter import org.springframework.security.access.prepost.PreAuthorize From 2303302027ba4b101f698c5f7579290a021100f4 Mon Sep 17 00:00:00 2001 From: Brian Koehmstedt <1261658+bkoehm@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:14:32 -0800 Subject: [PATCH 5/6] Grails 7: Per PR review, change Spring Security document links to point to 6.4.x --- docs/src/docs/usage/acls.adoc | 2 +- docs/src/docs/usage/domainClasses.adoc | 2 +- docs/src/docs/usage/serviceMethods.adoc | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/docs/usage/acls.adoc b/docs/src/docs/usage/acls.adoc index 804ce30..f807145 100644 --- a/docs/src/docs/usage/acls.adoc +++ b/docs/src/docs/usage/acls.adoc @@ -3,7 +3,7 @@ ==== Suggested application changes -To properly display access denied exceptions (e.g. when a user tries to perform an action but doesn't have a grant authorizing it), you should create a mapping in `grails-app/controllers/UrlMappings.groovy` for error code 403. In addition, it's possible to trigger a https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/acls/model/NotFoundException.html[NotFoundException] which will create an error 500, but should be treated like a 403 error, so you should add mappings for these conditions: +To properly display access denied exceptions (e.g. when a user tries to perform an action but doesn't have a grant authorizing it), you should create a mapping in `grails-app/controllers/UrlMappings.groovy` for error code 403. In addition, it's possible to trigger a https://docs.spring.io/spring-security/site/docs/6.4.x/api/org/springframework/security/acls/model/NotFoundException.html[NotFoundException] which will create an error 500, but should be treated like a 403 error, so you should add mappings for these conditions: [source,groovy] ---- diff --git a/docs/src/docs/usage/domainClasses.adoc b/docs/src/docs/usage/domainClasses.adoc index fe1aec4..ff1ea62 100644 --- a/docs/src/docs/usage/domainClasses.adoc +++ b/docs/src/docs/usage/domainClasses.adoc @@ -57,7 +57,7 @@ By default it's assumed that domain classes have a numeric primary key, but that Finally, the `AclEntry` domain class contains entries representing grants (or denials) of a permission on an object instance to a recipient. The `aclObjectIdentity` field references the domain class instance (since an instance can have many granted permissions). The `sid` field references the recipient. The `granting` field determines whether the entry grants the permission (`true`) or denies it (`false`). The `aceOrder` field specifies the position of the entry, which is important because the entries are evaluated in order and the first matching entry determines whether access is allowed. `auditSuccess` and `auditFailure` determine whether to log success and/or failure events (these both default to `false`). -The `mask` field holds the permission. This can be a source of confusion because the name (and the Spring Security documentation) indicates that it's a bit mask. A value of 1 indicates permission A, a value of 2 indicates permission B, a value of 4 indicates permission C, a value of 8 indicates permission D, etc. So you would think that a value of 5 would indicate a grant of both permission A and C. Unfortunately this is not the case. There is a https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/acls/domain/CumulativePermission.html[CumulativePermission] class that supports this, but the standard classes don't support it (`AclImpl.isGranted()` checks for == rather than using | (bitwise or) so a combined entry would never match). So rather than grouping all permissions for one recipient on one instances into a bit mask, you must create individual records for each. +The `mask` field holds the permission. This can be a source of confusion because the name (and the Spring Security documentation) indicates that it's a bit mask. A value of 1 indicates permission A, a value of 2 indicates permission B, a value of 4 indicates permission C, a value of 8 indicates permission D, etc. So you would think that a value of 5 would indicate a grant of both permission A and C. Unfortunately this is not the case. There is a https://docs.spring.io/spring-security/site/docs/6.4.x/api/org/springframework/security/acls/domain/CumulativePermission.html[CumulativePermission] class that supports this, but the standard classes don't support it (`AclImpl.isGranted()` checks for == rather than using | (bitwise or) so a combined entry would never match). So rather than grouping all permissions for one recipient on one instances into a bit mask, you must create individual records for each. [source,groovy] ---- diff --git a/docs/src/docs/usage/serviceMethods.adoc b/docs/src/docs/usage/serviceMethods.adoc index f94e0cc..cc6fc52 100644 --- a/docs/src/docs/usage/serviceMethods.adoc +++ b/docs/src/docs/usage/serviceMethods.adoc @@ -5,10 +5,10 @@ There are two primary use cases for ACL security: determining whether a user is There are four annotations: -* https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/access/prepost/PreAuthorize.html[@PreAuthorize] -* https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/access/prepost/PreFilter.html[@PreFilter] -* https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/access/prepost/PostAuthorize.html[@PostAuthorize] -* https://docs.spring.io/spring-security/site/docs/6.4.1/api/org/springframework/security/access/prepost/PostFilter.html[@PostFilter] +* https://docs.spring.io/spring-security/site/docs/6.4.x/api/org/springframework/security/access/prepost/PreAuthorize.html[@PreAuthorize] +* https://docs.spring.io/spring-security/site/docs/6.4.x/api/org/springframework/security/access/prepost/PreFilter.html[@PreFilter] +* https://docs.spring.io/spring-security/site/docs/6.4.x/api/org/springframework/security/access/prepost/PostAuthorize.html[@PostAuthorize] +* https://docs.spring.io/spring-security/site/docs/6.4.x/api/org/springframework/security/access/prepost/PostFilter.html[@PostFilter] The annotations use security-specific Spring expression language (SpEL) expressions - see https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html#authorization-expressions[the documentation] for the available standard and method expressions. From 91457514c34efab6b3e419394f72e4b1d5e638ce Mon Sep 17 00:00:00 2001 From: Brian Koehmstedt <1261658+bkoehm@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:34:01 -0800 Subject: [PATCH 6/6] Grails 7: Per PR review, get rid of asciidoctor warnings --- docs/build.gradle | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/build.gradle b/docs/build.gradle index 0a7c9b5..2f11464 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -54,6 +54,10 @@ asciidoctor { } outputDir = new File(buildDir, 'docs') attributes asciidoctorAttributes + executionMode = 'JAVA_EXEC' + forkOptions { + jvmArgs "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED" + } } asciidoctorPdf { @@ -63,6 +67,10 @@ asciidoctorPdf { include 'index.adoc' } outputDir = new File(buildDir, 'docs') + executionMode = 'JAVA_EXEC' + forkOptions { + jvmArgs "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED" + } asciidoctorj { requires 'rouge' @@ -77,6 +85,10 @@ asciidoctorEpub { include 'index.adoc' } outputDir = new File(buildDir, 'docs') + executionMode = 'JAVA_EXEC' + forkOptions { + jvmArgs "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED" + } asciidoctorj { requires 'rouge'