Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grails 7 #46: update documentation #50

Merged
merged 6 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
`./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`.
19 changes: 15 additions & 4 deletions docs/src/docs/installing.adoc
bkoehm marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion docs/src/docs/installing/distribution.adoc
Original file line number Diff line number Diff line change
@@ -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].

4 changes: 2 additions & 2 deletions docs/src/docs/installing/snapshots.adoc
Original file line number Diff line number Diff line change
@@ -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" }
----
4 changes: 2 additions & 2 deletions docs/src/docs/introduction.adoc
Original file line number Diff line number Diff line change
@@ -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 <<tutorial>> and in addition you can download and run a complete Grails application that uses the plugin. Installing and running the application are described in <<sampleApp>>.

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].
In addition to this document, you should read the https://docs.spring.io/spring-security/reference/servlet/authorization/acls.html[Spring Security documentation].
13 changes: 7 additions & 6 deletions docs/src/docs/tutorial.adoc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to run through this tutorial with a Grails 7 installation. I think you will find things that doesn't work, like some grails commands.

Copy link
Contributor Author

@bkoehm bkoehm Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not going to take on getting the tutorial up to speed for Grails 7 in this PR (other than adding the @P annotation to it), but in order to track this as an ongoing issue I have opened up #51 .

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/src/docs/usage/acls.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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:
bkoehm marked this conversation as resolved.
Show resolved Hide resolved

[source,java]
bkoehm marked this conversation as resolved.
Show resolved Hide resolved
----
Expand Down
2 changes: 1 addition & 1 deletion docs/src/docs/usage/configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
|====================
Expand Down
2 changes: 1 addition & 1 deletion docs/src/docs/usage/customPermissions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
----
2 changes: 1 addition & 1 deletion docs/src/docs/usage/domainClasses.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
bkoehm marked this conversation as resolved.
Show resolved Hide resolved

[source,groovy]
----
Expand Down
2 changes: 1 addition & 1 deletion docs/src/docs/usage/runAs.adoc
Original file line number Diff line number Diff line change
@@ -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:

Expand Down
17 changes: 9 additions & 8 deletions docs/src/docs/usage/serviceMethods.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ 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]
bkoehm marked this conversation as resolved.
Show resolved Hide resolved

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:

[source,java]
----
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
Expand All @@ -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)
}

Expand All @@ -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
Expand All @@ -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()
}
}
Expand Down
Loading