1
0
mirror of synced 2026-07-09 04:40:13 +00:00

Compare commits

..

206 Commits

Author SHA1 Message Date
dependabot[bot] e448e122b8 Bump antora from 3.2.0-alpha.11 to 3.2.0-alpha.12
---
updated-dependencies:
- dependency-name: antora
  dependency-version: 3.2.0-alpha.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-09 05:34:22 -04:00
dependabot[bot] 3b992b33d1 Bump @springio/antora-extensions from 1.14.11 to 1.14.12
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.11 to 1.14.12.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.11...v1.14.12)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-version: 1.14.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-09 05:28:41 -04:00
Josh Cummings 5c2f33c03b Update NodeJS to 24.15
This commit declares a NodeJS value whereas previously,
Antora would use the latest LTS version. When it updated to
Node 24.16, site production stopped, possibly due to
https://github.com/nodejs/node/commit/e95570c054.

Either way, it is helpful to have a pinned version in order
to stabilize builds, allowing us to update it in an orderly
fashion like other dependencies.

Closes gh-19206
2026-05-22 22:17:31 -06:00
dependabot[bot] 116e29ff2f Bump gradle-wrapper from 9.4.1 to 9.5.1
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.4.1 to 9.5.1.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.4.1...v9.5.1)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.5.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-21 13:29:05 -06:00
dependabot[bot] d2ecc270fd Bump @springio/antora-extensions from 1.14.10 to 1.14.11
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.10 to 1.14.11.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.10...v1.14.11)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-version: 1.14.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-17 11:58:12 -06:00
dependabot[bot] 7be81ead2e Bump @springio/antora-extensions from 1.14.9 to 1.14.10
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.9 to 1.14.10.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.9...v1.14.10)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-version: 1.14.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-07 10:04:30 -06:00
dependabot[bot] a0f4340f3f Bump @springio/antora-extensions from 1.14.7 to 1.14.9
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.7 to 1.14.9.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.7...v1.14.9)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-version: 1.14.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 15:22:25 -06:00
dependabot[bot] 23bac293f2 Bump gradle-wrapper from 9.4.0 to 9.4.1
Bumps gradle-wrapper from 9.4.0 to 9.4.1.

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 15:01:01 -06:00
dependabot[bot] b340936cb2 Bump @springio/asciidoctor-extensions
Bumps [@springio/asciidoctor-extensions](https://github.com/spring-io/asciidoctor-extensions) from 1.0.0-alpha.17 to 1.0.0-alpha.18.
- [Changelog](https://github.com/spring-io/asciidoctor-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/asciidoctor-extensions/compare/v1.0.0-alpha.17...v1.0.0-alpha.18)

---
updated-dependencies:
- dependency-name: "@springio/asciidoctor-extensions"
  dependency-version: 1.0.0-alpha.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-17 08:32:34 -04:00
github-actions[bot] de66811c51 Update Antora Spring UI to v0.4.26 2026-03-13 13:53:42 -04:00
dependabot[bot] fe7bbd36d8 Bump @antora/collector-extension from 1.0.2 to 1.0.3
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-version: 1.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-09 11:00:24 -04:00
dependabot[bot] dfc06488bd Bump gradle-wrapper from 9.3.1 to 9.4.0
Bumps gradle-wrapper from 9.3.1 to 9.4.0.

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-05 10:19:42 -07:00
Rob Winch 392dcf4b72 Add comment as to why using JDK 17
Add comment as to why using JDK 17

Signed-off-by: Rob Winch <362503+rwinch@users.noreply.github.com>
2026-02-25 08:15:54 -06:00
Rob Winch 2fd4caa169 Use JDK 17 Since 6.5.x Kotlin Does not Support JDK 25
Using JDK 25 a full build fails with

```
e: java.lang.IllegalArgumentException: 25.0.2
	at org.jetbrains.kotlin.com.intellij.util.lang.JavaVersion.parse(JavaVersion.java:307)
	at org.jetbrains.kotlin.com.intellij.util.lang.JavaVersion.current(JavaVersion.java:176)
	at org.jetbrains.kotlin.cli.jvm.modules.JavaVersionUtilsKt.isAtLeastJava9(javaVersionUtils.kt:11)
	at org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem.globalJrtFsCache$lambda$2(CoreJrtFileSystem.kt:8
```

Signed-off-by: Rob Winch <362503+rwinch@users.noreply.github.com>
2026-02-25 08:13:33 -06:00
dependabot[bot] 678d3ab242 Bump gradle-wrapper from 9.2.1 to 9.3.1
Bumps gradle-wrapper from 9.2.1 to 9.3.1.

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 10:30:49 -07:00
Josh Cummings 9730db490c Remove 6.4.x from OSS Docs Website 2026-02-11 17:27:58 -07:00
Josh Cummings 5b9d225816 Update to Gradle 9
Issue gh-18510

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-02-11 13:37:33 -07:00
Josh Cummings ba69471f6f Update Deploy Docs to JDK 25
Issue gh-18512

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-02-11 13:36:53 -07:00
dependabot[bot] 1c66fcd510 Bump github/codeql-action from 3 to 4
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-06 12:22:20 -06:00
dependabot[bot] 86bd220997 Bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-06 12:22:02 -06:00
dependabot[bot] 9f3d60ebf3 Bump spring-io/spring-doc-actions from 0.0.21 to 0.0.22
Bumps [spring-io/spring-doc-actions](https://github.com/spring-io/spring-doc-actions) from 0.0.21 to 0.0.22.
- [Commits](https://github.com/spring-io/spring-doc-actions/compare/v0.0.21...v0.0.22)

---
updated-dependencies:
- dependency-name: spring-io/spring-doc-actions
  dependency-version: 0.0.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-06 12:21:41 -06:00
dependabot[bot] 67cff1140e Bump actions/setup-java from 4 to 5
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-06 12:20:25 -06:00
dependabot[bot] db898fd9cf Bump gradle-wrapper from 8.7 to 8.14.4
Bumps gradle-wrapper from 8.7 to 8.14.4.

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 8.14.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-06 12:19:16 -06:00
dependabot[bot] da88ee4dc4 Bump antora from 3.2.0-alpha.10 to 3.2.0-alpha.11
Bumps [antora](https://gitlab.com/antora/antora) from 3.2.0-alpha.10 to 3.2.0-alpha.11.
- [Changelog](https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc)
- [Commits](https://gitlab.com/antora/antora/compare/v3.2.0-alpha.10...v3.2.0-alpha.11)

---
updated-dependencies:
- dependency-name: antora
  dependency-version: 3.2.0-alpha.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-30 16:21:36 -08:00
Rob Winch 6671432a5b antora-ui-spring 0.4.25 2025-11-21 09:42:01 -06:00
Rob Winch f77f801fed antora-ui-spring 0.4.24 2025-11-20 15:16:02 -06:00
Rob Winch aa0e22911a Update to antora-ui-spring 0.4.23 2025-11-20 09:49:39 -06:00
Rob Winch 29bf045c65 Update to antora-ui-spring 0.4.22 2025-11-19 17:22:07 -06:00
Rob Winch 38702d4eb9 Update to antora-ui 0.4.21 2025-11-19 11:19:43 -06:00
Rob Winch 9f214978a4 Update to spring-doc-actions 0.0.21 2025-11-13 14:19:24 -06:00
Rob Winch cc6f1d65b2 Update to antora-ui-spring 0.4.20 2025-11-12 08:08:14 -06:00
Rob Winch 0a540d2cb0 Update to antora-ui-spring v0.4.19 2025-11-11 11:05:36 -06:00
dependabot[bot] 8316c38b1e Bump antora from 3.2.0-alpha.9 to 3.2.0-alpha.10
Bumps [antora](https://gitlab.com/antora/antora) from 3.2.0-alpha.9 to 3.2.0-alpha.10.
- [Changelog](https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc)
- [Commits](https://gitlab.com/antora/antora/compare/v3.2.0-alpha.9...v3.2.0-alpha.10)

---
updated-dependencies:
- dependency-name: antora
  dependency-version: 3.2.0-alpha.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-05 17:13:01 -07:00
Rob Winch 1a96ec4539 Update branches/tags for 7.x 2025-09-22 10:32:34 -05:00
dependabot[bot] 57dacd975f Bump @antora/atlas-extension from 1.0.0-alpha.2 to 1.0.0-alpha.5
---
updated-dependencies:
- dependency-name: "@antora/atlas-extension"
  dependency-version: 1.0.0-alpha.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-14 17:10:49 -07:00
dependabot[bot] cdcc9130a1 Bump @antora/collector-extension from 1.0.1 to 1.0.2
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-version: 1.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 15:10:26 -07:00
dependabot[bot] d3e0e197e8 Bump antora from 3.2.0-alpha.8 to 3.2.0-alpha.9
Bumps [antora](https://gitlab.com/antora/antora) from 3.2.0-alpha.8 to 3.2.0-alpha.9.
- [Changelog](https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc)
- [Commits](https://gitlab.com/antora/antora/compare/v3.2.0-alpha.8...v3.2.0-alpha.9)

---
updated-dependencies:
- dependency-name: antora
  dependency-version: 3.2.0-alpha.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 15:09:06 -07:00
dependabot[bot] 9aa28236cd Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-14 15:08:57 -07:00
dependabot[bot] 38549ca60a Bump @springio/antora-extensions from 1.14.6 to 1.14.7
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.6 to 1.14.7.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.6...v1.14.7)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-version: 1.14.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-20 17:29:38 -07:00
dependabot[bot] 90258a5a85 Bump @springio/antora-extensions from 1.14.4 to 1.14.6
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.4 to 1.14.6.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.4...v1.14.6)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-version: 1.14.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-13 17:26:35 -07:00
Rob Winch f5d53c31b0 Add codeql.yml 2025-05-02 11:56:25 -05:00
Rob Winch 23008b97d4 Refine scopes
Signed-off-by: Rob Winch <362503+rwinch@users.noreply.github.com>
2025-04-25 13:02:25 -05:00
Rob Winch ec3df2ed66 Change Skip Condition
Signed-off-by: Rob Winch <362503+rwinch@users.noreply.github.com>
2025-04-25 12:45:45 -05:00
Rob Winch 92225c8b8d Update to spring-doc-actions 0.0.20
Signed-off-by: Rob Winch <362503+rwinch@users.noreply.github.com>
2025-04-03 12:56:42 -05:00
dependabot[bot] 8f83df8321 Bump @springio/asciidoctor-extensions
Bumps [@springio/asciidoctor-extensions](https://github.com/spring-io/asciidoctor-extensions) from 1.0.0-alpha.16 to 1.0.0-alpha.17.
- [Changelog](https://github.com/spring-io/asciidoctor-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/asciidoctor-extensions/compare/v1.0.0-alpha.16...v1.0.0-alpha.17)

---
updated-dependencies:
- dependency-name: "@springio/asciidoctor-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-23 18:39:05 -07:00
Rob Winch 93f7296492 rm Unsupported Branches from Antora 2025-03-21 14:42:37 -05:00
Rob Winch 6e540b20b7 Update to spring-doc-actions v0.0.19
Closes gh-16797
2025-03-21 14:32:41 -05:00
dependabot[bot] 42a6b9f57e Bump @springio/antora-extensions from 1.14.2 to 1.14.4
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.2 to 1.14.4.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.2...v1.14.4)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-23 17:51:47 -08:00
dependabot[bot] 301f447680 Bump @springio/asciidoctor-extensions
Bumps [@springio/asciidoctor-extensions](https://github.com/spring-io/asciidoctor-extensions) from 1.0.0-alpha.14 to 1.0.0-alpha.16.
- [Changelog](https://github.com/spring-io/asciidoctor-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/asciidoctor-extensions/compare/v1.0.0-alpha.14...v1.0.0-alpha.16)

---
updated-dependencies:
- dependency-name: "@springio/asciidoctor-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-02 17:00:26 -08:00
dependabot[bot] 5e0aa3e29f Bump antora from 3.2.0-alpha.6 to 3.2.0-alpha.8
Bumps [antora](https://gitlab.com/antora/antora) from 3.2.0-alpha.6 to 3.2.0-alpha.8.
- [Changelog](https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc)
- [Commits](https://gitlab.com/antora/antora/compare/v3.2.0-alpha.6...v3.2.0-alpha.8)

---
updated-dependencies:
- dependency-name: antora
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-08 16:13:01 -08:00
dependabot[bot] 4f929d1a8f Bump @antora/collector-extension from 1.0.0 to 1.0.1
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-08 16:12:47 -08:00
dependabot[bot] 009bfd3c44 Bump @antora/collector-extension from 1.0.0-rc.1 to 1.0.0
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-01 16:53:46 -08:00
dependabot[bot] 5c7651012a Bump @antora/collector-extension from 1.0.0-beta.5 to 1.0.0-rc.1
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-24 16:42:57 -08:00
github-actions[bot] 5bbb75923c Update Antora Spring UI to v0.4.18 2024-11-18 16:20:46 -06:00
dependabot[bot] ed3fffcf48 Bump @antora/collector-extension from 1.0.0-beta.4 to 1.0.0-beta.5
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-17 16:49:20 -08:00
dependabot[bot] 0701464480 Bump @antora/collector-extension from 1.0.0-beta.3 to 1.0.0-beta.4
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-03 16:48:16 -08:00
github-actions[bot] 72f0f4eb74 Update Antora Spring UI to v0.4.17 2024-10-16 10:06:43 +00:00
dependabot[bot] be4fafde5d Bump @antora/collector-extension from 1.0.0-beta.2 to 1.0.0-beta.3
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-13 17:59:58 -07:00
dependabot[bot] 928b4147a4 Bump @springio/asciidoctor-extensions
Bumps [@springio/asciidoctor-extensions](https://github.com/spring-io/asciidoctor-extensions) from 1.0.0-alpha.13 to 1.0.0-alpha.14.
- [Changelog](https://github.com/spring-io/asciidoctor-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/asciidoctor-extensions/compare/v1.0.0-alpha.13...v1.0.0-alpha.14)

---
updated-dependencies:
- dependency-name: "@springio/asciidoctor-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-22 17:32:13 -07:00
dependabot[bot] 3fae3e72b7 Bump @springio/asciidoctor-extensions
Bumps [@springio/asciidoctor-extensions](https://github.com/spring-io/asciidoctor-extensions) from 1.0.0-alpha.12 to 1.0.0-alpha.13.
- [Changelog](https://github.com/spring-io/asciidoctor-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/asciidoctor-extensions/compare/v1.0.0-alpha.12...v1.0.0-alpha.13)

---
updated-dependencies:
- dependency-name: "@springio/asciidoctor-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-08 17:38:50 -07:00
dependabot[bot] 7a7aed07af Bump @antora/collector-extension from 1.0.0-beta.1 to 1.0.0-beta.2
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-18 17:22:23 -07:00
dependabot[bot] 8f681accb7 Bump @springio/antora-extensions from 1.13.1 to 1.14.2
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.13.1 to 1.14.2.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.13.1...v1.14.2)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-18 17:16:52 -07:00
dependabot[bot] 342f7f89a1 Bump @springio/antora-extensions from 1.13.0 to 1.13.1
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.13.0 to 1.13.1.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.13.0...v1.13.1)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-11 17:08:03 -07:00
dependabot[bot] 5885bd6f95 Bump @antora/collector-extension from 1.0.0-alpha.7 to 1.0.0-beta.1
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-11 17:07:54 -07:00
dependabot[bot] ac2dd3863d Bump @springio/asciidoctor-extensions
Bumps [@springio/asciidoctor-extensions](https://github.com/spring-io/asciidoctor-extensions) from 1.0.0-alpha.11 to 1.0.0-alpha.12.
- [Changelog](https://github.com/spring-io/asciidoctor-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/asciidoctor-extensions/compare/v1.0.0-alpha.11...v1.0.0-alpha.12)

---
updated-dependencies:
- dependency-name: "@springio/asciidoctor-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-04 17:52:41 -07:00
dependabot[bot] 9fb2ceceb8 Bump @springio/antora-extensions from 1.12.0 to 1.13.0
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.12.0 to 1.13.0.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-04 17:51:45 -07:00
dependabot[bot] a5cae32a72 Bump antora from 3.2.0-alpha.5 to 3.2.0-alpha.6
Bumps [antora](https://gitlab.com/antora/antora) from 3.2.0-alpha.5 to 3.2.0-alpha.6.
- [Changelog](https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc)
- [Commits](https://gitlab.com/antora/antora/compare/v3.2.0-alpha.5...v3.2.0-alpha.6)

---
updated-dependencies:
- dependency-name: antora
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-28 17:25:13 -07:00
dependabot[bot] ad14fed8d8 Bump @antora/collector-extension from 1.0.0-alpha.6 to 1.0.0-alpha.7
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-28 17:24:02 -07:00
dependabot[bot] 43edfccc25 Bump @antora/collector-extension from 1.0.0-alpha.4 to 1.0.0-alpha.6
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-21 17:19:09 -07:00
Rob Winch 3d48b7060f Add javadoc-extension 2024-07-09 22:53:00 -05:00
Rob Winch 891adb60f9 Update to antora-extensions 1.12.0 2024-07-09 22:38:03 -05:00
Rob Winch 536c12703e Update to asciidoctor-extensions 1.0.0-alpha.11 2024-07-09 22:37:48 -05:00
dependabot[bot] 2e72c2a174 Bump antora from 3.2.0-alpha.4 to 3.2.0-alpha.5
Bumps [antora](https://gitlab.com/antora/antora) from 3.2.0-alpha.4 to 3.2.0-alpha.5.
- [Changelog](https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc)
- [Commits](https://gitlab.com/antora/antora/compare/v3.2.0-alpha.4...v3.2.0-alpha.5)

---
updated-dependencies:
- dependency-name: antora
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-30 18:09:54 -07:00
Rob Winch abc06ea10c Remove unused playbooks
We no longer use the local or per branch playbooks. Instead we can flex
the build using the same env variables that the partial build leverages
& fixed playbooks in each branch.
2024-06-27 16:35:50 -04:00
dependabot[bot] 7dca6269bb Bump spring-io/spring-doc-actions from 0.0.17 to 0.0.18
Bumps [spring-io/spring-doc-actions](https://github.com/spring-io/spring-doc-actions) from 0.0.17 to 0.0.18.
- [Commits](https://github.com/spring-io/spring-doc-actions/compare/v0.0.17...v0.0.18)

---
updated-dependencies:
- dependency-name: spring-io/spring-doc-actions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-23 18:15:04 -07:00
github-actions[bot] e56bcb3cfd Update Antora Spring UI to v0.4.16 2024-06-20 15:47:28 -03:00
Marcus Hert Da Coregio 816736d0db Delete update-antora-ui-spring.yml
Scheduled workflows must be placed in default branches otherwise they won't be triggered
2024-06-20 15:46:03 -03:00
dependabot[bot] 4a4410c40e Bump @antora/collector-extension from 1.0.0-alpha.3 to 1.0.0-alpha.4
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-24 07:42:51 -07:00
Marcus Hert Da Coregio 78eca084f1 Add update-antora-ui-spring.yml
Issue gh-15106
2024-05-24 09:36:16 -03:00
Marcus Hert Da Coregio 16d9a90e2c Upgrade to Gradle 8.7 2024-05-22 10:49:58 -03:00
Rob Winch 12c03dd5a3 Use composite extension 2024-05-21 16:41:49 -05:00
Rob Winch b7888bc6d1 Use Algolia from extensions 2024-05-21 16:32:15 -05:00
Rob Winch 845a423135 Update to antora-extensions 1.11.1 2024-05-21 16:12:04 -05:00
dependabot[bot] ace979014f ---
updated-dependencies:
- dependency-name: spring-io/spring-doc-actions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-21 10:25:37 -07:00
github-actions[bot] e4eeeb95c2 Update Antora Spring UI to v0.4.15 2024-05-20 13:50:54 -03:00
dependabot[bot] d44606f362 Bump @springio/antora-extensions from 1.10.0 to 1.11.0
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.10.0 to 1.11.0.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.10.0...v1.11.0)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-19 17:13:54 -07:00
marcusdacoregio ed1ef9b889 Update Antora UI Spring to v0.4.12 2024-05-07 08:52:41 -03:00
Steve Riesenberg cd04ea3675 Fix atlas-extension order
Fixes the following error from latest-version-extension:
    FATAL (antora): The latest-version-extension must be registered
    after the atlas-extension
2024-04-19 09:58:37 -05:00
dependabot[bot] ce6ba7fb9d Bump @springio/antora-extensions from 1.8.2 to 1.10.0
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.8.2 to 1.10.0.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.8.2...v1.10.0)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-17 16:52:47 -07:00
Marcus Hert Da Coregio 7d398dce6b Add merge-dependabot-pr.yml
Closes gh-14858
2024-04-09 10:39:01 -03:00
dependabot[bot] cf339d682b Bump antora from 3.2.0-alpha.2 to 3.2.0-alpha.4
Bumps [antora](https://gitlab.com/antora/antora) from 3.2.0-alpha.2 to 3.2.0-alpha.4.
- [Changelog](https://gitlab.com/antora/antora/blob/main/CHANGELOG.adoc)
- [Commits](https://gitlab.com/antora/antora/compare/v3.2.0-alpha.2...v3.2.0-alpha.4)

---
updated-dependencies:
- dependency-name: antora
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-05 15:59:52 -03:00
Marcus Hert Da Coregio 477276b52e Use package.json for antora package management
Issue gh-14819
2024-04-03 09:44:15 -03:00
Marcus Hert Da Coregio 3104bdb90d Revert "Use package.json for package management"
This reverts commit 3bf79cc7
2024-04-02 09:11:35 -03:00
dependabot[bot] 20814c6aa6 Bump @springio/antora-extensions from 1.7.0 to 1.8.2
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.7.0 to 1.8.2.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.7.0...v1.8.2)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-01 13:36:47 -03:00
dependabot[bot] b156b25602 Bump @antora/atlas-extension from 1.0.0-alpha.1 to 1.0.0-alpha.2
---
updated-dependencies:
- dependency-name: "@antora/atlas-extension"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-01 13:36:23 -03:00
dependabot[bot] e5c05bc746 Bump @asciidoctor/tabs from 1.0.0-beta.3 to 1.0.0-beta.6
Bumps [@asciidoctor/tabs](https://github.com/asciidoctor/asciidoctor-tabs) from 1.0.0-beta.3 to 1.0.0-beta.6.
- [Changelog](https://github.com/asciidoctor/asciidoctor-tabs/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/asciidoctor/asciidoctor-tabs/compare/v1.0.0-beta.3...v1.0.0-beta.6)

---
updated-dependencies:
- dependency-name: "@asciidoctor/tabs"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-01 13:36:13 -03:00
dependabot[bot] e001a53454 Bump @springio/asciidoctor-extensions
Bumps [@springio/asciidoctor-extensions](https://github.com/spring-io/asciidoctor-extensions) from 1.0.0-alpha.8 to 1.0.0-alpha.10.
- [Changelog](https://github.com/spring-io/asciidoctor-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/asciidoctor-extensions/compare/v1.0.0-alpha.8...v1.0.0-alpha.10)

---
updated-dependencies:
- dependency-name: "@springio/asciidoctor-extensions"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-01 13:36:04 -03:00
Marcus Hert Da Coregio 3bf79cc70c Use package.json for package management
Issue gh-14819
2024-04-01 13:21:00 -03:00
Marcus Hert Da Coregio e7468571ae Try range 0..99 for patch versions
Issue gh-14810
2024-03-27 14:46:51 -03:00
Marcus Hert Da Coregio f11a669dc7 Update tags
Issue gh-14810
2024-03-27 11:42:39 -03:00
Marcus Hert Da Coregio 629e2ea110 Update tags
Issue gh-14810
2024-03-27 10:22:06 -03:00
Marcus Hert Da Coregio baed662780 Ignore milestones from 5.8.3
Issue gh-14810
2024-03-27 09:22:25 -03:00
Marcus Hert Da Coregio 5536e21690 Update tags
5.8.x where x > 10 tags were not being deployed
2024-03-26 15:33:39 -03:00
Rob Winch 06961caba7 Update to spring-doc-actions v0.0.15 2024-03-18 17:01:25 -05:00
Rob Winch cfd02619cf Update antora-ui-spring v0.4.11 2024-03-18 11:11:38 -05:00
dependabot[bot] 7285fc8b52 Bump spring-io/spring-doc-actions from 0.0.13 to 0.0.14
Bumps [spring-io/spring-doc-actions](https://github.com/spring-io/spring-doc-actions) from 0.0.13 to 0.0.14.
- [Commits](https://github.com/spring-io/spring-doc-actions/compare/v0.0.13...v0.0.14)

---
updated-dependencies:
- dependency-name: spring-io/spring-doc-actions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 09:43:59 -03:00
dependabot[bot] c27dcf03b5 Bump spring-io/spring-doc-actions from 0.0.11 to 0.0.13
Bumps [spring-io/spring-doc-actions](https://github.com/spring-io/spring-doc-actions) from 0.0.11 to 0.0.13.
- [Commits](https://github.com/spring-io/spring-doc-actions/compare/v0.0.11...v0.0.13)

---
updated-dependencies:
- dependency-name: spring-io/spring-doc-actions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-14 08:50:15 -03:00
dependabot[bot] 46d52e8fee Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-14 08:49:58 -03:00
Rob Winch ac3935da09 Use Related Docs Static Page
- Update antora-ui-spring
- Update antora-extensions
- Configure related pages
2023-10-10 15:02:29 -05:00
Rob Winch dcc044a654 Update to antora-ui-spring v0.4.2 2023-09-21 11:14:51 -05:00
Rob Winch 71c9c82233 Search in all Spring Docs 2023-09-20 14:22:57 -05:00
Rob Winch 5930496049 Update to antora-ui-spring v0.3.7 2023-09-13 10:15:56 -05:00
Rob Winch a469b2e229 Update to antora-ui-spring 0.3.6 2023-09-07 12:20:59 -05:00
Rob Winch ed73d77e33 Update to antora-ui-spring 0.3.5 2023-08-28 12:12:26 -07:00
Rob Winch 85ccc20121 Update to antora-ui-spring 0.3.3 2023-07-18 11:11:30 -05:00
Rob Winch 8ba2c34175 Update to spring-antora-ui v0.3.2 2023-07-12 11:28:19 -05:00
Rob Winch 1485ad6951 Update to antora-ui-spring v0.3.1
- Adds GitHub Project Link
- Adds Stackoverflow Link
- Adds Related Documentation links
2023-07-12 10:47:38 -05:00
Rob Winch bcdd981578 Update to antora-extensions 1.4.2 2023-06-23 17:38:45 -05:00
Rob Winch ed6205da5e Update to antora-extensions 1.4.1 2023-06-23 16:51:45 -05:00
Rob Winch 991939af06 Update spring-doc-actions 0.0.11 2023-06-22 17:45:34 -05:00
Rob Winch f7363052de Remove inject-collector-config
All refs from the playbook now have the collector configured
2023-06-20 11:13:55 -05:00
Rob Winch 1e9893122b Remove publish-docsearch-config-extension
Algolia docsearch does the indexing on its side now
2023-06-20 11:13:11 -05:00
Rob Winch 75a280b68d Remove tabs-migration-extension
All versions brought in by the playbook now use asciidoctor tabs
2023-06-20 11:10:49 -05:00
Rob Winch 4414d76720 Antora fails if warnings are output 2023-06-20 11:05:33 -05:00
Rob Winch eb66d1ba41 Remove version-fix.js
The playbook no longer brings in versions that were broken.
2023-06-20 11:05:00 -05:00
Rob Winch 0a5f56e0de Remove fix-component-name-display.js
We no longer bring in tags that were broken
2023-06-20 11:04:16 -05:00
Rob Winch a5c8d737bd Exclude versions with name = ROOT 2023-06-20 11:03:39 -05:00
Rob Winch a69cd7d5d9 Use Antora name of security
Issue gh-13327
2023-06-16 21:58:41 -05:00
Rob Winch 17444d56a8 Update to spring-doc-actions 0.0.9 2023-06-16 11:03:22 -05:00
Rob Winch bda6e79e7c spring-doc-actions v0.0.8 2023-06-14 15:16:30 -05:00
Rob Winch 0df1dea1f8 Use spring-doc-actions v0.0.7 2023-06-14 13:44:29 -05:00
Rob Winch 7c562e2c88 Use bust-cloudflare-antora-cache 2023-06-14 11:33:45 -05:00
Rob Winch 6f2d43e03d Update to spring-antora-ui v0.3.0 2023-06-14 10:25:29 -05:00
Rob Winch 48d5488185 Cache collector output for tags 2023-06-11 23:43:31 -05:00
Rob Winch bdbeffc901 Fail Branches on Warning
Issue gh-13210
2023-06-08 17:25:09 -05:00
Rob Winch 1b954f1383 cralwer.algolia.com performs indexing 2023-06-08 13:46:10 -05:00
Steve Riesenberg fce9d893f9 Add .github/actions/.gitkeep 2023-06-07 17:05:18 -05:00
Rob Winch 8aa641410a Use rsync-antora-reference@v0.0.5 2023-05-02 10:36:53 -05:00
Rob Winch 058a49ad3d Use rsync-antora-reference@v0.0.4 2023-05-02 10:15:34 -05:00
Rob Winch 99afa32fae Use rsync-antora-reference@v0.0.3 2023-05-02 09:56:42 -05:00
Rob Winch 61f33d2d29 Use rsync-antora-reference@v0.0.2 2023-05-02 09:27:50 -05:00
Rob Winch 739641966f Use rsync-antora-reference@v0.0.1 2023-05-02 09:15:46 -05:00
Rob Winch 060a8f6cdb Use latest-version-extension 2023-04-27 20:01:57 -05:00
Rob Winch 02c9df1371 Update to docsearch 2023-04-27 20:01:33 -05:00
Rob Winch b5ff77339b Use ui-bundle v0.1.1 2023-03-09 12:23:34 -06:00
Rob Winch 26827157f9 Use publish-docsearch-config-extension
Closes gh-12794
2023-02-27 16:05:39 -06:00
Dan Allen cca087f04f upgrade docs extensions
- upgrade to Asciidoctor Tabs 1.0.0-beta.3
- upgrade to Antora Release Line Extension 1.0.0
- upgrade to Spring.io Antora Extensions 1.0.0
- fix require path of tabs migration extension in local playbook
- add reminder to update dependency versions in playbook template for branches
2023-02-08 11:18:59 -06:00
Steve Riesenberg 06d05691b9 Override collector config for 6.0.0-RC1
Closes gh-12607
2023-01-31 15:32:39 -06:00
Steve Riesenberg 02587c7407 Exclude toolchains.xml from being read
Issue gh-12568
Issue gh-12596
2023-01-30 19:51:17 -06:00
Dan Allen 8a56d49d35 upgrade Gradle Antora Plugin to 1.0.0 2023-01-30 16:22:30 -06:00
Dan Allen 8940bc8979 document the authoring process and publishing workflow for the documentation 2023-01-30 16:21:04 -06:00
Dan Allen 53acce43bc add docs to local Antora extensions 2023-01-30 16:19:37 -06:00
Rob Winch d9bb586257 Use spring-gradle-build-action@v2
We also need to use -Porg.gradle.java.installations.auto-detect=false so some of
the Gradle versions don't get xerces ClassCastExceptions.

Closes gh-12568
2023-01-20 13:17:37 -06:00
Rob Winch c4ab7e2353 Revert to spring-gradle-build-action@v1 2023-01-20 08:50:58 -06:00
Rob Winch ff30a7e3db --stacktrace 2023-01-20 08:50:58 -06:00
Rob Winch 06cb8bfe6a Not quiet scan 2023-01-20 08:50:46 -06:00
Rob Winch 0f0c341cfa Add GRADLE_ENTERPRISE_SECRET_ACCESS_KEY 2023-01-19 22:07:45 -06:00
Dan Allen 576e4d4711 prime atlas extension for use in playbook template
- declare atlas extension as package
- declare release line extension as package
- enable release line extension by default
- configure atlas extension, but do not enable by default (enable using --extension @antora/atlas-extension)
2023-01-18 16:37:54 -06:00
Rob Winch 765f4f65fd Update to spring-io/spring-gradle-build-action@v2 2023-01-18 16:34:34 -06:00
Dan Allen f2aa1c7687 upgrade Asciidoctor Tabs, the Antora Gradle plugin, and Node.js 2023-01-17 09:42:43 -03:00
Dan Allen fcc344dfa2 document how to trigger the docs build workflow using the GitHub Actions UI or GitHub CLI 2023-01-17 09:42:20 -03:00
Dan Allen ac8e3f6810 only index pages from the latest version in each release line 2023-01-11 13:58:46 -06:00
Dan Allen 33b1038eb6 upgrade Gradle Antora plugin and extension packages 2023-01-11 10:22:33 -07:00
Dan Allen 063b48c739 add packages (dependencies) to playbook template in docs-build branch 2023-01-11 10:22:33 -07:00
Dan Allen 9442191a84 enable AsciiDoc sourcemap in branch/author builds for more accurate log messages 2023-01-10 09:05:24 -06:00
Dan Allen ea5260adad add playbook template to support local build in content branches (#12497) 2023-01-06 15:12:37 -06:00
Dan Allen 1c181dc81c remove temporary playbook 2022-12-24 22:46:03 -03:00
Dan Allen fdfbdcc645 fix name of secret for Algolia API key [no ci] 2022-12-24 22:44:50 -03:00
Dan Allen 5ac63ed64f show build-refname value in Deploy Docs workflow name 2022-12-24 22:43:55 -03:00
Dan Allen 5855bbb378 fix secret for indexer workflow 2022-12-23 09:47:45 -03:00
Dan Allen 409bd29abd rework the search indexing process
- reconfigure the docsearch scraper to store component and version for each res
- switch from the Docker image to a resuable GitHub Action
- add publish-docsearch-config extension to transform Handlebars into YAML
2022-12-22 10:26:13 -03:00
Dan Allen 2fb91e6266 switch to published partial build extension and remove local extension 2022-12-19 10:38:04 -03:00
Dan Allen f500c0fbe0 upgrade version of Asciidoctor Tabs to 1.0.0-alpha.11 2022-12-08 21:44:26 -06:00
Dan Allen c9ee194832 invalidate CDN cache after full build 2022-12-06 17:04:09 -06:00
Dan Allen 7757eab714 add Asciidoctor Tabs, tabs migration, code chomping & code folding to production site 2022-12-06 15:29:01 -06:00
Dan Allen dd44928051 add 6.x branches to build 2022-11-28 08:22:40 -03:00
Dan Allen 35bdfa87a0 do full sync when Antora reverts to running a full build (#12263) 2022-11-22 09:06:56 -06:00
Dan Allen 51fa7c0c34 remove dry-run on sync script; replace with -c for partial build 2022-11-10 21:08:37 -06:00
Dan Allen 7491f8a76d configure Antora and CI workflow to support partial build (single version)
- upgrade to Antora 3.2.0-alpha.2
- upgrade release-line-extension to 1.0.0-alpha.2
- add atlas-extension
- add local autoconfig-partial-build extension
- configure deploy-docs.yml workflow to detect and set up partial build
- configure publish-docs.sh script to sync partial build
2022-11-10 14:25:04 -06:00
Dan Allen b211b55043 fix the version fix for 5.6.0-RC1
* look for number instead of string
2022-11-03 09:29:30 -05:00
Dan Allen 32e2793167 sync local-antora-playbook.yml with antora-playbook.yml 2022-10-26 16:51:45 -06:00
Dan Allen e2f3ba2245 add release line extension 2022-10-25 14:42:44 -05:00
Dan Allen c7d14e0c02 refactor version patching extension 2022-10-25 14:42:44 -05:00
Dan Allen 466b882d82 refine docs build
- upgrade Antora to 3.2.0-alpha.1
- remove extension (patch) to support linked worktrees
- rename local playbook to local-antora-playbook.yml
- use more precise tags pattern
2022-10-24 09:57:12 -05:00
Rob Winch d7d1ee2b7e Include tags for versions larger than 5
This includes tags for versions larger than 5, but only as large as 9. We
will need to fix this for versions >= 10.
2022-10-18 13:31:54 -05:00
Rob Winch 40f6324d8f Instead of deleting aggregate.prerelease set to true
This ensures that the aggregate is not marked as a stable version.
2022-10-18 13:30:55 -05:00
Rob Winch d6ccf679c4 Fix any prerelease that != true or undefined
Previously the build would fail with duplicate versions because milestones
and RCs had the same version with a different prerelease value.

Now this is patched by changing the version to contain any prerelease value
other than true and changing prerelease to true.
2022-10-18 13:29:52 -05:00
Dan Allen c4bff97fe8 apply base plugin to add clean task 2022-10-03 13:34:53 -05:00
Dan Allen 649823f401 switch to official Antora Plugin for Gradle
- lock version to latest release of Antora 3.1
- declare Node.js dependencies in build.gradle
- use Node.js version provided by plugin
- assign environment variables using environments property on extension block
- move group and description project properties to gradle.properties
- remove unneeded .npmrc
- upgrade to Gradle 7.5.1
- pass -q when running gradlew from Collector
2022-09-29 14:43:44 -05:00
Sarah White 957566ebd7 add a README that documents how to build the docs for a software branch and the whole project 2022-09-16 15:36:49 -05:00
Dan Allen f2f7eb7482 eliminate the dependency on the generated-docs repository
- add and register Antora Collector
- configure Antora Collection at runtime using another extension
2022-09-12 13:13:02 -05:00
Dan Allen 6bd10b4056 propagate exit code in publish-docs script 2022-09-12 13:11:54 -05:00
Dan Allen 79a23b8b27 fix docs publish script and trigger workflow on correct branch name 2022-09-09 15:28:01 -05:00
Rob Winch 3f979ac958 Fix branch name to docs-build 2022-09-09 15:12:40 -05:00
Rob Winch bcdde1c850 Fix error message to use SSH_KNOWN_HOST variable properly
Previously SSH_KNOWN_HOSTS was used which is undefined.
2022-09-09 15:10:15 -05:00
Rob Winch 826f776d9b Use .ssh for SSH_PRIVATE_KEY_PATH
This ensures the .ssh folder is being created and aligns the private key
and known host location.
2022-09-09 15:10:15 -05:00
Rob Winch edf2f55db1 Add on push deploy-docs 2022-09-09 15:06:40 -05:00
Rob Winch 93cae95b12 chmod +x publish-docs.sh
Issue: gh-11787
2022-09-09 13:05:25 -05:00
Dan Allen f2020a2cf4 build and publish RC versions 2022-09-09 12:54:10 -05:00
Rob Winch 7a8ba42d57 Merge pull request #11787 from opendevise/docs-build
Add Antora Build to docs-build branch
2022-09-08 09:00:15 -05:00
Dan Allen ca1a83fd06 enable restriction on workflows 2022-09-07 17:23:59 -06:00
Dan Allen ba7a6d1c64 add Antora playbook that is configured to use worktrees 2022-09-07 17:23:59 -06:00
Dan Allen a3024326a3 add CI workflow and scripts to rebuild search index 2022-09-06 15:23:09 -06:00
Dan Allen 7cea7b49aa set up CI workflow to deploy docs 2022-09-06 15:23:09 -06:00
Dan Allen c993da749c populate playbook branch for docs site 2022-09-06 15:23:09 -06:00
Rob Winch 743e37e481 Initial docs build branch 2022-09-06 14:57:12 -05:00
2413 changed files with 1022 additions and 311693 deletions
+15 -10
View File
@@ -1,17 +1,22 @@
# EditorConfig for Spring Security
# see https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.md#mind-the-whitespace
# see https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.adoc#mind-the-whitespace
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
charset = latin1
# 4 tabs indentation
indent_style = tab
tab_width = 4
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 120
[*.{java,xml}]
indent_style = tab
indent_size = 4
charset = utf-8
continuation_indent_size = 8
ij_smart_tabs = false
ij_java_align_multiline_parameters = false
[*.gradle]
indent_style = tab
-36
View File
@@ -1,36 +0,0 @@
### Summary
<!--
Please provide a high level summary of the issue you are having
-->
### Actual Behavior
<!--
Please describe step by step the behavior you are observing
-->
### Expected Behavior
<!--
Please describe step by step the behavior you expect
-->
### Configuration
<!--
Please provide any configuration you have.
-->
### Version
<!--
Please describe what version you are using. Does the problem occur in other versions?
-->
### Sample
<!--
Providing a complete sample (i.e. link to a github repository) will give this issue higher
priority than issues that do not have a complete sample
-->
-3
View File
@@ -1,3 +0,0 @@
<!--
Thanks for contributing to Spring Security. Please provide a brief description of your pull-request and reference any related issue numbers (prefix references with #).
-->
+80
View File
@@ -0,0 +1,80 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Advanced"
on:
push: # run if we update the workflow
workflow_dispatch:
schedule:
- cron: '39 13 * * 4'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ubuntu-latest
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v6
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
queries: security-extended,security-and-quality
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
+51
View File
@@ -0,0 +1,51 @@
name: Deploy Docs
run-name: ${{ format('{0} ({1})', github.workflow, github.event.inputs.build-refname || 'all') }}
on:
workflow_dispatch:
inputs:
build-refname:
description: Enter git refname to build (e.g., 5.7.x).
required: false
push:
branches: docs-build
env:
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
permissions: read-all
jobs:
build:
if: github.repository_owner == 'spring-projects'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 5
- name: Set Up Gradle
uses: spring-io/spring-gradle-build-action@v2
with:
# Use JDK 17 since 6.5.x Kotlin version does not support JDK 25 and thus will fail
java-version: '17'
distribution: temurin
- name: Set up refname build
if: github.event.inputs.build-refname
run: |
git fetch --depth 1 https://github.com/$GITHUB_REPOSITORY ${{ github.event.inputs.build-refname }}
echo BUILD_REFNAME=${{ github.event.inputs.build-refname }} >> $GITHUB_ENV
echo BUILD_VERSION=$(git cat-file --textconv FETCH_HEAD:gradle.properties | sed -n '/^version=/ { s/^version=//;p }') >> $GITHUB_ENV
- name: Run Antora
run: ./gradlew antora
- name: Copy the cache to be included in the site
run: cp -rf build/antora/inject-collector-cache-config-extension/.cache build/site/
- name: Publish Docs
uses: spring-io/spring-doc-actions/rsync-antora-reference@v0.0.22
with:
docs-username: ${{ secrets.DOCS_USERNAME }}
docs-host: ${{ secrets.DOCS_HOST }}
docs-ssh-key: ${{ secrets.DOCS_SSH_KEY }}
docs-ssh-host-key: ${{ secrets.DOCS_SSH_HOST_KEY }}
- name: Bust Clouflare Cache
uses: spring-io/spring-doc-actions/bust-cloudflare-antora-cache@v0.0.22
with:
context-root: spring-security
cloudflare-zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
cloudflare-cache-token: ${{ secrets.CLOUDFLARE_CACHE_TOKEN }}
+30
View File
@@ -0,0 +1,30 @@
name: Merge Dependabot PR
on: pull_request_target
run-name: Merge Dependabot PR ${{ github.ref_name }}
permissions:
pull-requests: write
contents: write
jobs:
merge-dependabot-pr:
runs-on: ubuntu-latest
if: github.event.pull_request.user.login == 'dependabot[bot]'
steps:
- uses: actions/checkout@v6
with:
show-progress: false
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17
- name: Merge Dependabot pull request
run: gh pr merge ${{ github.event.pull_request.number }} --auto --rebase
env:
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
+13 -25
View File
@@ -1,25 +1,13 @@
target/
*/src/*/java/META-INF
*/src/META-INF/
*/src/*/java/META-INF/
.classpath
.springBeans
.project
.DS_Store
.settings/
.idea/
out/
bin/
intellij/
build/
*.log
*.log.*
*.iml
*.ipr
*.iws
.gradle/
atlassian-ide-plugin.xml
!etc/eclipse/.checkstyle
.checkstyle
classes/
s101plugin.state
/.gradle/
/.idea/*
/.settings/
/.classpath
/.project
/build/
/node_modules/
/package-lock.json
/*.iml
/*.ipr
/*.iws
!/.idea/checkstyle-idea.xml
!/.idea/externalDependencies.xml
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CheckStyle-IDEA">
<option name="configuration">
<map>
<entry key="checkstyle-version" value="8.14" />
<entry key="copy-libs" value="false" />
<entry key="location-0" value="BUNDLED:(bundled):Sun Checks" />
<entry key="location-1" value="BUNDLED:(bundled):Google Checks" />
<entry key="scan-before-checkin" value="false" />
<entry key="scanscope" value="JavaOnlyWithTests" />
<entry key="suppress-errors" value="false" />
</map>
</option>
</component>
</project>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalDependencies">
<plugin id="CheckStyle-IDEA" />
<plugin id="org.jetbrains.plugins.gradle" />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
# Use sdkman to run "sdk env" to initialize with correct JDK version
# Enable auto-env through the sdkman_auto_env config
# See https://sdkman.io/usage#config
# A summary is to add the following to ~/.sdkman/etc/config
# sdkman_auto_env=true
java=17.0.3-tem
-16
View File
@@ -1,16 +0,0 @@
language: java
jdk:
- oraclejdk8
os:
- linux
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
script: ./gradlew build
-44
View File
@@ -1,44 +0,0 @@
= Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open
and welcoming community, we pledge to respect all people who contribute through reporting
issues, posting feature requests, updating documentation, submitting pull requests or
patches, and other activities.
We are committed to making participation in this project a harassment-free experience for
everyone, regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses,
without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or reject comments,
commits, code, wiki edits, issues, and other contributions that are not aligned to this
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
consistently applying these principles to every aspect of managing this project. Project
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
from the project team.
This Code of Conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
be reviewed and investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
with regard to the reporter of an incident.
This Code of Conduct is adapted from the
http://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
http://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]
-205
View File
@@ -1,205 +0,0 @@
_Have something you'd like to contribute to the framework? We welcome pull requests, but ask that you carefully read this document first to understand how best to submit them; what kind of changes are likely to be accepted; and what to expect from the Spring Security team when evaluating your submission._
_Please refer back to this document as a checklist before issuing any pull request; this will save time for everyone!_
# Code of Conduct
This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.adoc).
By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
# Similar but different
Each Spring module is slightly different than another in terms of team size, number of issues, etc. Therefore each project is managed slightly different. You will notice that this document is very similar to the [Spring Framework Contributor guidelines](https://github.com/SpringSource/spring-framework/wiki/Contributor-guidelines). However, there are some subtle differences between the two documents, so please be sure to read this document thoroughly.
# Importing into IDE
The following provides information on setting up a development environment that can run the sample in [Spring Tool Suite 3.6.0+](http://www.springsource.org/sts). Other IDE's should work using Gradle's IDE support, but have not been tested.
* IDE Setup
* Install Spring Tool Suite 3.6.0+
* You will need the following plugins installed (can be found on the Extensions Page)
* Gradle Eclipse
* Groovy Eclipse
* Importing the project into Spring Tool Suite
* File->Import...->Gradle Project
# Understand the basics
Not sure what a pull request is, or how to submit one? Take a look at GitHub's excellent [help documentation first](https://help.github.com/articles/using-pull-requests).
# Search GitHub issues; create an issue if necessary
Is there already an issue that addresses your concern? Do a bit of searching in our [GitHub issues ](https://github.com/spring-projects/spring-security/issues) to see if you can find something similar. If not, please create a new issue before submitting a pull request unless the change is not a user facing issue.
# Discuss non-trivial contribution ideas with committers
If you're considering anything more than correcting a typo or fixing a minor bug, please discuss it on the [Spring Security Gitter](https://gitter.im/spring-projects/spring-security) before submitting a pull request. We're happy to provide guidance but please spend an hour or two researching the subject on your own including searching the forums for prior discussions.
# Sign the Contributor License Agreement
If you have not previously done so, please fill out and
submit the [Contributor License Agreement](https://cla.pivotal.io/sign/spring).
# Create your branch from master
Create your topic branch to be submitted as a pull request from master. The Spring team will consider your pull request for backporting on a case-by-case basis; you don't need to worry about submitting anything for backporting.
# Use short branch names
Branches used when submitting pull requests should preferably be named according to GitHub issues, e.g. 'gh-1234' or 'gh-1234-fix-npe'. Otherwise, use succinct, lower-case, dash (-) delimited names, such as 'fix-warnings', 'fix-typo', etc. This is important, because branch names show up in the merge commits that result from accepting pull requests, and should be as expressive and concise as possible.
#Keep commits focused
Remember each ticket should be focused on a single item of interest since the tickets are used to produce the changelog. Since each commit should be tied to a single GitHub issue, ensure that your commits are focused. For example, do not include an update to a transitive library in your commit unless the GitHub is to update the library. Reviewing your commits is essential before sending a pull request.
# Mind the whitespace
Please carefully follow the whitespace and formatting conventions already present in the framework.
1. Tabs, not spaces
1. Unix (LF), not dos (CRLF) line endings
1. Eliminate all trailing whitespace
1. Aim to wrap code at 120 characters, but favor readability over wrapping
1. Preserve existing formatting; i.e. do not reformat code for its own sake
1. Search the codebase using git grep and other tools to discover common naming conventions, etc.
1. Latin-1 (ISO-8859-1) encoding for Java sources; use native2ascii to convert if necessary
Whitespace management tips
1. You can use the [AnyEdit Eclipse plugin](http://marketplace.eclipse.org/content/anyedit-tools) to ensure spaces are used and to clean up trailing whitespaces.
1. Use git's pre-commit.sample hook to prevent invalid whitespace from being pushed out. You can enable it by moving ~/spring-security/.git/hooks/pre-commit.sample to ~/spring-security/.git/hooks/pre-commit and ensuring it is executable. For more information on hooks refer to [Pro Git's Pre-Commit Hook's section](http://git-scm.com/book/cs/ch7-3.html)
# Add Apache license header to all new classes
<pre>
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ...;
</pre>
# Update Apache license header to modified files as necessary
Always check the date range in the license header. For example, if you've modified a file in 2012 whose header still reads
<pre>
* Copyright 2002-2011 the original author or authors.
</pre>
then be sure to update it to 2012 appropriately
<pre>
* Copyright 2002-2012 the original author or authors.
</pre>
# Use @since tags for newly-added public API types and methods
e.g.
<pre>
/**
* ...
*
* @author First Last
* @since 3.2
* @see ...
*/
</pre>
# Submit JUnit test cases for all behavior changes
Search the codebase to find related unit tests and add additional `@Test` methods within.
1. Any new tests should end in the name Tests (note this is plural). For example, a valid name would be `FilterChainProxyTests`. An invalid name would be `FilterChainProxyTest`.
2. New test methods should not start with test. This is an old JUnit3 convention and is not necessary since the method is annotated with @Test.
# Update spring-security-x.y.rnc for schema changes
Update the [RELAX NG](http://www.relaxng.org) schema `spring-security-x.y.rnc` instead of `spring-security-x.y.xsd` if you contribute changes to supported XML configuration. The XML schema file can be generated the following Gradle task:
<pre>
./gradlew spring-security-config:rncToXsd
</pre>
Changes to the XML schema will be overwritten by the Gradle build task.
# Squash commits
Use git rebase --interactive, git add --patch and other tools to "squash" multiple commits into atomic changes. In addition to the man pages for git, there are many resources online to help you understand how these tools work. Here is one: http://book.git-scm.com/4_interactive_rebasing.html.
# Use real name in git commits
Please configure git to use your real first and last name for any commits you intend to submit as pull requests. For example, this is not acceptable:
<pre>
Author: Nickname &lt;user@mail.com&gt;
</pre>
Rather, please include your first and last name, properly capitalized, as submitted against the SpringSource contributor license agreement:
<pre>
Author: First Last &lt;user@mail.com&gt;
</pre>
This helps ensure traceability against the CLA, and also goes a long way to ensuring useful output from tools like git shortlog and others.
You can configure this globally via the account admin area GitHub (useful for fork-and-edit cases); globally with
<pre>
git config --global user.name "First Last"
git config --global user.email user@mail.com
</pre>
or locally for the spring-security repository only by omitting the '--global' flag:
<pre>
cd spring-security
git config user.name "First Last"
git config user.email user@mail.com
</pre>
# Format commit messages
<pre>
Short (50 chars or less) summary of changes
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body. The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase can get confused if you run the
two together.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded by a
single space, with blank lines in between, but conventions vary here
Fixes gh-123
</pre>
1. Keep the subject line to 50 characters or less if possible
2. Do not end the subject line with a period
3. In the body of the commit message, explain how things worked before this commit, what has changed, and how things work now
3. Include Fixes gh-<issue-number> at the end if this fixes a GitHub issue
# Run all tests prior to submission
<pre>
cd spring-security
./gradlew clean build integrationTest
</pre>
# Submit your pull request
Subject line:
Follow the same conventions for pull request subject lines as mentioned above for commit message subject lines.
In the body:
1. Explain your use case. What led you to submit this change? Why were existing mechanisms in the framework insufficient? Make a case that this is a general-purpose problem and that yours is a general-purpose solution, etc
2. Add any additional information and ask questions; start a conversation, or continue one from GitHub Issues
3. Mention any GitHub Issues
4. Also mention that you have submitted the CLA as described above
Note that for pull requests containing a single commit, GitHub will default the subject line and body of the pull request to match the subject line and body of the commit message. This is fine, but please also include the items above in the body of the request.
# Mention your pull request on the associated GitHub issue
Add a comment to the associated GitHub issue(s) linking to your new pull request.
# Expect discussion and rework
The Spring team takes a very conservative approach to accepting contributions to the framework. This is to keep code quality and stability as high as possible, and to keep complexity at a minimum. Your changes, if accepted, may be heavily modified prior to merging. You will retain "Author:" attribution for your Git commits granted that the bulk of your changes remain intact. You may be asked to rework the submission for style (as explained above) and/or substance. Again, we strongly recommend discussing any serious submissions with the Spring Framework team prior to engaging in serious development work.
Note that you can always force push (git push -f) reworked / rebased commits against the branch used to submit your pull request. i.e. you do not need to issue a new pull request when asked to make changes.
+460 -48
View File
@@ -1,66 +1,478 @@
image::https://badges.gitter.im/Join%20Chat.svg[Gitter,link=https://gitter.im/spring-projects/spring-security?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge]
= Spring Security Docs Home
ifndef::env-github[:toc:]
ifdef::env-github[]
:important-caption: :exclamation:
:note-caption: :paperclip:
endif::[]
image:https://travis-ci.org/spring-projects/spring-security.svg?branch=master["Build Status", link="https://travis-ci.org/spring-security/spring-security"]
This README describes the processes and tools used to build the documentation for production as well as how to generate a local preview of the documentation.
= Spring Security
== Overview
Spring Security provides security services for the http://docs.spring.io[Spring IO Platform]. Spring Security 3.1 requires Spring 3.0.3 as
a minimum and also requires Java 5.
The Spring Security reference docs are generated using Antora.
The Gradle Antora Plugin is used as the primary interface to Antora.
For a detailed list of features and access to the latest release, please visit http://spring.io/projects[Spring projects].
You're viewing the playbook branch for the Spring Security project.
The playbook branch hosts the docs build used to build and publish the production docs site, and is thus the documentation home base.
If you're a docs developer, you'll mostly work in this branch.
If you're a writer, you'll mostly work in software branches.
== Code of Conduct
This project adheres to the Contributor Covenant link:CODE_OF_CONDUCT.adoc[code of conduct].
By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
=== Layout
== Downloading Artifacts
See https://github.com/spring-projects/spring-framework/wiki/Downloading-Spring-artifacts[downloading Spring artifacts] for Maven repository information.
The playbook branch, named *docs-build*, hosts the primary documentation build.
The documentation itself is located in a dedicated subproject in each software branch (i.e., the docs are stored alongside the code).
Software branches, also referred to as release line branches or content branches, follow the pattern `major.minor.x` (e.g., 6.0.x), or `main` for the latest release line.
Software tags follow the pattern `major.minor.patch` (e.g., 6.0.1).
The latest version of the docs for each release line is always sourced from a tag.
The release line branches host the prerelease materials for the next version.
== Documentation
Be sure to read the http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/[Spring Security Reference].
Extensive JavaDoc for the Spring Security code is also available in the http://docs.spring.io/spring-security/site/docs/current/apidocs/[Spring Security API Documentation].
=== Builds
== Quick Start
We recommend you visit http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/[Spring Security Reference] and read the "Getting Started" page.
The build for the production site is stored at the root of the playbook branch.
This branch also holds the search crawler configuration and runner for the production site.
This build is only needed for building the production site (or for developing and testing the docs build itself).
The build for each content branch is located in a subproject, typically the folder named _docs_.
This is the build that authors use to preview the docs for a single version when writing content.
== Building from Source
Spring Security uses a http://gradle.org[Gradle]-based build system.
In the instructions below, http://vimeo.com/34436402[`./gradlew`] is invoked from the root of the source tree and serves as
a cross-platform, self-contained bootstrap mechanism for the build.
Regardless of how the docs site is built, Antora is configured to run a separate command to compute the version of the docs per git reference.
This command is run by an Antora extension named Antora Collector.
Using Collector allows the version of the docs to be maintained centrally in the _gradle.properties_ file in each git reference.
The command also populates a collection of AsciiDoc attributes that provide access to software versions and resource URLs.
_The docs build will not work without Collector._
=== Prerequisites
http://help.github.com/set-up-git-redirect[Git] and the http://www.oracle.com/technetwork/java/javase/downloads[JDK8 build].
=== UI
Be sure that your `JAVA_HOME` environment variable points to the `jdk1.8.0` folder extracted from the JDK download.
The UI is developed in a separate project named https://github.com/spring-io/antora-ui-spring[antora-ui-spring].
That project generates and publishes a UI bundle, which the docs build refers to using its public bundle URL.
There is one UI for all versions of the documentation.
=== Check out sources
[indent=0]
----
git clone git@github.com:spring-projects/spring-security.git
----
The UI only shows the latest version in each release line.
In order to access other versions that are published, the URL must be known in advance.
=== Install all spring-\* jars into your local Maven cache
[indent=0]
----
./gradlew install
----
[#usage]
== Usage
=== Compile and test; build all jars, distribution zips, and docs
[indent=0]
----
./gradlew build
----
To prepare your system for building the documentation, <<prerequisites,install the prerequisites>>, then <<prepare-workspace,prepare your workspace>>.
Then you can build the documentation in either the main branch or 6.0.x branch.
Discover more commands with `./gradlew tasks`.
See also the https://github.com/spring-projects/spring-framework/wiki/Gradle-build-and-release-FAQ[Gradle build and release FAQ].
.Branch checkout instead of worktrees
[NOTE]
====
If you prefer to set up your workspace without worktrees, complete the steps in <<prerequisites,Prerequisites>> and clone the project repository onto your computer.
Then, check out the desired branch and follow the instructions in each section starting from the `sdk env || sdk env install` step.
====
== Getting Support
Check out the http://stackoverflow.com/questions/tagged/spring-security[Spring Security tags on Stack Overflow].
http://spring.io/services[Commercial support] is available too.
[#prerequisites]
=== Prerequisites (everyone)
== Contributing
http://help.github.com/send-pull-requests[Pull requests] are welcome; see the https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.md[contributor guidelines] for details.
These instructions assume you already have basic tools on your system, including bash, zip, unzip, git, and curl.
In addition to these basic tools, you need https://sdkman.io/install[SDKMAN!] installed so that the correct JDK is set for each branch.
== License
Spring Security is Open Source software released under the
http://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
. Open your terminal and enter the following command:
+
--
$ curl -s "https://get.sdkman.io" | bash
This command downloads and installs SDKMAN!
Once installation is complete, you should see a command displayed in your terminal that will initiate SDKMAN!.
--
. Copy the command displayed in your terminal and run it.
In the following command, `$HOME` is the path unique to your computer (e.g., _home/username/.sdkman/bin/sdkman-init.sh_).
$ source "$HOME/.sdkman/bin/sdkman-init.sh"
You'll use SDKMAN! in the following sections to install and switch to the JDK required for each branch.
Now you're ready to prepare your workspace.
[#prepare-workspace]
=== Prepare your workspace (everyone)
Your workspace will be the folder that contains the git worktrees of the project.
. In your terminal, create a directory for the project and then change into that directory.
$ mkdir spring-security
$ cd spring-security
. Clone the project repository and create the worktree for the main branch.
Then change into the new *main* worktree.
$ git clone https://github.com/spring-projects/spring-security main
$ cd main
. Set up a worktree for the 6.0.x branch by running the following commands:
$ git worktree add ../6.0.x 6.0.x --track
. Repeat the last step for all release line branches (e.g., 5.8.x, 5.7.x, etc) you intend to work with.
Now you're ready to build the docs in the main or 6.0.x branches.
=== Build the documentation in the main branch (writers)
NOTE: The instructions in this section assume you've completed the steps in <<prepare-workspace>>.
. First, make sure you're in the *main* worktree.
. Switch to the required JDK using SDKMAN! by running the following command:
+
--
$ sdk env || sdk env install
SDKMAN! will switch to the required JDK, provisioning it first if it isn't already available on your machine.
--
. Generate the documentation with Antora using the following command:
+
--
$ ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
You can also run this command directly from the _docs_ folder:
$ cd docs
$ ../gradlew -PbuildSrc.skipTests=true antora
This command will build the documentation for the *main* branch.
The Antora playbook is retrieved by the playbook provider from the *docs-build* branch.
The retrieved playbook file will be cached as _cached-antora-playbook.yml_.
--
. Navigate to the local file URI shown in the terminal to view the generated documentation.
=== Build the documentation in the 6.0.x branch (writers)
NOTE: The instructions in this section assume you've completed the steps in <<prepare-workspace>>.
. First, change to the *6.0.x* worktree.
$ cd ../6.0.x
. Switch to the required JDK using SDKMAN! by running the following command:
+
--
$ sdk env || sdk env install
SDKMAN! will switch to the required JDK, provisioning it first if it isn't already available on your machine.
--
. Generate the documentation with the following command:
+
--
$ ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
This command will build the documentation for the *6.0.x* branch.
The Antora playbook is retrieved by the playbook provider from the *docs-build* branch.
The retrieved playbook file will be cached as _cached-antora-playbook.yml_.
--
. Navigate to the local file URI shown in the terminal to view the generated documentation.
=== Build the documentation for production (docs developer)
NOTE: The instructions in this section assume you've completed the steps in <<prepare-workspace>>.
To build the project's production site, you'll set up a worktree for the *docs-build* branch of the repository.
. To add a worktree, you have to be in the main worktree.
In your terminal, change to the *main* worktree if you aren't already in it.
$ cd ../main
. Run the following command to set up the worktree for the *docs-build* branch.
Then change into the new _docs-build_ directory.
$ git worktree add ../docs-build docs-build --track
$ cd ../docs-build
. Switch to the required JDK or install it.
$ sdk env || sdk env install
. Generate the documentation for the project's production site using the following command:
+
--
$ ./gradlew antora
This command will build all of the documentation for the production site from the git repository on GitHub.
(Optional) To build the documentation using the current clone, using any available worktrees, run the following command instead:
$ ./gradlew antora --playbook local-antora-playbook.yml
--
. Navigate to the local file URI shown in the terminal to view the generated documentation.
=== Content authoring
We highly recommend relying on the https://intellij-asciidoc-plugin.ahus1.de/docs/users-guide/index.html[IntelliJ AsciiDoc Plugin] while writing.
It provides assistance and autocompletion for the AsciiDoc syntax, Antora resource IDs, attributes and keys set in the playbook, component version descriptor, etc.
It also provides single page preview.
Once you've completed your edits, you'll build the branch locally to review and validate the changes.
*You don't need to build the entire site!*
*You don't need to create or edit the playbook or gradle.properties file either.*
Rather, you'll interface with the docs build, and it will automatically set up a playbook for your branch and manage any required extensions, right from the docs subproject in your software (content) branch.
See <<usage>> to learn how to build the docs.
If the branch you modified has any AsciiDoc or Antora errors, they'll be printed to your terminal.
Once you've fixed any errors and reviewed your changes, submit a pull request to the relevant software branch.
If the change applies to multiple versions of the docs, you'll want to submit the pull request to the oldest active software branch.
The maintainer will then apply that change to each of the release line branches.
== CI workflows
CI workflows are run by GitHub Actions.
CI workflows are defined in YAML files in the _.github/workflows_ directory.
The CI workflows in the default (i.e., _main_) branch serve as the primary entry.
Corresponding CI workflows in a non-default branch may specialize the workflow for that branch.
However, the CI workflows in non-default branches do not receive all events and often have to be triggered.
A CI workflow must also be present in the default branch in order for it to appear in the list of workflows in the GitHub Actions web UI.
CI workflows are triggered either by activity, on schedule, by the `gh workflow` call, or manually through the GitHub Actions web UI.
Scheduled workflows only run on the default branch (i.e., *main*).
However, a scheduled workflow may trigger a workflow in another branch using the `gh workflow` call.
Activity on a branch or tag is only picked up by workflows in that reference.
However, a workflow running in a branch or tag may trigger a workflow in another branch using the `gh workflow` call.
There are two key CI workflows that pertain to the docs:
* Deploy Docs (_.github/workflows/deploy-docs.yml_)
* Rebuild Search Index (_.github/workflows/rebuild-search-index.yml_)
In both cases, the concrete steps are located in the CI workflow in the *docs-build* branch.
The CI workflows in the *main* branch only trigger the workflows in the *docs-build* branch.
The production site is only deployed from the CI workflow in the *docs-build* branch.
Often times, activity in a git reference or a scheduled workflow will trigger the CI workflow in the *docs-build* branch.
Thus, this workflow is also present in each software branch to pick up on that activity.
The production search index is built from the CI workflow in the *docs-build* branch.
This CI workflow is triggered once per day by the scheduler on the same workflow in the *main* branch.
This CI workflow must reside in the *main* branch in order to appear in the list of workflows in the CI branch so it can be triggered manually.
Here's a list of activity that does and does not trigger the Deploy Docs workflow:
pull request:: Does not trigger the Deploy Docs workflow.
push to software branch:: Triggers the Deploy Docs workflow in that branch, which in turn triggers the Deploy Docs workflow in the *docs-build* branch.
Attempts to run the docs build as partial build, if applicable.
push to docs-build branch:: Triggers the Deploy Docs workflow in that branch.
Runs the docs build as a full build.
push tag:: Triggers the Deploy Docs workflow in that tag, which in turn triggers the Deploy Docs workflow in the *docs-build* branch.
Always runs the docs build as a full build.
schedule:: Not configured for the Deploy Docs workflow.
It's possible to trigger the Deploy Docs manually from the GitHub Actions web UI.
Be sure to select docs-build as the branch so that it will run a full build.
See <<trigger>> for details.
Note that updating the UI bundle does not currently trigger the Deploy Docs workflow, though it could be configured to do so.
The Rebuild Search Index workflow is only triggered on a schedule, currently once per day.
== Publishing
The project in the *docs-build* branch supports publishing both full and partial builds of the production docs site.
The project uses a Gradle build to run Antora on the playbook file _antora-playbook.yml_ (i.e., `./gradlew antora`).
The production docs site is hosted on Linux server running Apache httpd.
Files are published over SSH to the server by the .github/actions/publish-docs.sh script.
A CDN (CloudFlare) caches URLs for a brief window of time.
The publish script attempts to invalidate the cache after publishing the files so new content is available immediately.
=== Full build
In a full build, the entire site is rebuilt from the content sources matched by the patterns listed in the playbook file.
The UI assets are also published when a full build is run.
[#partial-builds]
=== Partial builds
A partial build is a single version sources from a single git reference.
A partial build requested by git reference using the CI workflow variable *build-refname*.
Here's an example of how to trigger the CI workflow for a partial build:
$ gh workflow run deploy-docs.yml --repo spring-projects/spring-security --ref docs-build -f build-refname=5.7.x
The partial build is coordinated by the Antora Atlas extension and set up by the @springio/antora-extensions/partial-build-extension extension.
See https://github.com/spring-io/antora-extensions#partial-build[Partial Build] for a detailed explanation of the partial build extension and how to configure it.
During a partial build, Atlas runs in same site mode, which means it creates relative links (rather than absolute links) to files imported from the site manifest.
This feature assumes that the built files will be reunited with the previously built files in the published site.
The @springio/antora-extensions/partial-build-extension reconfigures the playbook to run a partial build if the BUILD_REFNAME environment variable is set, reverting to a full build if it determines a partial build is not appropriate.
During a partial build, only the version folder that was built is published to the web server.
Files in other folders are untouched.
The UI assets are not published when a partial build is run.
[#trigger]
=== Trigger the documentation build workflow (docs developer)
You can trigger the production document build using the Deploy Docs entry in the GitHub Actions web UI or using the https://cli.github.com/[GitHub CLI].
==== GitHub Actions web UI
. In the GitHub Actions web UI, click the Deploy Docs entry.
. Click on the "Run workflow" menu.
.. *To trigger full build*, select the *docs-build* branch and click "Run workflow".
.. *To trigger a partial build*, specify a release line branch name in the input field labeled "Enter git refname to build" and click "Run workflow".
==== GitHub CLI
*To trigger full build*, start from within the cloned repository (ideally the playbook branch) and enter the `gh` command and options in the GitHub CLI:
$ gh workflow run deploy-docs.yml --ref docs-build
*To trigger a partial build*, enter the `gh` command and options to build a single version (based on the release line branch name):
$ gh workflow run deploy-docs.yml --ref docs-build -f build-refname=5.7.x
Run `gh help workflow run` to show the docs for this command and other examples of how to use it.
If you're not running the `gh` command from within the cloned repository, you can specify the repository using the `--repo` CLI option (e.g., `--repo spring-projects/spring-security`).
== Extensions
The Spring Security docs have additional requirements above what Antora provides by default.
To fulfill these requirements, the docs build employs a handful of Antora and Asciidoctor extensions to build successfully.
You can't build the Spring Security docs using the base distribution of Antora.
Fortunately, this extra complexity is encapsulated in the Antora playbook and several distributed extensions.
IMPORTANT: The order of Antora extensions in the playbook matters.
If the order is changed, it could result in files or metadata that an extension relies on not being available at the time it runs.
For the most part, the extensions are retrieved from the npm package registry (npmjs.com).
There are also several local extensions in _lib/antora/extensions_.
The local extensions handle logic specific to this project and are only used for the production build.
Below is a summary of the Antora and Asciidoctor extensions used in the docs build.
=== Antora extensions
@springio/antora-extensions/partial-build-extension (prod only):: Configures a partial build, when requested, by setting the `primary-site-url` and `primary-site-manifest-url` AsciiDoc attributes.
See <<partial-builds>> for more information.
./lib/antora/extensions/inject-collector-config.js (prod only):: Injects configuration for Antora Collector into tags that predated Antora Collector being introduced.
See the next extension for details.
@antora/collector-extension:: Invokes a command (a Gradle task) to set the docs version from _gradle.properties_ and numerous AsciiDoc attributes that provide access to software versions and resource URLs.
The command that Antora Collector runs is essential for Antora to classify the docs properly.
./lib/antora/extensions/version-fix.js (prod only):: Fixes invalid metadata in _antora.yml_ and/or _gradle.properties_ in tags.
@antora/atlas-extension (prod only):: Generates the site manifest (_site-manifest.json_) and publishes it with the site.
Also coordinates the partial build when requested.
See <<partial-builds>> for details.
@opendevise/antora-release-line-extension:: Abbreviates the version segment (in the URL) of the latest version in each release line from major.minor.patch to major.minor.
The version segment of the latest overall version is still abbreviated to empty string by Antora.
@springio/antora-extensions/tabs-migration-extension:: Migrates the tabs syntax from Spring Tabs to Asciidoctor Tabs.
See <<tabs-migration>> for details.
./lib/antora/extensions/publish-docsearch-config.js (prod only):: Publishes the docsearch config file to the production site so the indexer can use it.
See <<search>> for details.
=== Asciidoctor extensions
@asciidoctor/tabs:: Enables the tabs block in AsciiDoc.
See <<tabs-migration>> and the https://github.com/asciidoctor/asciidoctor-tabs[Asciidoctor Tabs README] for details.
@springio/asciidoctor-extensions (prod only):: Provides various enhancements to the output generated by Asciidoctor, mostly around code blocks.
See https://github.com/spring-io/asciidoctor-extensions[Spring.io Asciidoctor Extensions] for an inventory of extensions and how to activate and configure them.
[#tabs-migration]
== Tabs migration
The Spring Security docs contain two variations of the tabs syntax, https://github.com/spring-io/spring-asciidoctor-backends#tabs[Spring Tabs] and https://github.com/asciidoctor/asciidoctor-tabs[Asciidoctor Tabs].
Moving forward, Asciidoctor Tabs is the syntax that should be used.
However, since the Spring Security docs include content from tags that were written before Asciidoctor Tabs was introduced, the docs build must still be able to process the Spring Tabs syntax where it is used.
When the docs build runs, the Spring Tabs are automatically converted to Asciidoctor Tabs by the @springio/antora-extensions/tabs-migration-extension extension.
Spring Tabs are never in the final output (unless the tabs migration extension is switched off).
This extension also has the ability to unwrap the example block that encloses adjacent tabs, when possible, so only the tabs block remains.
If Spring Tabs are not detected in a document, the migration will not run on that document.
See https://github.com/spring-io/antora-extensions#tabs-migration[Tabs Migration] for a detailed explanation of this extension and how to configure it.
For the Spring Security docs, the tabs migration will always have to be used as long as there are tags in the build that contain Spring Tabs.
However, to reduce the amount of work the tabs migration extension has to do, the migration should be made permanent where possible.
Thus, we recommend making the migration permanent in release line branches that are active, and thus all future tags.
Saving the result of the tabs migration is done one software branch at a time.
To start, switch to a branch and run the docs build in that branch (this will retrieve the Antora playbook).
Next, edit the _cached-antora-playbook.yml_ file and add `save: true` underneath the key `unwrap_example_block`.
This setting will save the migrated files back to their original location under the _docs_ folder.
Run the docs build in that branch again to apply the tabs migration.
Now commit the changed files.
Once that's done, the tabs migration won't have to run on any documents in that branch.
[#search]
== Search
The search component in the docs site is powered by Algolia DocSearch (specifically 2.6).
DocSearch is a documentation-oriented toolchain for using Algolia's search solution.
It provides both a crawler (aka scraper or indexer) and a search interface.
The search index is hosted on the Algolia platform and queried from the search interface via a web API.
=== Client
The search interface is integrated into the UI bundle and initialized when the page loads.
The search interface is configured using a collection of environment variables: `ALGOLIA_API_KEY`, `ALGOLIA_APP_ID`, and `ALGOLIA_INDEX_NAME`.
For now, these environment variables are defined in _build.gradle_ for the production build.
The search interface is only activated when all of these values are set.
NOTE: The docsearch.js 2.6 package is marked as deprecated in npmjs.com.
However, the new client (@docsearch/js 3.x) has a completely different interaction model and search result display that's not compatible with customized client adapter currently in use.
In other words, switching to it means developing the customizations from scratch.
Even if that were to be done, the way the new client displays search results is over simplified.
The search results provided by docsearch.js 2.6 have proven to be clearer and easier to comprehend.
=== Crawler
The search index is created by the crawler component of DocSearch.
There are two steps involved.
First, the crawler must be configured.
Second, the crawler must be run with that configuration.
==== Configure
The behavior of the crawler is configured by a file name _docsearch-config.json_.
However, this file is not stored directly in the playbook branch.
Rather, it's generated from a template to account for the versions in the published site.
The generation of the _docsearch-config.js_ file happens during the production build.
This file is generated by the Antora extension _lib/antora/extensions/publish-docsearch-config.js_.
The extension generates a docsearch config so that docsearch indexes the latest version in each release line.
To do so, the extension configures Handlebars to run using a model derived from information in Antora's content catalog.
It then evaluates the template at _.github/actions/docsearch-config.json.hbs_ to produce a file at the root of the generated site named _docsearch-config.js_.
That file is published as part of the site.
==== Run
The crawler is periodically run on the production site by the *Rebuild Search Index* workflow.
The crawler creates a fresh search index and replaces the previous one.
The name of the index is *spring-security-docs*.
When the crawler runs, it downloads the _docsearch-config.json_ file from the production site and runs the docsearch action on it.
NOTE: The crawler only needs to be run on files that are publicly accessible, so it makes sense that the configuration be located there too.
In order to publish the search records (and thus create the index), the crawler must be configured using a collection of variables: `ALGOLIA_APPLICATION_ID` and `ALGOLIA_WRITE_KEY`.
These variables must be configured as secrets in GitHub Actions.
The index name is not required here as it is stored in the docsearch config file.
== Maintenance
The docs build requires regular maintenance.
Here's an inventory of the files or software versions to check and keep up to date.
.Playbook branch (i.e., *docs-build*)
* Gradle Antora Plugin (_build.gradle_)
* GitHub Actions libraries (_.github/workflows/deploy-docs.yml_, _.github/workflows/rebuild-search-index.yml_)
* Java version (_.sdkmanrc_)
* Node.js packages (_build.gradle_ and _lib/antora/templates/per-branch-antora-playbook.yml_)
* Gradle Wrapper (_gradle/wrapper/gradle-wrapper.properties_)
* Content sources in playbook (_antora-playbook.yml_ and _local-antora-playbook.yml_; ideally use patterns to minimize maintenance)
* List of registered extensions (_antora-playbook.yml_, _local-antora-playbook.yml_, and _lib/antora/templates/per-branch-antora-playbook.yml_)
.Content branches (e.g., *6.0.x*)
* Gradle Antora Plugin (_docs/spring-security-docs.gradle_)
* GitHub Actions libraries (_.github/workflows/deploy-docs.yml_, _.github/workflows/rebuild-search-index.yml_)
Recall that the playbook used for the local docs preview in content branches is maintained in the *docs-build* branch in _lib/antora/templates/per-branch-antora-playbook.yml_.
-19
View File
@@ -1,19 +0,0 @@
// Acl Module build file
dependencies {
compile project(':spring-security-core'),
springCoreDependency,
'aopalliance:aopalliance:1.0',
"org.springframework:spring-aop:$springVersion",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-tx:$springVersion",
"org.springframework:spring-jdbc:$springVersion"
optional "net.sf.ehcache:ehcache:$ehcacheVersion"
testCompile "org.springframework:spring-beans:$springVersion",
"org.springframework:spring-context-support:$springVersion",
"org.springframework:spring-test:$springVersion"
testRuntime "org.hsqldb:hsqldb:$hsqlVersion"
}
-165
View File
@@ -1,165 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
<version>4.2.11.RELEASE</version>
<name>spring-security-acl</name>
<description>spring-security-acl</description>
<url>http://spring.io/spring-security</url>
<organization>
<name>spring.io</name>
<url>http://spring.io/</url>
</organization>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>rwinch</id>
<name>Rob Winch</name>
<email>rwinch@gopivotal.com</email>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-security</connection>
<developerConnection>scm:git:git://github.com/spring-projects/spring-security</developerConnection>
<url>https://github.com/spring-projects/spring-security</url>
</scm>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.22.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>4.2.11.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,274 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.vote.AbstractAclVoter;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* <p>
* Given a domain object instance passed as a method argument, ensures the principal has
* appropriate permission as indicated by the {@link AclService}.
* <p>
* The <tt>AclService</tt> is used to retrieve the access control list (ACL) permissions
* associated with a domain object instance for the current <tt>Authentication</tt>
* object.
* <p>
* The voter will vote if any {@link ConfigAttribute#getAttribute()} matches the
* {@link #processConfigAttribute}. The provider will then locate the first method
* argument of type {@link #processDomainObjectClass}. Assuming that method argument is
* non-null, the provider will then lookup the ACLs from the <code>AclManager</code> and
* ensure the principal is {@link Acl#isGranted(List, List, boolean)} when presenting the
* {@link #requirePermission} array to that method.
* <p>
* If the method argument is <tt>null</tt>, the voter will abstain from voting. If the
* method argument could not be found, an {@link AuthorizationServiceException} will be
* thrown.
* <p>
* In practical terms users will typically setup a number of <tt>AclEntryVoter</tt>s. Each
* will have a different {@link #setProcessDomainObjectClass processDomainObjectClass},
* {@link #processConfigAttribute} and {@link #requirePermission} combination. For
* example, a small application might employ the following instances of
* <tt>AclEntryVoter</tt>:
* <ul>
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
* <code>VOTE_ACL_BANK_ACCONT_READ</code>, require permission
* <code>BasePermission.READ</code></li>
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
* <code>VOTE_ACL_BANK_ACCOUNT_WRITE</code>, require permission list
* <code>BasePermission.WRITE</code> and <code>BasePermission.CREATE</code> (allowing the
* principal to have <b>either</b> of these two permissions)</li>
* <li>Process domain object class <code>Customer</code>, configuration attribute
* <code>VOTE_ACL_CUSTOMER_READ</code>, require permission
* <code>BasePermission.READ</code></li>
* <li>Process domain object class <code>Customer</code>, configuration attribute
* <code>VOTE_ACL_CUSTOMER_WRITE</code>, require permission list
* <code>BasePermission.WRITE</code> and <code>BasePermission.CREATE</code></li>
* </ul>
* Alternatively, you could have used a common superclass or interface for the
* {@link #processDomainObjectClass} if both <code>BankAccount</code> and
* <code>Customer</code> had common parents.
*
* <p>
* If the principal does not have sufficient permissions, the voter will vote to deny
* access.
*
* <p>
* All comparisons and prefixes are case sensitive.
*
*
* @author Ben Alex
*/
public class AclEntryVoter extends AbstractAclVoter {
// ~ Static fields/initializers
// =====================================================================================
private static final Log logger = LogFactory.getLog(AclEntryVoter.class);
// ~ Instance fields
// ================================================================================================
private AclService aclService;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private String internalMethod;
private String processConfigAttribute;
private List<Permission> requirePermission;
// ~ Constructors
// ===================================================================================================
public AclEntryVoter(AclService aclService, String processConfigAttribute,
Permission[] requirePermission) {
Assert.notNull(processConfigAttribute, "A processConfigAttribute is mandatory");
Assert.notNull(aclService, "An AclService is mandatory");
if ((requirePermission == null) || (requirePermission.length == 0)) {
throw new IllegalArgumentException(
"One or more requirePermission entries is mandatory");
}
this.aclService = aclService;
this.processConfigAttribute = processConfigAttribute;
this.requirePermission = Arrays.asList(requirePermission);
}
// ~ Methods
// ========================================================================================================
/**
* Optionally specifies a method of the domain object that will be used to obtain a
* contained domain object. That contained domain object will be used for the ACL
* evaluation. This is useful if a domain object contains a parent that an ACL
* evaluation should be targeted for, instead of the child domain object (which
* perhaps is being created and as such does not yet have any ACL permissions)
*
* @return <code>null</code> to use the domain object, or the name of a method (that
* requires no arguments) that should be invoked to obtain an <code>Object</code>
* which will be the domain object used for ACL evaluation
*/
protected String getInternalMethod() {
return internalMethod;
}
public void setInternalMethod(String internalMethod) {
this.internalMethod = internalMethod;
}
protected String getProcessConfigAttribute() {
return processConfigAttribute;
}
public void setObjectIdentityRetrievalStrategy(
ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
Assert.notNull(objectIdentityRetrievalStrategy,
"ObjectIdentityRetrievalStrategy required");
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public boolean supports(ConfigAttribute attribute) {
return (attribute.getAttribute() != null)
&& attribute.getAttribute().equals(getProcessConfigAttribute());
}
public int vote(Authentication authentication, MethodInvocation object,
Collection<ConfigAttribute> attributes) {
for (ConfigAttribute attr : attributes) {
if (!this.supports(attr)) {
continue;
}
// Need to make an access decision on this invocation
// Attempt to locate the domain object instance to process
Object domainObject = getDomainObjectInstance(object);
// If domain object is null, vote to abstain
if (domainObject == null) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to abstain - domainObject is null");
}
return ACCESS_ABSTAIN;
}
// Evaluate if we are required to use an inner domain object
if (StringUtils.hasText(internalMethod)) {
try {
Class<?> clazz = domainObject.getClass();
Method method = clazz.getMethod(internalMethod, new Class[0]);
domainObject = method.invoke(domainObject);
}
catch (NoSuchMethodException nsme) {
throw new AuthorizationServiceException("Object of class '"
+ domainObject.getClass()
+ "' does not provide the requested internalMethod: "
+ internalMethod);
}
catch (IllegalAccessException iae) {
logger.debug("IllegalAccessException", iae);
throw new AuthorizationServiceException(
"Problem invoking internalMethod: " + internalMethod
+ " for object: " + domainObject);
}
catch (InvocationTargetException ite) {
logger.debug("InvocationTargetException", ite);
throw new AuthorizationServiceException(
"Problem invoking internalMethod: " + internalMethod
+ " for object: " + domainObject);
}
}
// Obtain the OID applicable to the domain object
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy
.getObjectIdentity(domainObject);
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
Acl acl;
try {
// Lookup only ACLs for SIDs we're interested in
acl = aclService.readAclById(objectIdentity, sids);
}
catch (NotFoundException nfe) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - no ACLs apply for this principal");
}
return ACCESS_DENIED;
}
try {
if (acl.isGranted(requirePermission, sids, false)) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to grant access");
}
return ACCESS_GRANTED;
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - ACLs returned, but insufficient permissions for this principal");
}
return ACCESS_DENIED;
}
}
catch (NotFoundException nfe) {
if (logger.isDebugEnabled()) {
logger.debug("Voting to deny access - no ACLs apply for this principal");
}
return ACCESS_DENIED;
}
}
// No configuration attribute matched, so abstain
return ACCESS_ABSTAIN;
}
}
@@ -1,82 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.PermissionCacheOptimizer;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
/**
* Batch loads ACLs for collections of objects to allow optimised filtering.
*
* @author Luke Taylor
* @since 3.1
*/
public class AclPermissionCacheOptimizer implements PermissionCacheOptimizer {
private final Log logger = LogFactory.getLog(getClass());
private final AclService aclService;
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private ObjectIdentityRetrievalStrategy oidRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
public AclPermissionCacheOptimizer(AclService aclService) {
this.aclService = aclService;
}
public void cachePermissionsFor(Authentication authentication, Collection<?> objects) {
if (objects.isEmpty()) {
return;
}
List<ObjectIdentity> oidsToCache = new ArrayList<ObjectIdentity>(objects.size());
for (Object domainObject : objects) {
if (domainObject == null) {
continue;
}
ObjectIdentity oid = oidRetrievalStrategy.getObjectIdentity(domainObject);
oidsToCache.add(oid);
}
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
if (logger.isDebugEnabled()) {
logger.debug("Eagerly loading Acls for " + oidsToCache.size() + " objects");
}
aclService.readAclsById(oidsToCache, sids);
}
public void setObjectIdentityRetrievalStrategy(
ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
this.oidRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
}
@@ -1,177 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.acls.domain.DefaultPermissionFactory;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.PermissionFactory;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityGenerator;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
/**
* Used by Spring Security's expression-based access control implementation to evaluate
* permissions for a particular object using the ACL module. Similar in behaviour to
* {@link org.springframework.security.acls.AclEntryVoter AclEntryVoter}.
*
* @author Luke Taylor
* @since 3.0
*/
public class AclPermissionEvaluator implements PermissionEvaluator {
private final Log logger = LogFactory.getLog(getClass());
private final AclService aclService;
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
private ObjectIdentityGenerator objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
private PermissionFactory permissionFactory = new DefaultPermissionFactory();
public AclPermissionEvaluator(AclService aclService) {
this.aclService = aclService;
}
/**
* Determines whether the user has the given permission(s) on the domain object using
* the ACL configuration. If the domain object is null, returns false (this can always
* be overridden using a null check in the expression itself).
*/
public boolean hasPermission(Authentication authentication, Object domainObject,
Object permission) {
if (domainObject == null) {
return false;
}
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy
.getObjectIdentity(domainObject);
return checkPermission(authentication, objectIdentity, permission);
}
public boolean hasPermission(Authentication authentication, Serializable targetId,
String targetType, Object permission) {
ObjectIdentity objectIdentity = objectIdentityGenerator.createObjectIdentity(
targetId, targetType);
return checkPermission(authentication, objectIdentity, permission);
}
private boolean checkPermission(Authentication authentication, ObjectIdentity oid,
Object permission) {
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
List<Permission> requiredPermission = resolvePermission(permission);
final boolean debug = logger.isDebugEnabled();
if (debug) {
logger.debug("Checking permission '" + permission + "' for object '" + oid
+ "'");
}
try {
// Lookup only ACLs for SIDs we're interested in
Acl acl = aclService.readAclById(oid, sids);
if (acl.isGranted(requiredPermission, sids, false)) {
if (debug) {
logger.debug("Access is granted");
}
return true;
}
if (debug) {
logger.debug("Returning false - ACLs returned, but insufficient permissions for this principal");
}
}
catch (NotFoundException nfe) {
if (debug) {
logger.debug("Returning false - no ACLs apply for this principal");
}
}
return false;
}
List<Permission> resolvePermission(Object permission) {
if (permission instanceof Integer) {
return Arrays.asList(permissionFactory.buildFromMask(((Integer) permission)
.intValue()));
}
if (permission instanceof Permission) {
return Arrays.asList((Permission) permission);
}
if (permission instanceof Permission[]) {
return Arrays.asList((Permission[]) permission);
}
if (permission instanceof String) {
String permString = (String) permission;
Permission p;
try {
p = permissionFactory.buildFromName(permString);
}
catch (IllegalArgumentException notfound) {
p = permissionFactory.buildFromName(permString.toUpperCase(Locale.ENGLISH));
}
if (p != null) {
return Arrays.asList(p);
}
}
throw new IllegalArgumentException("Unsupported permission: " + permission);
}
public void setObjectIdentityRetrievalStrategy(
ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
public void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {
this.objectIdentityGenerator = objectIdentityGenerator;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public void setPermissionFactory(PermissionFactory permissionFactory) {
this.permissionFactory = permissionFactory;
}
}
@@ -1,135 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
import java.util.List;
import org.springframework.security.access.AfterInvocationProvider;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* Abstract {@link AfterInvocationProvider} which provides commonly-used ACL-related
* services.
*
* @author Ben Alex
*/
public abstract class AbstractAclProvider implements AfterInvocationProvider {
// ~ Instance fields
// ================================================================================================
protected final AclService aclService;
protected Class<?> processDomainObjectClass = Object.class;
protected ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
protected SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
protected String processConfigAttribute;
protected final List<Permission> requirePermission;
// ~ Constructors
// ===================================================================================================
public AbstractAclProvider(AclService aclService, String processConfigAttribute,
List<Permission> requirePermission) {
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
Assert.notNull(aclService, "An AclService is mandatory");
if (requirePermission == null || requirePermission.isEmpty()) {
throw new IllegalArgumentException(
"One or more requirePermission entries is mandatory");
}
this.aclService = aclService;
this.processConfigAttribute = processConfigAttribute;
this.requirePermission = requirePermission;
}
// ~ Methods
// ========================================================================================================
protected Class<?> getProcessDomainObjectClass() {
return processDomainObjectClass;
}
protected boolean hasPermission(Authentication authentication, Object domainObject) {
// Obtain the OID applicable to the domain object
ObjectIdentity objectIdentity = objectIdentityRetrievalStrategy
.getObjectIdentity(domainObject);
// Obtain the SIDs applicable to the principal
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
try {
// Lookup only ACLs for SIDs we're interested in
Acl acl = aclService.readAclById(objectIdentity, sids);
return acl.isGranted(requirePermission, sids, false);
}
catch (NotFoundException ignore) {
return false;
}
}
public void setObjectIdentityRetrievalStrategy(
ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
Assert.notNull(objectIdentityRetrievalStrategy,
"ObjectIdentityRetrievalStrategy required");
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
}
protected void setProcessConfigAttribute(String processConfigAttribute) {
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
this.processConfigAttribute = processConfigAttribute;
}
public void setProcessDomainObjectClass(Class<?> processDomainObjectClass) {
Assert.notNull(processDomainObjectClass,
"processDomainObjectClass cannot be set to null");
this.processDomainObjectClass = processDomainObjectClass;
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
public boolean supports(ConfigAttribute attribute) {
return processConfigAttribute.equals(attribute.getAttribute());
}
/**
* This implementation supports any type of class, because it does not query the
* presented secure object.
*
* @param clazz the secure object
*
* @return always <code>true</code>
*/
public boolean supports(Class<?> clazz) {
return true;
}
}
@@ -1,139 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
/**
* <p>
* Given a <code>Collection</code> of domain object instances returned from a secure
* object invocation, remove any <code>Collection</code> elements the principal does not
* have appropriate permission to access as defined by the {@link AclService}.
* <p>
* The <code>AclService</code> is used to retrieve the access control list (ACL)
* permissions associated with each <code>Collection</code> domain object instance element
* for the current <code>Authentication</code> object.
* <p>
* This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()}
* matches the {@link #processConfigAttribute}. The provider will then lookup the ACLs
* from the <code>AclService</code> and ensure the principal is
* {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)
* Acl.isGranted()} when presenting the {@link #requirePermission} array to that method.
* <p>
* If the principal does not have permission, that element will not be included in the
* returned <code>Collection</code>.
* <p>
* Often users will setup a <code>BasicAclEntryAfterInvocationProvider</code> with a
* {@link #processConfigAttribute} of <code>AFTER_ACL_COLLECTION_READ</code> and a
* {@link #requirePermission} of <code>BasePermission.READ</code>. These are also the
* defaults.
* <p>
* If the provided <code>returnObject</code> is <code>null</code>, a <code>null</code>
* <code>Collection</code> will be returned. If the provided <code>returnObject</code> is
* not a <code>Collection</code>, an {@link AuthorizationServiceException} will be thrown.
* <p>
* All comparisons and prefixes are case sensitive.
*
* @author Ben Alex
* @author Paulo Neves
*/
public class AclEntryAfterInvocationCollectionFilteringProvider extends
AbstractAclProvider {
// ~ Static fields/initializers
// =====================================================================================
protected static final Log logger = LogFactory
.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
// ~ Constructors
// ===================================================================================================
public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService,
List<Permission> requirePermission) {
super(aclService, "AFTER_ACL_COLLECTION_READ", requirePermission);
}
// ~ Methods
// ========================================================================================================
@SuppressWarnings("unchecked")
public Object decide(Authentication authentication, Object object,
Collection<ConfigAttribute> config, Object returnedObject)
throws AccessDeniedException {
if (returnedObject == null) {
logger.debug("Return object is null, skipping");
return null;
}
for (ConfigAttribute attr : config) {
if (!this.supports(attr)) {
continue;
}
// Need to process the Collection for this invocation
Filterer filterer;
if (returnedObject instanceof Collection) {
filterer = new CollectionFilterer((Collection) returnedObject);
}
else if (returnedObject.getClass().isArray()) {
filterer = new ArrayFilterer((Object[]) returnedObject);
}
else {
throw new AuthorizationServiceException(
"A Collection or an array (or null) was required as the "
+ "returnedObject, but the returnedObject was: "
+ returnedObject);
}
// Locate unauthorised Collection elements
for (Object domainObject : filterer) {
// Ignore nulls or entries which aren't instances of the configured domain
// object class
if (domainObject == null
|| !getProcessDomainObjectClass().isAssignableFrom(
domainObject.getClass())) {
continue;
}
if (!hasPermission(authentication, domainObject)) {
filterer.remove(domainObject);
if (logger.isDebugEnabled()) {
logger.debug("Principal is NOT authorised for element: "
+ domainObject);
}
}
}
return filterer.getFilteredObject();
}
return returnedObject;
}
}
@@ -1,132 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
/**
* Given a domain object instance returned from a secure object invocation, ensures the
* principal has appropriate permission as defined by the {@link AclService}.
* <p>
* The <code>AclService</code> is used to retrieve the access control list (ACL)
* permissions associated with a domain object instance for the current
* <code>Authentication</code> object.
* <p>
* This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()}
* matches the {@link #processConfigAttribute}. The provider will then lookup the ACLs
* from the <tt>AclService</tt> and ensure the principal is
* {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)
* Acl.isGranted(List, List, boolean)} when presenting the {@link #requirePermission}
* array to that method.
* <p>
* Often users will set up an <code>AclEntryAfterInvocationProvider</code> with a
* {@link #processConfigAttribute} of <code>AFTER_ACL_READ</code> and a
* {@link #requirePermission} of <code>BasePermission.READ</code>. These are also the
* defaults.
* <p>
* If the principal does not have sufficient permissions, an
* <code>AccessDeniedException</code> will be thrown.
* <p>
* If the provided <tt>returnedObject</tt> is <code>null</code>, permission will always be
* granted and <code>null</code> will be returned.
* <p>
* All comparisons and prefixes are case sensitive.
*/
public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements
MessageSourceAware {
// ~ Static fields/initializers
// =====================================================================================
protected static final Log logger = LogFactory
.getLog(AclEntryAfterInvocationProvider.class);
// ~ Instance fields
// ================================================================================================
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
// ~ Constructors
// ===================================================================================================
public AclEntryAfterInvocationProvider(AclService aclService,
List<Permission> requirePermission) {
this(aclService, "AFTER_ACL_READ", requirePermission);
}
public AclEntryAfterInvocationProvider(AclService aclService,
String processConfigAttribute, List<Permission> requirePermission) {
super(aclService, processConfigAttribute, requirePermission);
}
// ~ Methods
// ========================================================================================================
public Object decide(Authentication authentication, Object object,
Collection<ConfigAttribute> config, Object returnedObject)
throws AccessDeniedException {
if (returnedObject == null) {
// AclManager interface contract prohibits nulls
// As they have permission to null/nothing, grant access
logger.debug("Return object is null, skipping");
return null;
}
if (!getProcessDomainObjectClass().isAssignableFrom(returnedObject.getClass())) {
logger.debug("Return object is not applicable for this provider, skipping");
return returnedObject;
}
for (ConfigAttribute attr : config) {
if (!this.supports(attr)) {
continue;
}
// Need to make an access decision on this invocation
if (hasPermission(authentication, returnedObject)) {
return returnedObject;
}
logger.debug("Denying access");
throw new AccessDeniedException(messages.getMessage(
"AclEntryAfterInvocationProvider.noPermission", new Object[] {
authentication.getName(), returnedObject },
"Authentication {0} has NO permissions to the domain object {1}"));
}
return returnedObject;
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
}
@@ -1,122 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
import java.lang.reflect.Array;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A filter used to filter arrays.
*
* @author Ben Alex
* @author Paulo Neves
*/
class ArrayFilterer<T> implements Filterer<T> {
// ~ Static fields/initializers
// =====================================================================================
protected static final Log logger = LogFactory.getLog(ArrayFilterer.class);
// ~ Instance fields
// ================================================================================================
private final Set<T> removeList;
private final T[] list;
// ~ Constructors
// ===================================================================================================
ArrayFilterer(T[] list) {
this.list = list;
// Collect the removed objects to a HashSet so that
// it is fast to lookup them when a filtered array
// is constructed.
removeList = new HashSet<T>();
}
// ~ Methods
// ========================================================================================================
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#getFilteredObject()
*/
@SuppressWarnings("unchecked")
public T[] getFilteredObject() {
// Recreate an array of same type and filter the removed objects.
int originalSize = list.length;
int sizeOfResultingList = originalSize - removeList.size();
T[] filtered = (T[]) Array.newInstance(list.getClass().getComponentType(),
sizeOfResultingList);
for (int i = 0, j = 0; i < list.length; i++) {
T object = list[i];
if (!removeList.contains(object)) {
filtered[j] = object;
j++;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Original array contained " + originalSize
+ " elements; now contains " + sizeOfResultingList + " elements");
}
return filtered;
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#iterator()
*/
public Iterator<T> iterator() {
return new Iterator<T>() {
private int index = 0;
public boolean hasNext() {
return index < list.length;
}
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return list[index++];
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#remove(java.lang.Object)
*/
public void remove(T object) {
removeList.add(object);
}
}
@@ -1,102 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* A filter used to filter Collections.
*
* @author Ben Alex
* @author Paulo Neves
*/
class CollectionFilterer<T> implements Filterer<T> {
// ~ Static fields/initializers
// =====================================================================================
protected static final Log logger = LogFactory.getLog(CollectionFilterer.class);
// ~ Instance fields
// ================================================================================================
private final Collection<T> collection;
private final Set<T> removeList;
// ~ Constructors
// ===================================================================================================
CollectionFilterer(Collection<T> collection) {
this.collection = collection;
// We create a Set of objects to be removed from the Collection,
// as ConcurrentModificationException prevents removal during
// iteration, and making a new Collection to be returned is
// problematic as the original Collection implementation passed
// to the method may not necessarily be re-constructable (as
// the Collection(collection) constructor is not guaranteed and
// manually adding may lose sort order or other capabilities)
removeList = new HashSet<T>();
}
// ~ Methods
// ========================================================================================================
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#getFilteredObject()
*/
public Object getFilteredObject() {
// Now the Iterator has ended, remove Objects from Collection
Iterator<T> removeIter = removeList.iterator();
int originalSize = collection.size();
while (removeIter.hasNext()) {
collection.remove(removeIter.next());
}
if (logger.isDebugEnabled()) {
logger.debug("Original collection contained " + originalSize
+ " elements; now contains " + collection.size() + " elements");
}
return collection;
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#iterator()
*/
public Iterator<T> iterator() {
return collection.iterator();
}
/**
*
* @see org.springframework.security.acls.afterinvocation.Filterer#remove(java.lang.Object)
*/
public void remove(T object) {
removeList.add(object);
}
}
@@ -1,51 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
import java.util.Iterator;
/**
* Filterer strategy interface.
*
* @author Ben Alex
* @author Paulo Neves
*/
interface Filterer<T> extends Iterable<T> {
// ~ Methods
// ========================================================================================================
/**
* Gets the filtered collection or array.
*
* @return the filtered collection or array
*/
Object getFilteredObject();
/**
* Returns an iterator over the filtered collection or array.
*
* @return an Iterator
*/
Iterator<T> iterator();
/**
* Removes the given object from the resulting list.
*
* @param object the object to be removed
*/
void remove(T object);
}
@@ -1,21 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* After-invocation providers for collection and array filtering. Consider using a {@code PostFilter} annotation in
* preference.
*/
package org.springframework.security.acls.afterinvocation;
@@ -1,91 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
/**
* Provides an abstract superclass for {@link Permission} implementations.
*
* @author Ben Alex
* @since 2.0.3
*/
public abstract class AbstractPermission implements Permission {
// ~ Instance fields
// ================================================================================================
protected final char code;
protected int mask;
// ~ Constructors
// ===================================================================================================
/**
* Sets the permission mask and uses the '*' character to represent active bits when
* represented as a bit pattern string.
*
* @param mask the integer bit mask for the permission
*/
protected AbstractPermission(int mask) {
this.mask = mask;
this.code = '*';
}
/**
* Sets the permission mask and uses the specified character for active bits.
*
* @param mask the integer bit mask for the permission
* @param code the character to print for each active bit in the mask (see
* {@link Permission#getPattern()})
*/
protected AbstractPermission(int mask, char code) {
this.mask = mask;
this.code = code;
}
// ~ Methods
// ========================================================================================================
public final boolean equals(Object arg0) {
if (arg0 == null) {
return false;
}
if (!(arg0 instanceof Permission)) {
return false;
}
Permission rhs = (Permission) arg0;
return (this.mask == rhs.getMask());
}
public final int getMask() {
return mask;
}
public String getPattern() {
return AclFormattingUtils.printBinary(mask, code);
}
public final String toString() {
return this.getClass().getSimpleName() + "[" + getPattern() + "=" + mask + "]";
}
public final int hashCode() {
return this.mask;
}
}
@@ -1,185 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* An immutable default implementation of <code>AccessControlEntry</code>.
*
* @author Ben Alex
*/
public class AccessControlEntryImpl implements AccessControlEntry,
AuditableAccessControlEntry {
// ~ Instance fields
// ================================================================================================
private final Acl acl;
private Permission permission;
private final Serializable id;
private final Sid sid;
private boolean auditFailure = false;
private boolean auditSuccess = false;
private final boolean granting;
// ~ Constructors
// ===================================================================================================
public AccessControlEntryImpl(Serializable id, Acl acl, Sid sid,
Permission permission, boolean granting, boolean auditSuccess,
boolean auditFailure) {
Assert.notNull(acl, "Acl required");
Assert.notNull(sid, "Sid required");
Assert.notNull(permission, "Permission required");
this.id = id;
this.acl = acl; // can be null
this.sid = sid;
this.permission = permission;
this.granting = granting;
this.auditSuccess = auditSuccess;
this.auditFailure = auditFailure;
}
// ~ Methods
// ========================================================================================================
public boolean equals(Object arg0) {
if (!(arg0 instanceof AccessControlEntryImpl)) {
return false;
}
AccessControlEntryImpl rhs = (AccessControlEntryImpl) arg0;
if (this.acl == null) {
if (rhs.getAcl() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
}
else {
// this.acl is non-null
if (rhs.getAcl() == null) {
return false;
}
// Both this.acl and rhs.acl are non-null, so do a comparison
if (this.acl.getObjectIdentity() == null) {
if (rhs.acl.getObjectIdentity() != null) {
return false;
}
// Both this.acl and rhs.acl are null and thus equal
}
else {
// Both this.acl.objectIdentity and rhs.acl.objectIdentity are non-null
if (!this.acl.getObjectIdentity()
.equals(rhs.getAcl().getObjectIdentity())) {
return false;
}
}
}
if (this.id == null) {
if (rhs.id != null) {
return false;
}
// Both this.id and rhs.id are null and thus equal
}
else {
// this.id is non-null
if (rhs.id == null) {
return false;
}
// Both this.id and rhs.id are non-null
if (!this.id.equals(rhs.id)) {
return false;
}
}
if ((this.auditFailure != rhs.isAuditFailure())
|| (this.auditSuccess != rhs.isAuditSuccess())
|| (this.granting != rhs.isGranting())
|| !this.permission.equals(rhs.getPermission())
|| !this.sid.equals(rhs.getSid())) {
return false;
}
return true;
}
public Acl getAcl() {
return acl;
}
public Serializable getId() {
return id;
}
public Permission getPermission() {
return permission;
}
public Sid getSid() {
return sid;
}
public boolean isAuditFailure() {
return auditFailure;
}
public boolean isAuditSuccess() {
return auditSuccess;
}
public boolean isGranting() {
return granting;
}
void setAuditFailure(boolean auditFailure) {
this.auditFailure = auditFailure;
}
void setAuditSuccess(boolean auditSuccess) {
this.auditSuccess = auditSuccess;
}
void setPermission(Permission permission) {
Assert.notNull(permission, "Permission required");
this.permission = permission;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AccessControlEntryImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("granting: ").append(this.granting).append("; ");
sb.append("sid: ").append(this.sid).append("; ");
sb.append("permission: ").append(this.permission).append("; ");
sb.append("auditSuccess: ").append(this.auditSuccess).append("; ");
sb.append("auditFailure: ").append(this.auditFailure);
sb.append("]");
return sb.toString();
}
}
@@ -1,39 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Acl;
/**
* Strategy used by {@link AclImpl} to determine whether a principal is permitted to call
* adminstrative methods on the <code>AclImpl</code>.
*
* @author Ben Alex
*/
public interface AclAuthorizationStrategy {
// ~ Static fields/initializers
// =====================================================================================
int CHANGE_OWNERSHIP = 0;
int CHANGE_AUDITING = 1;
int CHANGE_GENERAL = 2;
// ~ Methods
// ========================================================================================================
void securityCheck(Acl acl, int changeType);
}
@@ -1,154 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* Default implementation of {@link AclAuthorizationStrategy}.
* <p>
* Permission will be granted if at least one of the following conditions is true for the
* current principal.
* <ul>
* <li>is the owner (as defined by the ACL).</li>
* <li>holds the relevant system-wide {@link GrantedAuthority} injected into the
* constructor.</li>
* <li>has {@link BasePermission#ADMINISTRATION} permission (as defined by the ACL).</li>
* </ul>
*
* @author Ben Alex
*/
public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
// ~ Instance fields
// ================================================================================================
private final GrantedAuthority gaGeneralChanges;
private final GrantedAuthority gaModifyAuditing;
private final GrantedAuthority gaTakeOwnership;
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
// ~ Constructors
// ===================================================================================================
/**
* Constructor. The only mandatory parameter relates to the system-wide
* {@link GrantedAuthority} instances that can be held to always permit ACL changes.
*
* @param auths the <code>GrantedAuthority</code>s that have special permissions
* (index 0 is the authority needed to change ownership, index 1 is the authority
* needed to modify auditing details, index 2 is the authority needed to change other
* ACL and ACE details) (required)
* <p>
* Alternatively, a single value can be supplied for all three permissions.
*/
public AclAuthorizationStrategyImpl(GrantedAuthority... auths) {
Assert.isTrue(auths != null && (auths.length == 3 || auths.length == 1),
"One or three GrantedAuthority instances required");
if (auths.length == 3) {
gaTakeOwnership = auths[0];
gaModifyAuditing = auths[1];
gaGeneralChanges = auths[2];
}
else {
gaTakeOwnership = gaModifyAuditing = gaGeneralChanges = auths[0];
}
}
// ~ Methods
// ========================================================================================================
public void securityCheck(Acl acl, int changeType) {
if ((SecurityContextHolder.getContext() == null)
|| (SecurityContextHolder.getContext().getAuthentication() == null)
|| !SecurityContextHolder.getContext().getAuthentication()
.isAuthenticated()) {
throw new AccessDeniedException(
"Authenticated principal required to operate with ACLs");
}
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
// Check if authorized by virtue of ACL ownership
Sid currentUser = createCurrentUser(authentication);
if (currentUser.equals(acl.getOwner())
&& ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
return;
}
// Not authorized by ACL ownership; try via adminstrative permissions
GrantedAuthority requiredAuthority;
if (changeType == CHANGE_AUDITING) {
requiredAuthority = this.gaModifyAuditing;
}
else if (changeType == CHANGE_GENERAL) {
requiredAuthority = this.gaGeneralChanges;
}
else if (changeType == CHANGE_OWNERSHIP) {
requiredAuthority = this.gaTakeOwnership;
}
else {
throw new IllegalArgumentException("Unknown change type");
}
// Iterate this principal's authorities to determine right
Set<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (authorities.contains(requiredAuthority.getAuthority())) {
return;
}
// Try to get permission via ACEs within the ACL
List<Sid> sids = sidRetrievalStrategy.getSids(authentication);
if (acl.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), sids, false)) {
return;
}
throw new AccessDeniedException(
"Principal does not have required ACL permissions to perform requested operation");
}
/**
* Creates a principal-like sid from the authentication information.
*
* @param authentication the authentication information that can provide principal and
* thus the sid's id will be dependant on the value inside
* @return a sid with the ID taken from the authentication information
*/
protected Sid createCurrentUser(Authentication authentication) {
return new PrincipalSid(authentication);
}
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
this.sidRetrievalStrategy = sidRetrievalStrategy;
}
}
@@ -1,112 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
import org.springframework.util.Assert;
/**
* Utility methods for displaying ACL information.
*
* @author Ben Alex
*/
public abstract class AclFormattingUtils {
public static String demergePatterns(String original, String removeBits) {
Assert.notNull(original, "Original string required");
Assert.notNull(removeBits, "Bits To Remove string required");
Assert.isTrue(original.length() == removeBits.length(),
"Original and Bits To Remove strings must be identical length");
char[] replacement = new char[original.length()];
for (int i = 0; i < original.length(); i++) {
if (removeBits.charAt(i) == Permission.RESERVED_OFF) {
replacement[i] = original.charAt(i);
}
else {
replacement[i] = Permission.RESERVED_OFF;
}
}
return new String(replacement);
}
public static String mergePatterns(String original, String extraBits) {
Assert.notNull(original, "Original string required");
Assert.notNull(extraBits, "Extra Bits string required");
Assert.isTrue(original.length() == extraBits.length(),
"Original and Extra Bits strings must be identical length");
char[] replacement = new char[extraBits.length()];
for (int i = 0; i < extraBits.length(); i++) {
if (extraBits.charAt(i) == Permission.RESERVED_OFF) {
replacement[i] = original.charAt(i);
}
else {
replacement[i] = extraBits.charAt(i);
}
}
return new String(replacement);
}
/**
* Returns a representation of the active bits in the presented mask, with each active
* bit being denoted by character '*'.
* <p>
* Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.
*
* @param i the integer bit mask to print the active bits for
*
* @return a 32-character representation of the bit mask
*/
public static String printBinary(int i) {
return printBinary(i, '*', Permission.RESERVED_OFF);
}
/**
* Returns a representation of the active bits in the presented mask, with each active
* bit being denoted by the passed character.
* <p>
* Inactive bits will be denoted by character {@link Permission#RESERVED_OFF}.
*
* @param mask the integer bit mask to print the active bits for
* @param code the character to print when an active bit is detected
*
* @return a 32-character representation of the bit mask
*/
public static String printBinary(int mask, char code) {
Assert.doesNotContain(Character.toString(code),
Character.toString(Permission.RESERVED_ON), Permission.RESERVED_ON
+ " is a reserved character code");
Assert.doesNotContain(Character.toString(code),
Character.toString(Permission.RESERVED_OFF), Permission.RESERVED_OFF
+ " is a reserved character code");
return printBinary(mask, Permission.RESERVED_ON, Permission.RESERVED_OFF)
.replace(Permission.RESERVED_ON, code);
}
private static String printBinary(int i, char on, char off) {
String s = Integer.toBinaryString(i);
String pattern = Permission.THIRTY_TWO_RESERVED_OFF;
String temp2 = pattern.substring(0, pattern.length() - s.length()) + s;
return temp2.replace('0', off).replace('1', on);
}
}
@@ -1,364 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAcl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.OwnershipAcl;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.UnloadedSidException;
import org.springframework.util.Assert;
/**
* Base implementation of <code>Acl</code>.
*
* @author Ben Alex
*/
public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
// ~ Instance fields
// ================================================================================================
private Acl parentAcl;
private transient AclAuthorizationStrategy aclAuthorizationStrategy;
private transient PermissionGrantingStrategy permissionGrantingStrategy;
private final List<AccessControlEntry> aces = new ArrayList<AccessControlEntry>();
private ObjectIdentity objectIdentity;
private Serializable id;
private Sid owner; // OwnershipAcl
private List<Sid> loadedSids = null; // includes all SIDs the WHERE clause covered,
// even if there was no ACE for a SID
private boolean entriesInheriting = true;
// ~ Constructors
// ===================================================================================================
/**
* Minimal constructor, which should be used
* {@link org.springframework.security.acls.model.MutableAclService#createAcl(ObjectIdentity)}
* .
*
* @param objectIdentity the object identity this ACL relates to (required)
* @param id the primary key assigned to this ACL (required)
* @param aclAuthorizationStrategy authorization strategy (required)
* @param auditLogger audit logger (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id,
AclAuthorizationStrategy aclAuthorizationStrategy, AuditLogger auditLogger) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(auditLogger, "AuditLogger required");
this.objectIdentity = objectIdentity;
this.id = id;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(
auditLogger);
}
/**
* Full constructor, which should be used by persistence tools that do not provide
* field-level access features.
*
* @param objectIdentity the object identity this ACL relates to
* @param id the primary key assigned to this ACL
* @param aclAuthorizationStrategy authorization strategy
* @param grantingStrategy the {@code PermissionGrantingStrategy} which will be used
* by the {@code isGranted()} method
* @param parentAcl the parent (may be may be {@code null})
* @param loadedSids the loaded SIDs if only a subset were loaded (may be {@code null}
* )
* @param entriesInheriting if ACEs from the parent should inherit into this ACL
* @param owner the owner (required)
*/
public AclImpl(ObjectIdentity objectIdentity, Serializable id,
AclAuthorizationStrategy aclAuthorizationStrategy,
PermissionGrantingStrategy grantingStrategy, Acl parentAcl,
List<Sid> loadedSids, boolean entriesInheriting, Sid owner) {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(id, "Id required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(owner, "Owner required");
this.objectIdentity = objectIdentity;
this.id = id;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.parentAcl = parentAcl; // may be null
this.loadedSids = loadedSids; // may be null
this.entriesInheriting = entriesInheriting;
this.owner = owner;
this.permissionGrantingStrategy = grantingStrategy;
}
/**
* Private no-argument constructor for use by reflection-based persistence tools along
* with field-level access.
*/
@SuppressWarnings("unused")
private AclImpl() {
}
// ~ Methods
// ========================================================================================================
public void deleteAce(int aceIndex) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
this.aces.remove(aceIndex);
}
}
private void verifyAceIndexExists(int aceIndex) {
if (aceIndex < 0) {
throw new NotFoundException("aceIndex must be greater than or equal to zero");
}
if (aceIndex >= this.aces.size()) {
throw new NotFoundException(
"aceIndex must refer to an index of the AccessControlEntry list. "
+ "List size is " + aces.size() + ", index was " + aceIndex);
}
}
public void insertAce(int atIndexLocation, Permission permission, Sid sid,
boolean granting) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.notNull(permission, "Permission required");
Assert.notNull(sid, "Sid required");
if (atIndexLocation < 0) {
throw new NotFoundException(
"atIndexLocation must be greater than or equal to zero");
}
if (atIndexLocation > this.aces.size()) {
throw new NotFoundException(
"atIndexLocation must be less than or equal to the size of the AccessControlEntry collection");
}
AccessControlEntryImpl ace = new AccessControlEntryImpl(null, this, sid,
permission, granting, false, false);
synchronized (aces) {
this.aces.add(atIndexLocation, ace);
}
}
public List<AccessControlEntry> getEntries() {
// Can safely return AccessControlEntry directly, as they're immutable outside the
// ACL package
return new ArrayList<AccessControlEntry>(aces);
}
public Serializable getId() {
return this.id;
}
public ObjectIdentity getObjectIdentity() {
return objectIdentity;
}
public boolean isEntriesInheriting() {
return entriesInheriting;
}
/**
* Delegates to the {@link PermissionGrantingStrategy}.
*
* @throws UnloadedSidException if the passed SIDs are unknown to this ACL because the
* ACL was only loaded for a subset of SIDs
* @see DefaultPermissionGrantingStrategy
*/
public boolean isGranted(List<Permission> permission, List<Sid> sids,
boolean administrativeMode) throws NotFoundException, UnloadedSidException {
Assert.notEmpty(permission, "Permissions required");
Assert.notEmpty(sids, "SIDs required");
if (!this.isSidLoaded(sids)) {
throw new UnloadedSidException("ACL was not loaded for one or more SID");
}
return permissionGrantingStrategy.isGranted(this, permission, sids,
administrativeMode);
}
public boolean isSidLoaded(List<Sid> sids) {
// If loadedSides is null, this indicates all SIDs were loaded
// Also return true if the caller didn't specify a SID to find
if ((this.loadedSids == null) || (sids == null) || (sids.size() == 0)) {
return true;
}
// This ACL applies to a SID subset only. Iterate to check it applies.
for (Sid sid : sids) {
boolean found = false;
for (Sid loadedSid : loadedSids) {
if (sid.equals(loadedSid)) {
// this SID is OK
found = true;
break; // out of loadedSids for loop
}
}
if (!found) {
return false;
}
}
return true;
}
public void setEntriesInheriting(boolean entriesInheriting) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
this.entriesInheriting = entriesInheriting;
}
public void setOwner(Sid newOwner) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
Assert.notNull(newOwner, "Owner required");
this.owner = newOwner;
}
public Sid getOwner() {
return this.owner;
}
public void setParent(Acl newParent) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
Assert.isTrue(newParent == null || !newParent.equals(this),
"Cannot be the parent of yourself");
this.parentAcl = newParent;
}
public Acl getParentAcl() {
return parentAcl;
}
public void updateAce(int aceIndex, Permission permission) throws NotFoundException {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_GENERAL);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
ace.setPermission(permission);
}
}
public void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure) {
aclAuthorizationStrategy.securityCheck(this,
AclAuthorizationStrategy.CHANGE_AUDITING);
verifyAceIndexExists(aceIndex);
synchronized (aces) {
AccessControlEntryImpl ace = (AccessControlEntryImpl) aces.get(aceIndex);
ace.setAuditSuccess(auditSuccess);
ace.setAuditFailure(auditFailure);
}
}
public boolean equals(Object obj) {
if (obj instanceof AclImpl) {
AclImpl rhs = (AclImpl) obj;
if (this.aces.equals(rhs.aces)) {
if ((this.parentAcl == null && rhs.parentAcl == null)
|| (this.parentAcl != null && this.parentAcl
.equals(rhs.parentAcl))) {
if ((this.objectIdentity == null && rhs.objectIdentity == null)
|| (this.objectIdentity != null && this.objectIdentity
.equals(rhs.objectIdentity))) {
if ((this.id == null && rhs.id == null)
|| (this.id != null && this.id.equals(rhs.id))) {
if ((this.owner == null && rhs.owner == null)
|| (this.owner != null && this.owner
.equals(rhs.owner))) {
if (this.entriesInheriting == rhs.entriesInheriting) {
if ((this.loadedSids == null && rhs.loadedSids == null)) {
return true;
}
if (this.loadedSids != null
&& (this.loadedSids.size() == rhs.loadedSids
.size())) {
for (int i = 0; i < this.loadedSids.size(); i++) {
if (!this.loadedSids.get(i).equals(
rhs.loadedSids.get(i))) {
return false;
}
}
return true;
}
}
}
}
}
}
}
}
return false;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("AclImpl[");
sb.append("id: ").append(this.id).append("; ");
sb.append("objectIdentity: ").append(this.objectIdentity).append("; ");
sb.append("owner: ").append(this.owner).append("; ");
int count = 0;
for (AccessControlEntry ace : aces) {
count++;
if (count == 1) {
sb.append("\n");
}
sb.append(ace).append("\n");
}
if (count == 0) {
sb.append("no ACEs; ");
}
sb.append("inheriting: ").append(this.entriesInheriting).append("; ");
sb.append("parent: ").append(
(this.parentAcl == null) ? "Null" : this.parentAcl.getObjectIdentity()
.toString());
sb.append("; ");
sb.append("aclAuthorizationStrategy: ").append(this.aclAuthorizationStrategy)
.append("; ");
sb.append("permissionGrantingStrategy: ").append(this.permissionGrantingStrategy);
sb.append("]");
return sb.toString();
}
}
@@ -1,31 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.AccessControlEntry;
/**
* Used by <code>AclImpl</code> to log audit events.
*
* @author Ben Alex
*
*/
public interface AuditLogger {
// ~ Methods
// ========================================================================================================
void logIfNeeded(boolean granted, AccessControlEntry ace);
}
@@ -1,44 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
/**
* A set of standard permissions.
*
* <p>
* You may subclass this class to add additional permissions, or use this class as a guide
* for creating your own permission classes.
* </p>
*
* @author Ben Alex
*/
public class BasePermission extends AbstractPermission {
public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1
public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2
public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4
public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8
public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16
protected BasePermission(int mask) {
super(mask);
}
protected BasePermission(int mask, char code) {
super(mask, code);
}
}
@@ -1,46 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
import org.springframework.util.Assert;
/**
* A basic implementation of {@link AuditLogger}.
*
* @author Ben Alex
*/
public class ConsoleAuditLogger implements AuditLogger {
// ~ Methods
// ========================================================================================================
public void logIfNeeded(boolean granted, AccessControlEntry ace) {
Assert.notNull(ace, "AccessControlEntry required");
if (ace instanceof AuditableAccessControlEntry) {
AuditableAccessControlEntry auditableAce = (AuditableAccessControlEntry) ace;
if (granted && auditableAce.isAuditSuccess()) {
System.out.println("GRANTED due to ACE: " + ace);
}
else if (!granted && auditableAce.isAuditFailure()) {
System.out.println("DENIED due to ACE: " + ace);
}
}
}
}
@@ -1,65 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
/**
* Represents a <code>Permission</code> that is constructed at runtime from other
* permissions.
*
* <p>
* Methods return <code>this</code>, in order to facilitate method chaining.
* </p>
*
* @author Ben Alex
*/
public class CumulativePermission extends AbstractPermission {
private String pattern = THIRTY_TWO_RESERVED_OFF;
public CumulativePermission() {
super(0, ' ');
}
public CumulativePermission clear(Permission permission) {
this.mask &= ~permission.getMask();
this.pattern = AclFormattingUtils.demergePatterns(this.pattern,
permission.getPattern());
return this;
}
public CumulativePermission clear() {
this.mask = 0;
this.pattern = THIRTY_TWO_RESERVED_OFF;
return this;
}
public CumulativePermission set(Permission permission) {
this.mask |= permission.getMask();
this.pattern = AclFormattingUtils.mergePatterns(this.pattern,
permission.getPattern());
return this;
}
public String getPattern() {
return this.pattern;
}
}
@@ -1,168 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.security.acls.model.Permission;
import org.springframework.util.Assert;
/**
* Default implementation of {@link PermissionFactory}.
* <p>
* Used as a strategy by classes which wish to map integer masks and permission names to
* <tt>Permission</tt> instances for use with the ACL implementation.
* <p>
* Maintains a registry of permission names and masks to <tt>Permission</tt> instances.
*
* @author Ben Alex
* @author Luke Taylor
* @since 2.0.3
*/
public class DefaultPermissionFactory implements PermissionFactory {
private final Map<Integer, Permission> registeredPermissionsByInteger = new HashMap<Integer, Permission>();
private final Map<String, Permission> registeredPermissionsByName = new HashMap<String, Permission>();
/**
* Registers the <tt>Permission</tt> fields from the <tt>BasePermission</tt> class.
*/
public DefaultPermissionFactory() {
registerPublicPermissions(BasePermission.class);
}
/**
* Registers the <tt>Permission</tt> fields from the supplied class.
*/
public DefaultPermissionFactory(Class<? extends Permission> permissionClass) {
registerPublicPermissions(permissionClass);
}
/**
* Registers a map of named <tt>Permission</tt> instances.
*
* @param namedPermissions the map of <tt>Permission</tt>s, keyed by name.
*/
public DefaultPermissionFactory(Map<String, ? extends Permission> namedPermissions) {
for (String name : namedPermissions.keySet()) {
registerPermission(namedPermissions.get(name), name);
}
}
/**
* Registers the public static fields of type {@link Permission} for a give class.
* <p>
* These permissions will be registered under the name of the field. See
* {@link BasePermission} for an example.
*
* @param clazz a {@link Permission} class with public static fields to register
*/
protected void registerPublicPermissions(Class<? extends Permission> clazz) {
Assert.notNull(clazz, "Class required");
Field[] fields = clazz.getFields();
for (Field field : fields) {
try {
Object fieldValue = field.get(null);
if (Permission.class.isAssignableFrom(fieldValue.getClass())) {
// Found a Permission static field
Permission perm = (Permission) fieldValue;
String permissionName = field.getName();
registerPermission(perm, permissionName);
}
}
catch (Exception ignore) {
}
}
}
protected void registerPermission(Permission perm, String permissionName) {
Assert.notNull(perm, "Permission required");
Assert.hasText(permissionName, "Permission name required");
Integer mask = Integer.valueOf(perm.getMask());
// Ensure no existing Permission uses this integer or code
Assert.isTrue(!registeredPermissionsByInteger.containsKey(mask),
"An existing Permission already provides mask " + mask);
Assert.isTrue(!registeredPermissionsByName.containsKey(permissionName),
"An existing Permission already provides name '" + permissionName + "'");
// Register the new Permission
registeredPermissionsByInteger.put(mask, perm);
registeredPermissionsByName.put(permissionName, perm);
}
public Permission buildFromMask(int mask) {
if (registeredPermissionsByInteger.containsKey(Integer.valueOf(mask))) {
// The requested mask has an exact match against a statically-defined
// Permission, so return it
return registeredPermissionsByInteger.get(Integer.valueOf(mask));
}
// To get this far, we have to use a CumulativePermission
CumulativePermission permission = new CumulativePermission();
for (int i = 0; i < 32; i++) {
int permissionToCheck = 1 << i;
if ((mask & permissionToCheck) == permissionToCheck) {
Permission p = registeredPermissionsByInteger.get(Integer
.valueOf(permissionToCheck));
if (p == null) {
throw new IllegalStateException("Mask '" + permissionToCheck
+ "' does not have a corresponding static Permission");
}
permission.set(p);
}
}
return permission;
}
public Permission buildFromName(String name) {
Permission p = registeredPermissionsByName.get(name);
if (p == null) {
throw new IllegalArgumentException("Unknown permission '" + name + "'");
}
return p;
}
public List<Permission> buildFromNames(List<String> names) {
if ((names == null) || (names.size() == 0)) {
return Collections.emptyList();
}
List<Permission> permissions = new ArrayList<Permission>(names.size());
for (String name : names) {
permissions.add(buildFromName(name));
}
return permissions;
}
}
@@ -1,145 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.util.List;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.acls.model.Sid;
import org.springframework.util.Assert;
public class DefaultPermissionGrantingStrategy implements PermissionGrantingStrategy {
private final transient AuditLogger auditLogger;
/**
* Creates an instance with the logger which will be used to record granting and
* denial of requested permissions.
*/
public DefaultPermissionGrantingStrategy(AuditLogger auditLogger) {
Assert.notNull(auditLogger, "auditLogger cannot be null");
this.auditLogger = auditLogger;
}
/**
* Determines authorization. The order of the <code>permission</code> and
* <code>sid</code> arguments is <em>extremely important</em>! The method will iterate
* through each of the <code>permission</code>s in the order specified. For each
* iteration, all of the <code>sid</code>s will be considered, again in the order they
* are presented. A search will then be performed for the first
* {@link AccessControlEntry} object that directly matches that
* <code>permission:sid</code> combination. When the <em>first full match</em> is
* found (ie an ACE that has the SID currently being searched for and the exact
* permission bit mask being search for), the grant or deny flag for that ACE will
* prevail. If the ACE specifies to grant access, the method will return
* <code>true</code>. If the ACE specifies to deny access, the loop will stop and the
* next <code>permission</code> iteration will be performed. If each permission
* indicates to deny access, the first deny ACE found will be considered the reason
* for the failure (as it was the first match found, and is therefore the one most
* logically requiring changes - although not always). If absolutely no matching ACE
* was found at all for any permission, the parent ACL will be tried (provided that
* there is a parent and {@link Acl#isEntriesInheriting()} is <code>true</code>. The
* parent ACL will also scan its parent and so on. If ultimately no matching ACE is
* found, a <code>NotFoundException</code> will be thrown and the caller will need to
* decide how to handle the permission check. Similarly, if any of the SID arguments
* presented to the method were not loaded by the ACL,
* <code>UnloadedSidException</code> will be thrown.
*
* @param permission the exact permissions to scan for (order is important)
* @param sids the exact SIDs to scan for (order is important)
* @param administrativeMode if <code>true</code> denotes the query is for
* administrative purposes and no auditing will be undertaken
*
* @return <code>true</code> if one of the permissions has been granted,
* <code>false</code> if one of the permissions has been specifically revoked
*
* @throws NotFoundException if an exact ACE for one of the permission bit masks and
* SID combination could not be found
*/
public boolean isGranted(Acl acl, List<Permission> permission, List<Sid> sids,
boolean administrativeMode) throws NotFoundException {
final List<AccessControlEntry> aces = acl.getEntries();
AccessControlEntry firstRejection = null;
for (Permission p : permission) {
for (Sid sid : sids) {
// Attempt to find exact match for this permission mask and SID
boolean scanNextSid = true;
for (AccessControlEntry ace : aces) {
if ((ace.getPermission().getMask() == p.getMask())
&& ace.getSid().equals(sid)) {
// Found a matching ACE, so its authorization decision will
// prevail
if (ace.isGranting()) {
// Success
if (!administrativeMode) {
auditLogger.logIfNeeded(true, ace);
}
return true;
}
// Failure for this permission, so stop search
// We will see if they have a different permission
// (this permission is 100% rejected for this SID)
if (firstRejection == null) {
// Store first rejection for auditing reasons
firstRejection = ace;
}
scanNextSid = false; // helps break the loop
break; // exit aces loop
}
}
if (!scanNextSid) {
break; // exit SID for loop (now try next permission)
}
}
}
if (firstRejection != null) {
// We found an ACE to reject the request at this point, as no
// other ACEs were found that granted a different permission
if (!administrativeMode) {
auditLogger.logIfNeeded(false, firstRejection);
}
return false;
}
// No matches have been found so far
if (acl.isEntriesInheriting() && (acl.getParentAcl() != null)) {
// We have a parent, so let them try to find a matching ACE
return acl.getParentAcl().isGranted(permission, sids, false);
}
else {
// We either have no parent, or we're the uppermost parent
throw new NotFoundException(
"Unable to locate a matching ACE for passed permissions and SIDs");
}
}
}
@@ -1,162 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.io.Serializable;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;
/**
* Simple implementation of {@link AclCache} that delegates to EH-CACHE.
* <p>
* Designed to handle the transient fields in {@link AclImpl}. Note that this
* implementation assumes all {@link AclImpl} instances share the same
* {@link PermissionGrantingStrategy} and {@link AclAuthorizationStrategy} instances.
*
* @author Ben Alex
*/
public class EhCacheBasedAclCache implements AclCache {
// ~ Instance fields
// ================================================================================================
private final Ehcache cache;
private PermissionGrantingStrategy permissionGrantingStrategy;
private AclAuthorizationStrategy aclAuthorizationStrategy;
// ~ Constructors
// ===================================================================================================
public EhCacheBasedAclCache(Ehcache cache,
PermissionGrantingStrategy permissionGrantingStrategy,
AclAuthorizationStrategy aclAuthorizationStrategy) {
Assert.notNull(cache, "Cache required");
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
this.cache = cache;
this.permissionGrantingStrategy = permissionGrantingStrategy;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
}
// ~ Methods
// ========================================================================================================
public void evictFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
MutableAcl acl = getFromCache(pk);
if (acl != null) {
cache.remove(acl.getId());
cache.remove(acl.getObjectIdentity());
}
}
public void evictFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
MutableAcl acl = getFromCache(objectIdentity);
if (acl != null) {
cache.remove(acl.getId());
cache.remove(acl.getObjectIdentity());
}
}
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
Element element = null;
try {
element = cache.get(objectIdentity);
}
catch (CacheException ignored) {
}
if (element == null) {
return null;
}
return initializeTransientFields((MutableAcl) element.getValue());
}
public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
Element element = null;
try {
element = cache.get(pk);
}
catch (CacheException ignored) {
}
if (element == null) {
return null;
}
return initializeTransientFields((MutableAcl) element.getValue());
}
public void putInCache(MutableAcl acl) {
Assert.notNull(acl, "Acl required");
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
Assert.notNull(acl.getId(), "ID required");
if (this.aclAuthorizationStrategy == null) {
if (acl instanceof AclImpl) {
this.aclAuthorizationStrategy = (AclAuthorizationStrategy) FieldUtils
.getProtectedFieldValue("aclAuthorizationStrategy", acl);
this.permissionGrantingStrategy = (PermissionGrantingStrategy) FieldUtils
.getProtectedFieldValue("permissionGrantingStrategy", acl);
}
}
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}
cache.put(new Element(acl.getObjectIdentity(), acl));
cache.put(new Element(acl.getId(), acl));
}
private MutableAcl initializeTransientFields(MutableAcl value) {
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value,
this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value,
this.permissionGrantingStrategy);
}
if (value.getParentAcl() != null) {
initializeTransientFields((MutableAcl) value.getParentAcl());
}
return value;
}
public void clearCache() {
cache.removeAll();
}
}
@@ -1,80 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;
/**
* Represents a <code>GrantedAuthority</code> as a <code>Sid</code>.
* <p>
* This is a basic implementation that simply uses the <code>String</code>-based principal
* for <code>Sid</code> comparison. More complex principal objects may wish to provide an
* alternative <code>Sid</code> implementation that uses some other identifier.
* </p>
*
* @author Ben Alex
*/
public class GrantedAuthoritySid implements Sid {
// ~ Instance fields
// ================================================================================================
private final String grantedAuthority;
// ~ Constructors
// ===================================================================================================
public GrantedAuthoritySid(String grantedAuthority) {
Assert.hasText(grantedAuthority, "GrantedAuthority required");
this.grantedAuthority = grantedAuthority;
}
public GrantedAuthoritySid(GrantedAuthority grantedAuthority) {
Assert.notNull(grantedAuthority, "GrantedAuthority required");
Assert.notNull(
grantedAuthority.getAuthority(),
"This Sid is only compatible with GrantedAuthoritys that provide a non-null getAuthority()");
this.grantedAuthority = grantedAuthority.getAuthority();
}
// ~ Methods
// ========================================================================================================
public boolean equals(Object object) {
if ((object == null) || !(object instanceof GrantedAuthoritySid)) {
return false;
}
// Delegate to getGrantedAuthority() to perform actual comparison (both should be
// identical)
return ((GrantedAuthoritySid) object).getGrantedAuthority().equals(
this.getGrantedAuthority());
}
public int hashCode() {
return this.getGrantedAuthority().hashCode();
}
public String getGrantedAuthority() {
return grantedAuthority;
}
public String toString() {
return "GrantedAuthoritySid[" + this.grantedAuthority + "]";
}
}
@@ -1,46 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
/**
* Thrown if an ACL identity could not be extracted from an object.
*
* @author Ben Alex
*/
public class IdentityUnavailableException extends RuntimeException {
// ~ Constructors
// ===================================================================================================
/**
* Constructs an <code>IdentityUnavailableException</code> with the specified message.
*
* @param msg the detail message
*/
public IdentityUnavailableException(String msg) {
super(msg);
}
/**
* Constructs an <code>IdentityUnavailableException</code> with the specified message
* and root cause.
*
* @param msg the detail message
* @param t root cause
*/
public IdentityUnavailableException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -1,166 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Simple implementation of {@link ObjectIdentity}.
* <p>
* Uses <code>String</code>s to store the identity of the domain object instance. Also
* offers a constructor that uses reflection to build the identity information.
*
* @author Ben Alex
*/
public class ObjectIdentityImpl implements ObjectIdentity {
// ~ Instance fields
// ================================================================================================
private final String type;
private Serializable identifier;
// ~ Constructors
// ===================================================================================================
public ObjectIdentityImpl(String type, Serializable identifier) {
Assert.hasText(type, "Type required");
Assert.notNull(identifier, "identifier required");
this.identifier = identifier;
this.type = type;
}
/**
* Constructor which uses the name of the supplied class as the <tt>type</tt>
* property.
*/
public ObjectIdentityImpl(Class<?> javaType, Serializable identifier) {
Assert.notNull(javaType, "Java Type required");
Assert.notNull(identifier, "identifier required");
this.type = javaType.getName();
this.identifier = identifier;
}
/**
* Creates the <code>ObjectIdentityImpl</code> based on the passed object instance.
* The passed object must provide a <code>getId()</code> method, otherwise an
* exception will be thrown.
* <p>
* The class name of the object passed will be considered the {@link #type}, so if
* more control is required, a different constructor should be used.
*
* @param object the domain object instance to create an identity for.
*
* @throws IdentityUnavailableException if identity could not be extracted
*/
public ObjectIdentityImpl(Object object) throws IdentityUnavailableException {
Assert.notNull(object, "object cannot be null");
Class<?> typeClass = ClassUtils.getUserClass(object.getClass());
type = typeClass.getName();
Object result;
try {
Method method = typeClass.getMethod("getId", new Class[] {});
result = method.invoke(object);
}
catch (Exception e) {
throw new IdentityUnavailableException(
"Could not extract identity from object " + object, e);
}
Assert.notNull(result, "getId() is required to return a non-null value");
Assert.isInstanceOf(Serializable.class, result,
"Getter must provide a return value of type Serializable");
this.identifier = (Serializable) result;
}
// ~ Methods
// ========================================================================================================
/**
* Important so caching operates properly.
* <p>
* Considers an object of the same class equal if it has the same
* <code>classname</code> and <code>id</code> properties.
* <p>
* Numeric identities (Integer and Long values) are considered equal if they are
* numerically equal. Other serializable types are evaluated using a simple equality.
*
* @param arg0 object to compare
*
* @return <code>true</code> if the presented object matches this object
*/
public boolean equals(Object arg0) {
if (arg0 == null || !(arg0 instanceof ObjectIdentityImpl)) {
return false;
}
ObjectIdentityImpl other = (ObjectIdentityImpl) arg0;
if (identifier instanceof Number && other.identifier instanceof Number) {
// Integers and Longs with same value should be considered equal
if (((Number) identifier).longValue() != ((Number) other.identifier)
.longValue()) {
return false;
}
}
else {
// Use plain equality for other serializable types
if (!identifier.equals(other.identifier)) {
return false;
}
}
return type.equals(other.type);
}
public Serializable getIdentifier() {
return identifier;
}
public String getType() {
return type;
}
/**
* Important so caching operates properly.
*
* @return the hash
*/
public int hashCode() {
int code = 31;
code ^= this.type.hashCode();
code ^= this.identifier.hashCode();
return code;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getName()).append("[");
sb.append("Type: ").append(this.type);
sb.append("; Identifier: ").append(this.identifier).append("]");
return sb.toString();
}
}
@@ -1,44 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.io.Serializable;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityGenerator;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
/**
* Basic implementation of {@link ObjectIdentityRetrievalStrategy} and
* <tt>ObjectIdentityGenerator</tt> that uses the constructors of
* {@link ObjectIdentityImpl} to create the {@link ObjectIdentity}.
*
* @author Ben Alex
*/
public class ObjectIdentityRetrievalStrategyImpl implements
ObjectIdentityRetrievalStrategy, ObjectIdentityGenerator {
// ~ Methods
// ========================================================================================================
public ObjectIdentity getObjectIdentity(Object domainObject) {
return new ObjectIdentityImpl(domainObject);
}
public ObjectIdentity createObjectIdentity(Serializable id, String type) {
return new ObjectIdentityImpl(type, id);
}
}
@@ -1,45 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.util.List;
import org.springframework.security.acls.model.Permission;
/**
* Provides a simple mechanism to retrieve {@link Permission} instances from integer
* masks.
*
* @author Ben Alex
* @since 2.0.3
*
*/
public interface PermissionFactory {
/**
* Dynamically creates a <code>CumulativePermission</code> or
* <code>BasePermission</code> representing the active bits in the passed mask.
*
* @param mask to build
*
* @return a Permission representing the requested object
*/
Permission buildFromMask(int mask);
Permission buildFromName(String name);
List<Permission> buildFromNames(List<String> names);
}
@@ -1,84 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
/**
* Represents an <code>Authentication.getPrincipal()</code> as a <code>Sid</code>.
* <p>
* This is a basic implementation that simply uses the <code>String</code>-based principal
* for <code>Sid</code> comparison. More complex principal objects may wish to provide an
* alternative <code>Sid</code> implementation that uses some other identifier.
* </p>
*
* @author Ben Alex
*/
public class PrincipalSid implements Sid {
// ~ Instance fields
// ================================================================================================
private final String principal;
// ~ Constructors
// ===================================================================================================
public PrincipalSid(String principal) {
Assert.hasText(principal, "Principal required");
this.principal = principal;
}
public PrincipalSid(Authentication authentication) {
Assert.notNull(authentication, "Authentication required");
Assert.notNull(authentication.getPrincipal(), "Principal required");
if (authentication.getPrincipal() instanceof UserDetails) {
this.principal = ((UserDetails) authentication.getPrincipal()).getUsername();
}
else {
this.principal = authentication.getPrincipal().toString();
}
}
// ~ Methods
// ========================================================================================================
public boolean equals(Object object) {
if ((object == null) || !(object instanceof PrincipalSid)) {
return false;
}
// Delegate to getPrincipal() to perform actual comparison (both should be
// identical)
return ((PrincipalSid) object).getPrincipal().equals(this.getPrincipal());
}
public int hashCode() {
return this.getPrincipal().hashCode();
}
public String getPrincipal() {
return principal;
}
public String toString() {
return "PrincipalSid[" + this.principal + "]";
}
}
@@ -1,70 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;
/**
* Basic implementation of {@link SidRetrievalStrategy} that creates a {@link Sid} for the
* principal, as well as every granted authority the principal holds. Can optionally have
* a <tt>RoleHierarchy</tt> injected in order to determine the extended list of
* authorities that the principal is assigned.
* <p>
* The returned array will always contain the {@link PrincipalSid} before any
* {@link GrantedAuthoritySid} elements.
*
* @author Ben Alex
*/
public class SidRetrievalStrategyImpl implements SidRetrievalStrategy {
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
public SidRetrievalStrategyImpl() {
}
public SidRetrievalStrategyImpl(RoleHierarchy roleHierarchy) {
Assert.notNull(roleHierarchy, "RoleHierarchy must not be null");
this.roleHierarchy = roleHierarchy;
}
// ~ Methods
// ========================================================================================================
public List<Sid> getSids(Authentication authentication) {
Collection<? extends GrantedAuthority> authorities = roleHierarchy
.getReachableGrantedAuthorities(authentication.getAuthorities());
List<Sid> sids = new ArrayList<Sid>(authorities.size() + 1);
sids.add(new PrincipalSid(authentication));
for (GrantedAuthority authority : authorities) {
sids.add(new GrantedAuthoritySid(authority));
}
return sids;
}
}
@@ -1,138 +0,0 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.cache.Cache;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* Simple implementation of {@link org.springframework.security.acls.model.AclCache} that
* delegates to {@link Cache} implementation.
* <p>
* Designed to handle the transient fields in
* {@link org.springframework.security.acls.domain.AclImpl}. Note that this implementation
* assumes all {@link org.springframework.security.acls.domain.AclImpl} instances share
* the same {@link org.springframework.security.acls.model.PermissionGrantingStrategy} and
* {@link org.springframework.security.acls.domain.AclAuthorizationStrategy} instances.
*
* @author Marten Deinum
* @since 3.2
*/
public class SpringCacheBasedAclCache implements AclCache {
// ~ Instance fields
// ================================================================================================
private final Cache cache;
private PermissionGrantingStrategy permissionGrantingStrategy;
private AclAuthorizationStrategy aclAuthorizationStrategy;
// ~ Constructors
// ===================================================================================================
public SpringCacheBasedAclCache(Cache cache,
PermissionGrantingStrategy permissionGrantingStrategy,
AclAuthorizationStrategy aclAuthorizationStrategy) {
Assert.notNull(cache, "Cache required");
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
this.cache = cache;
this.permissionGrantingStrategy = permissionGrantingStrategy;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
}
// ~ Methods
// ========================================================================================================
public void evictFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
MutableAcl acl = getFromCache(pk);
if (acl != null) {
cache.evict(acl.getId());
cache.evict(acl.getObjectIdentity());
}
}
public void evictFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
MutableAcl acl = getFromCache(objectIdentity);
if (acl != null) {
cache.evict(acl.getId());
cache.evict(acl.getObjectIdentity());
}
}
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
return getFromCache((Object) objectIdentity);
}
public MutableAcl getFromCache(Serializable pk) {
Assert.notNull(pk, "Primary key (identifier) required");
return getFromCache((Object) pk);
}
public void putInCache(MutableAcl acl) {
Assert.notNull(acl, "Acl required");
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
Assert.notNull(acl.getId(), "ID required");
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
putInCache((MutableAcl) acl.getParentAcl());
}
cache.put(acl.getObjectIdentity(), acl);
cache.put(acl.getId(), acl);
}
private MutableAcl getFromCache(Object key) {
Cache.ValueWrapper element = cache.get(key);
if (element == null) {
return null;
}
return initializeTransientFields((MutableAcl) element.get());
}
private MutableAcl initializeTransientFields(MutableAcl value) {
if (value instanceof AclImpl) {
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value,
this.aclAuthorizationStrategy);
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value,
this.permissionGrantingStrategy);
}
if (value.getParentAcl() != null) {
initializeTransientFields((MutableAcl) value.getParentAcl());
}
return value;
}
public void clearCache() {
cache.clear();
}
}
@@ -1,20 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Basic implementation of access control lists (ACLs) interfaces.
*/
package org.springframework.security.acls.domain;
@@ -1,705 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.jdbc;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.security.acls.domain.AccessControlEntryImpl;
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
import org.springframework.security.acls.domain.AclImpl;
import org.springframework.security.acls.domain.AuditLogger;
import org.springframework.security.acls.domain.DefaultPermissionFactory;
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PermissionFactory;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.PermissionGrantingStrategy;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.UnloadedSidException;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;
/**
* Performs lookups in a manner that is compatible with ANSI SQL.
* <p>
* NB: This implementation does attempt to provide reasonably optimised lookups - within
* the constraints of a normalised database and standard ANSI SQL features. If you are
* willing to sacrifice either of these constraints (e.g. use a particular database
* feature such as hierarchical queries or materalized views, or reduce normalisation) you
* are likely to achieve better performance. In such situations you will need to provide
* your own custom <code>LookupStrategy</code>. This class does not support subclassing,
* as it is likely to change in future releases and therefore subclassing is unsupported.
* <p>
* There are two SQL queries executed, one in the <tt>lookupPrimaryKeys</tt> method and
* one in <tt>lookupObjectIdentities</tt>. These are built from the same select and
* "order by" clause, using a different where clause in each case. In order to use custom
* schema or column names, each of these SQL clauses can be customized, but they must be
* consistent with each other and with the expected result set generated by the the
* default values.
*
* @author Ben Alex
*/
public class BasicLookupStrategy implements LookupStrategy {
public final static String DEFAULT_SELECT_CLAUSE = "select acl_object_identity.object_id_identity, "
+ "acl_entry.ace_order, "
+ "acl_object_identity.id as acl_id, "
+ "acl_object_identity.parent_object, "
+ "acl_object_identity.entries_inheriting, "
+ "acl_entry.id as ace_id, "
+ "acl_entry.mask, "
+ "acl_entry.granting, "
+ "acl_entry.audit_success, "
+ "acl_entry.audit_failure, "
+ "acl_sid.principal as ace_principal, "
+ "acl_sid.sid as ace_sid, "
+ "acli_sid.principal as acl_principal, "
+ "acli_sid.sid as acl_sid, "
+ "acl_class.class "
+ "from acl_object_identity "
+ "left join acl_sid acli_sid on acli_sid.id = acl_object_identity.owner_sid "
+ "left join acl_class on acl_class.id = acl_object_identity.object_id_class "
+ "left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity "
+ "left join acl_sid on acl_entry.sid = acl_sid.id " + "where ( ";
private final static String DEFAULT_LOOKUP_KEYS_WHERE_CLAUSE = "(acl_object_identity.id = ?)";
private final static String DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE = "(acl_object_identity.object_id_identity = ? and acl_class.class = ?)";
public final static String DEFAULT_ORDER_BY_CLAUSE = ") order by acl_object_identity.object_id_identity"
+ " asc, acl_entry.ace_order asc";
// ~ Instance fields
// ================================================================================================
private final AclAuthorizationStrategy aclAuthorizationStrategy;
private PermissionFactory permissionFactory = new DefaultPermissionFactory();
private final AclCache aclCache;
private final PermissionGrantingStrategy grantingStrategy;
private final JdbcTemplate jdbcTemplate;
private int batchSize = 50;
private final Field fieldAces = FieldUtils.getField(AclImpl.class, "aces");
private final Field fieldAcl = FieldUtils.getField(AccessControlEntryImpl.class,
"acl");
// SQL Customization fields
private String selectClause = DEFAULT_SELECT_CLAUSE;
private String lookupPrimaryKeysWhereClause = DEFAULT_LOOKUP_KEYS_WHERE_CLAUSE;
private String lookupObjectIdentitiesWhereClause = DEFAULT_LOOKUP_IDENTITIES_WHERE_CLAUSE;
private String orderByClause = DEFAULT_ORDER_BY_CLAUSE;
// ~ Constructors
// ===================================================================================================
/**
* Constructor accepting mandatory arguments
*
* @param dataSource to access the database
* @param aclCache the cache where fully-loaded elements can be stored
* @param aclAuthorizationStrategy authorization strategy (required)
*/
public BasicLookupStrategy(DataSource dataSource, AclCache aclCache,
AclAuthorizationStrategy aclAuthorizationStrategy, AuditLogger auditLogger) {
this(dataSource, aclCache, aclAuthorizationStrategy,
new DefaultPermissionGrantingStrategy(auditLogger));
}
/**
* Creates a new instance
*
* @param dataSource to access the database
* @param aclCache the cache where fully-loaded elements can be stored
* @param aclAuthorizationStrategy authorization strategy (required)
* @param grantingStrategy the PermissionGrantingStrategy
*/
public BasicLookupStrategy(DataSource dataSource, AclCache aclCache,
AclAuthorizationStrategy aclAuthorizationStrategy,
PermissionGrantingStrategy grantingStrategy) {
Assert.notNull(dataSource, "DataSource required");
Assert.notNull(aclCache, "AclCache required");
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
Assert.notNull(grantingStrategy, "grantingStrategy required");
jdbcTemplate = new JdbcTemplate(dataSource);
this.aclCache = aclCache;
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
this.grantingStrategy = grantingStrategy;
fieldAces.setAccessible(true);
fieldAcl.setAccessible(true);
}
// ~ Methods
// ========================================================================================================
private String computeRepeatingSql(String repeatingSql, int requiredRepetitions) {
assert requiredRepetitions > 0 : "requiredRepetitions must be > 0";
final String startSql = selectClause;
final String endSql = orderByClause;
StringBuilder sqlStringBldr = new StringBuilder(startSql.length()
+ endSql.length() + requiredRepetitions * (repeatingSql.length() + 4));
sqlStringBldr.append(startSql);
for (int i = 1; i <= requiredRepetitions; i++) {
sqlStringBldr.append(repeatingSql);
if (i != requiredRepetitions) {
sqlStringBldr.append(" or ");
}
}
sqlStringBldr.append(endSql);
return sqlStringBldr.toString();
}
@SuppressWarnings("unchecked")
private List<AccessControlEntryImpl> readAces(AclImpl acl) {
try {
return (List<AccessControlEntryImpl>) fieldAces.get(acl);
}
catch (IllegalAccessException e) {
throw new IllegalStateException("Could not obtain AclImpl.aces field", e);
}
}
private void setAclOnAce(AccessControlEntryImpl ace, AclImpl acl) {
try {
fieldAcl.set(ace, acl);
}
catch (IllegalAccessException e) {
throw new IllegalStateException(
"Could not or set AclImpl on AccessControlEntryImpl fields", e);
}
}
private void setAces(AclImpl acl, List<AccessControlEntryImpl> aces) {
try {
fieldAces.set(acl, aces);
}
catch (IllegalAccessException e) {
throw new IllegalStateException("Could not set AclImpl entries", e);
}
}
/**
* Locates the primary key IDs specified in "findNow", adding AclImpl instances with
* StubAclParents to the "acls" Map.
*
* @param acls the AclImpls (with StubAclParents)
* @param findNow Long-based primary keys to retrieve
* @param sids
*/
private void lookupPrimaryKeys(final Map<Serializable, Acl> acls,
final Set<Long> findNow, final List<Sid> sids) {
Assert.notNull(acls, "ACLs are required");
Assert.notEmpty(findNow, "Items to find now required");
String sql = computeRepeatingSql(lookupPrimaryKeysWhereClause, findNow.size());
Set<Long> parentsToLookup = jdbcTemplate.query(sql,
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
int i = 0;
for (Long toFind : findNow) {
i++;
ps.setLong(i, toFind);
}
}
}, new ProcessResultSet(acls, sids));
// Lookup the parents, now that our JdbcTemplate has released the database
// connection (SEC-547)
if (parentsToLookup.size() > 0) {
lookupPrimaryKeys(acls, parentsToLookup, sids);
}
}
/**
* The main method.
* <p>
* WARNING: This implementation completely disregards the "sids" argument! Every item
* in the cache is expected to contain all SIDs. If you have serious performance needs
* (e.g. a very large number of SIDs per object identity), you'll probably want to
* develop a custom {@link LookupStrategy} implementation instead.
* <p>
* The implementation works in batch sizes specified by {@link #batchSize}.
*
* @param objects the identities to lookup (required)
* @param sids the SIDs for which identities are required (ignored by this
* implementation)
*
* @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the
* located {@link Acl} and values are the located {@link Acl} (never <tt>null</tt>
* although some entries may be missing; this method should not throw
* {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used to
* automatically create entries if required)
*/
public final Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects,
List<Sid> sids) {
Assert.isTrue(batchSize >= 1, "BatchSize must be >= 1");
Assert.notEmpty(objects, "Objects to lookup required");
// Map<ObjectIdentity,Acl>
Map<ObjectIdentity, Acl> result = new HashMap<ObjectIdentity, Acl>(); // contains
// FULLY
// loaded
// Acl
// objects
Set<ObjectIdentity> currentBatchToLoad = new HashSet<ObjectIdentity>();
for (int i = 0; i < objects.size(); i++) {
final ObjectIdentity oid = objects.get(i);
boolean aclFound = false;
// Check we don't already have this ACL in the results
if (result.containsKey(oid)) {
aclFound = true;
}
// Check cache for the present ACL entry
if (!aclFound) {
Acl acl = aclCache.getFromCache(oid);
// Ensure any cached element supports all the requested SIDs
// (they should always, as our base impl doesn't filter on SID)
if (acl != null) {
if (acl.isSidLoaded(sids)) {
result.put(acl.getObjectIdentity(), acl);
aclFound = true;
}
else {
throw new IllegalStateException(
"Error: SID-filtered element detected when implementation does not perform SID filtering "
+ "- have you added something to the cache manually?");
}
}
}
// Load the ACL from the database
if (!aclFound) {
currentBatchToLoad.add(oid);
}
// Is it time to load from JDBC the currentBatchToLoad?
if ((currentBatchToLoad.size() == this.batchSize)
|| ((i + 1) == objects.size())) {
if (currentBatchToLoad.size() > 0) {
Map<ObjectIdentity, Acl> loadedBatch = lookupObjectIdentities(
currentBatchToLoad, sids);
// Add loaded batch (all elements 100% initialized) to results
result.putAll(loadedBatch);
// Add the loaded batch to the cache
for (Acl loadedAcl : loadedBatch.values()) {
aclCache.putInCache((AclImpl) loadedAcl);
}
currentBatchToLoad.clear();
}
}
}
return result;
}
/**
* Looks up a batch of <code>ObjectIdentity</code>s directly from the database.
* <p>
* The caller is responsible for optimization issues, such as selecting the identities
* to lookup, ensuring the cache doesn't contain them already, and adding the returned
* elements to the cache etc.
* <p>
* This subclass is required to return fully valid <code>Acl</code>s, including
* properly-configured parent ACLs.
*
*/
private Map<ObjectIdentity, Acl> lookupObjectIdentities(
final Collection<ObjectIdentity> objectIdentities, List<Sid> sids) {
Assert.notEmpty(objectIdentities, "Must provide identities to lookup");
final Map<Serializable, Acl> acls = new HashMap<Serializable, Acl>(); // contains
// Acls
// with
// StubAclParents
// Make the "acls" map contain all requested objectIdentities
// (including markers to each parent in the hierarchy)
String sql = computeRepeatingSql(lookupObjectIdentitiesWhereClause,
objectIdentities.size());
Set<Long> parentsToLookup = jdbcTemplate.query(sql,
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
int i = 0;
for (ObjectIdentity oid : objectIdentities) {
// Determine prepared statement values for this iteration
String type = oid.getType();
// No need to check for nulls, as guaranteed non-null by
// ObjectIdentity.getIdentifier() interface contract
String identifier = oid.getIdentifier().toString();
long id = (Long.valueOf(identifier)).longValue();
// Inject values
ps.setLong((2 * i) + 1, id);
ps.setString((2 * i) + 2, type);
i++;
}
}
}, new ProcessResultSet(acls, sids));
// Lookup the parents, now that our JdbcTemplate has released the database
// connection (SEC-547)
if (parentsToLookup.size() > 0) {
lookupPrimaryKeys(acls, parentsToLookup, sids);
}
// Finally, convert our "acls" containing StubAclParents into true Acls
Map<ObjectIdentity, Acl> resultMap = new HashMap<ObjectIdentity, Acl>();
for (Acl inputAcl : acls.values()) {
Assert.isInstanceOf(AclImpl.class, inputAcl,
"Map should have contained an AclImpl");
Assert.isInstanceOf(Long.class, ((AclImpl) inputAcl).getId(),
"Acl.getId() must be Long");
Acl result = convert(acls, (Long) ((AclImpl) inputAcl).getId());
resultMap.put(result.getObjectIdentity(), result);
}
return resultMap;
}
/**
* The final phase of converting the <code>Map</code> of <code>AclImpl</code>
* instances which contain <code>StubAclParent</code>s into proper, valid
* <code>AclImpl</code>s with correct ACL parents.
*
* @param inputMap the unconverted <code>AclImpl</code>s
* @param currentIdentity the current<code>Acl</code> that we wish to convert (this
* may be
*
*/
private AclImpl convert(Map<Serializable, Acl> inputMap, Long currentIdentity) {
Assert.notEmpty(inputMap, "InputMap required");
Assert.notNull(currentIdentity, "CurrentIdentity required");
// Retrieve this Acl from the InputMap
Acl uncastAcl = inputMap.get(currentIdentity);
Assert.isInstanceOf(AclImpl.class, uncastAcl,
"The inputMap contained a non-AclImpl");
AclImpl inputAcl = (AclImpl) uncastAcl;
Acl parent = inputAcl.getParentAcl();
if ((parent != null) && parent instanceof StubAclParent) {
// Lookup the parent
StubAclParent stubAclParent = (StubAclParent) parent;
parent = convert(inputMap, stubAclParent.getId());
}
// Now we have the parent (if there is one), create the true AclImpl
AclImpl result = new AclImpl(inputAcl.getObjectIdentity(),
(Long) inputAcl.getId(), aclAuthorizationStrategy, grantingStrategy,
parent, null, inputAcl.isEntriesInheriting(), inputAcl.getOwner());
// Copy the "aces" from the input to the destination
// Obtain the "aces" from the input ACL
List<AccessControlEntryImpl> aces = readAces(inputAcl);
// Create a list in which to store the "aces" for the "result" AclImpl instance
List<AccessControlEntryImpl> acesNew = new ArrayList<AccessControlEntryImpl>();
// Iterate over the "aces" input and replace each nested
// AccessControlEntryImpl.getAcl() with the new "result" AclImpl instance
// This ensures StubAclParent instances are removed, as per SEC-951
for (AccessControlEntryImpl ace : aces) {
setAclOnAce(ace, result);
acesNew.add(ace);
}
// Finally, now that the "aces" have been converted to have the "result" AclImpl
// instance, modify the "result" AclImpl instance
setAces(result, acesNew);
return result;
}
/**
* Creates a particular implementation of {@link Sid} depending on the arguments.
*
* @param sid the name of the sid representing its unique identifier. In typical ACL
* database schema it's located in table {@code acl_sid} table, {@code sid} column.
* @param isPrincipal whether it's a user or granted authority like role
* @return the instance of Sid with the {@code sidName} as an identifier
*/
protected Sid createSid(boolean isPrincipal, String sid) {
if (isPrincipal) {
return new PrincipalSid(sid);
}
else {
return new GrantedAuthoritySid(sid);
}
}
/**
* Sets the {@code PermissionFactory} instance which will be used to convert loaded
* permission data values to {@code Permission}s. A {@code DefaultPermissionFactory}
* will be used by default.
*
* @param permissionFactory
*/
public final void setPermissionFactory(PermissionFactory permissionFactory) {
this.permissionFactory = permissionFactory;
}
public final void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
/**
* The SQL for the select clause. If customizing in order to modify column names,
* schema etc, the other SQL customization fields must also be set to match.
*
* @param selectClause the select clause, which defaults to
* {@link #DEFAULT_SELECT_CLAUSE}.
*/
public final void setSelectClause(String selectClause) {
this.selectClause = selectClause;
}
/**
* The SQL for the where clause used in the <tt>lookupPrimaryKey</tt> method.
*/
public final void setLookupPrimaryKeysWhereClause(String lookupPrimaryKeysWhereClause) {
this.lookupPrimaryKeysWhereClause = lookupPrimaryKeysWhereClause;
}
/**
* The SQL for the where clause used in the <tt>lookupObjectIdentities</tt> method.
*/
public final void setLookupObjectIdentitiesWhereClause(
String lookupObjectIdentitiesWhereClause) {
this.lookupObjectIdentitiesWhereClause = lookupObjectIdentitiesWhereClause;
}
/**
* The SQL for the "order by" clause used in both queries.
*/
public final void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
// ~ Inner Classes
// ==================================================================================================
private class ProcessResultSet implements ResultSetExtractor<Set<Long>> {
private final Map<Serializable, Acl> acls;
private final List<Sid> sids;
public ProcessResultSet(Map<Serializable, Acl> acls, List<Sid> sids) {
Assert.notNull(acls, "ACLs cannot be null");
this.acls = acls;
this.sids = sids; // can be null
}
/**
* Implementation of {@link ResultSetExtractor#extractData(ResultSet)}. Creates an
* {@link Acl} for each row in the {@link ResultSet} and ensures it is in member
* field <tt>acls</tt>. Any {@link Acl} with a parent will have the parents id
* returned in a set. The returned set of ids may requires further processing.
* @param rs The {@link ResultSet} to be processed
* @return a list of parent IDs remaining to be looked up (may be empty, but never
* <tt>null</tt>)
* @throws SQLException
*/
public Set<Long> extractData(ResultSet rs) throws SQLException {
Set<Long> parentIdsToLookup = new HashSet<Long>(); // Set of parent_id Longs
while (rs.next()) {
// Convert current row into an Acl (albeit with a StubAclParent)
convertCurrentResultIntoObject(acls, rs);
// Figure out if this row means we need to lookup another parent
long parentId = rs.getLong("parent_object");
if (parentId != 0) {
// See if it's already in the "acls"
if (acls.containsKey(new Long(parentId))) {
continue; // skip this while iteration
}
// Now try to find it in the cache
MutableAcl cached = aclCache.getFromCache(new Long(parentId));
if ((cached == null) || !cached.isSidLoaded(sids)) {
parentIdsToLookup.add(new Long(parentId));
}
else {
// Pop into the acls map, so our convert method doesn't
// need to deal with an unsynchronized AclCache
acls.put(cached.getId(), cached);
}
}
}
// Return the parents left to lookup to the caller
return parentIdsToLookup;
}
/**
* Accepts the current <code>ResultSet</code> row, and converts it into an
* <code>AclImpl</code> that contains a <code>StubAclParent</code>
*
* @param acls the Map we should add the converted Acl to
* @param rs the ResultSet focused on a current row
*
* @throws SQLException if something goes wrong converting values
*/
private void convertCurrentResultIntoObject(Map<Serializable, Acl> acls,
ResultSet rs) throws SQLException {
Long id = new Long(rs.getLong("acl_id"));
// If we already have an ACL for this ID, just create the ACE
Acl acl = acls.get(id);
if (acl == null) {
// Make an AclImpl and pop it into the Map
ObjectIdentity objectIdentity = new ObjectIdentityImpl(
rs.getString("class"), Long.valueOf(rs
.getLong("object_id_identity")));
Acl parentAcl = null;
long parentAclId = rs.getLong("parent_object");
if (parentAclId != 0) {
parentAcl = new StubAclParent(Long.valueOf(parentAclId));
}
boolean entriesInheriting = rs.getBoolean("entries_inheriting");
Sid owner = createSid(rs.getBoolean("acl_principal"),
rs.getString("acl_sid"));
acl = new AclImpl(objectIdentity, id, aclAuthorizationStrategy,
grantingStrategy, parentAcl, null, entriesInheriting, owner);
acls.put(id, acl);
}
// Add an extra ACE to the ACL (ORDER BY maintains the ACE list order)
// It is permissible to have no ACEs in an ACL (which is detected by a null
// ACE_SID)
if (rs.getString("ace_sid") != null) {
Long aceId = new Long(rs.getLong("ace_id"));
Sid recipient = createSid(rs.getBoolean("ace_principal"),
rs.getString("ace_sid"));
int mask = rs.getInt("mask");
Permission permission = permissionFactory.buildFromMask(mask);
boolean granting = rs.getBoolean("granting");
boolean auditSuccess = rs.getBoolean("audit_success");
boolean auditFailure = rs.getBoolean("audit_failure");
AccessControlEntryImpl ace = new AccessControlEntryImpl(aceId, acl,
recipient, permission, granting, auditSuccess, auditFailure);
// Field acesField = FieldUtils.getField(AclImpl.class, "aces");
List<AccessControlEntryImpl> aces = readAces((AclImpl) acl);
// Add the ACE if it doesn't already exist in the ACL.aces field
if (!aces.contains(ace)) {
aces.add(ace);
}
}
}
}
private static class StubAclParent implements Acl {
private final Long id;
public StubAclParent(Long id) {
this.id = id;
}
public List<AccessControlEntry> getEntries() {
throw new UnsupportedOperationException("Stub only");
}
public Long getId() {
return id;
}
public ObjectIdentity getObjectIdentity() {
throw new UnsupportedOperationException("Stub only");
}
public Sid getOwner() {
throw new UnsupportedOperationException("Stub only");
}
public Acl getParentAcl() {
throw new UnsupportedOperationException("Stub only");
}
public boolean isEntriesInheriting() {
throw new UnsupportedOperationException("Stub only");
}
public boolean isGranted(List<Permission> permission, List<Sid> sids,
boolean administrativeMode) throws NotFoundException,
UnloadedSidException {
throw new UnsupportedOperationException("Stub only");
}
public boolean isSidLoaded(List<Sid> sids) {
throw new UnsupportedOperationException("Stub only");
}
}
}
@@ -1,141 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Sid;
import org.springframework.util.Assert;
/**
* Simple JDBC-based implementation of <code>AclService</code>.
* <p>
* Requires the "dirty" flags in {@link org.springframework.security.acls.domain.AclImpl}
* and {@link org.springframework.security.acls.domain.AccessControlEntryImpl} to be set,
* so that the implementation can detect changed parameters easily.
*
* @author Ben Alex
*/
public class JdbcAclService implements AclService {
// ~ Static fields/initializers
// =====================================================================================
protected static final Log log = LogFactory.getLog(JdbcAclService.class);
private static final String DEFAULT_SELECT_ACL_WITH_PARENT_SQL = "select obj.object_id_identity as obj_id, class.class as class "
+ "from acl_object_identity obj, acl_object_identity parent, acl_class class "
+ "where obj.parent_object = parent.id and obj.object_id_class = class.id "
+ "and parent.object_id_identity = ? and parent.object_id_class = ("
+ "select id FROM acl_class where acl_class.class = ?)";
// ~ Instance fields
// ================================================================================================
protected final JdbcTemplate jdbcTemplate;
private final LookupStrategy lookupStrategy;
private String findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL;
// ~ Constructors
// ===================================================================================================
public JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) {
Assert.notNull(dataSource, "DataSource required");
Assert.notNull(lookupStrategy, "LookupStrategy required");
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.lookupStrategy = lookupStrategy;
}
// ~ Methods
// ========================================================================================================
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = { parentIdentity.getIdentifier(), parentIdentity.getType() };
List<ObjectIdentity> objects = jdbcTemplate.query(findChildrenSql, args,
new RowMapper<ObjectIdentity>() {
public ObjectIdentity mapRow(ResultSet rs, int rowNum)
throws SQLException {
String javaType = rs.getString("class");
Long identifier = new Long(rs.getLong("obj_id"));
return new ObjectIdentityImpl(javaType, identifier);
}
});
if (objects.size() == 0) {
return null;
}
return objects;
}
public Acl readAclById(ObjectIdentity object, List<Sid> sids)
throws NotFoundException {
Map<ObjectIdentity, Acl> map = readAclsById(Arrays.asList(object), sids);
Assert.isTrue(map.containsKey(object),
"There should have been an Acl entry for ObjectIdentity " + object);
return (Acl) map.get(object);
}
public Acl readAclById(ObjectIdentity object) throws NotFoundException {
return readAclById(object, null);
}
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects)
throws NotFoundException {
return readAclsById(objects, null);
}
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects,
List<Sid> sids) throws NotFoundException {
Map<ObjectIdentity, Acl> result = lookupStrategy.readAclsById(objects, sids);
// Check every requested object identity was found (throw NotFoundException if
// needed)
for (ObjectIdentity oid : objects) {
if (!result.containsKey(oid)) {
throw new NotFoundException(
"Unable to find ACL information for object identity '" + oid
+ "'");
}
}
return result;
}
/**
* Allows customization of the SQL query used to find child object identities.
*
* @param findChildrenSql
*/
public void setFindChildrenQuery(String findChildrenSql) {
this.findChildrenSql = findChildrenSql;
}
}
@@ -1,488 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.jdbc;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.security.acls.domain.AccessControlEntryImpl;
import org.springframework.security.acls.domain.GrantedAuthoritySid;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.domain.PrincipalSid;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclCache;
import org.springframework.security.acls.model.AlreadyExistsException;
import org.springframework.security.acls.model.ChildrenExistException;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* Provides a base JDBC implementation of {@link MutableAclService}.
* <p>
* The default settings are for HSQLDB. If you are using a different database you will
* probably need to set the {@link #setSidIdentityQuery(String) sidIdentityQuery} and
* {@link #setClassIdentityQuery(String) classIdentityQuery} properties appropriately. The
* other queries, SQL inserts and updates can also be customized to accomodate schema
* variations, but must produce results consistent with those expected by the defaults.
* <p>
* See the appendix of the Spring Security reference manual for more information on the
* expected schema and how it is used. Information on using PostgreSQL is also included.
*
* @author Ben Alex
* @author Johannes Zlattinger
*/
public class JdbcMutableAclService extends JdbcAclService implements MutableAclService {
// ~ Instance fields
// ================================================================================================
private boolean foreignKeysInDatabase = true;
private final AclCache aclCache;
private String deleteEntryByObjectIdentityForeignKey = "delete from acl_entry where acl_object_identity=?";
private String deleteObjectIdentityByPrimaryKey = "delete from acl_object_identity where id=?";
private String classIdentityQuery = "call identity()";
private String sidIdentityQuery = "call identity()";
private String insertClass = "insert into acl_class (class) values (?)";
private String insertEntry = "insert into acl_entry "
+ "(acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)"
+ "values (?, ?, ?, ?, ?, ?, ?)";
private String insertObjectIdentity = "insert into acl_object_identity "
+ "(object_id_class, object_id_identity, owner_sid, entries_inheriting) "
+ "values (?, ?, ?, ?)";
private String insertSid = "insert into acl_sid (principal, sid) values (?, ?)";
private String selectClassPrimaryKey = "select id from acl_class where class=?";
private String selectObjectIdentityPrimaryKey = "select acl_object_identity.id from acl_object_identity, acl_class "
+ "where acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
+ "and acl_object_identity.object_id_identity = ?";
private String selectSidPrimaryKey = "select id from acl_sid where principal=? and sid=?";
private String updateObjectIdentity = "update acl_object_identity set "
+ "parent_object = ?, owner_sid = ?, entries_inheriting = ?"
+ " where id = ?";
// ~ Constructors
// ===================================================================================================
public JdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy,
AclCache aclCache) {
super(dataSource, lookupStrategy);
Assert.notNull(aclCache, "AclCache required");
this.aclCache = aclCache;
}
// ~ Methods
// ========================================================================================================
public MutableAcl createAcl(ObjectIdentity objectIdentity)
throws AlreadyExistsException {
Assert.notNull(objectIdentity, "Object Identity required");
// Check this object identity hasn't already been persisted
if (retrieveObjectIdentityPrimaryKey(objectIdentity) != null) {
throw new AlreadyExistsException("Object identity '" + objectIdentity
+ "' already exists");
}
// Need to retrieve the current principal, in order to know who "owns" this ACL
// (can be changed later on)
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
PrincipalSid sid = new PrincipalSid(auth);
// Create the acl_object_identity row
createObjectIdentity(objectIdentity, sid);
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval
// etc)
Acl acl = readAclById(objectIdentity);
Assert.isInstanceOf(MutableAcl.class, acl, "MutableAcl should be been returned");
return (MutableAcl) acl;
}
/**
* Creates a new row in acl_entry for every ACE defined in the passed MutableAcl
* object.
*
* @param acl containing the ACEs to insert
*/
protected void createEntries(final MutableAcl acl) {
if (acl.getEntries().isEmpty()) {
return;
}
jdbcTemplate.batchUpdate(insertEntry, new BatchPreparedStatementSetter() {
public int getBatchSize() {
return acl.getEntries().size();
}
public void setValues(PreparedStatement stmt, int i) throws SQLException {
AccessControlEntry entry_ = acl.getEntries().get(i);
Assert.isTrue(entry_ instanceof AccessControlEntryImpl,
"Unknown ACE class");
AccessControlEntryImpl entry = (AccessControlEntryImpl) entry_;
stmt.setLong(1, ((Long) acl.getId()).longValue());
stmt.setInt(2, i);
stmt.setLong(3, createOrRetrieveSidPrimaryKey(entry.getSid(), true)
.longValue());
stmt.setInt(4, entry.getPermission().getMask());
stmt.setBoolean(5, entry.isGranting());
stmt.setBoolean(6, entry.isAuditSuccess());
stmt.setBoolean(7, entry.isAuditFailure());
}
});
}
/**
* Creates an entry in the acl_object_identity table for the passed ObjectIdentity.
* The Sid is also necessary, as acl_object_identity has defined the sid column as
* non-null.
*
* @param object to represent an acl_object_identity for
* @param owner for the SID column (will be created if there is no acl_sid entry for
* this particular Sid already)
*/
protected void createObjectIdentity(ObjectIdentity object, Sid owner) {
Long sidId = createOrRetrieveSidPrimaryKey(owner, true);
Long classId = createOrRetrieveClassPrimaryKey(object.getType(), true);
jdbcTemplate.update(insertObjectIdentity, classId, object.getIdentifier(), sidId,
Boolean.TRUE);
}
/**
* Retrieves the primary key from {@code acl_class}, creating a new row if needed and
* the {@code allowCreate} property is {@code true}.
*
* @param type to find or create an entry for (often the fully-qualified class name)
* @param allowCreate true if creation is permitted if not found
*
* @return the primary key or null if not found
*/
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate) {
List<Long> classIds = jdbcTemplate.queryForList(selectClassPrimaryKey,
new Object[] { type }, Long.class);
if (!classIds.isEmpty()) {
return classIds.get(0);
}
if (allowCreate) {
jdbcTemplate.update(insertClass, type);
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
"Transaction must be running");
return jdbcTemplate.queryForObject(classIdentityQuery, Long.class);
}
return null;
}
/**
* Retrieves the primary key from acl_sid, creating a new row if needed and the
* allowCreate property is true.
*
* @param sid to find or create
* @param allowCreate true if creation is permitted if not found
*
* @return the primary key or null if not found
*
* @throws IllegalArgumentException if the <tt>Sid</tt> is not a recognized
* implementation.
*/
protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
Assert.notNull(sid, "Sid required");
String sidName;
boolean sidIsPrincipal = true;
if (sid instanceof PrincipalSid) {
sidName = ((PrincipalSid) sid).getPrincipal();
}
else if (sid instanceof GrantedAuthoritySid) {
sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();
sidIsPrincipal = false;
}
else {
throw new IllegalArgumentException("Unsupported implementation of Sid");
}
return createOrRetrieveSidPrimaryKey(sidName, sidIsPrincipal, allowCreate);
}
/**
* Retrieves the primary key from acl_sid, creating a new row if needed and the
* allowCreate property is true.
* @param sidName name of Sid to find or to create
* @param sidIsPrincipal whether it's a user or granted authority like role
* @param allowCreate true if creation is permitted if not found
* @return the primary key or null if not found
*/
protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal,
boolean allowCreate) {
List<Long> sidIds = jdbcTemplate.queryForList(selectSidPrimaryKey, new Object[] {
Boolean.valueOf(sidIsPrincipal), sidName }, Long.class);
if (!sidIds.isEmpty()) {
return sidIds.get(0);
}
if (allowCreate) {
jdbcTemplate.update(insertSid, Boolean.valueOf(sidIsPrincipal), sidName);
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
"Transaction must be running");
return jdbcTemplate.queryForObject(sidIdentityQuery, Long.class);
}
return null;
}
public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
throws ChildrenExistException {
Assert.notNull(objectIdentity, "Object Identity required");
Assert.notNull(objectIdentity.getIdentifier(),
"Object Identity doesn't provide an identifier");
if (deleteChildren) {
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
for (ObjectIdentity child : children) {
deleteAcl(child, true);
}
}
}
else {
if (!foreignKeysInDatabase) {
// We need to perform a manual verification for what a FK would normally
// do
// We generally don't do this, in the interests of deadlock management
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
throw new ChildrenExistException("Cannot delete '" + objectIdentity
+ "' (has " + children.size() + " children)");
}
}
}
Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(oidPrimaryKey);
// Delete this ACL's acl_object_identity row
deleteObjectIdentity(oidPrimaryKey);
// Clear the cache
aclCache.evictFromCache(objectIdentity);
}
/**
* Deletes all ACEs defined in the acl_entry table belonging to the presented
* ObjectIdentity primary key.
*
* @param oidPrimaryKey the rows in acl_entry to delete
*/
protected void deleteEntries(Long oidPrimaryKey) {
jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey, oidPrimaryKey);
}
/**
* Deletes a single row from acl_object_identity that is associated with the presented
* ObjectIdentity primary key.
* <p>
* We do not delete any entries from acl_class, even if no classes are using that
* class any longer. This is a deadlock avoidance approach.
*
* @param oidPrimaryKey to delete the acl_object_identity
*/
protected void deleteObjectIdentity(Long oidPrimaryKey) {
// Delete the acl_object_identity row
jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, oidPrimaryKey);
}
/**
* Retrieves the primary key from the acl_object_identity table for the passed
* ObjectIdentity. Unlike some other methods in this implementation, this method will
* NOT create a row (use {@link #createObjectIdentity(ObjectIdentity, Sid)} instead).
*
* @param oid to find
*
* @return the object identity or null if not found
*/
protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
try {
return jdbcTemplate.queryForObject(selectObjectIdentityPrimaryKey, Long.class,
oid.getType(), oid.getIdentifier());
}
catch (DataAccessException notFound) {
return null;
}
}
/**
* This implementation will simply delete all ACEs in the database and recreate them
* on each invocation of this method. A more comprehensive implementation might use
* dirty state checking, or more likely use ORM capabilities for create, update and
* delete operations of {@link MutableAcl}.
*/
public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException {
Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier");
// Delete this ACL's ACEs in the acl_entry table
deleteEntries(retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity()));
// Create this ACL's ACEs in the acl_entry table
createEntries(acl);
// Change the mutable columns in acl_object_identity
updateObjectIdentity(acl);
// Clear the cache, including children
clearCacheIncludingChildren(acl.getObjectIdentity());
// Retrieve the ACL via superclass (ensures cache registration, proper retrieval
// etc)
return (MutableAcl) super.readAclById(acl.getObjectIdentity());
}
private void clearCacheIncludingChildren(ObjectIdentity objectIdentity) {
Assert.notNull(objectIdentity, "ObjectIdentity required");
List<ObjectIdentity> children = findChildren(objectIdentity);
if (children != null) {
for (ObjectIdentity child : children) {
clearCacheIncludingChildren(child);
}
}
aclCache.evictFromCache(objectIdentity);
}
/**
* Updates an existing acl_object_identity row, with new information presented in the
* passed MutableAcl object. Also will create an acl_sid entry if needed for the Sid
* that owns the MutableAcl.
*
* @param acl to modify (a row must already exist in acl_object_identity)
*
* @throws NotFoundException if the ACL could not be found to update.
*/
protected void updateObjectIdentity(MutableAcl acl) {
Long parentId = null;
if (acl.getParentAcl() != null) {
Assert.isInstanceOf(ObjectIdentityImpl.class, acl.getParentAcl()
.getObjectIdentity(),
"Implementation only supports ObjectIdentityImpl");
ObjectIdentityImpl oii = (ObjectIdentityImpl) acl.getParentAcl()
.getObjectIdentity();
parentId = retrieveObjectIdentityPrimaryKey(oii);
}
Assert.notNull(acl.getOwner(), "Owner is required in this implementation");
Long ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true);
int count = jdbcTemplate.update(updateObjectIdentity, parentId, ownerSid,
Boolean.valueOf(acl.isEntriesInheriting()), acl.getId());
if (count != 1) {
throw new NotFoundException("Unable to locate ACL to update");
}
}
/**
* Sets the query that will be used to retrieve the identity of a newly created row in
* the <tt>acl_class</tt> table.
*
* @param classIdentityQuery the query, which should return the identifier. Defaults
* to <tt>call identity()</tt>
*/
public void setClassIdentityQuery(String classIdentityQuery) {
Assert.hasText(classIdentityQuery, "New classIdentityQuery query is required");
this.classIdentityQuery = classIdentityQuery;
}
/**
* Sets the query that will be used to retrieve the identity of a newly created row in
* the <tt>acl_sid</tt> table.
*
* @param sidIdentityQuery the query, which should return the identifier. Defaults to
* <tt>call identity()</tt>
*/
public void setSidIdentityQuery(String sidIdentityQuery) {
Assert.hasText(sidIdentityQuery, "New sidIdentityQuery query is required");
this.sidIdentityQuery = sidIdentityQuery;
}
public void setDeleteEntryByObjectIdentityForeignKeySql(
String deleteEntryByObjectIdentityForeignKey) {
this.deleteEntryByObjectIdentityForeignKey = deleteEntryByObjectIdentityForeignKey;
}
public void setDeleteObjectIdentityByPrimaryKeySql(
String deleteObjectIdentityByPrimaryKey) {
this.deleteObjectIdentityByPrimaryKey = deleteObjectIdentityByPrimaryKey;
}
public void setInsertClassSql(String insertClass) {
this.insertClass = insertClass;
}
public void setInsertEntrySql(String insertEntry) {
this.insertEntry = insertEntry;
}
public void setInsertObjectIdentitySql(String insertObjectIdentity) {
this.insertObjectIdentity = insertObjectIdentity;
}
public void setInsertSidSql(String insertSid) {
this.insertSid = insertSid;
}
public void setClassPrimaryKeyQuery(String selectClassPrimaryKey) {
this.selectClassPrimaryKey = selectClassPrimaryKey;
}
public void setObjectIdentityPrimaryKeyQuery(String selectObjectIdentityPrimaryKey) {
this.selectObjectIdentityPrimaryKey = selectObjectIdentityPrimaryKey;
}
public void setSidPrimaryKeyQuery(String selectSidPrimaryKey) {
this.selectSidPrimaryKey = selectSidPrimaryKey;
}
public void setUpdateObjectIdentity(String updateObjectIdentity) {
this.updateObjectIdentity = updateObjectIdentity;
}
/**
* @param foreignKeysInDatabase if false this class will perform additional FK
* constrain checking, which may cause deadlocks (the default is true, so deadlocks
* are avoided but the database is expected to enforce FKs)
*/
public void setForeignKeysInDatabase(boolean foreignKeysInDatabase) {
this.foreignKeysInDatabase = foreignKeysInDatabase;
}
}
@@ -1,49 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.jdbc;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Sid;
import java.util.List;
import java.util.Map;
/**
* Performs lookups for {@link org.springframework.security.acls.model.AclService}.
*
* @author Ben Alex
*/
public interface LookupStrategy {
// ~ Methods
// ========================================================================================================
/**
* Perform database-specific optimized lookup.
*
* @param objects the identities to lookup (required)
* @param sids the SIDs for which identities are required (may be <tt>null</tt> -
* implementations may elect not to provide SID optimisations)
*
* @return a <tt>Map</tt> where keys represent the {@link ObjectIdentity} of the
* located {@link Acl} and values are the located {@link Acl} (never <tt>null</tt>
* although some entries may be missing; this method should not throw
* {@link NotFoundException}, as a chain of {@link LookupStrategy}s may be used to
* automatically create entries if required)
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids);
}
@@ -1,20 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* JDBC-based persistence of ACL information
*/
package org.springframework.security.acls.jdbc;
@@ -1,55 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* Represents an individual permission assignment within an {@link Acl}.
*
* <p>
* Instances MUST be immutable, as they are returned by <code>Acl</code> and should not
* allow client modification.
* </p>
*
* @author Ben Alex
*
*/
public interface AccessControlEntry extends Serializable {
// ~ Methods
// ========================================================================================================
Acl getAcl();
/**
* Obtains an identifier that represents this ACE.
*
* @return the identifier, or <code>null</code> if unsaved
*/
Serializable getId();
Permission getPermission();
Sid getSid();
/**
* Indicates the permission is being granted to the relevant Sid. If false,
* indicates the permission is being revoked/blocked.
*
* @return true if being granted, false otherwise
*/
boolean isGranting();
}
@@ -1,202 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import java.io.Serializable;
import java.util.List;
/**
* Represents an access control list (ACL) for a domain object.
*
* <p>
* An <tt>Acl</tt> represents all ACL entries for a given domain object. In order to avoid
* needing references to the domain object itself, this interface handles indirection
* between a domain object and an ACL object identity via the
* {@link org.springframework.security.acls.model.ObjectIdentity} interface.
* </p>
*
* <p>
* Implementing classes may elect to return instances that represent
* {@link org.springframework.security.acls.model.Permission} information for either some
* OR all {@link org.springframework.security.acls.model.Sid} instances. Therefore, an
* instance may NOT necessarily contain ALL <tt>Sid</tt>s for a given domain object.
* </p>
*
* @author Ben Alex
*/
public interface Acl extends Serializable {
/**
* Returns all of the entries represented by the present <tt>Acl</tt>. Entries
* associated with the <tt>Acl</tt> parents are not returned.
*
* <p>
* This method is typically used for administrative purposes.
* </p>
*
* <p>
* The order that entries appear in the array is important for methods declared in the
* {@link MutableAcl} interface. Furthermore, some implementations MAY use ordering as
* part of advanced permission checking.
* </p>
*
* <p>
* Do <em>NOT</em> use this method for making authorization decisions. Instead use
* {@link #isGranted(List, List, boolean)}.
* </p>
*
* <p>
* This method must operate correctly even if the <tt>Acl</tt> only represents a
* subset of <tt>Sid</tt>s. The caller is responsible for correctly handling the
* result if only a subset of <tt>Sid</tt>s is represented.
* </p>
*
* @return the list of entries represented by the <tt>Acl</tt>, or <tt>null</tt> if
* there are no entries presently associated with this <tt>Acl</tt>.
*/
List<AccessControlEntry> getEntries();
/**
* Obtains the domain object this <tt>Acl</tt> provides entries for. This is immutable
* once an <tt>Acl</tt> is created.
*
* @return the object identity (never <tt>null</tt>)
*/
ObjectIdentity getObjectIdentity();
/**
* Determines the owner of the <tt>Acl</tt>. The meaning of ownership varies by
* implementation and is unspecified.
*
* @return the owner (may be <tt>null</tt> if the implementation does not use
* ownership concepts)
*/
Sid getOwner();
/**
* A domain object may have a parent for the purpose of ACL inheritance. If there is a
* parent, its ACL can be accessed via this method. In turn, the parent's parent
* (grandparent) can be accessed and so on.
*
* <p>
* This method solely represents the presence of a navigation hierarchy between the
* parent <tt>Acl</tt> and this <tt>Acl</tt>. For actual inheritance to take place,
* the {@link #isEntriesInheriting()} must also be <tt>true</tt>.
* </p>
*
* <p>
* This method must operate correctly even if the <tt>Acl</tt> only represents a
* subset of <tt>Sid</tt>s. The caller is responsible for correctly handling the
* result if only a subset of <tt>Sid</tt>s is represented.
* </p>
*
* @return the parent <tt>Acl</tt> (may be <tt>null</tt> if this <tt>Acl</tt> does not
* have a parent)
*/
Acl getParentAcl();
/**
* Indicates whether the ACL entries from the {@link #getParentAcl()} should flow down
* into the current <tt>Acl</tt>.
* <p>
* The mere link between an <tt>Acl</tt> and a parent <tt>Acl</tt> on its own is
* insufficient to cause ACL entries to inherit down. This is because a domain object
* may wish to have entirely independent entries, but maintain the link with the
* parent for navigation purposes. Thus, this method denotes whether or not the
* navigation relationship also extends to the actual inheritance of entries.
* </p>
*
* @return <tt>true</tt> if parent ACL entries inherit into the current <tt>Acl</tt>
*/
boolean isEntriesInheriting();
/**
* This is the actual authorization logic method, and must be used whenever ACL
* authorization decisions are required.
*
* <p>
* An array of <tt>Sid</tt>s are presented, representing security identifies of the
* current principal. In addition, an array of <tt>Permission</tt>s is presented which
* will have one or more bits set in order to indicate the permissions needed for an
* affirmative authorization decision. An array is presented because holding
* <em>any</em> of the <tt>Permission</tt>s inside the array will be sufficient for an
* affirmative authorization.
* </p>
*
* <p>
* The actual approach used to make authorization decisions is left to the
* implementation and is not specified by this interface. For example, an
* implementation <em>MAY</em> search the current ACL in the order the ACL entries
* have been stored. If a single entry is found that has the same active bits as are
* shown in a passed <tt>Permission</tt>, that entry's grant or deny state may
* determine the authorization decision. If the case of a deny state, the deny
* decision will only be relevant if all other <tt>Permission</tt>s passed in the
* array have also been unsuccessfully searched. If no entry is found that match the
* bits in the current ACL, provided that {@link #isEntriesInheriting()} is
* <tt>true</tt>, the authorization decision may be passed to the parent ACL. If there
* is no matching entry, the implementation MAY throw an exception, or make a
* predefined authorization decision.
* </p>
*
* <p>
* This method must operate correctly even if the <tt>Acl</tt> only represents a
* subset of <tt>Sid</tt>s, although the implementation is permitted to throw one of
* the signature-defined exceptions if the method is called requesting an
* authorization decision for a {@link Sid} that was never loaded in this <tt>Acl</tt>
* .
* </p>
*
* @param permission the permission or permissions required (at least one entry
* required)
* @param sids the security identities held by the principal (at least one entry
* required)
* @param administrativeMode if <tt>true</tt> denotes the query is for administrative
* purposes and no logging or auditing (if supported by the implementation) should be
* undertaken
*
* @return <tt>true</tt> if authorization is granted
*
* @throws NotFoundException MUST be thrown if an implementation cannot make an
* authoritative authorization decision, usually because there is no ACL information
* for this particular permission and/or SID
* @throws UnloadedSidException thrown if the <tt>Acl</tt> does not have details for
* one or more of the <tt>Sid</tt>s passed as arguments
*/
boolean isGranted(List<Permission> permission, List<Sid> sids,
boolean administrativeMode) throws NotFoundException, UnloadedSidException;
/**
* For efficiency reasons an <tt>Acl</tt> may be loaded and <em>not</em> contain
* entries for every <tt>Sid</tt> in the system. If an <tt>Acl</tt> has been loaded
* and does not represent every <tt>Sid</tt>, all methods of the <tt>Acl</tt> can only
* be used within the limited scope of the <tt>Sid</tt> instances it actually
* represents.
* <p>
* It is normal to load an <tt>Acl</tt> for only particular <tt>Sid</tt>s if read-only
* authorization decisions are being made. However, if user interface reporting or
* modification of <tt>Acl</tt>s are desired, an <tt>Acl</tt> should be loaded with
* all <tt>Sid</tt>s. This method denotes whether or not the specified <tt>Sid</tt>s
* have been loaded or not.
* </p>
*
* @param sids one or more security identities the caller is interest in knowing
* whether this <tt>Sid</tt> supports
*
* @return <tt>true</tt> if every passed <tt>Sid</tt> is represented by this
* <tt>Acl</tt> instance
*/
boolean isSidLoaded(List<Sid> sids);
}
@@ -1,43 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import org.springframework.security.acls.jdbc.JdbcAclService;
import java.io.Serializable;
/**
* A caching layer for {@link JdbcAclService}.
*
* @author Ben Alex
*
*/
public interface AclCache {
// ~ Methods
// ========================================================================================================
void evictFromCache(Serializable pk);
void evictFromCache(ObjectIdentity objectIdentity);
MutableAcl getFromCache(ObjectIdentity objectIdentity);
MutableAcl getFromCache(Serializable pk);
void putInCache(MutableAcl acl);
void clearCache();
}
@@ -1,46 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
/**
* Abstract base class for Acl data operations.
*
* @author Luke Taylor
* @since 3.0
*/
public abstract class AclDataAccessException extends RuntimeException {
/**
* Constructs an <code>AclDataAccessException</code> with the specified message and
* root cause.
*
* @param msg the detail message
* @param cause the root cause
*/
public AclDataAccessException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Constructs an <code>AclDataAccessException</code> with the specified message and no
* root cause.
*
* @param msg the detail message
*/
public AclDataAccessException(String msg) {
super(msg);
}
}
@@ -1,119 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import java.util.List;
import java.util.Map;
/**
* Provides retrieval of {@link Acl} instances.
*
* @author Ben Alex
*/
public interface AclService {
// ~ Methods
// ========================================================================================================
/**
* Locates all object identities that use the specified parent. This is useful for
* administration tools.
*
* @param parentIdentity to locate children of
*
* @return the children (or <tt>null</tt> if none were found)
*/
List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity);
/**
* Same as {@link #readAclsById(List)} except it returns only a single Acl.
* <p>
* This method should not be called as it does not leverage the underlying
* implementation's potential ability to filter <tt>Acl</tt> entries based on a
* {@link Sid} parameter.
* </p>
*
* @param object to locate an {@link Acl} for
*
* @return the {@link Acl} for the requested {@link ObjectIdentity} (never
* <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for the requested
* {@link ObjectIdentity}
*/
Acl readAclById(ObjectIdentity object) throws NotFoundException;
/**
* Same as {@link #readAclsById(List, List)} except it returns only a single Acl.
*
* @param object to locate an {@link Acl} for
* @param sids the security identities for which {@link Acl} information is required
* (may be <tt>null</tt> to denote all entries)
*
* @return the {@link Acl} for the requested {@link ObjectIdentity} (never
* <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for the requested
* {@link ObjectIdentity}
*/
Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s.
* <p>
* The returned map is keyed on the passed objects, with the values being the
* <tt>Acl</tt> instances. Any unknown objects will not have a map key.
* </p>
*
* @param objects the objects to find {@link Acl} information for
*
* @return a map with exactly one element for each {@link ObjectIdentity} passed as an
* argument (never <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for each requested
* {@link ObjectIdentity}
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects)
throws NotFoundException;
/**
* Obtains all the <tt>Acl</tt>s that apply for the passed <tt>Object</tt>s, but only
* for the security identifies passed.
* <p>
* Implementations <em>MAY</em> provide a subset of the ACLs via this method although
* this is NOT a requirement. This is intended to allow performance optimisations
* within implementations. Callers should therefore use this method in preference to
* the alternative overloaded version which does not have performance optimisation
* opportunities.
* </p>
* <p>
* The returned map is keyed on the passed objects, with the values being the
* <tt>Acl</tt> instances. Any unknown objects (or objects for which the interested
* <tt>Sid</tt>s do not have entries) will not have a map key.
* </p>
*
* @param objects the objects to find {@link Acl} information for
* @param sids the security identities for which {@link Acl} information is required
* (may be <tt>null</tt> to denote all entries)
*
* @return a map with exactly one element for each {@link ObjectIdentity} passed as an
* argument (never <tt>null</tt>)
*
* @throws NotFoundException if an {@link Acl} was not found for each requested
* {@link ObjectIdentity}
*/
Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids)
throws NotFoundException;
}
@@ -1,46 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
/**
* Thrown if an <code>Acl</code> entry already exists for the object.
*
* @author Ben Alex
*/
public class AlreadyExistsException extends AclDataAccessException {
// ~ Constructors
// ===================================================================================================
/**
* Constructs an <code>AlreadyExistsException</code> with the specified message.
*
* @param msg the detail message
*/
public AlreadyExistsException(String msg) {
super(msg);
}
/**
* Constructs an <code>AlreadyExistsException</code> with the specified message and
* root cause.
*
* @param msg the detail message
* @param t root cause
*/
public AlreadyExistsException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -1,31 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
/**
* Represents an ACE that provides auditing information.
*
* @author Ben Alex
*
*/
public interface AuditableAccessControlEntry extends AccessControlEntry {
// ~ Methods
// ========================================================================================================
boolean isAuditFailure();
boolean isAuditSuccess();
}
@@ -1,29 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
/**
* A mutable ACL that provides audit capabilities.
*
* @author Ben Alex
*
*/
public interface AuditableAcl extends MutableAcl {
// ~ Methods
// ========================================================================================================
void updateAuditing(int aceIndex, boolean auditSuccess, boolean auditFailure);
}
@@ -1,46 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
/**
* Thrown if an {@link Acl} cannot be deleted because children <code>Acl</code>s exist.
*
* @author Ben Alex
*/
public class ChildrenExistException extends AclDataAccessException {
// ~ Constructors
// ===================================================================================================
/**
* Constructs an <code>ChildrenExistException</code> with the specified message.
*
* @param msg the detail message
*/
public ChildrenExistException(String msg) {
super(msg);
}
/**
* Constructs an <code>ChildrenExistException</code> with the specified message and
* root cause.
*
* @param msg the detail message
* @param t root cause
*/
public ChildrenExistException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -1,66 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* A mutable <tt>Acl</tt>.
* <p>
* A mutable ACL must ensure that appropriate security checks are performed before
* allowing access to its methods.
*
* @author Ben Alex
*/
public interface MutableAcl extends Acl {
// ~ Methods
// ========================================================================================================
void deleteAce(int aceIndex) throws NotFoundException;
/**
* Obtains an identifier that represents this <tt>MutableAcl</tt>.
*
* @return the identifier, or <tt>null</tt> if unsaved
*/
Serializable getId();
void insertAce(int atIndexLocation, Permission permission, Sid sid, boolean granting)
throws NotFoundException;
/**
* Changes the present owner to a different owner.
*
* @param newOwner the new owner (mandatory; cannot be null)
*/
void setOwner(Sid newOwner);
/**
* Change the value returned by {@link Acl#isEntriesInheriting()}.
*
* @param entriesInheriting the new value
*/
void setEntriesInheriting(boolean entriesInheriting);
/**
* Changes the parent of this ACL.
*
* @param newParent the new parent
*/
void setParent(Acl newParent);
void updateAce(int aceIndex, Permission permission) throws NotFoundException;
}
@@ -1,61 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
/**
* Provides support for creating and storing <code>Acl</code> instances.
*
* @author Ben Alex
*/
public interface MutableAclService extends AclService {
// ~ Methods
// ========================================================================================================
/**
* Creates an empty <code>Acl</code> object in the database. It will have no entries.
* The returned object will then be used to add entries.
*
* @param objectIdentity the object identity to create
*
* @return an ACL object with its ID set
*
* @throws AlreadyExistsException if the passed object identity already has a record
*/
MutableAcl createAcl(ObjectIdentity objectIdentity) throws AlreadyExistsException;
/**
* Removes the specified entry from the database.
*
* @param objectIdentity the object identity to remove
* @param deleteChildren whether to cascade the delete to children
*
* @throws ChildrenExistException if the deleteChildren argument was
* <code>false</code> but children exist
*/
void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
throws ChildrenExistException;
/**
* Changes an existing <code>Acl</code> in the database.
*
* @param acl to modify
*
* @throws NotFoundException if the relevant record could not be found (did you
* remember to use {@link #createAcl(ObjectIdentity)} to create the object, rather
* than creating it with the <code>new</code> keyword?)
*/
MutableAcl updateAcl(MutableAcl acl) throws NotFoundException;
}
@@ -1,46 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
/**
* Thrown if an ACL-related object cannot be found.
*
* @author Ben Alex
*/
public class NotFoundException extends AclDataAccessException {
// ~ Constructors
// ===================================================================================================
/**
* Constructs an <code>NotFoundException</code> with the specified message.
*
* @param msg the detail message
*/
public NotFoundException(String msg) {
super(msg);
}
/**
* Constructs an <code>NotFoundException</code> with the specified message and root
* cause.
*
* @param msg the detail message
* @param t root cause
*/
public NotFoundException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -1,75 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* Represents the identity of an individual domain object instance.
*
* <p>
* As implementations of <tt>ObjectIdentity</tt> are used as the key to represent domain
* objects in the ACL subsystem, it is essential that implementations provide methods so
* that object-equality rather than reference-equality can be relied upon reliably. In
* other words, the ACL subsystem can consider two <tt>ObjectIdentity</tt>s equal if
* <tt>identity1.equals(identity2)</tt>, rather than reference-equality of
* <tt>identity1==identity2</tt>.
* </p>
*
* @author Ben Alex
*/
public interface ObjectIdentity extends Serializable {
// ~ Methods
// ========================================================================================================
/**
* @param obj to be compared
*
* @return <tt>true</tt> if the objects are equal, <tt>false</tt> otherwise
* @see Object#equals(Object)
*/
boolean equals(Object obj);
/**
* Obtains the actual identifier. This identifier must not be reused to represent
* other domain objects with the same <tt>javaType</tt>.
*
* <p>
* Because ACLs are largely immutable, it is strongly recommended to use a synthetic
* identifier (such as a database sequence number for the primary key). Do not use an
* identifier with business meaning, as that business meaning may change in the future
* such change will cascade to the ACL subsystem data.
* </p>
*
* @return the identifier (unique within this <tt>type</tt>; never <tt>null</tt>)
*/
Serializable getIdentifier();
/**
* Obtains the "type" metadata for the domain object. This will often be a Java type
* name (an interface or a class) &ndash; traditionally it is the name of the domain
* object implementation class.
*
* @return the "type" of the domain object (never <tt>null</tt>).
*/
String getType();
/**
* @return a hash code representation of the <tt>ObjectIdentity</tt>
* @see Object#hashCode()
*/
int hashCode();
}
@@ -1,41 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* Strategy which creates an {@link ObjectIdentity} from an object identifier (such as a
* primary key) and type information.
* <p>
* Differs from {@link ObjectIdentityRetrievalStrategy} in that it is used in situations
* when the actual object instance isn't available.
*
* @author Luke Taylor
* @since 3.0
*/
public interface ObjectIdentityGenerator {
/**
*
* @param id the identifier of the domain object, not null
* @param type the type of the object (often a class name), not null
* @return the identity constructed using the supplied identifier and type
* information.
*/
ObjectIdentity createObjectIdentity(Serializable id, String type);
}
@@ -1,31 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
/**
* Strategy interface that provides the ability to determine which {@link ObjectIdentity}
* will be returned for a particular domain object
*
* @author Ben Alex
*
*/
public interface ObjectIdentityRetrievalStrategy {
// ~ Methods
// ========================================================================================================
ObjectIdentity getObjectIdentity(Object domainObject);
}
@@ -1,32 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
/**
* A mutable ACL that provides ownership capabilities.
*
* <p>
* Generally the owner of an ACL is able to call any ACL mutator method, as well as assign
* a new owner.
*
* @author Ben Alex
*/
public interface OwnershipAcl extends MutableAcl {
// ~ Methods
// ========================================================================================================
void setOwner(Sid newOwner);
}
@@ -1,63 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* Represents a permission granted to a <tt>Sid</tt> for a given domain object.
*
* @author Ben Alex
*/
public interface Permission extends Serializable {
// ~ Static fields/initializers
// =====================================================================================
char RESERVED_ON = '~';
char RESERVED_OFF = '.';
String THIRTY_TWO_RESERVED_OFF = "................................";
// ~ Methods
// ========================================================================================================
/**
* Returns the bits that represents the permission.
*
* @return the bits that represent the permission
*/
int getMask();
/**
* Returns a 32-character long bit pattern <code>String</code> representing this
* permission.
* <p>
* Implementations are free to format the pattern as they see fit, although under no
* circumstances may {@link #RESERVED_OFF} or {@link #RESERVED_ON} be used within the
* pattern. An exemption is in the case of {@link #RESERVED_OFF} which is used to
* denote a bit that is off (clear). Implementations may also elect to use
* {@link #RESERVED_ON} internally for computation purposes, although this method may
* not return any <code>String</code> containing {@link #RESERVED_ON}.
* <p>
* The returned String must be 32 characters in length.
* <p>
* This method is only used for user interface and logging purposes. It is not used in
* any permission calculations. Therefore, duplication of characters within the output
* is permitted.
*
* @return a 32-character bit pattern
*/
String getPattern();
}
@@ -1,36 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import java.util.List;
/**
* Allow customization of the logic for determining whether a permission or permissions
* are granted to a particular sid or sids by an {@link Acl}.
*
* @author Luke Taylor
* @since 3.0.2
*/
public interface PermissionGrantingStrategy {
/**
* Returns true if the supplied strategy decides that the supplied {@code Acl}
* grants access based on the supplied list of permissions and sids.
*/
boolean isGranted(Acl acl, List<Permission> permission, List<Sid> sids,
boolean administrativeMode);
}
@@ -1,54 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import java.io.Serializable;
/**
* A security identity recognised by the ACL system.
*
* <p>
* This interface provides indirection between actual security objects (eg principals,
* roles, groups etc) and what is stored inside an <code>Acl</code>. This is because an
* <code>Acl</code> will not store an entire security object, but only an abstraction of
* it. This interface therefore provides a simple way to compare these abstracted security
* identities with other security identities and actual security objects.
* </p>
*
* @author Ben Alex
*/
public interface Sid extends Serializable {
// ~ Methods
// ========================================================================================================
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface
* contract.
*
* @param obj to be compared
*
* @return <code>true</code> if the objects are equal, <code>false</code> otherwise
*/
boolean equals(Object obj);
/**
* Refer to the <code>java.lang.Object</code> documentation for the interface
* contract.
*
* @return a hash code representation of this object
*/
int hashCode();
}
@@ -1,34 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
import java.util.List;
import org.springframework.security.core.Authentication;
/**
* Strategy interface that provides an ability to determine the {@link Sid} instances
* applicable for an {@link Authentication}.
*
* @author Ben Alex
*/
public interface SidRetrievalStrategy {
// ~ Methods
// ========================================================================================================
List<Sid> getSids(Authentication authentication);
}
@@ -1,48 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.model;
/**
* Thrown if an {@link Acl} cannot perform an operation because it only loaded a subset of
* <code>Sid</code>s and the caller has requested details for an unloaded <code>Sid</code>
* .
*
* @author Ben Alex
*/
public class UnloadedSidException extends AclDataAccessException {
// ~ Constructors
// ===================================================================================================
/**
* Constructs an <code>NotFoundException</code> with the specified message.
*
* @param msg the detail message
*/
public UnloadedSidException(String msg) {
super(msg);
}
/**
* Constructs an <code>NotFoundException</code> with the specified message and root
* cause.
*
* @param msg the detail message
* @param t root cause
*/
public UnloadedSidException(String msg, Throwable t) {
super(msg, t);
}
}
@@ -1,20 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Interfaces and shared classes to manage access control lists (ACLs) for domain object instances.
*/
package org.springframework.security.acls.model;
@@ -1,24 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* The Spring Security ACL package which implements instance-based security for domain objects.
* <p>
* Consider using the annotation based approach ({@code @PreAuthorize}, {@code @PostFilter} annotations) combined
* with a {@link org.springframework.security.acls.AclPermissionEvaluator} in preference to the older and more verbose
* attribute/voter/after-invocation approach from versions before Spring Security 3.0.
*/
package org.springframework.security.acls;
@@ -1,46 +0,0 @@
-- ACL schema sql used in HSQLDB
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
create table acl_sid(
id bigint generated by default as identity(start with 100) not null primary key,
principal boolean not null,
sid varchar_ignorecase(100) not null,
constraint unique_uk_1 unique(sid,principal)
);
create table acl_class(
id bigint generated by default as identity(start with 100) not null primary key,
class varchar_ignorecase(100) not null,
constraint unique_uk_2 unique(class)
);
create table acl_object_identity(
id bigint generated by default as identity(start with 100) not null primary key,
object_id_class bigint not null,
object_id_identity bigint not null,
parent_object bigint,
owner_sid bigint,
entries_inheriting boolean not null,
constraint unique_uk_3 unique(object_id_class,object_id_identity),
constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),
constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),
constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)
);
create table acl_entry(
id bigint generated by default as identity(start with 100) not null primary key,
acl_object_identity bigint not null,
ace_order int not null,
sid bigint not null,
mask integer not null,
granting boolean not null,
audit_success boolean not null,
audit_failure boolean not null,
constraint unique_uk_4 unique(acl_object_identity,ace_order),
constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),
constraint foreign_fk_5 foreign key(sid) references acl_sid(id)
);
@@ -1,46 +0,0 @@
-- ACL Schema SQL for MySQL 5.5+ / MariaDB equivalent
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
CREATE TABLE acl_sid (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
principal BOOLEAN NOT NULL,
sid VARCHAR(100) NOT NULL,
UNIQUE KEY unique_acl_sid (sid, principal)
) ENGINE=InnoDB;
CREATE TABLE acl_class (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
class VARCHAR(100) NOT NULL,
UNIQUE KEY uk_acl_class (class)
) ENGINE=InnoDB;
CREATE TABLE acl_object_identity (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
object_id_class BIGINT UNSIGNED NOT NULL,
object_id_identity BIGINT NOT NULL,
parent_object BIGINT UNSIGNED,
owner_sid BIGINT UNSIGNED,
entries_inheriting BOOLEAN NOT NULL,
UNIQUE KEY uk_acl_object_identity (object_id_class, object_id_identity),
CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),
CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)
) ENGINE=InnoDB;
CREATE TABLE acl_entry (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
acl_object_identity BIGINT UNSIGNED NOT NULL,
ace_order INTEGER NOT NULL,
sid BIGINT UNSIGNED NOT NULL,
mask INTEGER UNSIGNED NOT NULL,
granting BOOLEAN NOT NULL,
audit_success BOOLEAN NOT NULL,
audit_failure BOOLEAN NOT NULL,
UNIQUE KEY unique_acl_entry (acl_object_identity, ace_order),
CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)
) ENGINE=InnoDB;
@@ -1,82 +0,0 @@
-- ACL Schema SQL for Oracle Database 10g+
-- drop trigger acl_sid_id_trigger;
-- drop trigger acl_class_id_trigger;
-- drop trigger acl_object_identity_id_trigger;
-- drop trigger acl_entry_id_trigger;
-- drop sequence acl_sid_sequence;
-- drop sequence acl_class_sequence;
-- drop sequence acl_object_identity_sequence;
-- drop sequence acl_entry_sequence;
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
CREATE TABLE acl_sid (
id NUMBER(38) NOT NULL PRIMARY KEY,
principal NUMBER(1) NOT NULL CHECK (principal in (0, 1)),
sid NVARCHAR2(100) NOT NULL,
CONSTRAINT unique_acl_sid UNIQUE (sid, principal)
);
CREATE SEQUENCE acl_sid_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_sid_id_trigger
BEFORE INSERT ON acl_sid
FOR EACH ROW
BEGIN
SELECT acl_sid_sequence.nextval INTO :new.id FROM dual;
END;
CREATE TABLE acl_class (
id NUMBER(38) NOT NULL PRIMARY KEY,
class NVARCHAR2(100) NOT NULL,
CONSTRAINT uk_acl_class UNIQUE (class)
);
CREATE SEQUENCE acl_class_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_class_id_trigger
BEFORE INSERT ON acl_class
FOR EACH ROW
BEGIN
SELECT acl_class_sequence.nextval INTO :new.id FROM dual;
END;
CREATE TABLE acl_object_identity (
id NUMBER(38) NOT NULL PRIMARY KEY,
object_id_class NUMBER(38) NOT NULL,
object_id_identity NUMBER(38) NOT NULL,
parent_object NUMBER(38),
owner_sid NUMBER(38),
entries_inheriting NUMBER(1) NOT NULL CHECK (entries_inheriting in (0, 1)),
CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity),
CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),
CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)
);
CREATE SEQUENCE acl_object_identity_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_object_identity_id_trigger
BEFORE INSERT ON acl_object_identity
FOR EACH ROW
BEGIN
SELECT acl_object_identity_sequence.nextval INTO :new.id FROM dual;
END;
CREATE TABLE acl_entry (
id NUMBER(38) NOT NULL PRIMARY KEY,
acl_object_identity NUMBER(38) NOT NULL,
ace_order INTEGER NOT NULL,
sid NUMBER(38) NOT NULL,
mask INTEGER NOT NULL,
granting NUMBER(1) NOT NULL CHECK (granting in (0, 1)),
audit_success NUMBER(1) NOT NULL CHECK (audit_success in (0, 1)),
audit_failure NUMBER(1) NOT NULL CHECK (audit_failure in (0, 1)),
CONSTRAINT unique_acl_entry UNIQUE (acl_object_identity, ace_order),
CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)
);
CREATE SEQUENCE acl_entry_sequence START WITH 1 INCREMENT BY 1 NOMAXVALUE;
CREATE OR REPLACE TRIGGER acl_entry_id_trigger
BEFORE INSERT ON acl_entry
FOR EACH ROW
BEGIN
SELECT acl_entry_sequence.nextval INTO :new.id FROM dual;
END;
@@ -1,46 +0,0 @@
-- ACL Schema SQL for PostgreSQL
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
create table acl_sid(
id bigserial not null primary key,
principal boolean not null,
sid varchar(100) not null,
constraint unique_uk_1 unique(sid,principal)
);
create table acl_class(
id bigserial not null primary key,
class varchar(100) not null,
constraint unique_uk_2 unique(class)
);
create table acl_object_identity(
id bigserial primary key,
object_id_class bigint not null,
object_id_identity bigint not null,
parent_object bigint,
owner_sid bigint,
entries_inheriting boolean not null,
constraint unique_uk_3 unique(object_id_class,object_id_identity),
constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),
constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),
constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)
);
create table acl_entry(
id bigserial primary key,
acl_object_identity bigint not null,
ace_order int not null,
sid bigint not null,
mask integer not null,
granting boolean not null,
audit_success boolean not null,
audit_failure boolean not null,
constraint unique_uk_4 unique(acl_object_identity,ace_order),
constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),
constraint foreign_fk_5 foreign key(sid) references acl_sid(id)
);
@@ -1,46 +0,0 @@
-- ACL Schema SQL for Microsoft SQL Server 2008+
-- drop table acl_entry;
-- drop table acl_object_identity;
-- drop table acl_class;
-- drop table acl_sid;
CREATE TABLE acl_sid (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
principal BIT NOT NULL,
sid VARCHAR(100) NOT NULL,
CONSTRAINT unique_acl_sid UNIQUE (sid, principal)
);
CREATE TABLE acl_class (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
class VARCHAR(100) NOT NULL,
CONSTRAINT uk_acl_class UNIQUE (class)
);
CREATE TABLE acl_object_identity (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
object_id_class BIGINT NOT NULL,
object_id_identity BIGINT NOT NULL,
parent_object BIGINT,
owner_sid BIGINT,
entries_inheriting BIT NOT NULL,
CONSTRAINT uk_acl_object_identity UNIQUE (object_id_class, object_id_identity),
CONSTRAINT fk_acl_object_identity_parent FOREIGN KEY (parent_object) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_object_identity_class FOREIGN KEY (object_id_class) REFERENCES acl_class (id),
CONSTRAINT fk_acl_object_identity_owner FOREIGN KEY (owner_sid) REFERENCES acl_sid (id)
);
CREATE TABLE acl_entry (
id BIGINT NOT NULL IDENTITY PRIMARY KEY,
acl_object_identity BIGINT NOT NULL,
ace_order INTEGER NOT NULL,
sid BIGINT NOT NULL,
mask INTEGER NOT NULL,
granting BIT NOT NULL,
audit_success BIT NOT NULL,
audit_failure BIT NOT NULL,
CONSTRAINT unique_acl_entry UNIQUE (acl_object_identity, ace_order),
CONSTRAINT fk_acl_entry_object FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity (id),
CONSTRAINT fk_acl_entry_acl FOREIGN KEY (sid) REFERENCES acl_sid (id)
);
-39
View File
@@ -1,39 +0,0 @@
-- Not required. Just shows the sort of queries being sent to DB.
select acl_object_identity.object_id_identity,
acl_entry.ace_order,
acl_object_identity.id as acl_id,
acl_object_identity.parent_object,
acl_object_identity,
entries_inheriting,
acl_entry.id as ace_id,
acl_entry.mask,
acl_entry.granting,
acl_entry.audit_success,
acl_entry.audit_failure,
acl_sid.principal as ace_principal,
acl_sid.sid as ace_sid,
acli_sid.principal as acl_principal,
acli_sid.sid as acl_sid,
acl_class.class
from acl_object_identity,
acl_sid acli_sid,
acl_class
left join acl_entry on acl_object_identity.id = acl_entry.acl_object_identity
left join acl_sid on acl_entry.sid = acl_sid.id
where
acli_sid.id = acl_object_identity.owner_sid
and acl_class.id = acl_object_identity.object_id_class
and (
(acl_object_identity.object_id_identity = 1 and acl_class.class = 'sample.contact.contact')
or
(acl_object_identity.object_id_identity = 2000 and acl_class.class = 'sample.contact.contact')
) order by acl_object_identity.object_id_identity asc, acl_entry.ace_order asc
@@ -1,155 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import org.junit.Test;
import org.springframework.security.acls.domain.AclFormattingUtils;
import org.springframework.security.acls.model.Permission;
/**
* Tests for {@link AclFormattingUtils}.
*
* @author Andrei Stefan
*/
public class AclFormattingUtilsTests {
// ~ Methods
// ========================================================================================================
@Test
public final void testDemergePatternsParametersConstraints() throws Exception {
try {
AclFormattingUtils.demergePatterns(null, "SOME STRING");
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", "LONGER SOME STRING");
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
AclFormattingUtils.demergePatterns("SOME STRING", "SAME LENGTH");
}
catch (IllegalArgumentException notExpected) {
fail("It shouldn't have thrown IllegalArgumentException");
}
}
@Test
public final void testDemergePatterns() throws Exception {
String original = "...........................A...R";
String removeBits = "...............................R";
assertThat(AclFormattingUtils.demergePatterns(original, removeBits)).isEqualTo(
"...........................A....");
assertThat(AclFormattingUtils.demergePatterns("ABCDEF", "......")).isEqualTo(
"ABCDEF");
assertThat(AclFormattingUtils.demergePatterns("ABCDEF", "GHIJKL")).isEqualTo(
"......");
}
@Test
public final void testMergePatternsParametersConstraints() throws Exception {
try {
AclFormattingUtils.mergePatterns(null, "SOME STRING");
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", "LONGER SOME STRING");
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
AclFormattingUtils.mergePatterns("SOME STRING", "SAME LENGTH");
}
catch (IllegalArgumentException notExpected) {
}
}
@Test
public final void testMergePatterns() throws Exception {
String original = "...............................R";
String extraBits = "...........................A....";
assertThat(AclFormattingUtils.mergePatterns(original, extraBits)).isEqualTo(
"...........................A...R");
assertThat(AclFormattingUtils.mergePatterns("ABCDEF", "......")).isEqualTo(
"ABCDEF");
assertThat(AclFormattingUtils.mergePatterns("ABCDEF", "GHIJKL")).isEqualTo(
"GHIJKL");
}
@Test
public final void testBinaryPrints() throws Exception {
assertThat(AclFormattingUtils.printBinary(15)).isEqualTo(
"............................****");
try {
AclFormattingUtils.printBinary(15, Permission.RESERVED_ON);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException notExpected) {
}
try {
AclFormattingUtils.printBinary(15, Permission.RESERVED_OFF);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException notExpected) {
}
assertThat(AclFormattingUtils.printBinary(15, 'x')).isEqualTo(
"............................xxxx");
}
@Test
public void testPrintBinaryNegative() {
assertThat(AclFormattingUtils.printBinary(0x80000000)).isEqualTo(
"*...............................");
}
@Test
public void testPrintBinaryMinusOne() {
assertThat(AclFormattingUtils.printBinary(0xffffffff)).isEqualTo(
"********************************");
}
}
@@ -1,72 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @author Luke Taylor
*/
@SuppressWarnings({ "unchecked" })
public class AclPermissionCacheOptimizerTests {
@Test
public void eagerlyLoadsRequiredAcls() throws Exception {
AclService service = mock(AclService.class);
AclPermissionCacheOptimizer pco = new AclPermissionCacheOptimizer(service);
ObjectIdentityRetrievalStrategy oidStrat = mock(ObjectIdentityRetrievalStrategy.class);
SidRetrievalStrategy sidStrat = mock(SidRetrievalStrategy.class);
pco.setObjectIdentityRetrievalStrategy(oidStrat);
pco.setSidRetrievalStrategy(sidStrat);
Object[] dos = { new Object(), null, new Object() };
ObjectIdentity[] oids = { new ObjectIdentityImpl("A", "1"),
new ObjectIdentityImpl("A", "2") };
when(oidStrat.getObjectIdentity(dos[0])).thenReturn(oids[0]);
when(oidStrat.getObjectIdentity(dos[2])).thenReturn(oids[1]);
pco.cachePermissionsFor(mock(Authentication.class), Arrays.asList(dos));
// AclService should be invoked with the list of required Oids
verify(service).readAclsById(eq(Arrays.asList(oids)), any(List.class));
}
@Test
public void ignoresEmptyCollection() {
AclService service = mock(AclService.class);
AclPermissionCacheOptimizer pco = new AclPermissionCacheOptimizer(service);
ObjectIdentityRetrievalStrategy oids = mock(ObjectIdentityRetrievalStrategy.class);
SidRetrievalStrategy sids = mock(SidRetrievalStrategy.class);
pco.setObjectIdentityRetrievalStrategy(oids);
pco.setSidRetrievalStrategy(sids);
pco.cachePermissionsFor(mock(Authentication.class), Collections.emptyList());
verifyZeroInteractions(service, sids, oids);
}
}
@@ -1,79 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.Locale;
import org.junit.Test;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.SidRetrievalStrategy;
import org.springframework.security.core.Authentication;
/**
*
* @author Luke Taylor
* @since 3.0
*/
public class AclPermissionEvaluatorTests {
@Test
public void hasPermissionReturnsTrueIfAclGrantsPermission() throws Exception {
AclService service = mock(AclService.class);
AclPermissionEvaluator pe = new AclPermissionEvaluator(service);
ObjectIdentity oid = mock(ObjectIdentity.class);
ObjectIdentityRetrievalStrategy oidStrategy = mock(ObjectIdentityRetrievalStrategy.class);
when(oidStrategy.getObjectIdentity(anyObject())).thenReturn(oid);
pe.setObjectIdentityRetrievalStrategy(oidStrategy);
pe.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Acl acl = mock(Acl.class);
when(service.readAclById(any(ObjectIdentity.class), anyListOf(Sid.class))).thenReturn(acl);
when(acl.isGranted(anyListOf(Permission.class), anyListOf(Sid.class), eq(false))).thenReturn(true);
assertThat(pe.hasPermission(mock(Authentication.class), new Object(), "READ")).isTrue();
}
@Test
public void resolvePermissionNonEnglishLocale() {
Locale systemLocale = Locale.getDefault();
Locale.setDefault(new Locale("tr"));
AclService service = mock(AclService.class);
AclPermissionEvaluator pe = new AclPermissionEvaluator(service);
ObjectIdentity oid = mock(ObjectIdentity.class);
ObjectIdentityRetrievalStrategy oidStrategy = mock(ObjectIdentityRetrievalStrategy.class);
when(oidStrategy.getObjectIdentity(anyObject())).thenReturn(oid);
pe.setObjectIdentityRetrievalStrategy(oidStrategy);
pe.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Acl acl = mock(Acl.class);
when(service.readAclById(any(ObjectIdentity.class), anyListOf(Sid.class))).thenReturn(acl);
when(acl.isGranted(anyListOf(Permission.class), anyListOf(Sid.class), eq(false))).thenReturn(true);
assertThat(pe.hasPermission(mock(Authentication.class), new Object(), "write")).isTrue();
Locale.setDefault(systemLocale);
}
}
@@ -1,25 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls;
/**
* Dummy domain object class
*
* @author Luke Taylor
*/
public final class TargetObject {
}
@@ -1,89 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.acls.model.*;
import org.springframework.security.core.Authentication;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @author Luke Taylor
*/
@SuppressWarnings({ "unchecked" })
public class AclEntryAfterInvocationCollectionFilteringProviderTests {
@Test
public void objectsAreRemovedIfPermissionDenied() throws Exception {
AclService service = mock(AclService.class);
Acl acl = mock(Acl.class);
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(
false);
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(
acl);
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(
service, Arrays.asList(mock(Permission.class)));
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
provider.setProcessDomainObjectClass(Object.class);
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Object returned = provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), new ArrayList(
Arrays.asList(new Object(), new Object())));
assertThat(returned).isInstanceOf(List.class);
assertThat(((List) returned)).isEmpty();
returned = provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("UNSUPPORTED", "AFTER_ACL_COLLECTION_READ"),
new Object[] { new Object(), new Object() });
assertThat(returned instanceof Object[]).isTrue();
assertThat(((Object[]) returned).length == 0).isTrue();
}
@Test
public void accessIsGrantedIfNoAttributesDefined() throws Exception {
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(
mock(AclService.class), Arrays.asList(mock(Permission.class)));
Object returned = new Object();
assertThat(returned)
.isSameAs(
provider.decide(mock(Authentication.class), new Object(),
Collections.<ConfigAttribute> emptyList(), returned));
}
@Test
public void nullReturnObjectIsIgnored() throws Exception {
AclService service = mock(AclService.class);
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(
service, Arrays.asList(mock(Permission.class)));
assertThat(provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null))
.isNull();;
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
}
}
@@ -1,144 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.afterinvocation;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.acls.model.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.SpringSecurityMessageSource;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @author Luke Taylor
*/
@SuppressWarnings({ "unchecked" })
public class AclEntryAfterInvocationProviderTests {
@Test(expected = IllegalArgumentException.class)
public void rejectsMissingPermissions() throws Exception {
try {
new AclEntryAfterInvocationProvider(mock(AclService.class), null);
fail("Exception expected");
}
catch (IllegalArgumentException expected) {
}
new AclEntryAfterInvocationProvider(mock(AclService.class),
Collections.<Permission> emptyList());
}
@Test
public void accessIsAllowedIfPermissionIsGranted() {
AclService service = mock(AclService.class);
Acl acl = mock(Acl.class);
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(
true);
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(
acl);
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(
service, Arrays.asList(mock(Permission.class)));
provider.setMessageSource(new SpringSecurityMessageSource());
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
provider.setProcessDomainObjectClass(Object.class);
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
Object returned = new Object();
assertThat(
returned)
.isSameAs(
provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("AFTER_ACL_READ"), returned));
}
@Test
public void accessIsGrantedIfNoAttributesDefined() throws Exception {
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(
mock(AclService.class), Arrays.asList(mock(Permission.class)));
Object returned = new Object();
assertThat(
returned)
.isSameAs(
provider.decide(mock(Authentication.class), new Object(),
Collections.<ConfigAttribute> emptyList(), returned));
}
@Test
public void accessIsGrantedIfObjectTypeNotSupported() throws Exception {
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(
mock(AclService.class), Arrays.asList(mock(Permission.class)));
provider.setProcessDomainObjectClass(String.class);
// Not a String
Object returned = new Object();
assertThat(
returned)
.isSameAs(
provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("AFTER_ACL_READ"), returned));
}
@Test(expected = AccessDeniedException.class)
public void accessIsDeniedIfPermissionIsNotGranted() {
AclService service = mock(AclService.class);
Acl acl = mock(Acl.class);
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenReturn(
false);
// Try a second time with no permissions found
when(acl.isGranted(any(List.class), any(List.class), anyBoolean())).thenThrow(
new NotFoundException(""));
when(service.readAclById(any(ObjectIdentity.class), any(List.class))).thenReturn(
acl);
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(
service, Arrays.asList(mock(Permission.class)));
provider.setProcessConfigAttribute("MY_ATTRIBUTE");
provider.setMessageSource(new SpringSecurityMessageSource());
provider.setObjectIdentityRetrievalStrategy(mock(ObjectIdentityRetrievalStrategy.class));
provider.setProcessDomainObjectClass(Object.class);
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
try {
provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"),
new Object());
fail("Expected Exception");
}
catch (AccessDeniedException expected) {
}
// Second scenario with no acls found
provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object());
}
@Test
public void nullReturnObjectIsIgnored() throws Exception {
AclService service = mock(AclService.class);
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(
service, Arrays.asList(mock(Permission.class)));
assertThat(provider.decide(mock(Authentication.class), new Object(),
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null))
.isNull();;
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
}
}
@@ -1,117 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Sid;
/**
* Tests for {@link AccessControlEntryImpl}.
*
* @author Andrei Stefan
*/
public class AccessControlImplEntryTests {
// ~ Methods
// ========================================================================================================
@Test
public void testConstructorRequiredFields() {
// Check Acl field is present
try {
new AccessControlEntryImpl(null, null, new PrincipalSid("johndoe"),
BasePermission.ADMINISTRATION, true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check Sid field is present
try {
new AccessControlEntryImpl(null, mock(Acl.class), null,
BasePermission.ADMINISTRATION, true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check Permission field is present
try {
new AccessControlEntryImpl(null, mock(Acl.class),
new PrincipalSid("johndoe"), null, true, true, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void testAccessControlEntryImplGetters() {
Acl mockAcl = mock(Acl.class);
Sid sid = new PrincipalSid("johndoe");
// Create a sample entry
AccessControlEntry ace = new AccessControlEntryImpl(Long.valueOf(1), mockAcl,
sid, BasePermission.ADMINISTRATION, true, true, true);
// and check every get() method
assertThat(ace.getId()).isEqualTo(new Long(1));
assertThat(ace.getAcl()).isEqualTo(mockAcl);
assertThat(ace.getSid()).isEqualTo(sid);
assertThat(ace.isGranting()).isTrue();
assertThat(ace.getPermission()).isEqualTo(BasePermission.ADMINISTRATION);
assertThat(((AuditableAccessControlEntry) ace).isAuditFailure()).isTrue();
assertThat(((AuditableAccessControlEntry) ace).isAuditSuccess()).isTrue();
}
@Test
public void testEquals() {
final Acl mockAcl = mock(Acl.class);
final ObjectIdentity oid = mock(ObjectIdentity.class);
when(mockAcl.getObjectIdentity()).thenReturn(oid);
Sid sid = new PrincipalSid("johndoe");
AccessControlEntry ace = new AccessControlEntryImpl(Long.valueOf(1), mockAcl,
sid, BasePermission.ADMINISTRATION, true, true, true);
assertThat(ace).isNotNull();
assertThat(ace).isNotEqualTo(Long.valueOf(100));
assertThat(ace).isEqualTo(ace);
assertThat(ace).isEqualTo(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true));
assertThat(ace).isNotEqualTo(new AccessControlEntryImpl(Long.valueOf(2), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, true));
assertThat(ace).isNotEqualTo(new AccessControlEntryImpl(Long.valueOf(1), mockAcl,
new PrincipalSid("scott"), BasePermission.ADMINISTRATION, true, true,
true));
assertThat(ace).isNotEqualTo(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.WRITE, true, true, true));
assertThat(ace).isNotEqualTo(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, false, true, true));
assertThat(ace).isNotEqualTo(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, false, true));
assertThat(ace).isNotEqualTo(new AccessControlEntryImpl(Long.valueOf(1), mockAcl, sid,
BasePermission.ADMINISTRATION, true, true, false));
}
}
@@ -1,72 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import java.util.Arrays;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
/**
*
* @author Rob Winch
*
*/
@RunWith(MockitoJUnitRunner.class)
public class AclAuthorizationStrategyImplTests {
@Mock
Acl acl;
GrantedAuthority authority;
AclAuthorizationStrategyImpl strategy;
@Before
public void setup() {
authority = new SimpleGrantedAuthority("ROLE_AUTH");
TestingAuthenticationToken authentication = new TestingAuthenticationToken("foo", "bar", Arrays.asList(authority));
authentication.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
@After
public void cleanup() {
SecurityContextHolder.clearContext();
}
// gh-4085
@Test
public void securityCheckWhenCustomAuthorityThenNameIsUsed() {
strategy = new AclAuthorizationStrategyImpl(new CustomAuthority());
strategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL);
}
@SuppressWarnings("serial")
class CustomAuthority implements GrantedAuthority {
@Override
public String getAuthority() {
return authority.getAuthority();
}
}
}
@@ -1,634 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.*;
import org.springframework.security.acls.model.*;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.util.FieldUtils;
import java.lang.reflect.Field;
import java.util.*;
/**
* Tests for {@link AclImpl}.
*
* @author Andrei Stefan
*/
public class AclImplTests {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
private static final List<Permission> READ = Arrays.asList(BasePermission.READ);
private static final List<Permission> WRITE = Arrays.asList(BasePermission.WRITE);
private static final List<Permission> CREATE = Arrays.asList(BasePermission.CREATE);
private static final List<Permission> DELETE = Arrays.asList(BasePermission.DELETE);
private static final List<Sid> SCOTT = Arrays.asList((Sid) new PrincipalSid("scott"));
private static final List<Sid> BEN = Arrays.asList((Sid) new PrincipalSid("ben"));
Authentication auth = new TestingAuthenticationToken("joe", "ignored",
"ROLE_ADMINISTRATOR");
AclAuthorizationStrategy authzStrategy;
PermissionGrantingStrategy pgs;
AuditLogger mockAuditLogger;
ObjectIdentity objectIdentity = new ObjectIdentityImpl(TARGET_CLASS, 100);
// ~ Methods
// ========================================================================================================
@Before
public void setUp() throws Exception {
SecurityContextHolder.getContext().setAuthentication(auth);
authzStrategy = mock(AclAuthorizationStrategy.class);
mockAuditLogger = mock(AuditLogger.class);
pgs = new DefaultPermissionGrantingStrategy(mockAuditLogger);
auth.setAuthenticated(true);
}
@After
public void tearDown() throws Exception {
SecurityContextHolder.clearContext();
}
@Test(expected = IllegalArgumentException.class)
public void constructorsRejectNullObjectIdentity() throws Exception {
try {
new AclImpl(null, 1, authzStrategy, pgs, null, null, true, new PrincipalSid(
"joe"));
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
new AclImpl(null, 1, authzStrategy, mockAuditLogger);
}
@Test(expected = IllegalArgumentException.class)
public void constructorsRejectNullId() throws Exception {
try {
new AclImpl(objectIdentity, null, authzStrategy, pgs, null, null, true,
new PrincipalSid("joe"));
fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
new AclImpl(objectIdentity, null, authzStrategy, mockAuditLogger);
}
@SuppressWarnings("deprecation")
@Test(expected = IllegalArgumentException.class)
public void constructorsRejectNullAclAuthzStrategy() throws Exception {
try {
new AclImpl(objectIdentity, 1, null, new DefaultPermissionGrantingStrategy(
mockAuditLogger), null, null, true, new PrincipalSid("joe"));
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
new AclImpl(objectIdentity, 1, null, mockAuditLogger);
}
@Test
public void insertAceRejectsNullParameters() throws Exception {
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
true, new PrincipalSid("joe"));
try {
acl.insertAce(0, null, new GrantedAuthoritySid("ROLE_IGNORED"), true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
acl.insertAce(0, BasePermission.READ, null, true);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void insertAceAddsElementAtCorrectIndex() throws Exception {
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
true, new PrincipalSid("joe"));
MockAclService service = new MockAclService();
// Insert one permission
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST1"), true);
service.updateAcl(acl);
// Check it was successfully added
assertThat(acl.getEntries()).hasSize(1);
assertThat(acl).isEqualTo(acl.getEntries().get(0).getAcl());
assertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(0).getPermission());
assertThat(acl.getEntries().get(0).getSid()).isEqualTo(new GrantedAuthoritySid(
"ROLE_TEST1"));
// Add a second permission
acl.insertAce(1, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"), true);
service.updateAcl(acl);
// Check it was added on the last position
assertThat(acl.getEntries()).hasSize(2);
assertThat(acl).isEqualTo(acl.getEntries().get(1).getAcl());
assertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(1).getPermission());
assertThat(acl.getEntries().get(1).getSid()).isEqualTo(new GrantedAuthoritySid(
"ROLE_TEST2"));
// Add a third permission, after the first one
acl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_TEST3"),
false);
service.updateAcl(acl);
assertThat(acl.getEntries()).hasSize(3);
// Check the third entry was added between the two existent ones
assertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(0).getPermission());
assertThat(acl.getEntries().get(0).getSid()).isEqualTo(new GrantedAuthoritySid(
"ROLE_TEST1"));
assertThat(BasePermission.WRITE).isEqualTo(acl.getEntries().get(1).getPermission());
assertThat(acl.getEntries().get(1).getSid()).isEqualTo( new GrantedAuthoritySid(
"ROLE_TEST3"));
assertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(2).getPermission());
assertThat(acl.getEntries().get(2).getSid()).isEqualTo(new GrantedAuthoritySid(
"ROLE_TEST2"));
}
@Test(expected = NotFoundException.class)
public void insertAceFailsForNonExistentElement() throws Exception {
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
true, new PrincipalSid("joe"));
MockAclService service = new MockAclService();
// Insert one permission
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST1"), true);
service.updateAcl(acl);
acl.insertAce(55, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"),
true);
}
@Test
public void deleteAceKeepsInitialOrdering() throws Exception {
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
true, new PrincipalSid("joe"));
MockAclService service = new MockAclService();
// Add several permissions
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST1"), true);
acl.insertAce(1, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"), true);
acl.insertAce(2, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST3"), true);
service.updateAcl(acl);
// Delete first permission and check the order of the remaining permissions is
// kept
acl.deleteAce(0);
assertThat(acl.getEntries()).hasSize(2);
assertThat(acl.getEntries().get(0).getSid()).isEqualTo(new GrantedAuthoritySid(
"ROLE_TEST2"));
assertThat(acl.getEntries().get(1).getSid()).isEqualTo(new GrantedAuthoritySid(
"ROLE_TEST3"));
// Add one more permission and remove the permission in the middle
acl.insertAce(2, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST4"), true);
service.updateAcl(acl);
acl.deleteAce(1);
assertThat(acl.getEntries()).hasSize(2);
assertThat(acl.getEntries().get(0).getSid()).isEqualTo(new GrantedAuthoritySid(
"ROLE_TEST2"));
assertThat(acl.getEntries().get(1).getSid()).isEqualTo(new GrantedAuthoritySid(
"ROLE_TEST4"));
// Remove remaining permissions
acl.deleteAce(1);
acl.deleteAce(0);
assertThat(acl.getEntries()).isEmpty();
}
@Test
public void deleteAceFailsForNonExistentElement() throws Exception {
AclAuthorizationStrategyImpl strategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
MutableAcl acl = new AclImpl(objectIdentity, (1), strategy, pgs, null, null,
true, new PrincipalSid("joe"));
try {
acl.deleteAce(99);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
}
@Test
public void isGrantingRejectsEmptyParameters() throws Exception {
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
true, new PrincipalSid("joe"));
Sid ben = new PrincipalSid("ben");
try {
acl.isGranted(new ArrayList<Permission>(0), Arrays.asList(ben), false);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
try {
acl.isGranted(READ, new ArrayList<Sid>(0), false);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void isGrantingGrantsAccessForAclWithNoParent() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
"ROLE_GENERAL", "ROLE_GUEST");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity rootOid = new ObjectIdentityImpl(TARGET_CLASS, 100);
// Create an ACL which owner is not the authenticated principal
MutableAcl rootAcl = new AclImpl(rootOid, 1, authzStrategy, pgs, null, null,
false, new PrincipalSid("joe"));
// Grant some permissions
rootAcl.insertAce(0, BasePermission.READ, new PrincipalSid("ben"), false);
rootAcl.insertAce(1, BasePermission.WRITE, new PrincipalSid("scott"), true);
rootAcl.insertAce(2, BasePermission.WRITE, new PrincipalSid("rod"), false);
rootAcl.insertAce(3, BasePermission.WRITE, new GrantedAuthoritySid(
"WRITE_ACCESS_ROLE"), true);
// Check permissions granting
List<Permission> permissions = Arrays.asList(BasePermission.READ,
BasePermission.CREATE);
List<Sid> sids = Arrays.asList(new PrincipalSid("ben"), new GrantedAuthoritySid(
"ROLE_GUEST"));
assertThat(rootAcl.isGranted(permissions, sids, false)).isFalse();
try {
rootAcl.isGranted(permissions, SCOTT, false);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
assertThat(rootAcl.isGranted(WRITE, SCOTT, false)).isTrue();
assertThat(rootAcl.isGranted(WRITE, Arrays.asList(new PrincipalSid("rod"),
new GrantedAuthoritySid("WRITE_ACCESS_ROLE")), false)).isFalse();
assertThat(rootAcl.isGranted(WRITE, Arrays.asList(new GrantedAuthoritySid(
"WRITE_ACCESS_ROLE"), new PrincipalSid("rod")), false)).isTrue();
try {
// Change the type of the Sid and check the granting process
rootAcl.isGranted(WRITE, Arrays.asList(new GrantedAuthoritySid("rod"),
new PrincipalSid("WRITE_ACCESS_ROLE")), false);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
}
@Test
public void isGrantingGrantsAccessForInheritableAcls() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
"ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity grandParentOid = new ObjectIdentityImpl(TARGET_CLASS, 100);
ObjectIdentity parentOid1 = new ObjectIdentityImpl(TARGET_CLASS, 101);
ObjectIdentity parentOid2 = new ObjectIdentityImpl(TARGET_CLASS, 102);
ObjectIdentity childOid1 = new ObjectIdentityImpl(TARGET_CLASS, 103);
ObjectIdentity childOid2 = new ObjectIdentityImpl(TARGET_CLASS, 104);
// Create ACLs
PrincipalSid joe = new PrincipalSid("joe");
MutableAcl grandParentAcl = new AclImpl(grandParentOid, 1, authzStrategy, pgs,
null, null, false, joe);
MutableAcl parentAcl1 = new AclImpl(parentOid1, 2, authzStrategy, pgs, null,
null, true, joe);
MutableAcl parentAcl2 = new AclImpl(parentOid2, 3, authzStrategy, pgs, null,
null, true, joe);
MutableAcl childAcl1 = new AclImpl(childOid1, 4, authzStrategy, pgs, null, null,
true, joe);
MutableAcl childAcl2 = new AclImpl(childOid2, 4, authzStrategy, pgs, null, null,
false, joe);
// Create hierarchies
childAcl2.setParent(childAcl1);
childAcl1.setParent(parentAcl1);
parentAcl2.setParent(grandParentAcl);
parentAcl1.setParent(grandParentAcl);
// Add some permissions
grandParentAcl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid(
"ROLE_USER_READ"), true);
grandParentAcl.insertAce(1, BasePermission.WRITE, new PrincipalSid("ben"), true);
grandParentAcl
.insertAce(2, BasePermission.DELETE, new PrincipalSid("ben"), false);
grandParentAcl.insertAce(3, BasePermission.DELETE, new PrincipalSid("scott"),
true);
parentAcl1.insertAce(0, BasePermission.READ, new PrincipalSid("scott"), true);
parentAcl1.insertAce(1, BasePermission.DELETE, new PrincipalSid("scott"), false);
parentAcl2.insertAce(0, BasePermission.CREATE, new PrincipalSid("ben"), true);
childAcl1.insertAce(0, BasePermission.CREATE, new PrincipalSid("scott"), true);
// Check granting process for parent1
assertThat(parentAcl1.isGranted(READ, SCOTT, false)).isTrue();
assertThat(parentAcl1.isGranted(READ,
Arrays.asList((Sid) new GrantedAuthoritySid("ROLE_USER_READ")), false))
.isTrue();
assertThat(parentAcl1.isGranted(WRITE, BEN, false)).isTrue();
assertThat(parentAcl1.isGranted(DELETE, BEN, false)).isFalse();
assertThat(parentAcl1.isGranted(DELETE, SCOTT, false)).isFalse();
// Check granting process for parent2
assertThat(parentAcl2.isGranted(CREATE, BEN, false)).isTrue();
assertThat(parentAcl2.isGranted(WRITE, BEN, false)).isTrue();
assertThat(parentAcl2.isGranted(DELETE, BEN, false)).isFalse();
// Check granting process for child1
assertThat(childAcl1.isGranted(CREATE, SCOTT, false)).isTrue();
assertThat(childAcl1.isGranted(READ,
Arrays.asList((Sid) new GrantedAuthoritySid("ROLE_USER_READ")), false))
.isTrue();
assertThat(childAcl1.isGranted(DELETE, BEN, false)).isFalse();
// Check granting process for child2 (doesn't inherit the permissions from its
// parent)
try {
assertThat(childAcl2.isGranted(CREATE, SCOTT, false)).isTrue();
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
try {
childAcl2.isGranted(CREATE,
Arrays.asList((Sid) new PrincipalSid("joe")), false);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
}
@Test
public void updatedAceValuesAreCorrectlyReflectedInAcl() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
"ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
false, new PrincipalSid("joe"));
MockAclService service = new MockAclService();
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"),
true);
acl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_USER_READ"),
true);
acl.insertAce(2, BasePermission.CREATE, new PrincipalSid("ben"), true);
service.updateAcl(acl);
assertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(0).getPermission());
assertThat(BasePermission.WRITE).isEqualTo(acl.getEntries().get(1).getPermission());
assertThat(BasePermission.CREATE).isEqualTo(acl.getEntries().get(2).getPermission());
// Change each permission
acl.updateAce(0, BasePermission.CREATE);
acl.updateAce(1, BasePermission.DELETE);
acl.updateAce(2, BasePermission.READ);
// Check the change was successfully made
assertThat(BasePermission.CREATE).isEqualTo(acl.getEntries().get(0).getPermission());
assertThat(BasePermission.DELETE).isEqualTo(acl.getEntries().get(1).getPermission());
assertThat(BasePermission.READ).isEqualTo(acl.getEntries().get(2).getPermission());
}
@Test
public void auditableEntryFlagsAreUpdatedCorrectly() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
"ROLE_AUDITING", "ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
false, new PrincipalSid("joe"));
MockAclService service = new MockAclService();
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"),
true);
acl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_USER_READ"),
true);
service.updateAcl(acl);
assertThat(((AuditableAccessControlEntry) acl.getEntries().get(0))
.isAuditFailure())
.isFalse();
assertThat(((AuditableAccessControlEntry) acl.getEntries().get(1))
.isAuditFailure())
.isFalse();
assertThat(((AuditableAccessControlEntry) acl.getEntries().get(0))
.isAuditSuccess())
.isFalse();
assertThat(((AuditableAccessControlEntry) acl.getEntries().get(1))
.isAuditSuccess())
.isFalse();
// Change each permission
((AuditableAcl) acl).updateAuditing(0, true, true);
((AuditableAcl) acl).updateAuditing(1, true, true);
// Check the change was successfuly made
assertThat(acl.getEntries()).extracting("auditSuccess").containsOnly(true, true);
assertThat(acl.getEntries()).extracting("auditFailure").containsOnly(true, true);
}
@Test
public void gettersAndSettersAreConsistent() throws Exception {
Authentication auth = new TestingAuthenticationToken("ben", "ignored",
"ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, (100));
ObjectIdentity identity2 = new ObjectIdentityImpl(TARGET_CLASS, (101));
MutableAcl acl = new AclImpl(identity, 1, authzStrategy, pgs, null, null, true,
new PrincipalSid("joe"));
MutableAcl parentAcl = new AclImpl(identity2, 2, authzStrategy, pgs, null, null,
true, new PrincipalSid("joe"));
MockAclService service = new MockAclService();
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_USER_READ"),
true);
acl.insertAce(1, BasePermission.WRITE, new GrantedAuthoritySid("ROLE_USER_READ"),
true);
service.updateAcl(acl);
assertThat(1).isEqualTo(acl.getId());
assertThat(identity).isEqualTo(acl.getObjectIdentity());
assertThat(new PrincipalSid("joe")).isEqualTo(acl.getOwner());
assertThat(acl.getParentAcl()).isNull();
assertThat(acl.isEntriesInheriting()).isTrue();
assertThat(acl.getEntries()).hasSize(2);
acl.setParent(parentAcl);
assertThat(parentAcl).isEqualTo(acl.getParentAcl());
acl.setEntriesInheriting(false);
assertThat(acl.isEntriesInheriting()).isFalse();
acl.setOwner(new PrincipalSid("ben"));
assertThat(new PrincipalSid("ben")).isEqualTo(acl.getOwner());
}
@Test
public void isSidLoadedBehavesAsExpected() throws Exception {
List<Sid> loadedSids = Arrays.asList(new PrincipalSid("ben"),
new GrantedAuthoritySid("ROLE_IGNORED"));
MutableAcl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null,
loadedSids, true, new PrincipalSid("joe"));
assertThat(acl.isSidLoaded(loadedSids)).isTrue();
assertThat(acl.isSidLoaded(Arrays.asList(new GrantedAuthoritySid("ROLE_IGNORED"),
new PrincipalSid("ben"))))
.isTrue();
assertThat(acl.isSidLoaded(Arrays.asList((Sid)new GrantedAuthoritySid(
"ROLE_IGNORED"))))
.isTrue();
assertThat(acl.isSidLoaded(BEN)).isTrue();
assertThat(acl.isSidLoaded(null)).isTrue();
assertThat(acl.isSidLoaded(new ArrayList<Sid>(0))).isTrue();
assertThat(acl.isSidLoaded(Arrays.asList((Sid) new GrantedAuthoritySid(
"ROLE_IGNORED"), new GrantedAuthoritySid("ROLE_IGNORED"))))
.isTrue();
assertThat(acl.isSidLoaded(Arrays.asList((Sid) new GrantedAuthoritySid(
"ROLE_GENERAL"), new GrantedAuthoritySid("ROLE_IGNORED"))))
.isFalse();
assertThat(acl.isSidLoaded(Arrays.asList((Sid)new GrantedAuthoritySid(
"ROLE_IGNORED"), new GrantedAuthoritySid("ROLE_GENERAL"))))
.isFalse();
}
@Test(expected = NotFoundException.class)
public void insertAceRaisesNotFoundExceptionForIndexLessThanZero() throws Exception {
AclImpl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
true, new PrincipalSid("joe"));
acl.insertAce(-1, mock(Permission.class), mock(Sid.class), true);
}
@Test(expected = NotFoundException.class)
public void deleteAceRaisesNotFoundExceptionForIndexLessThanZero() throws Exception {
AclImpl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
true, new PrincipalSid("joe"));
acl.deleteAce(-1);
}
@Test(expected = NotFoundException.class)
public void insertAceRaisesNotFoundExceptionForIndexGreaterThanSize()
throws Exception {
AclImpl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
true, new PrincipalSid("joe"));
// Insert at zero, OK.
acl.insertAce(0, mock(Permission.class), mock(Sid.class), true);
// Size is now 1
acl.insertAce(2, mock(Permission.class), mock(Sid.class), true);
}
// SEC-1151
@Test(expected = NotFoundException.class)
public void deleteAceRaisesNotFoundExceptionForIndexEqualToSize() throws Exception {
AclImpl acl = new AclImpl(objectIdentity, 1, authzStrategy, pgs, null, null,
true, new PrincipalSid("joe"));
acl.insertAce(0, mock(Permission.class), mock(Sid.class), true);
// Size is now 1
acl.deleteAce(1);
}
// SEC-1795
@Test
public void changingParentIsSuccessful() throws Exception {
AclImpl parentAcl = new AclImpl(objectIdentity, 1L, authzStrategy,
mockAuditLogger);
AclImpl childAcl = new AclImpl(objectIdentity, 2L, authzStrategy, mockAuditLogger);
AclImpl changeParentAcl = new AclImpl(objectIdentity, 3L, authzStrategy,
mockAuditLogger);
childAcl.setParent(parentAcl);
childAcl.setParent(changeParentAcl);
}
// ~ Inner Classes
// ==================================================================================================
private class MockAclService implements MutableAclService {
public MutableAcl createAcl(ObjectIdentity objectIdentity)
throws AlreadyExistsException {
return null;
}
public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
throws ChildrenExistException {
}
/*
* Mock implementation that populates the aces list with fully initialized
* AccessControlEntries
*
* @see
* org.springframework.security.acls.MutableAclService#updateAcl(org.springframework
* .security.acls.MutableAcl)
*/
@SuppressWarnings("unchecked")
public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException {
List<AccessControlEntry> oldAces = acl.getEntries();
Field acesField = FieldUtils.getField(AclImpl.class, "aces");
acesField.setAccessible(true);
List newAces;
try {
newAces = (List) acesField.get(acl);
newAces.clear();
for (int i = 0; i < oldAces.size(); i++) {
AccessControlEntry ac = oldAces.get(i);
// Just give an ID to all this acl's aces, rest of the fields are just
// copied
newAces.add(new AccessControlEntryImpl((i + 1), ac.getAcl(), ac
.getSid(), ac.getPermission(), ac.isGranting(),
((AuditableAccessControlEntry) ac).isAuditSuccess(),
((AuditableAccessControlEntry) ac).isAuditFailure()));
}
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
return acl;
}
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
return null;
}
public Acl readAclById(ObjectIdentity object) throws NotFoundException {
return null;
}
public Acl readAclById(ObjectIdentity object, List<Sid> sids)
throws NotFoundException {
return null;
}
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects)
throws NotFoundException {
return null;
}
public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects,
List<Sid> sids) throws NotFoundException {
return null;
}
}
}
@@ -1,312 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import static org.assertj.core.api.Assertions.*;
import org.junit.*;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Test class for {@link AclAuthorizationStrategyImpl} and {@link AclImpl} security
* checks.
*
* @author Andrei Stefan
*/
public class AclImplementationSecurityCheckTests {
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
// ~ Methods
// ========================================================================================================
@Before
public void setUp() throws Exception {
SecurityContextHolder.clearContext();
}
@After
public void tearDown() throws Exception {
SecurityContextHolder.clearContext();
}
@Test
public void testSecurityCheckNoACEs() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password",
"ROLE_GENERAL", "ROLE_AUDITING", "ROLE_OWNERSHIP");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
Acl acl = new AclImpl(identity, new Long(1), aclAuthorizationStrategy,
new ConsoleAuditLogger());
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_GENERAL);
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_AUDITING);
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
// Create another authorization strategy
AclAuthorizationStrategy aclAuthorizationStrategy2 = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ONE"), new SimpleGrantedAuthority(
"ROLE_TWO"), new SimpleGrantedAuthority("ROLE_THREE"));
Acl acl2 = new AclImpl(identity, new Long(1), aclAuthorizationStrategy2,
new ConsoleAuditLogger());
// Check access in case the principal has no authorization rights
try {
aclAuthorizationStrategy2.securityCheck(acl2,
AclAuthorizationStrategy.CHANGE_GENERAL);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy2.securityCheck(acl2,
AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy2.securityCheck(acl2,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
}
@Test
public void testSecurityCheckWithMultipleACEs() throws Exception {
// Create a simple authentication with ROLE_GENERAL
Authentication auth = new TestingAuthenticationToken("user", "password",
"ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
// Authorization strategy will require a different role for each access
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
// Let's give the principal the ADMINISTRATION permission, without
// granting access
MutableAcl aclFirstDeny = new AclImpl(identity, new Long(1),
aclAuthorizationStrategy, new ConsoleAuditLogger());
aclFirstDeny.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
false);
// The CHANGE_GENERAL test should pass as the principal has ROLE_GENERAL
aclAuthorizationStrategy.securityCheck(aclFirstDeny,
AclAuthorizationStrategy.CHANGE_GENERAL);
// The CHANGE_AUDITING and CHANGE_OWNERSHIP should fail since the
// principal doesn't have these authorities,
// nor granting access
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny,
AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
// Add granting access to this principal
aclFirstDeny.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
true);
// and try again for CHANGE_AUDITING - the first ACE's granting flag
// (false) will deny this access
try {
aclAuthorizationStrategy.securityCheck(aclFirstDeny,
AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown AccessDeniedException");
}
catch (AccessDeniedException expected) {
}
// Create another ACL and give the principal the ADMINISTRATION
// permission, with granting access
MutableAcl aclFirstAllow = new AclImpl(identity, new Long(1),
aclAuthorizationStrategy, new ConsoleAuditLogger());
aclFirstAllow.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
true);
// The CHANGE_AUDITING test should pass as there is one ACE with
// granting access
aclAuthorizationStrategy.securityCheck(aclFirstAllow,
AclAuthorizationStrategy.CHANGE_AUDITING);
// Add a deny ACE and test again for CHANGE_AUDITING
aclFirstAllow.insertAce(1, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
false);
try {
aclAuthorizationStrategy.securityCheck(aclFirstAllow,
AclAuthorizationStrategy.CHANGE_AUDITING);
}
catch (AccessDeniedException notExpected) {
fail("It shouldn't have thrown AccessDeniedException");
}
// Create an ACL with no ACE
MutableAcl aclNoACE = new AclImpl(identity, new Long(1),
aclAuthorizationStrategy, new ConsoleAuditLogger());
try {
aclAuthorizationStrategy.securityCheck(aclNoACE,
AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
// and still grant access for CHANGE_GENERAL
try {
aclAuthorizationStrategy.securityCheck(aclNoACE,
AclAuthorizationStrategy.CHANGE_GENERAL);
}
catch (NotFoundException expected) {
fail("It shouldn't have thrown NotFoundException");
}
}
@Test
public void testSecurityCheckWithInheritableACEs() throws Exception {
// Create a simple authentication with ROLE_GENERAL
Authentication auth = new TestingAuthenticationToken("user", "password",
"ROLE_GENERAL");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100);
// Authorization strategy will require a different role for each access
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ONE"), new SimpleGrantedAuthority(
"ROLE_TWO"), new SimpleGrantedAuthority("ROLE_GENERAL"));
// Let's give the principal an ADMINISTRATION permission, with granting
// access
MutableAcl parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy,
new ConsoleAuditLogger());
parentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
true);
MutableAcl childAcl = new AclImpl(identity, 2, aclAuthorizationStrategy,
new ConsoleAuditLogger());
// Check against the 'child' acl, which doesn't offer any authorization
// rights on CHANGE_OWNERSHIP
try {
aclAuthorizationStrategy.securityCheck(childAcl,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
fail("It should have thrown NotFoundException");
}
catch (NotFoundException expected) {
}
// Link the child with its parent and test again against the
// CHANGE_OWNERSHIP right
childAcl.setParent(parentAcl);
childAcl.setEntriesInheriting(true);
try {
aclAuthorizationStrategy.securityCheck(childAcl,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
}
catch (NotFoundException expected) {
fail("It shouldn't have thrown NotFoundException");
}
// Create a root parent and link it to the middle parent
MutableAcl rootParentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy,
new ConsoleAuditLogger());
parentAcl = new AclImpl(identity, 1, aclAuthorizationStrategy,
new ConsoleAuditLogger());
rootParentAcl.insertAce(0, BasePermission.ADMINISTRATION, new PrincipalSid(auth),
true);
parentAcl.setEntriesInheriting(true);
parentAcl.setParent(rootParentAcl);
childAcl.setParent(parentAcl);
try {
aclAuthorizationStrategy.securityCheck(childAcl,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
}
catch (NotFoundException expected) {
fail("It shouldn't have thrown NotFoundException");
}
}
@Test
public void testSecurityCheckPrincipalOwner() throws Exception {
Authentication auth = new TestingAuthenticationToken("user", "password",
"ROLE_ONE");
auth.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(auth);
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100);
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority(
"ROLE_AUDITING"), new SimpleGrantedAuthority("ROLE_GENERAL"));
Acl acl = new AclImpl(identity, 1, aclAuthorizationStrategy,
new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), null,
null, false, new PrincipalSid(auth));
try {
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_GENERAL);
}
catch (AccessDeniedException notExpected) {
fail("It shouldn't have thrown AccessDeniedException");
}
try {
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_AUDITING);
fail("It shouldn't have thrown AccessDeniedException");
}
catch (NotFoundException expected) {
}
try {
aclAuthorizationStrategy.securityCheck(acl,
AclAuthorizationStrategy.CHANGE_OWNERSHIP);
}
catch (AccessDeniedException notExpected) {
fail("It shouldn't have thrown AccessDeniedException");
}
}
}
@@ -1,95 +0,0 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.acls.model.AccessControlEntry;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
/**
* Test class for {@link ConsoleAuditLogger}.
*
* @author Andrei Stefan
*/
public class AuditLoggerTests {
// ~ Instance fields
// ================================================================================================
private PrintStream console;
private ByteArrayOutputStream bytes = new ByteArrayOutputStream();
private ConsoleAuditLogger logger;
private AuditableAccessControlEntry ace;
// ~ Methods
// ========================================================================================================
@Before
public void setUp() throws Exception {
logger = new ConsoleAuditLogger();
ace = mock(AuditableAccessControlEntry.class);
console = System.out;
System.setOut(new PrintStream(bytes));
}
@After
public void tearDown() throws Exception {
System.setOut(console);
bytes.reset();
}
@Test
public void nonAuditableAceIsIgnored() {
AccessControlEntry ace = mock(AccessControlEntry.class);
logger.logIfNeeded(true, ace);
assertThat(bytes.size()).isZero();
}
@Test
public void successIsNotLoggedIfAceDoesntRequireSuccessAudit() throws Exception {
when(ace.isAuditSuccess()).thenReturn(false);
logger.logIfNeeded(true, ace);
assertThat(bytes.size()).isZero();
}
@Test
public void successIsLoggedIfAceRequiresSuccessAudit() throws Exception {
when(ace.isAuditSuccess()).thenReturn(true);
logger.logIfNeeded(true, ace);
assertThat(bytes.toString()).startsWith("GRANTED due to ACE");
}
@Test
public void failureIsntLoggedIfAceDoesntRequireFailureAudit() throws Exception {
when(ace.isAuditFailure()).thenReturn(false);
logger.logIfNeeded(false, ace);
assertThat(bytes.size()).isZero();
}
@Test
public void failureIsLoggedIfAceRequiresFailureAudit() throws Exception {
when(ace.isAuditFailure()).thenReturn(true);
logger.logIfNeeded(false, ace);
assertThat(bytes.toString()).startsWith("DENIED due to ACE");
}
}
@@ -1,207 +0,0 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.springframework.security.acls.domain.IdentityUnavailableException;
import org.springframework.security.acls.domain.ObjectIdentityImpl;
import org.springframework.security.acls.model.ObjectIdentity;
/**
* Tests for {@link ObjectIdentityImpl}.
*
* @author Andrei Stefan
*/
@SuppressWarnings("unused")
public class ObjectIdentityImplTests {
private static final String DOMAIN_CLASS = "org.springframework.security.acls.domain.ObjectIdentityImplTests$MockIdDomainObject";
// ~ Methods
// ========================================================================================================
@Test
public void constructorsRespectRequiredFields() throws Exception {
// Check one-argument constructor required field
try {
new ObjectIdentityImpl(null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check String-Serializable constructor required field
try {
new ObjectIdentityImpl("", Long.valueOf(1));
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// Check Serializable parameter is not null
try {
new ObjectIdentityImpl(DOMAIN_CLASS, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// The correct way of using String-Serializable constructor
try {
new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
}
catch (IllegalArgumentException notExpected) {
fail("It shouldn't have thrown IllegalArgumentException");
}
// Check the Class-Serializable constructor
try {
new ObjectIdentityImpl(MockIdDomainObject.class, null);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
}
@Test
public void gettersReturnExpectedValues() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
assertThat(obj.getIdentifier()).isEqualTo(Long.valueOf(1));
assertThat(obj.getType()).isEqualTo(MockIdDomainObject.class.getName());
}
@Test
public void testGetIdMethodConstraints() throws Exception {
// Check the getId() method is present
try {
new ObjectIdentityImpl("A_STRING_OBJECT");
fail("It should have thrown IdentityUnavailableException");
}
catch (IdentityUnavailableException expected) {
}
// getId() should return a non-null value
MockIdDomainObject mockId = new MockIdDomainObject();
try {
new ObjectIdentityImpl(mockId);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// getId() should return a Serializable object
mockId.setId(new MockIdDomainObject());
try {
new ObjectIdentityImpl(mockId);
fail("It should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
}
// getId() should return a Serializable object
mockId.setId(new Long(100));
try {
new ObjectIdentityImpl(mockId);
}
catch (IllegalArgumentException expected) {
}
}
@Test(expected = IllegalArgumentException.class)
public void constructorRejectsInvalidTypeParameter() throws Exception {
new ObjectIdentityImpl("", Long.valueOf(1));
}
@Test
public void testEquals() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
MockIdDomainObject mockObj = new MockIdDomainObject();
mockObj.setId(Long.valueOf(1));
String string = "SOME_STRING";
assertThat(string).isNotSameAs(obj);
assertThat(obj).isNotNull();
assertThat(obj).isNotEqualTo("DIFFERENT_OBJECT_TYPE");
assertThat(obj).isNotEqualTo(new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(2)));
assertThat(obj).isNotEqualTo(new ObjectIdentityImpl(
"org.springframework.security.acls.domain.ObjectIdentityImplTests$MockOtherIdDomainObject",
Long.valueOf(1)));
assertThat(new ObjectIdentityImpl(DOMAIN_CLASS, 1L)).isEqualTo(obj);
assertThat(new ObjectIdentityImpl(mockObj)).isEqualTo(obj);
}
@Test
public void hashcodeIsDifferentForDifferentJavaTypes() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, Long.valueOf(1));
ObjectIdentity obj2 = new ObjectIdentityImpl(String.class, Long.valueOf(1));
assertThat(obj.hashCode()).isNotEqualTo(obj2.hashCode());
}
@Test
public void longAndIntegerIdsWithSameValueAreEqualAndHaveSameHashcode() {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, new Long(5));
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, Integer.valueOf(5));
assertThat(obj2).isEqualTo(obj);
assertThat(obj2.hashCode()).isEqualTo(obj.hashCode());
}
@Test
public void equalStringIdsAreEqualAndHaveSameHashcode() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, "1000");
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, "1000");
assertThat(obj2).isEqualTo(obj);
assertThat(obj2.hashCode()).isEqualTo(obj.hashCode());
}
@Test
public void stringAndNumericIdsAreNotEqual() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(Object.class, "1000");
ObjectIdentity obj2 = new ObjectIdentityImpl(Object.class, Long.valueOf(1000));
assertThat(obj).isNotEqualTo(obj2);
}
// ~ Inner Classes
// ==================================================================================================
private class MockIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
private class MockOtherIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
}
@@ -1,60 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
/**
* Tests for {@link ObjectIdentityRetrievalStrategyImpl}
*
* @author Andrei Stefan
*/
public class ObjectIdentityRetrievalStrategyImplTests {
// ~ Methods
// ========================================================================================================
@Test
public void testObjectIdentityCreation() throws Exception {
MockIdDomainObject domain = new MockIdDomainObject();
domain.setId(Integer.valueOf(1));
ObjectIdentityRetrievalStrategy retStrategy = new ObjectIdentityRetrievalStrategyImpl();
ObjectIdentity identity = retStrategy.getObjectIdentity(domain);
assertThat(identity).isNotNull();
assertThat(new ObjectIdentityImpl(domain)).isEqualTo(identity);
}
// ~ Inner Classes
// ==================================================================================================
@SuppressWarnings("unused")
private class MockIdDomainObject {
private Object id;
public Object getId() {
return id;
}
public void setId(Object id) {
this.id = id;
}
}
}
@@ -1,97 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import static org.assertj.core.api.Assertions.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.acls.model.Permission;
/**
* Tests classes associated with Permission.
*
* @author Ben Alex
*/
public class PermissionTests {
private DefaultPermissionFactory permissionFactory;
@Before
public void createPermissionfactory() {
permissionFactory = new DefaultPermissionFactory();
}
@Test
public void basePermissionTest() {
Permission p = permissionFactory.buildFromName("WRITE");
assertThat(p).isNotNull();
}
@Test
public void expectedIntegerValues() {
assertThat(BasePermission.READ.getMask()).isEqualTo(1);
assertThat(BasePermission.ADMINISTRATION.getMask()).isEqualTo(16);
assertThat(
new CumulativePermission().set(BasePermission.READ)
.set(BasePermission.WRITE).set(BasePermission.CREATE).getMask())
.isEqualTo(7);
assertThat(
new CumulativePermission().set(BasePermission.READ)
.set(BasePermission.ADMINISTRATION).getMask())
.isEqualTo(17);
}
@Test
public void fromInteger() {
Permission permission = permissionFactory.buildFromMask(7);
permission = permissionFactory.buildFromMask(4);
}
@Test
public void stringConversion() {
permissionFactory.registerPublicPermissions(SpecialPermission.class);
assertThat(BasePermission.READ.toString())
.isEqualTo("BasePermission[...............................R=1]");
assertThat(
BasePermission.ADMINISTRATION.toString())
.isEqualTo("BasePermission[...........................A....=16]");
assertThat(
new CumulativePermission().set(BasePermission.READ).toString())
.isEqualTo("CumulativePermission[...............................R=1]");
assertThat(new CumulativePermission().set(SpecialPermission.ENTER)
.set(BasePermission.ADMINISTRATION).toString())
.isEqualTo("CumulativePermission[..........................EA....=48]");
assertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION)
.set(BasePermission.READ).toString())
.isEqualTo("CumulativePermission[...........................A...R=17]");
assertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION)
.set(BasePermission.READ).clear(BasePermission.ADMINISTRATION)
.toString())
.isEqualTo("CumulativePermission[...............................R=1]");
assertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION)
.set(BasePermission.READ).clear(BasePermission.ADMINISTRATION)
.clear(BasePermission.READ).toString())
.isEqualTo("CumulativePermission[................................=0]");
}
}
@@ -1,32 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.domain;
import org.springframework.security.acls.model.Permission;
/**
* A test permission.
*
* @author Ben Alex
*/
public class SpecialPermission extends BasePermission {
public static final Permission ENTER = new SpecialPermission(1 << 5, 'E'); // 32
public static final Permission LEAVE = new SpecialPermission(1 << 6, 'L');
protected SpecialPermission(int mask, char code) {
super(mask, code);
}
}
@@ -1,344 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.jdbc;
import static org.assertj.core.api.Assertions.*;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import org.junit.*;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import org.springframework.security.acls.domain.*;
import org.springframework.security.acls.model.Acl;
import org.springframework.security.acls.model.AuditableAccessControlEntry;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.NotFoundException;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.util.FileCopyUtils;
import java.util.*;
/**
* Tests {@link BasicLookupStrategy}
*
* @author Andrei Stefan
*/
public class BasicLookupStrategyTests {
private static final Sid BEN_SID = new PrincipalSid("ben");
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
// ~ Instance fields
// ================================================================================================
private static JdbcTemplate jdbcTemplate;
private BasicLookupStrategy strategy;
private static SingleConnectionDataSource dataSource;
private static CacheManager cacheManager;
// ~ Methods
// ========================================================================================================
@BeforeClass
public static void initCacheManaer() {
cacheManager = CacheManager.create();
cacheManager.addCache(new Cache("basiclookuptestcache", 500, false, false, 30, 30));
}
@BeforeClass
public static void createDatabase() throws Exception {
dataSource = new SingleConnectionDataSource("jdbc:hsqldb:mem:lookupstrategytest", "sa", "", true);
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
jdbcTemplate = new JdbcTemplate(dataSource);
Resource resource = new ClassPathResource("createAclSchema.sql");
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
jdbcTemplate.execute(sql);
}
@AfterClass
public static void dropDatabase() throws Exception {
dataSource.destroy();
}
@AfterClass
public static void shutdownCacheManager() {
cacheManager.removalAll();
cacheManager.shutdown();
}
@Before
public void populateDatabase() {
String query = "INSERT INTO acl_sid(ID,PRINCIPAL,SID) VALUES (1,1,'ben');"
+ "INSERT INTO acl_class(ID,CLASS) VALUES (2,'" + TARGET_CLASS + "');"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (1,2,100,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (2,2,101,1,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (3,2,102,2,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (1,1,0,1,1,1,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (2,1,1,1,2,0,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (3,2,0,1,8,1,0,0);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (4,3,0,1,8,0,0,0);";
jdbcTemplate.execute(query);
}
@Before
public void initializeBeans() {
EhCacheBasedAclCache cache = new EhCacheBasedAclCache(getCache(),
new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()),
new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_USER")));
AclAuthorizationStrategy authorizationStrategy = new AclAuthorizationStrategyImpl(
new SimpleGrantedAuthority("ROLE_ADMINISTRATOR"));
strategy = new BasicLookupStrategy(dataSource, cache, authorizationStrategy,
new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()));
strategy.setPermissionFactory(new DefaultPermissionFactory());
}
@After
public void emptyDatabase() {
String query = "DELETE FROM acl_entry;" + "DELETE FROM acl_object_identity WHERE ID = 7;"
+ "DELETE FROM acl_object_identity WHERE ID = 6;" + "DELETE FROM acl_object_identity WHERE ID = 5;"
+ "DELETE FROM acl_object_identity WHERE ID = 4;" + "DELETE FROM acl_object_identity WHERE ID = 3;"
+ "DELETE FROM acl_object_identity WHERE ID = 2;" + "DELETE FROM acl_object_identity WHERE ID = 1;"
+ "DELETE FROM acl_class;" + "DELETE FROM acl_sid;";
jdbcTemplate.execute(query);
}
private Ehcache getCache() {
Ehcache cache = cacheManager.getCache("basiclookuptestcache");
cache.removeAll();
return cache;
}
@Test
public void testAclsRetrievalWithDefaultBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101));
// Deliberately use an integer for the child, to reproduce bug report in
// SEC-819
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(102));
Map<ObjectIdentity, Acl> map = this.strategy
.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
@Test
public void testAclsRetrievalFromCacheOnly() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
// Objects were put in cache
strategy.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
// Let's empty the database to force acls retrieval from cache
emptyDatabase();
Map<ObjectIdentity, Acl> map = this.strategy
.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
@Test
public void testAclsRetrievalWithCustomBatchSize() throws Exception {
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(102));
// Set a batch size to allow multiple database queries in order to
// retrieve all
// acls
this.strategy.setBatchSize(1);
Map<ObjectIdentity, Acl> map = this.strategy
.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
checkEntries(topParentOid, middleParentOid, childOid, map);
}
private void checkEntries(ObjectIdentity topParentOid, ObjectIdentity middleParentOid, ObjectIdentity childOid,
Map<ObjectIdentity, Acl> map) throws Exception {
assertThat(map).hasSize(3);
MutableAcl topParent = (MutableAcl) map.get(topParentOid);
MutableAcl middleParent = (MutableAcl) map.get(middleParentOid);
MutableAcl child = (MutableAcl) map.get(childOid);
// Check the retrieved versions has IDs
assertThat(topParent.getId()).isNotNull();
assertThat(middleParent.getId()).isNotNull();
assertThat(child.getId()).isNotNull();
// Check their parents were correctly retrieved
assertThat(topParent.getParentAcl()).isNull();
assertThat(middleParent.getParentAcl().getObjectIdentity()).isEqualTo(topParentOid);
assertThat(child.getParentAcl().getObjectIdentity()).isEqualTo(middleParentOid);
// Check their ACEs were correctly retrieved
assertThat(topParent.getEntries()).hasSize(2);
assertThat(middleParent.getEntries()).hasSize(1);
assertThat(child.getEntries()).hasSize(1);
// Check object identities were correctly retrieved
assertThat(topParent.getObjectIdentity()).isEqualTo(topParentOid);
assertThat(middleParent.getObjectIdentity()).isEqualTo(middleParentOid);
assertThat(child.getObjectIdentity()).isEqualTo(childOid);
// Check each entry
assertThat(topParent.isEntriesInheriting()).isTrue();
assertThat(Long.valueOf(1)).isEqualTo(topParent.getId());
assertThat(new PrincipalSid("ben")).isEqualTo(topParent.getOwner());
assertThat(Long.valueOf(1)).isEqualTo(topParent.getEntries().get(0).getId());
assertThat(topParent.getEntries().get(0).getPermission()).isEqualTo(BasePermission.READ);
assertThat(topParent.getEntries().get(0).getSid()).isEqualTo(new PrincipalSid("ben"));
assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditFailure()).isFalse();
assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(0)).isAuditSuccess()).isFalse();
assertThat((topParent.getEntries().get(0)).isGranting()).isTrue();
assertThat(Long.valueOf(2)).isEqualTo(topParent.getEntries().get(1).getId());
assertThat(topParent.getEntries().get(1).getPermission()).isEqualTo(BasePermission.WRITE);
assertThat(topParent.getEntries().get(1).getSid()).isEqualTo(new PrincipalSid("ben"));
assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditFailure()).isFalse();
assertThat(((AuditableAccessControlEntry) topParent.getEntries().get(1)).isAuditSuccess()).isFalse();
assertThat(topParent.getEntries().get(1).isGranting()).isFalse();
assertThat(middleParent.isEntriesInheriting()).isTrue();
assertThat(Long.valueOf(2)).isEqualTo(middleParent.getId());
assertThat(new PrincipalSid("ben")).isEqualTo(middleParent.getOwner());
assertThat(Long.valueOf(3)).isEqualTo(middleParent.getEntries().get(0).getId());
assertThat(middleParent.getEntries().get(0).getPermission()).isEqualTo(BasePermission.DELETE);
assertThat(middleParent.getEntries().get(0).getSid()).isEqualTo(new PrincipalSid("ben"));
assertThat(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditFailure()).isFalse();
assertThat(((AuditableAccessControlEntry) middleParent.getEntries().get(0)).isAuditSuccess()).isFalse();
assertThat(middleParent.getEntries().get(0).isGranting()).isTrue();
assertThat(child.isEntriesInheriting()).isTrue();
assertThat(Long.valueOf(3)).isEqualTo(child.getId());
assertThat(new PrincipalSid("ben")).isEqualTo(child.getOwner());
assertThat(Long.valueOf(4)).isEqualTo(child.getEntries().get(0).getId());
assertThat(child.getEntries().get(0).getPermission()).isEqualTo(BasePermission.DELETE);
assertThat(new PrincipalSid("ben")).isEqualTo(child.getEntries().get(0).getSid());
assertThat(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditFailure()).isFalse();
assertThat(((AuditableAccessControlEntry) child.getEntries().get(0)).isAuditSuccess()).isFalse();
assertThat((child.getEntries().get(0)).isGranting()).isFalse();
}
@Test
public void testAllParentsAreRetrievedWhenChildIsLoaded() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,103,1,1,1);";
jdbcTemplate.execute(query);
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(100));
ObjectIdentity middleParentOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(101));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(102));
ObjectIdentity middleParent2Oid = new ObjectIdentityImpl(TARGET_CLASS, Long.valueOf(103));
// Retrieve the child
Map<ObjectIdentity, Acl> map = this.strategy.readAclsById(Arrays.asList(childOid), null);
// Check that the child and all its parents were retrieved
assertThat(map.get(childOid)).isNotNull();
assertThat(map.get(childOid).getObjectIdentity()).isEqualTo(childOid);
assertThat(map.get(middleParentOid)).isNotNull();
assertThat(map.get(middleParentOid).getObjectIdentity()).isEqualTo(middleParentOid);
assertThat(map.get(topParentOid)).isNotNull();
assertThat(map.get(topParentOid).getObjectIdentity()).isEqualTo(topParentOid);
// The second parent shouldn't have been retrieved
assertThat(map.get(middleParent2Oid)).isNull();
}
/**
* Test created from SEC-590.
*/
@Test
public void testReadAllObjectIdentitiesWhenLastElementIsAlreadyCached() throws Exception {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (5,2,105,4,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (6,2,106,4,1,1);"
+ "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (7,2,107,5,1,1);"
+ "INSERT INTO acl_entry(ID,ACL_OBJECT_IDENTITY,ACE_ORDER,SID,MASK,GRANTING,AUDIT_SUCCESS,AUDIT_FAILURE) VALUES (5,4,0,1,1,1,0,0)";
jdbcTemplate.execute(query);
ObjectIdentity grandParentOid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104));
ObjectIdentity parent1Oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(105));
ObjectIdentity parent2Oid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(106));
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, Integer.valueOf(107));
// First lookup only child, thus populating the cache with grandParent,
// parent1
// and child
List<Permission> checkPermission = Arrays.asList(BasePermission.READ);
List<Sid> sids = Arrays.asList(BEN_SID);
List<ObjectIdentity> childOids = Arrays.asList(childOid);
strategy.setBatchSize(6);
Map<ObjectIdentity, Acl> foundAcls = strategy.readAclsById(childOids, sids);
Acl foundChildAcl = foundAcls.get(childOid);
assertThat(foundChildAcl).isNotNull();
assertThat(foundChildAcl.isGranted(checkPermission, sids, false)).isTrue();
// Search for object identities has to be done in the following order:
// last
// element have to be one which
// is already in cache and the element before it must not be stored in
// cache
List<ObjectIdentity> allOids = Arrays.asList(grandParentOid, parent1Oid, parent2Oid, childOid);
try {
foundAcls = strategy.readAclsById(allOids, sids);
} catch (NotFoundException notExpected) {
fail("It shouldn't have thrown NotFoundException");
}
Acl foundParent2Acl = foundAcls.get(parent2Oid);
assertThat(foundParent2Acl).isNotNull();
assertThat(foundParent2Acl.isGranted(checkPermission, sids, false)).isTrue();
}
@Test(expected = IllegalArgumentException.class)
public void nullOwnerIsNotSupported() {
String query = "INSERT INTO acl_object_identity(ID,OBJECT_ID_CLASS,OBJECT_ID_IDENTITY,PARENT_OBJECT,OWNER_SID,ENTRIES_INHERITING) VALUES (4,2,104,null,null,1);";
jdbcTemplate.execute(query);
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, new Long(104));
strategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID));
}
@Test
public void testCreatePrincipalSid() {
Sid result = strategy.createSid(true, "sid");
assertThat(result.getClass()).isEqualTo(PrincipalSid.class);
assertThat(((PrincipalSid) result).getPrincipal()).isEqualTo("sid");
}
@Test
public void testCreateGrantedAuthority() {
Sid result = strategy.createSid(false, "sid");
assertThat(result.getClass()).isEqualTo(GrantedAuthoritySid.class);
assertThat(((GrantedAuthoritySid) result).getGrantedAuthority()).isEqualTo("sid");
}
}
@@ -1,46 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.acls.jdbc;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import java.io.IOException;
import javax.sql.DataSource;
/**
* Seeds the database for {@link JdbcMutableAclServiceTests}.
*
* @author Ben Alex
*/
public class DatabaseSeeder {
// ~ Constructors
// ===================================================================================================
public DatabaseSeeder(DataSource dataSource, Resource resource) throws IOException {
Assert.notNull(dataSource, "dataSource required");
Assert.notNull(resource, "resource required");
JdbcTemplate template = new JdbcTemplate(dataSource);
String sql = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
template.execute(sql);
}
}

Some files were not shown because too many files have changed in this diff Show More