1
0
mirror of synced 2026-07-17 08:35:13 +00:00

Compare commits

..

180 Commits

Author SHA1 Message Date
github-actions[bot] 0686b461af Release 6.0.5 2023-07-17 20:43:30 +00:00
Josh Cummings fe7bee9236 Merge branch '5.8.x' into 6.0.x 2023-07-17 11:09:38 -06:00
Josh Cummings bb46a54270 Add DispatcherServlet to Tests
Issue gh-13551
2023-07-17 10:58:30 -06:00
Josh Cummings df239b6448 Improve RequestMatcher Validation
Closes gh-13551
2023-07-17 08:41:30 -06:00
Marcus Da Coregio cbef118026 Merge branch '5.8.x' into 6.0.x 2023-07-17 09:16:20 -03:00
Marcus Da Coregio a939f17890 Merge branch '5.7.x' into 5.8.x 2023-07-17 09:15:56 -03:00
Marcus Da Coregio fe9bc26bdc Merge branch '5.6.x' into 5.7.x 2023-07-17 09:13:28 -03:00
Marcus Da Coregio 7813a9ba26 Use default PathPatternParser instance 2023-07-17 09:12:28 -03:00
Josh Cummings da9b81b314 Update org.springframework.data to 2022.0.8
Closes gh-13521
2023-07-14 14:30:23 -06:00
Josh Cummings 613ce98b0f Update org.springframework to 6.0.11
Closes gh-13520
2023-07-14 14:30:18 -06:00
Josh Cummings eb420b1095 Update jakarta.websocket to 2.1.1
Closes gh-13519
2023-07-14 14:30:12 -06:00
Josh Cummings de222572b9 Update io.projectreactor to 2022.0.9
Closes gh-13518
2023-07-14 14:30:07 -06:00
Josh Cummings e204a3bca6 Update micrometer-observation to 1.10.9
Closes gh-13517
2023-07-14 14:29:59 -06:00
Marcus Da Coregio 96416a924b Merge branch '5.8.x' into 6.0.x 2023-07-14 11:04:57 -03:00
Marcus Da Coregio 7d58b9391a Merge branch '5.7.x' into 5.8.x 2023-07-14 11:04:26 -03:00
Marcus Da Coregio dc3cb68c76 Merge branch '5.6.x' into 5.7.x
Closes gh-dry-run
2023-07-14 11:04:01 -03:00
Marcus Da Coregio 8555c100d7 Update org.springframework.data to 2021.2.14
Closes gh-13516
2023-07-14 10:53:12 -03:00
Marcus Da Coregio ae46531add Update org.springframework to 5.3.29
Closes gh-13515
2023-07-14 10:53:08 -03:00
Marcus Da Coregio 0121b08a85 Update io.projectreactor to 2020.0.34
Closes gh-13513
2023-07-14 10:53:01 -03:00
Marcus Da Coregio 56292c9971 Update org.springframework.data to 2021.2.14
Closes gh-13512
2023-07-14 10:52:14 -03:00
Marcus Da Coregio 4fc938555e Update org.springframework to 5.3.29
Closes gh-13511
2023-07-14 10:52:11 -03:00
Marcus Da Coregio e574415244 Update io.projectreactor to 2020.0.34
Closes gh-13509
2023-07-14 10:52:03 -03:00
Marcus Da Coregio c5da886310 Update org.springframework to 5.3.29
Closes gh-13508
2023-07-14 10:42:27 -03:00
Marcus Da Coregio 3bef5fd3ed Update io.projectreactor to 2020.0.34
Closes gh-13505
2023-07-14 10:42:27 -03:00
Steve Riesenberg f5538be8d0 Merge branch '5.8.x' into 6.0.x 2023-07-12 16:06:20 -05:00
Steve Riesenberg b7a9a654f0 Merge branch '5.7.x' into 5.8.x 2023-07-12 16:06:02 -05:00
Steve Riesenberg e7201c48d1 Fix copy/paste from main
Issue gh-13500
2023-07-12 16:04:35 -05:00
Steve Riesenberg f0f6de435d Merge branch '5.8.x' into 6.0.x 2023-07-12 15:53:14 -05:00
Steve Riesenberg a642fdb004 Merge branch '5.7.x' into 5.8.x 2023-07-12 15:52:55 -05:00
Steve Riesenberg 991b398c55 Create release with full docs path
Closes gh-13500
2023-07-12 15:51:44 -05:00
Josh Cummings dbf0c66301 Fix Client Authentication Method Error Message
Closes gh-13496
2023-07-12 14:15:31 -06:00
Marcus Da Coregio 933b302979 Fix once-per-request="true" not taking any effect
Closes gh-13491
2023-07-12 14:30:18 -03:00
Josh Cummings 6393702e70 Fix allOf/anyOf Abstain Logic
Closes gh-13487
2023-07-11 17:02:07 -06:00
Josh Cummings 83c0f4231e Merge branch '5.8.x' into 6.0.x
Closes gh-13481
2023-07-10 16:13:04 -06:00
Josh Cummings 40d61743b9 Replace Existing Continue Parameter
Closes gh-13438
2023-07-10 16:12:05 -06:00
Josh Cummings b5d63e3485 Merge branch '5.8.x' into 6.0.x
Closes gh-13477
2023-07-10 13:39:05 -06:00
Josh Cummings 8895a66a2b Add hasIpAddress Migration Steps
Closes gh-13474
2023-07-10 13:35:16 -06:00
Marcus Da Coregio 863aa5f65f Fix Documented Default Value for AuthorizationFilter properties
Closes gh-13456
2023-07-07 14:35:11 -03:00
Marcus Da Coregio b02594d451 Upgrade to Gradle 7.6.2
Closes gh-13447
2023-07-03 15:21:59 -03:00
Josh Cummings 1abfd2c801 Only Register as Advisor in Proxy Mode
Now that https://github.com/spring-projects/spring-framework/issues/30689
is addressed.

Closes gh-13198
2023-06-29 10:07:11 -06:00
Marcus Da Coregio 6e2c9b421b Merge branch '5.8.x' into 6.0.x
Closes gh-13420
2023-06-23 10:38:21 -03:00
Marcus Da Coregio 80a5028f3f saml2Login filterProcessingUrl should be loginProcessingUrl
Closes gh-13417
2023-06-23 10:38:04 -03:00
Marcus Da Coregio 225f3536c5 Merge branch '5.8.x' into 6.0.x
Closes gh-13413
2023-06-22 10:28:56 -03:00
Marcus Da Coregio c30bacac10 Improve Security Filters Documentation
Closes gh-8167
2023-06-22 10:11:18 -03:00
Marcus Da Coregio e8ab855d43 Use samplesBranch=6.0.x
Closes gh-12380
2023-06-21 16:23:18 -03:00
Marcus Da Coregio 797b2a5473 Merge branch '5.8.x' into 6.0.x 2023-06-19 14:19:12 -03:00
Marcus Da Coregio a104dec30d Merge branch '5.7.x' into 5.8.x 2023-06-19 14:18:50 -03:00
Marcus Da Coregio 488b6ea531 Merge branch '5.6.x' into 5.7.x 2023-06-19 14:18:23 -03:00
github-actions[bot] ef04ea9d68 Next development version 2023-06-19 17:05:13 +00:00
github-actions[bot] b7be4c462c Next development version 2023-06-19 16:55:18 +00:00
Marcus Da Coregio aa8440e1ee Release 5.6.11 2023-06-19 13:23:20 -03:00
github-actions[bot] a9723d7ce4 Next development version 2023-06-19 16:22:19 +00:00
github-actions[bot] 6d3f1699c0 Release 5.7.9 2023-06-19 16:09:53 +00:00
github-actions[bot] bb72eed8d1 Next development version 2023-06-19 16:02:54 +00:00
github-actions[bot] 87e2071b17 Release 6.0.4 2023-06-19 15:21:40 +00:00
github-actions[bot] 108075763b Release 5.8.4 2023-06-19 15:21:27 +00:00
Marcus Da Coregio 2686af0c4d Revert "Only Register as Advisor in Proxy Mode"
This reverts commit 35ad1f85
2023-06-19 11:13:39 -03:00
Rob Winch f66a5bab99 Merge branch '5.8.x' into 6.0.x
Closes gh-13406
2023-06-18 21:33:58 -05:00
Rob Winch 7da99acca7 Merge branch '5.7.x' into 5.8.x
Closes gh-13405
2023-06-18 21:32:35 -05:00
Rob Winch 0cf95dbf61 Merge branch '5.6.x' into 5.7.x
Closes gh-13404
2023-06-18 21:31:35 -05:00
Rob Winch 39c43159f4 Convert to Asciidoctor Tabs
Closes gh-13403
2023-06-18 21:30:41 -05:00
Marcus Da Coregio be0c2bd44c Merge branch '5.8.x' into 6.0.x 2023-06-18 11:17:52 -03:00
Marcus Da Coregio 67f262199a Merge branch '5.7.x' into 5.8.x 2023-06-18 11:17:31 -03:00
Marcus Da Coregio 0a022a348c Merge branch '5.6.x' into 5.7.x
Closes gh-dry-run
2023-06-18 11:16:54 -03:00
Marcus Da Coregio afec90c8e8 Remove /plugins-release and /plugins-snapshot from build
Those repositories now need authentication, and we do not actually need them, the dependencies can be resolved from Maven Central

Closes gh-13245

(cherry picked from commit 663f5cf76b)
2023-06-17 19:32:48 -03:00
Marcus Da Coregio 9806674481 Merge branch '5.8.x' into 6.0.x 2023-06-17 18:24:04 -03:00
Marcus Da Coregio b29772269f Merge branch '5.7.x' into 5.8.x 2023-06-17 18:23:35 -03:00
Marcus Da Coregio c7ccba66c7 Merge branch '5.6.x' into 5.7.x
Closes gh-dry-run
2023-06-17 18:23:15 -03:00
Marcus Da Coregio a6599f9874 Update org.springframework to 5.3.28
Closes gh-13401
2023-06-17 17:58:50 -03:00
Marcus Da Coregio 9f581850a1 Update hibernate-entitymanager to 5.6.15.Final
Closes gh-13400
2023-06-17 17:58:50 -03:00
Marcus Da Coregio 6e13927b48 Update org.eclipse.jetty to 9.4.51.v20230217
Closes gh-13399
2023-06-17 17:58:49 -03:00
Marcus Da Coregio cead207c5b Update org.aspectj to 1.9.19
Closes gh-13398
2023-06-17 17:58:49 -03:00
Marcus Da Coregio 9e22d40264 Update io.spring.nohttp to 0.0.11
Closes gh-13394
2023-06-17 17:58:49 -03:00
Marcus Da Coregio c54a191042 Update io.rsocket to 1.1.4
Closes gh-13392
2023-06-17 17:58:49 -03:00
Marcus Da Coregio 0da2e5a33e Update blockhound to 1.0.8.RELEASE
Closes gh-13390
2023-06-17 17:58:45 -03:00
Marcus Da Coregio 08b84502e3 Update io.projectreactor to 2020.0.33
Closes gh-13387
2023-06-17 17:58:22 -03:00
Marcus Da Coregio 6bacf21c76 Update org.springframework.data to 2021.2.13
Closes gh-13397
2023-06-17 17:17:52 -03:00
Marcus Da Coregio fe8f56e41c Update org.springframework to 5.3.28
Closes gh-13395
2023-06-17 17:17:48 -03:00
Marcus Da Coregio f402679996 Update junit-bom to 5.9.3
Closes gh-13391
2023-06-17 17:16:40 -03:00
Marcus Da Coregio 708af21576 Update hsqldb to 2.7.2
Closes gh-13388
2023-06-17 17:16:36 -03:00
Marcus Da Coregio dbcff3f1af Update org.springframework.data to 2021.2.13
Closes gh-13385
2023-06-17 17:16:33 -03:00
Marcus Da Coregio 57b27cacdf Update io.spring.javaformat to 0.0.39
Closes gh-13386
2023-06-17 17:16:33 -03:00
Marcus Da Coregio ed0cc16bb6 Update io.rsocket to 1.1.4
Closes gh-13383
2023-06-17 17:16:30 -03:00
Marcus Da Coregio e87fae40df Update org.springframework to 5.3.28
Closes gh-13382
2023-06-17 17:16:29 -03:00
Marcus Da Coregio 32d25e8f23 Update io.rsocket to 1.1.4
Closes gh-13379
2023-06-17 17:16:25 -03:00
Marcus Da Coregio 0c63c7f611 Update io.projectreactor to 2020.0.33
Closes gh-13377
2023-06-17 17:16:23 -03:00
Marcus Da Coregio 9656f79afe Update jackson-bom to 2.13.5
Closes gh-13375
2023-06-17 17:16:21 -03:00
Marcus Da Coregio dc5e1eae1d Update com.nimbusds to 9.43.3
Closes gh-13374
2023-06-17 17:16:20 -03:00
Marcus Da Coregio 81723fca9a Update io.projectreactor to 2020.0.33
Closes gh-13373
2023-06-17 17:16:19 -03:00
Marcus Da Coregio ac32095bc8 Update logback-classic to 1.2.12
Closes gh-13372
2023-06-17 17:16:17 -03:00
Rob Winch 3aedeea676 Merge branch '5.8.x' into 6.0.x
Closes gh-13330
2023-06-16 20:24:27 -05:00
Rob Winch 25390ab618 Merge branch '5.7.x' into 5.8.x
Closes gh-13329
2023-06-16 20:23:51 -05:00
Rob Winch 30206b6d0c Merge branch '5.6.x' into 5.7.x
Closes gh-13328
2023-06-16 20:23:19 -05:00
Rob Winch 3e22c1e8de Use antora name of security
Closes gh-13327
2023-06-16 17:14:56 -05:00
Steve Riesenberg 78b5c3e124 Update spring-ldap-core to 3.0.4
Closes gh-13365
2023-06-16 12:16:40 -05:00
Steve Riesenberg 8d60ec32bd Update org.springframework.data to 2022.0.7
Closes gh-13364
2023-06-16 12:16:37 -05:00
Steve Riesenberg 1345cb7960 Update org.springframework to 6.0.10
Closes gh-13363
2023-06-16 12:16:34 -05:00
Steve Riesenberg 73104daa7f Update junit-bom to 5.9.3
Closes gh-13360
2023-06-16 12:16:25 -05:00
Steve Riesenberg 9a71837fc7 Update hsqldb to 2.7.2
Closes gh-13359
2023-06-16 12:16:22 -05:00
Steve Riesenberg e7a06fe4d4 Update io.spring.javaformat to 0.0.39
Closes gh-13358
2023-06-16 12:16:18 -05:00
Steve Riesenberg 2205fc9aaf Update io.rsocket to 1.1.4
Closes gh-13357
2023-06-16 12:16:15 -05:00
Steve Riesenberg 629299d6ee Update io.projectreactor to 2022.0.8
Closes gh-13355
2023-06-16 12:16:09 -05:00
Steve Riesenberg d71a051fd3 Update micrometer-observation to 1.10.8
Closes gh-13354
2023-06-16 12:16:06 -05:00
Steve Riesenberg 064e4e03ce Update unboundid-ldapsdk to 6.0.9
Closes gh-13353
2023-06-16 12:16:02 -05:00
Steve Riesenberg 43916836e5 Update com.nimbusds to 9.43.3
Closes gh-13352
2023-06-16 12:15:59 -05:00
Steve Riesenberg 8f6f6ab423 Update jackson-bom to 2.14.3
Closes gh-13349
2023-06-16 12:15:50 -05:00
Steve Riesenberg 498772d7f6 Update logback-classic to 1.4.8
Closes gh-13348
2023-06-16 12:15:46 -05:00
Marcus Da Coregio 898791b584 Merge branch '5.8.x' into 6.0.x
Closes gh-13323
2023-06-15 08:46:26 -03:00
Marcus Da Coregio 13530d5eba Merge branch '5.7.x' into 5.8.x
Closes gh-13322
2023-06-15 08:46:03 -03:00
Marcus Da Coregio d67c11cfbd Add Proxy Server Configuration to nav
Closes gh-13313
2023-06-15 08:45:49 -03:00
Rob Winch 720809ff7d Merge branch '5.8.x' into 6.0.x
Closes gh-13317
2023-06-14 16:21:14 -05:00
Rob Winch a81c0459e2 Merge branch '5.7.x' into 5.8.x
Closes gh-13316
2023-06-14 16:20:40 -05:00
Rob Winch a387f401b7 Merge branch '5.6.x' into 5.7.x
Closes gh-13315
2023-06-14 16:20:02 -05:00
Rob Winch 9bd04ed0e0 Fix Documentation Title
Closes gh-13314
2023-06-14 16:18:46 -05:00
Josh Cummings 5f26daedcb Error On Unsupported Client Authentication Methods
Closes gh-13144
2023-06-12 15:13:13 -06:00
Rob Winch b472a06848 Merge branch '5.8.x' into 6.0.x
Closes gh-13293
2023-06-08 17:17:34 -05:00
Rob Winch 312b758b3a Merge branch '5.7.x' into 5.8.x
Closes gh-13292
2023-06-08 17:14:34 -05:00
Rob Winch 68cec50bad Merge branch '5.6.x' into 5.7.x
Closes gh-13291
2023-06-08 17:08:54 -05:00
Rob Winch 04692d9ee8 Fix Antora Warnings
Closes gh-13210
2023-06-08 17:02:19 -05:00
Rob Winch 8cfb6a4cdc cralwer.algolia.com performs indexing 2023-06-08 13:46:46 -05:00
Marcus Da Coregio e1e61a9bd1 Remove net.sourceforge.nekohtml:nekohtml dependency
This dependency was being used by the deprecated spring-security-openid module, now that it is removed, we can also remove the dependency

Closes gh-13286
2023-06-06 10:06:05 -03:00
Josh Cummings ce90bcc6fc Merge branch '5.8.x' into 6.0.x 2023-06-05 14:28:40 -06:00
Josh Cummings 1c6dc1aaac Merge branch '5.7.x' into 5.8.x 2023-06-05 14:27:56 -06:00
Anubhav Ahlawat 2279f9fd39 Typos in persistence section 2023-06-05 14:27:43 -06:00
Janne Valkealahti c053f6f0c6 Make eclipse/vscode project import work
- Gradle projects contain cycles which comes from dependencies to
  test sources which is not a problem in gradle but eclipse metadata
  generation is getting confused. Thus we need settings to relax errors
  org.eclipse.jdt.core.circularClasspath=warning
  org.eclipse.jdt.core.incompleteClasspath=warning
- Additionally .classpath entries needs to be changes having
  without_test_code=false
  test=false
- Aspects end up getting source dirs `build/classes/java/main`
  and `build/resources/main` which never have sources. Vscode complains
  about that, eclipse is fine. Remove those from entries.
- In tests `htmlunit` depends on `xml-apis`. `xml-apis` are now part
  of jdk and eclipse complains about that. Excluse these in a gradle build.
- Both eclipse and vscode don't currently work with buildship, due to
  project cycles and buildship cannot be configured. It's possible to
  create metadata from `eclipse` task manually which then can be imported.
  For this we need to disable automatic import in vscode using buildship.
  This goes to `.vscode/settings.json` workspace config.
- Then with these changes user can do something like
  git clean -fxd && ./gradlew clean build cleanEclipse eclipse -x checkstyleNohttp -x test -x integrationTest
  and import projects manually.
2023-06-05 13:34:30 -06:00
Josh Cummings d8db849400 Merge branch '5.8.x' into 6.0.x 2023-06-05 13:24:03 -06:00
Josh Cummings df4411fb48 Merge branch '5.7.x' into 5.8.x 2023-06-05 13:18:22 -06:00
Josh Cummings e834543eed Change possessives to use two ticks 2023-06-05 13:17:15 -06:00
1993heqiang 783f674704 Fix typo authentication.adoc 2023-06-05 13:17:15 -06:00
1993heqiang 933debebeb Fix typo overview.adoc 2023-06-05 13:17:15 -06:00
delver 2629fb2061 Fix Kotlin typo 2023-06-05 13:08:17 -06:00
delver 77c337bac4 Fix invalid link 2023-06-05 13:08:17 -06:00
Marcus Da Coregio 4e3517e03a Merge branch '5.8.x' into 6.0.x
Closes gh-13281
2023-06-05 16:03:58 -03:00
Marcus Da Coregio b47420f8a2 Merge branch '5.7.x' into 5.8.x
Closes gh-13280
2023-06-05 16:02:30 -03:00
Marcus Da Coregio 7250abc185 Does not apply a Configurer when disabled from another DSL
Closes gh-13203
2023-06-05 16:01:20 -03:00
Vladimir Kidyaev d47e3e8cd8 Delete duplicate line from oauth2/client/core.adoc 2023-06-05 12:56:24 -06:00
Josh Cummings 45683349a4 Merge branch '5.8.x' into 6.0.x
Closes gh-13278
2023-06-05 12:48:43 -06:00
Josh Cummings 9ac286e8ea Merge branch '5.7.x' into 5.8.x
Closes gh-13231
2023-06-05 12:47:23 -06:00
Christoph Zuleger 06e58e4c34 Update JavaDoc of BasicAuthenticationFilter
Remove deprecated hint to use Digest Auth in favor of Basic Auth.
2023-06-05 12:46:30 -06:00
Josh Cummings 590e9e23d9 Merge branch '5.8.x' into 6.0.x
Closes gh-13253
2023-05-31 15:42:41 -06:00
Josh Cummings 79f1cf799d Merge branch '5.7.x' into 5.8.x
Closes gh-13252
2023-05-31 15:31:31 -06:00
Josh Cummings bcc1cfc28a Restore OAuth2AuthorizedClientRepository Test Instrumentation
Closes gh-13113
2023-05-31 15:30:03 -06:00
Marcus Da Coregio adeadae92b Merge branch '5.8.x' into 6.0.x
Closes gh-13247
2023-05-30 15:27:44 -03:00
Marcus Da Coregio aa8b691ee3 Merge branch '5.7.x' into 5.8.x
Closes gh-13246
2023-05-30 15:26:29 -03:00
Marcus Da Coregio 663f5cf76b Remove /plugins-release and /plugins-snapshot from build
Those repositories now need authentication, and we do not actually need them, the dependencies can be resolved from Maven Central

Closes gh-13245
2023-05-30 15:25:54 -03:00
Josh Cummings 0ac554ee3e Merge branch '5.8.x' into 6.0.x
Closes gh-13238
2023-05-26 14:25:20 -06:00
Josh Cummings 8ccc03fdb6 Update Resource Server Docs
Closes gh-13147
2023-05-26 14:24:31 -06:00
Marcus Da Coregio f9b6d173ab Add initial Native section to reference docs
Also add the first subsection approaching how to handle method security in a native image

Closes gh-12029
Closes gh-13226
2023-05-26 15:04:18 -03:00
Marcus Da Coregio 55c374da32 Merge branch '5.8.x' into 6.0.x
Closes gh-13228
2023-05-25 09:52:34 -03:00
Marcus Da Coregio 5c88b95af5 Mention that authorizeHttpRequests does not support GrantedAuthorityDefaults
Closes gh-13227
2023-05-25 09:51:36 -03:00
Josh Cummings 69b17f3d3f Merge branch '5.8.x' into 6.0.x
Closes gh-13222
2023-05-24 15:29:39 -06:00
Josh Cummings c1002ff745 Improve Error Handling
Closes gh-13143
2023-05-24 15:29:15 -06:00
Josh Cummings 71703dc371 Update Symlink for 6.0
Issue gh-13131
2023-05-24 14:40:50 -06:00
Josh Cummings 90b37d6d07 Merge branch '5.8.x' into 6.0.x 2023-05-24 14:38:23 -06:00
Josh Cummings 73cb9862ad Update Symlink for 5.8
Issue gh-13131
2023-05-24 14:37:18 -06:00
Marcus Da Coregio ce5aa9e694 Merge branch '5.8.x' into 6.0.x 2023-05-24 15:00:17 -03:00
Marcus Da Coregio f8e39336cb Merge branch '5.7.x' into 5.8.x 2023-05-24 14:59:27 -03:00
Marcus Da Coregio a53cbb838b Polish
Issue gh-13155
2023-05-24 14:59:16 -03:00
joerg-richter-5234 8287289bcb Fix XContentTypeOptionsServerHttpHeadersWriter
set constant value to X-Content-Type-Options

Closes gh-13155
2023-05-24 14:59:14 -03:00
Josh Cummings 40699dc3b7 Merge branch '5.8.x' into 6.0.x
Closes gh-13217
2023-05-24 10:10:36 -06:00
Josh Cummings 68b052218a Add @EnableTransactionManagement Details
Closes gh-13152
2023-05-24 10:10:00 -06:00
Josh Cummings 7c54c0e4fa Merge branch '5.8.x' into 6.0.x
Closes gh-13208
2023-05-22 15:43:27 -06:00
Josh Cummings 62ede47d86 Merge branch '5.7.x' into 5.8.x
Closes gh-13207
2023-05-22 15:42:36 -06:00
Josh Cummings 1eefd433b6 Add spring-security.xsd symlink
Closes gh-13131
2023-05-22 15:42:02 -06:00
Josh Cummings c3174179a3 Merge branch '5.8.x' into 6.0.x
Closes gh-13205
2023-05-22 14:45:28 -06:00
Jan Marten 6b19728c54 Fix legacy-websocket-configuration cross-reference
Closes gh-12969
2023-05-22 14:44:34 -06:00
Josh Cummings 7efa275abc Merge branch '5.8.x' into 6.0.x
Closes gh-13198
2023-05-19 16:43:57 -06:00
Josh Cummings 35ad1f857e Only Register as Advisor in Proxy Mode
Closes gh-13160
2023-05-19 16:33:46 -06:00
Josh Cummings cdac708051 Merge branch '5.8.x' into 6.0.x 2023-05-18 11:08:00 -06:00
Josh Cummings 219faf29d4 Merge branch '5.7.x' into 5.8.x 2023-05-18 11:07:42 -06:00
moli b6f3cb71e6 Add Missing AuthorizationRequestRepository in Snippet
Closes PR-13099
2023-05-18 10:54:19 -06:00
Josh Cummings 7a937fc900 Merge branch '5.8.x' into 6.0.x 2023-05-18 09:59:58 -06:00
daisuzz 734dc98e50 Fix typo in authorization.adoc 2023-05-18 09:59:23 -06:00
Josh Cummings 4c5bf3bdf5 Polish
Use StringUtils#hasText

PR gh-13179
2023-05-18 09:17:02 -06:00
Dennis Frommknecht af233a2a00 Use consistent list of micrometer tags in web observation handler
The tag `spring.security.reached.filter.name` is only set if a
filter-name is available, otherwise the tag is omitted entirely. This
leads to issues with metric-exporters that don't support dynamic tags,
but rather expect tag-names of a metric to be always the same. The most
prominent example is the Prometheus-exporter.

Instead of omitting the tag if no filer-name is set, a none-value is
applied instead, making the tag-list consistent in all cases

Closes gh-13179
2023-05-18 09:17:02 -06:00
Josh Cummings b438bc5384 Merge branch '5.8.x' into 6.0.x
Closes gh-13182
2023-05-15 17:30:14 -06:00
lukasz.migdalek f4915890cc Use Spec Order for Verifying Signatures
Closes gh-12346
2023-05-15 17:24:22 -06:00
Josh Cummings 06c15035fd Merge branch '5.8.x' into 6.0.x 2023-05-15 15:02:30 -06:00
Frederico Alves ed0369ac71 Address CVE-2023-1370
Change oauth2-oidc-sdk to 9.43.2
2023-05-15 14:59:22 -06:00
Frederico Alves ce5570bb06 Address CVE-2023-1370
Bump oauth2-oidc-sdk to 10.7.1 to update json-smart to 2.4.10
2023-05-15 14:59:22 -06:00
403 changed files with 10564 additions and 24475 deletions
+2 -2
View File
@@ -5,8 +5,8 @@ on:
tags: '**'
repository_dispatch:
types: request-build-reference # legacy
#schedule:
#- cron: '0 10 * * *' # Once per day at 10am UTC
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions: read-all
jobs:
@@ -1,20 +0,0 @@
name: Rebuild Search Index
on:
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions: read-all
jobs:
build:
if: github.repository_owner == 'spring-projects'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: docs-build
fetch-depth: 1
- name: Dispatch
env:
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
run: gh workflow run rebuild-search-index.yml -r $(git rev-parse --abbrev-ref HEAD)
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
# List of active maintenance branches.
branch: [ main, 6.0.x, 5.8.x, 5.7.x ]
branch: [ main, 6.0.x, 5.8.x, 5.7.x, 5.6.x ]
runs-on: ubuntu-latest
steps:
- name: Checkout
+3
View File
@@ -0,0 +1,3 @@
{
"java.import.gradle.enabled": false
}
+196 -118
View File
@@ -1,154 +1,232 @@
= Contributing to Spring Security
_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._
First off, thank you for taking the time to contribute! :+1: :tada:
_Please refer back to this document as a checklist before issuing any pull request; this will save time for everyone!_
== Table of Contents
= Code of Conduct
* <<code-of-conduct>>
* <<how-to-contribute>>
* <<ask-questions>>
* <<find-an-issue>>
* <<create-an-issue>>
* <<issue-lifecycle>>
* <<submit-a-pull-request>>
* <<build-from-source>>
* <<code-style>>
Please see our https://github.com/spring-projects/.github/blob/main/CODE_OF_CONDUCT.md[code of conduct].
[[code-of-conduct]]
== Code of Conduct
= Similar but different
This project is governed by the https://github.com/spring-projects/.github/blob/main/CODE_OF_CONDUCT.md[Spring code of conduct].
By participating you are expected to uphold this code.
Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
Each Spring module is slightly different from one 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 https://github.com/spring-projects/spring-framework/wiki/Contributor-guidelines[Spring Framework Contributor guidelines]. However, there are some subtle differences between the two documents, so please be sure to read this document thoroughly.
[[how-to-contribute]]
== How to Contribute
= Importing into IDE
[[ask-questions]]
=== Ask Questions
The following provides information on setting up a development environment that can run the sample in https://www.springsource.org/sts[Spring Tool Suite 3.6.0+]. Other IDE's should work using Gradle's IDE support, but have not been tested.
If you have a question, check Stack Overflow using
https://stackoverflow.com/questions/tagged/spring-security+or+spring-ldap+or+spring-authorization-server+or+spring-session?tab=Newest[this list of tags].
Find an existing discussion, or start a new one if necessary.
* 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
If you believe there is an issue, search through https://github.com/spring-projects/spring-security/issues[existing issues] trying a few different ways to find discussions, past or current, that are related to the issue.
Reading those discussions helps you to learn about the issue, and helps us to make a decision.
As of new versions of Spring Tool Suite, you might need to install Groovy Eclipse pointing directly to the updated plugin location. To install Groovy Eclipse on Spring Tool Suite based on Eclipse Oxigen you must do the following steps:
[[find-an-issue]]
=== Find an Existing Issue
Help -> Install New Software… -> Add the following URL into _Work with_ field:
https://dist.springsource.org/snapshot/GRECLIPSE/e4.7/[https://dist.springsource.org/snapshot/GRECLIPSE/e4.7/]
There are many issues in Spring Security with the labels https://github.com/spring-projects/spring-security/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+ideal-for-contribution%22[`ideal-for-contribution`] or https://github.com/spring-projects/spring-security/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+first-timers-only%22[`first-timers-only`] that are a great way to contribute to a discussion or <<submit-a-pull-request,to a PR>>.
You can volunteer by commenting on these tickets, and we will assign them to you.
= Understand the basics
[[create-an-issue]]
=== Create an Issue
Not sure what a pull request is, or how to submit one? Take a look at GitHub's excellent https://help.github.com/articles/using-pull-requests[help documentation first].
Reporting an issue or making a feature request is a great way to contribute.
Your feedback and the conversations that result from it provide a continuous flow of ideas.
However, before creating a ticket, please take the time to <<ask-questions,ask and research>> first.
= Search GitHub issues; create an issue if necessary
If you create an issue after a discussion on Stack Overflow, please provide a description in the issue instead of simply referring to Stack Overflow.
The issue tracker is an important place of record for design discussions and should be self-sufficient.
Is there already an issue that addresses your concern? Do a bit of searching in our https://github.com/spring-projects/spring-security/issues[GitHub 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.
Once you're ready, create an issue on https://github.com/spring-projects/spring-security/issues[GitHub].
= Discuss non-trivial contribution ideas with committers
Many issues are caused by subtle behavior, typos, and unintended configuration.
Creating a https://stackoverflow.com/help/minimal-reproducible-example[Minimal Reproducible Example] (starting with https://start.spring.io for example) of the problem helps the team quickly triage your issue and get to the core of the problem.
If you're considering anything more than correcting a typo or fixing a minor bug, please discuss it on the https://gitter.im/spring-projects/spring-security[Spring Security Gitter] 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.
We love contributors, and we may ask you to <<submit-a-pull-request,submit a PR with a fix>>.
= Sign the Contributor License Agreement
[[issue-lifecycle]]
=== Issue Lifecycle
If you have not previously done so, please fill out and submit the https://cla.pivotal.io/sign/spring[Contributor License Agreement].
When an issue is first created, it is flagged `waiting-for-triage` waiting for a team member to triage it.
Once the issue has been reviewed, the team may ask for further information if needed, and based on the findings, the issue is either assigned a target branch (or no branch if a feature) or is closed with a specific status.
The target branch is https://spring.io/projects/spring-security#support[the earliest supported branch] where <<choose-a-branch,the change will be applied>>.
= Create your branch from oldest maintenance branch
When a fix is ready, the issue is closed and may still be re-opened until the fix is released.
After that the issue will typically no longer be reopened.
In rare cases if the issue was not at all fixed, the issue may be re-opened.
In most cases however any follow-up reports will need to be created as new issues with a fresh description.
Create your topic branch to be submitted as a pull request from the oldest impacted and supported maintenance branch.
You can find the supported versions by looking at the https://github.com/spring-projects/spring-security/milestones[milestones page].
Switch to a branch named `<major>.<minor>.x` from the smallest milestone in the format of `<major>.<minor>.<patch>(-<prerelease>)`.
The spring team will ensure the code gets merged forward into additional branches.
[[build-from-source]]
=== Build from Source
= Use short branch names
See https://github.com/spring-projects/spring-security/tree/main#building-from-source[Build from Source] for instructions on how to check out, build, and import the Spring Security source code into your IDE.
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` or `fix-typo`. 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.
[[code-style]]
=== Source Code Style
= Keep commits focused
The wiki pages https://github.com/spring-projects/spring-framework/wiki/Code-Style[Code Style] and https://github.com/spring-projects/spring-framework/wiki/IntelliJ-IDEA-Editor-Settings[IntelliJ IDEA Editor Settings] define the source file coding standards we use along with some IDEA editor settings we customize.
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.
To format the code as well as check the style, run `./gradle format check`.
= Mind the whitespace
[[submit-a-pull-request]]
=== Submit a Pull Request
Please carefully follow the whitespace and formatting conventions already present in the framework.
We are excited for your pull request! :heart:
. Tabs, not spaces
. Unix (LF), not dos (CRLF) line endings
. Eliminate all trailing whitespace
. Aim to wrap code at 120 characters, but favor readability over wrapping
. Preserve existing formatting; i.e. do not reformat code for its own sake
. Search the codebase using `git grep` and other tools to discover common naming conventions, etc.
. UTF-8 encoding for Java sources and XML files
Please do your best to follow these steps.
Don't worry if you don't get them all correct the first time, we will help you.
Whitespace management tips
. You can use the https://marketplace.eclipse.org/content/anyedit-tools[AnyEdit Eclipse plugin] to ensure spaces are used and to clean up trailing whitespaces.
. Use Git's `pre-commit.sample` hook to prevent invalid whitespace from being pushed out. You can enable it by moving `.git/hooks/pre-commit.sample` to `.git/hooks/pre-commit` and ensuring it is executable. For more information on hooks refer to https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks[https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks].
= Add Apache license header to all new classes
[[sign-cla]]
1. If you have not previously done so, please sign the https://cla.spring.io/sign/spring[Contributor License Agreement].
You will be reminded automatically when you submit the PR.
[[create-an-issue]]
1. Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier discuss with the team first to determine the right fix or enhancement.
For typos and straightforward bug fixes, starting with a pull request is encouraged.
Please include a description for context and motivation.
Note that the team may close your pull request if it's not a fit for the project.
[[choose-a-branch]]
1. Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).
If there is no milestone, choose `main`.
Once merged, the fix will be forwarded-ported to applicable branches including `main`.
[[create-a-local-branch]]
1. Create a local branch
If this is for an issue, consider a branch name with the issue number, like `gh-22276`.
[[write-tests]]
1. Add JUnit Tests for your changes
[[update-copyright]]
1. In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
[[add-since]]
1. If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
[[change-rnc]]
1. If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
[[format-code]]
1. For each commit, build the code using `./gradlew format check`.
This command ensures the code meets most of <<code-style,the style guide>>; a notable exception is import order.
[[commit-atomically]]
1. Choose the granularity of your commits consciously and squash commits that represent
multiple edits or corrections of the same logical change.
See https://git-scm.com/book/en/Git-Tools-Rewriting-History[Rewriting History section of Pro Git] for an overview of streamlining the commit history.
[[format-commit-messages]]
1. Format commit messages using 55 characters for the subject line, 72 characters per line
for the description, followed by the issue fixed, for example, `Closes gh-22276`.
See the https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines[Commit Guidelines section of Pro Git] for best practices around commit messages, and use `git log` to see some examples.
Present tense is preferred.
+
[indent=0]
----
Address NullPointerException
/*
* Copyright 2002-2020 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
*
* https://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.
*/
Closes gh-22276
----
[[reference-issue]]
1. If there is a prior issue, reference the GitHub issue number in the description of the pull request.
+
[indent=0]
----
Closes gh-22276
package ...;
----
If accepted, your contribution may be heavily modified as needed prior to merging.
You will likely retain author attribution for your Git commits granted that the bulk of your changes remain intact.
You may also be asked to rework the submission.
= Update Apache license header to modified files as necessary
If asked to make corrections, simply push the changes against the same branch, and your pull request will be updated.
In other words, you do not need to create a new pull request when asked to make changes.
When it is time to merge, you'll be asked to squash your commits.
Always check the date range in the license header. For example, if you've modified a file in 2020 whose header still reads
==== Participate in Reviews
----
* Copyright 2002-2012 the original author or authors.
----
Helping to review pull requests is another great way to contribute.
Your feedback can help to shape the implementation of new features.
When reviewing pull requests, however, please refrain from approving or rejecting a PR unless you are a core committer for Spring Security.
then be sure to update it to the current year appropriately (e.g. 2020)
----
* Copyright 2002-2020 the original author or authors.
----
= Use @since tags for newly-added public API types and methods
Example:
----
/**
* …
*
* @author First Last
* @since 5.4
* @see …
*/
----
= Submit JUnit test cases for all behavior changes
Search the codebase to find related unit tests and add additional `@Test` methods within.
. 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`.
. 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 https://www.relaxng.org[RELAX NG] 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:
----
./gradlew :spring-security-config:rncToXsd
----
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 https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History[many resources online] to help you understand how these tools work.
= 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. Make sure the name is properly capitalized as submitted to the https://cla.pivotal.io[Pivotal Contributor License Agreement]:
----
First Last <user@mail.com>
----
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:
----
git config --global user.name "First Last"
git config --global user.email user@example.com
----
or locally for the current repository by omitting the `--global` flag:
----
git config user.name "First Last"
git config user.email user@example.com
----
= Format commit messages
. Keep the subject line to 50 characters or less if possible
. Do not end the subject line with a period
. In the body of the commit message, explain how things worked before this commit, what has changed, and how things work now
. Include `Closes gh-<issue-number>` at the end if this fixes a GitHub issue
. Avoid markdown, including back-ticks identifying code
Example:
----
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
Closes gh-123
----
= Run all tests prior to submission
----
./gradlew clean build integrationTest
----
= 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:*
. 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
. Add any additional information and ask questions; start a conversation, or continue one from GitHub Issues
. Mention any GitHub Issues
. 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.
+1 -1
View File
@@ -59,9 +59,9 @@ The scheduled release process currently runs every Monday but only releases when
The automated release process occurs on the following branches:
* `main`
* `6.0.x`
* `5.8.x`
* `5.7.x`
* `5.6.x`
For each of the above branches, the automated process performs the following checks before proceeding with the release:
-1
View File
@@ -9,7 +9,6 @@ buildscript {
classpath "com.netflix.nebula:nebula-project-plugin:8.2.0"
}
repositories {
maven { url 'https://repo.spring.io/plugins-snapshot' }
maven { url 'https://plugins.gradle.org/m2/' }
}
}
+2 -3
View File
@@ -10,7 +10,6 @@ sourceCompatibility = JavaVersion.VERSION_17
repositories {
gradlePluginPortal()
mavenCentral()
maven { url 'https://repo.spring.io/plugins-release/' }
}
sourceSets {
@@ -87,8 +86,8 @@ dependencies {
implementation localGroovy()
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
implementation 'io.projectreactor:reactor-core:3.5.6'
implementation 'org.gretty:gretty:4.0.3'
implementation 'io.projectreactor:reactor-core:3.5.8'
implementation 'org.gretty:gretty:3.0.9'
implementation 'com.apollographql.apollo:apollo-runtime:2.4.5'
implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0'
implementation 'com.github.spullara.mustache.java:compiler:0.9.4'
@@ -54,6 +54,7 @@ public abstract class AbstractSpringJavaPlugin implements Plugin<Project> {
pluginManager.apply("io.spring.convention.javadoc-options");
pluginManager.apply("io.spring.convention.checkstyle");
pluginManager.apply(CopyPropertiesPlugin);
pluginManager.apply("io.spring.convention.eclipse");
project.jar {
manifest.attributes["Created-By"] =
@@ -0,0 +1,72 @@
/*
* Copyright 2023 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
*
* https://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 io.spring.gradle.convention
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.plugins.ide.eclipse.EclipsePlugin
/**
* Plugin to tweak .classpath settings so that eclipse/sts/vscode can
* be imported cleanly.
*
* @author Janne Valkealahti
*/
class EclipsePlugin implements Plugin<Project> {
@Override
void apply(Project project) {
project.plugins.apply(EclipsePlugin)
project.eclipse {
classpath {
file {
whenMerged {
// for aspects gradle eclipse integration wrongly creates lib entries for
// aspects/build/classes/java/main and aspects/build/resources/main which
// never has anything. eclipse/sts don't care, vscode language server
// complains about missing folders. So remove those from a .classpath
entries.removeAll {
entry -> entry.kind == 'lib' && (entry.path.contains('aspects/build/classes/java/main') || entry.path.contains('aspects/build/resources/main'))
}
// there are cycles and then dependencies between projects main sources and
// test sources. Looks like gradle eclipse integration is getting confused.
// thus just relax rules so that projects always sees everything from
// dependant project.
entries
.findAll { entry -> entry instanceof org.gradle.plugins.ide.eclipse.model.ProjectDependency }
.each {
it.entryAttributes['without_test_code'] = 'false'
it.entryAttributes['test'] = 'false'
}
}
}
}
jdt {
file {
withProperties { properties ->
// there are cycles and then dependencies between projects main sources and
// test sources. Relax those from error to warning
properties['org.eclipse.jdt.core.circularClasspath'] = 'warning'
properties['org.eclipse.jdt.core.incompleteClasspath'] = 'warning'
}
}
}
}
}
}
@@ -1,9 +1,9 @@
package io.spring.gradle.convention
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.bundling.Zip
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.bundling.Zip
public class SchemaZipPlugin implements Plugin<Project> {
@@ -37,6 +37,15 @@ public class SchemaZipPlugin implements Plugin<Project> {
from xsdFile.path
}
}
File symlink = module.sourceSets.main.resources.find {
it.path.endsWith('org/springframework/security/config/spring-security.xsd')
}
if (symlink != null) {
schemaZip.into('security') {
duplicatesStrategy 'exclude'
from symlink.path
}
}
}
}
}
@@ -16,6 +16,10 @@
package org.springframework.gradle.sagan;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.Assert;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.TaskAction;
@@ -25,6 +29,8 @@ import org.springframework.gradle.github.user.User;
public class SaganCreateReleaseTask extends DefaultTask {
private static final Pattern VERSION_PATTERN = Pattern.compile("^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-.+)?$");
@Input
private String gitHubAccessToken;
@Input
@@ -44,9 +50,12 @@ public class SaganCreateReleaseTask extends DefaultTask {
// Antora reference docs URLs for snapshots do not contain -SNAPSHOT
String referenceDocUrl = this.referenceDocUrl;
if (this.version.endsWith("-SNAPSHOT")) {
referenceDocUrl = this.referenceDocUrl
.replace("{version}", this.version)
.replace("-SNAPSHOT", "");
Matcher versionMatcher = VERSION_PATTERN.matcher(this.version);
Assert.isTrue(versionMatcher.matches(), "Version " + this.version + " does not match expected pattern");
String majorVersion = versionMatcher.group(1);
String minorVersion = versionMatcher.group(2);
String majorMinorVersion = String.format("%s.%s-SNAPSHOT", majorVersion, minorVersion);
referenceDocUrl = this.referenceDocUrl.replace("{version}", majorMinorVersion);
}
SaganApi sagan = new SaganApi(user.getLogin(), this.gitHubAccessToken);
@@ -0,0 +1 @@
implementation-class=io.spring.gradle.convention.EclipsePlugin
-25
View File
@@ -1,25 +0,0 @@
apply plugin: 'io.spring.convention.spring-module'
dependencies {
management platform(project(":spring-security-dependencies"))
api project(':spring-security-core')
api project(':spring-security-web')
api 'org.apereo.cas.client:cas-client-core'
api 'org.springframework:spring-beans'
api 'org.springframework:spring-context'
api 'org.springframework:spring-core'
api 'org.springframework:spring-web'
optional 'com.fasterxml.jackson.core:jackson-databind'
provided 'jakarta.servlet:jakarta.servlet-api'
testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api"
testImplementation "org.junit.jupiter:junit-jupiter-params"
testImplementation "org.junit.jupiter:junit-jupiter-engine"
testImplementation "org.mockito:mockito-core"
testImplementation "org.mockito:mockito-junit-jupiter"
testImplementation "org.springframework:spring-test"
testImplementation 'org.skyscreamer:jsonassert'
}
@@ -1,37 +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
*
* https://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.cas;
/**
* Sets the appropriate parameters for CAS's implementation of SAML (which is not
* guaranteed to be actually SAML compliant).
*
* @author Scott Battaglia
* @since 3.0
*/
public final class SamlServiceProperties extends ServiceProperties {
public static final String DEFAULT_SAML_ARTIFACT_PARAMETER = "SAMLart";
public static final String DEFAULT_SAML_SERVICE_PARAMETER = "TARGET";
public SamlServiceProperties() {
super.setArtifactParameter(DEFAULT_SAML_ARTIFACT_PARAMETER);
super.setServiceParameter(DEFAULT_SAML_SERVICE_PARAMETER);
}
}
@@ -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
*
* https://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.cas;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
* Stores properties related to this CAS service.
* <p>
* Each web application capable of processing CAS tickets is known as a service. This
* class stores the properties that are relevant to the local CAS service, being the
* application that is being secured by Spring Security.
*
* @author Ben Alex
*/
public class ServiceProperties implements InitializingBean {
public static final String DEFAULT_CAS_ARTIFACT_PARAMETER = "ticket";
public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service";
private String service;
private boolean authenticateAllArtifacts;
private boolean sendRenew = false;
private String artifactParameter = DEFAULT_CAS_ARTIFACT_PARAMETER;
private String serviceParameter = DEFAULT_CAS_SERVICE_PARAMETER;
@Override
public void afterPropertiesSet() {
Assert.hasLength(this.service, "service cannot be empty.");
Assert.hasLength(this.artifactParameter, "artifactParameter cannot be empty.");
Assert.hasLength(this.serviceParameter, "serviceParameter cannot be empty.");
}
/**
* Represents the service the user is authenticating to.
* <p>
* This service is the callback URL belonging to the local Spring Security System for
* Spring secured application. For example,
*
* <pre>
* https://www.mycompany.com/application/login/cas
* </pre>
* @return the URL of the service the user is authenticating to
*/
public final String getService() {
return this.service;
}
/**
* Indicates whether the <code>renew</code> parameter should be sent to the CAS login
* URL and CAS validation URL.
* <p>
* If <code>true</code>, it will force CAS to authenticate the user again (even if the
* user has previously authenticated). During ticket validation it will require the
* ticket was generated as a consequence of an explicit login. High security
* applications would probably set this to <code>true</code>. Defaults to
* <code>false</code>, providing automated single sign on.
* @return whether to send the <code>renew</code> parameter to CAS
*/
public final boolean isSendRenew() {
return this.sendRenew;
}
public final void setSendRenew(final boolean sendRenew) {
this.sendRenew = sendRenew;
}
public final void setService(final String service) {
this.service = service;
}
public final String getArtifactParameter() {
return this.artifactParameter;
}
/**
* Configures the Request Parameter to look for when attempting to see if a CAS ticket
* was sent from the server.
* @param artifactParameter the id to use. Default is "ticket".
*/
public final void setArtifactParameter(final String artifactParameter) {
this.artifactParameter = artifactParameter;
}
/**
* Configures the Request parameter to look for when attempting to send a request to
* CAS.
* @return the service parameter to use. Default is "service".
*/
public final String getServiceParameter() {
return this.serviceParameter;
}
public final void setServiceParameter(final String serviceParameter) {
this.serviceParameter = serviceParameter;
}
public final boolean isAuthenticateAllArtifacts() {
return this.authenticateAllArtifacts;
}
/**
* If true, then any non-null artifact (ticket) should be authenticated. Additionally,
* the service will be determined dynamically in order to ensure the service matches
* the expected value for this artifact.
* @param authenticateAllArtifacts
*/
public final void setAuthenticateAllArtifacts(final boolean authenticateAllArtifacts) {
this.authenticateAllArtifacts = authenticateAllArtifacts;
}
}
@@ -1,60 +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
*
* https://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.cas.authentication;
import java.util.ArrayList;
import org.apereo.cas.client.validation.Assertion;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.SpringSecurityCoreVersion;
/**
* Temporary authentication object needed to load the user details service.
*
* @author Scott Battaglia
* @since 3.0
*/
public final class CasAssertionAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Assertion assertion;
private final String ticket;
public CasAssertionAuthenticationToken(final Assertion assertion, final String ticket) {
super(new ArrayList<>());
this.assertion = assertion;
this.ticket = ticket;
}
@Override
public Object getPrincipal() {
return this.assertion.getPrincipal().getName();
}
@Override
public Object getCredentials() {
return this.ticket;
}
public Assertion getAssertion() {
return this.assertion;
}
}
@@ -1,234 +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
*
* https://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.cas.authentication;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.TicketValidationException;
import org.apereo.cas.client.validation.TicketValidator;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.core.log.LogMessage;
import org.springframework.security.authentication.AccountStatusUserDetailsChecker;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.util.Assert;
/**
* An {@link AuthenticationProvider} implementation that integrates with JA-SIG Central
* Authentication Service (CAS).
* <p>
* This <code>AuthenticationProvider</code> is capable of validating
* {@link CasServiceTicketAuthenticationToken} requests which contain a
* <code>principal</code> name equal to either
* {@link CasServiceTicketAuthenticationToken#CAS_STATEFUL_IDENTIFIER} or
* {@link CasServiceTicketAuthenticationToken#CAS_STATELESS_IDENTIFIER}. It can also
* validate a previously created {@link CasAuthenticationToken}.
*
* @author Ben Alex
* @author Scott Battaglia
*/
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
private final UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
private String key;
private TicketValidator ticketValidator;
private ServiceProperties serviceProperties;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
@Override
public void afterPropertiesSet() {
Assert.notNull(this.authenticationUserDetailsService, "An authenticationUserDetailsService must be set");
Assert.notNull(this.ticketValidator, "A ticketValidator must be set");
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
Assert.hasText(this.key,
"A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
Assert.notNull(this.messages, "A message source must be set");
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
// If an existing CasAuthenticationToken, just check we created it
if (authentication instanceof CasAuthenticationToken) {
if (this.key.hashCode() != ((CasAuthenticationToken) authentication).getKeyHash()) {
throw new BadCredentialsException(this.messages.getMessage("CasAuthenticationProvider.incorrectKey",
"The presented CasAuthenticationToken does not contain the expected key"));
}
return authentication;
}
// Ensure credentials are presented
if ((authentication.getCredentials() == null) || "".equals(authentication.getCredentials())) {
throw new BadCredentialsException(this.messages.getMessage("CasAuthenticationProvider.noServiceTicket",
"Failed to provide a CAS service ticket to validate"));
}
boolean stateless = (authentication instanceof CasServiceTicketAuthenticationToken token && token.isStateless());
CasAuthenticationToken result = null;
if (stateless) {
// Try to obtain from cache
result = this.statelessTicketCache.getByTicketId(authentication.getCredentials().toString());
}
if (result == null) {
result = this.authenticateNow(authentication);
result.setDetails(authentication.getDetails());
}
if (stateless) {
// Add to cache
this.statelessTicketCache.putTicketInCache(result);
}
return result;
}
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
try {
Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(),
getServiceUrl(authentication));
UserDetails userDetails = loadUserByAssertion(assertion);
this.userDetailsChecker.check(userDetails);
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
}
catch (TicketValidationException ex) {
throw new BadCredentialsException(ex.getMessage(), ex);
}
}
/**
* Gets the serviceUrl. If the {@link Authentication#getDetails()} is an instance of
* {@link ServiceAuthenticationDetails}, then
* {@link ServiceAuthenticationDetails#getServiceUrl()} is used. Otherwise, the
* {@link ServiceProperties#getService()} is used.
* @param authentication
* @return
*/
private String getServiceUrl(Authentication authentication) {
String serviceUrl;
if (authentication.getDetails() instanceof ServiceAuthenticationDetails) {
return ((ServiceAuthenticationDetails) authentication.getDetails()).getServiceUrl();
}
Assert.state(this.serviceProperties != null,
"serviceProperties cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
Assert.state(this.serviceProperties.getService() != null,
"serviceProperties.getService() cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
serviceUrl = this.serviceProperties.getService();
logger.debug(LogMessage.format("serviceUrl = %s", serviceUrl));
return serviceUrl;
}
/**
* Template method for retrieving the UserDetails based on the assertion. Default is
* to call configured userDetailsService and pass the username. Deployers can override
* this method and retrieve the user based on any criteria they desire.
* @param assertion The CAS Assertion.
* @return the UserDetails.
*/
protected UserDetails loadUserByAssertion(final Assertion assertion) {
final CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, "");
return this.authenticationUserDetailsService.loadUserDetails(token);
}
@SuppressWarnings("unchecked")
/**
* Sets the UserDetailsService to use. This is a convenience method to invoke
*/
public void setUserDetailsService(final UserDetailsService userDetailsService) {
this.authenticationUserDetailsService = new UserDetailsByNameServiceWrapper(userDetailsService);
}
public void setAuthenticationUserDetailsService(
final AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService) {
this.authenticationUserDetailsService = authenticationUserDetailsService;
}
public void setServiceProperties(final ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
protected String getKey() {
return this.key;
}
public void setKey(String key) {
this.key = key;
}
public StatelessTicketCache getStatelessTicketCache() {
return this.statelessTicketCache;
}
protected TicketValidator getTicketValidator() {
return this.ticketValidator;
}
@Override
public void setMessageSource(final MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
public void setStatelessTicketCache(final StatelessTicketCache statelessTicketCache) {
this.statelessTicketCache = statelessTicketCache;
}
public void setTicketValidator(final TicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
}
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
@Override
public boolean supports(final Class<?> authentication) {
return (CasServiceTicketAuthenticationToken.class.isAssignableFrom(authentication))
|| (CasAuthenticationToken.class.isAssignableFrom(authentication))
|| (CasAssertionAuthenticationToken.class.isAssignableFrom(authentication));
}
}
@@ -1,173 +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
*
* https://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.cas.authentication;
import java.io.Serializable;
import java.util.Collection;
import org.apereo.cas.client.validation.Assertion;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Represents a successful CAS <code>Authentication</code>.
*
* @author Ben Alex
* @author Scott Battaglia
*/
public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object credentials;
private final Object principal;
private final UserDetails userDetails;
private final int keyHash;
private final Assertion assertion;
/**
* Constructor.
* @param key to identify if this object made by a given
* {@link CasAuthenticationProvider}
* @param principal typically the UserDetails object (cannot be <code>null</code>)
* @param credentials the service/proxy ticket ID from CAS (cannot be
* <code>null</code>)
* @param authorities the authorities granted to the user (from the
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
* be <code>null</code>)
* @param userDetails the user details (from the
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
* be <code>null</code>)
* @param assertion the assertion returned from the CAS servers. It contains the
* principal and how to obtain a proxy ticket for the user.
* @throws IllegalArgumentException if a <code>null</code> was passed
*/
public CasAuthenticationToken(final String key, final Object principal, final Object credentials,
final Collection<? extends GrantedAuthority> authorities, final UserDetails userDetails,
final Assertion assertion) {
this(extractKeyHash(key), principal, credentials, authorities, userDetails, assertion);
}
/**
* Private constructor for Jackson Deserialization support
* @param keyHash hashCode of provided key to identify if this object made by a given
* {@link CasAuthenticationProvider}
* @param principal typically the UserDetails object (cannot be <code>null</code>)
* @param credentials the service/proxy ticket ID from CAS (cannot be
* <code>null</code>)
* @param authorities the authorities granted to the user (from the
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
* be <code>null</code>)
* @param userDetails the user details (from the
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
* be <code>null</code>)
* @param assertion the assertion returned from the CAS servers. It contains the
* principal and how to obtain a proxy ticket for the user.
* @throws IllegalArgumentException if a <code>null</code> was passed
* @since 4.2
*/
private CasAuthenticationToken(final Integer keyHash, final Object principal, final Object credentials,
final Collection<? extends GrantedAuthority> authorities, final UserDetails userDetails,
final Assertion assertion) {
super(authorities);
if ((principal == null) || "".equals(principal) || (credentials == null) || "".equals(credentials)
|| (authorities == null) || (userDetails == null) || (assertion == null)) {
throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
}
this.keyHash = keyHash;
this.principal = principal;
this.credentials = credentials;
this.userDetails = userDetails;
this.assertion = assertion;
setAuthenticated(true);
}
private static Integer extractKeyHash(String key) {
Assert.hasLength(key, "key cannot be null or empty");
return key.hashCode();
}
@Override
public boolean equals(final Object obj) {
if (!super.equals(obj)) {
return false;
}
if (obj instanceof CasAuthenticationToken) {
CasAuthenticationToken test = (CasAuthenticationToken) obj;
if (!this.assertion.equals(test.getAssertion())) {
return false;
}
if (this.getKeyHash() != test.getKeyHash()) {
return false;
}
return true;
}
return false;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + this.credentials.hashCode();
result = 31 * result + this.principal.hashCode();
result = 31 * result + this.userDetails.hashCode();
result = 31 * result + this.keyHash;
result = 31 * result + ObjectUtils.nullSafeHashCode(this.assertion);
return result;
}
@Override
public Object getCredentials() {
return this.credentials;
}
public int getKeyHash() {
return this.keyHash;
}
@Override
public Object getPrincipal() {
return this.principal;
}
public Assertion getAssertion() {
return this.assertion;
}
public UserDetails getUserDetails() {
return this.userDetails;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString());
sb.append(" Assertion: ").append(this.assertion);
sb.append(" Credentials (Service/Proxy Ticket): ").append(this.credentials);
return (sb.toString());
}
}
@@ -1,112 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.cas.authentication;
import java.io.Serial;
import java.util.Collection;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.util.Assert;
/**
* An {@link org.springframework.security.core.Authentication} implementation that is
* designed to process CAS service ticket.
*
* @author Hal Deadman
* @since 6.1
*/
public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationToken {
static final String CAS_STATELESS_IDENTIFIER = "_cas_stateless_";
static final String CAS_STATEFUL_IDENTIFIER = "_cas_stateful_";
@Serial
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final String identifier;
private Object credentials;
/**
* This constructor can be safely used by any code that wishes to create a
* <code>CasServiceTicketAuthenticationToken</code>, as the {@link #isAuthenticated()}
* will return <code>false</code>.
*
*/
public CasServiceTicketAuthenticationToken(String identifier, Object credentials) {
super(null);
this.identifier = identifier;
this.credentials = credentials;
setAuthenticated(false);
}
/**
* This constructor should only be used by <code>AuthenticationManager</code> or
* <code>AuthenticationProvider</code> implementations that are satisfied with
* producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
* authentication token.
* @param identifier
* @param credentials
* @param authorities
*/
public CasServiceTicketAuthenticationToken(String identifier, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.identifier = identifier;
this.credentials = credentials;
super.setAuthenticated(true);
}
public static CasServiceTicketAuthenticationToken stateful(Object credentials) {
return new CasServiceTicketAuthenticationToken(CAS_STATEFUL_IDENTIFIER, credentials);
}
public static CasServiceTicketAuthenticationToken stateless(Object credentials) {
return new CasServiceTicketAuthenticationToken(CAS_STATELESS_IDENTIFIER, credentials);
}
public boolean isStateless() {
return CAS_STATELESS_IDENTIFIER.equals(this.identifier);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.identifier;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
Assert.isTrue(!isAuthenticated,
"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}
@@ -1,64 +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
*
* https://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.cas.authentication;
/**
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
* in instances where storing of tickets for stateless session management is not required.
* <p>
* This is the default StatelessTicketCache of the @link {@link CasAuthenticationProvider}
* to eliminate the unnecessary dependency on EhCache that applications have even if they
* are not using the stateless session management.
*
* @author Scott Battaglia
* @see CasAuthenticationProvider
*/
public final class NullStatelessTicketCache implements StatelessTicketCache {
/**
* @return null since we are not storing any tickets.
*/
@Override
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
return null;
}
/**
* This is a no-op since we are not storing tickets.
*/
@Override
public void putTicketInCache(final CasAuthenticationToken token) {
// nothing to do
}
/**
* This is a no-op since we are not storing tickets.
*/
@Override
public void removeTicketFromCache(final CasAuthenticationToken token) {
// nothing to do
}
/**
* This is a no-op since we are not storing tickets.
*/
@Override
public void removeTicketFromCache(final String serviceTicket) {
// nothing to do
}
}
@@ -1,69 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.cas.authentication;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cache.Cache;
import org.springframework.core.log.LogMessage;
import org.springframework.util.Assert;
/**
* Caches tickets using a Spring IoC defined {@link Cache}.
*
* @author Marten Deinum
* @since 3.2
*
*/
public class SpringCacheBasedTicketCache implements StatelessTicketCache {
private static final Log logger = LogFactory.getLog(SpringCacheBasedTicketCache.class);
private final Cache cache;
public SpringCacheBasedTicketCache(Cache cache) {
Assert.notNull(cache, "cache mandatory");
this.cache = cache;
}
@Override
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
final Cache.ValueWrapper element = (serviceTicket != null) ? this.cache.get(serviceTicket) : null;
logger.debug(LogMessage.of(() -> "Cache hit: " + (element != null) + "; service ticket: " + serviceTicket));
return (element != null) ? (CasAuthenticationToken) element.get() : null;
}
@Override
public void putTicketInCache(final CasAuthenticationToken token) {
String key = token.getCredentials().toString();
logger.debug(LogMessage.of(() -> "Cache put: " + key));
this.cache.put(key, token);
}
@Override
public void removeTicketFromCache(final CasAuthenticationToken token) {
logger.debug(LogMessage.of(() -> "Cache remove: " + token.getCredentials().toString()));
this.removeTicketFromCache(token.getCredentials().toString());
}
@Override
public void removeTicketFromCache(final String serviceTicket) {
this.cache.evict(serviceTicket);
}
}
@@ -1,110 +0,0 @@
/*
* Copyright 2004 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
*
* https://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.cas.authentication;
/**
* Caches CAS service tickets and CAS proxy tickets for stateless connections.
*
* <p>
* When a service ticket or proxy ticket is validated against the CAS server, it is unable
* to be used again. Most types of callers are stateful and are associated with a given
* <code>HttpSession</code>. This allows the affirmative CAS validation outcome to be
* stored in the <code>HttpSession</code>, meaning the removal of the ticket from the CAS
* server is not an issue.
* </p>
*
* <P>
* Stateless callers, such as remoting protocols, cannot take advantage of
* <code>HttpSession</code>. If the stateless caller is located a significant network
* distance from the CAS server, acquiring a fresh service ticket or proxy ticket for each
* invocation would be expensive.
* </p>
*
* <P>
* To avoid this issue with stateless callers, it is expected stateless callers will
* obtain a single service ticket or proxy ticket, and then present this same ticket to
* the Spring Security secured application on each occasion. As no
* <code>HttpSession</code> is available for such callers, the affirmative CAS validation
* outcome cannot be stored in this location.
* </p>
*
* <P>
* The <code>StatelessTicketCache</code> enables the service tickets and proxy tickets
* belonging to stateless callers to be placed in a cache. This in-memory cache stores the
* <code>CasAuthenticationToken</code>, effectively providing the same capability as a
* <code>HttpSession</code> with the ticket identifier being the key rather than a session
* identifier.
* </p>
*
* <P>
* Implementations should provide a reasonable timeout on stored entries, such that the
* stateless caller are not required to unnecessarily acquire fresh CAS service tickets or
* proxy tickets.
* </p>
*
* @author Ben Alex
*/
public interface StatelessTicketCache {
/**
* Retrieves the <code>CasAuthenticationToken</code> associated with the specified
* ticket.
*
* <P>
* If not found, returns a <code>null</code><code>CasAuthenticationToken</code>.
* </p>
* @return the fully populated authentication token
*/
CasAuthenticationToken getByTicketId(String serviceTicket);
/**
* Adds the specified <code>CasAuthenticationToken</code> to the cache.
*
* <P>
* The {@link CasAuthenticationToken#getCredentials()} method is used to retrieve the
* service ticket number.
* </p>
* @param token to be added to the cache
*/
void putTicketInCache(CasAuthenticationToken token);
/**
* Removes the specified ticket from the cache, as per
* {@link #removeTicketFromCache(String)}.
*
* <P>
* Implementations should use {@link CasAuthenticationToken#getCredentials()} to
* obtain the ticket and then delegate to the {@link #removeTicketFromCache(String)}
* method.
* </p>
* @param token to be removed
*/
void removeTicketFromCache(CasAuthenticationToken token);
/**
* Removes the specified ticket from the cache, meaning that future calls will require
* a new service ticket.
*
* <P>
* This is in case applications wish to provide a session termination capability for
* their stateless clients.
* </p>
* @param serviceTicket to be removed
*/
void removeTicketFromCache(String serviceTicket);
}
@@ -1,21 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.
*/
/**
* An {@code AuthenticationProvider} that can process CAS service tickets and proxy
* tickets.
*/
package org.springframework.security.cas.authentication;
@@ -1,69 +0,0 @@
/*
* Copyright 2015-2023 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
*
* https://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.cas.jackson2;
import java.util.Date;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apereo.cas.client.authentication.AttributePrincipal;
/**
* Helps in jackson deserialization of class
* {@link org.apereo.cas.client.validation.AssertionImpl}, which is used with
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken}. To use
* this class we need to register with
* {@link com.fasterxml.jackson.databind.ObjectMapper}. Type information will be stored
* in @class property.
* <p>
* <pre>
* ObjectMapper mapper = new ObjectMapper();
* mapper.registerModule(new CasJackson2Module());
* </pre>
*
* @author Jitendra Singh
* @since 4.2
* @see CasJackson2Module
* @see org.springframework.security.jackson2.SecurityJackson2Modules
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
class AssertionImplMixin {
/**
* Mixin Constructor helps in deserialize
* {@link org.apereo.cas.client.validation.AssertionImpl}
* @param principal the Principal to associate with the Assertion.
* @param validFromDate when the assertion is valid from.
* @param validUntilDate when the assertion is valid to.
* @param authenticationDate when the assertion is authenticated.
* @param attributes the key/value pairs for this attribute.
*/
@JsonCreator
AssertionImplMixin(@JsonProperty("principal") AttributePrincipal principal,
@JsonProperty("validFromDate") Date validFromDate, @JsonProperty("validUntilDate") Date validUntilDate,
@JsonProperty("authenticationDate") Date authenticationDate,
@JsonProperty("attributes") Map<String, Object> attributes) {
}
}
@@ -1,66 +0,0 @@
/*
* Copyright 2015-2023 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
*
* https://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.cas.jackson2;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apereo.cas.client.proxy.ProxyRetriever;
/**
* Helps in deserialize
* {@link org.apereo.cas.client.authentication.AttributePrincipalImpl} which is used with
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken}. Type
* information will be stored in property named @class.
* <p>
* <pre>
* ObjectMapper mapper = new ObjectMapper();
* mapper.registerModule(new CasJackson2Module());
* </pre>
*
* @author Jitendra Singh
* @since 4.2
* @see CasJackson2Module
* @see org.springframework.security.jackson2.SecurityJackson2Modules
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
class AttributePrincipalImplMixin {
/**
* Mixin Constructor helps in deserialize
* {@link org.apereo.cas.client.authentication.AttributePrincipalImpl}
* @param name the unique identifier for the principal.
* @param attributes the key/value pairs for this principal.
* @param proxyGrantingTicket the ticket associated with this principal.
* @param proxyRetriever the ProxyRetriever implementation to call back to the CAS
* server.
*/
@JsonCreator
AttributePrincipalImplMixin(@JsonProperty("name") String name,
@JsonProperty("attributes") Map<String, Object> attributes,
@JsonProperty("proxyGrantingTicket") String proxyGrantingTicket,
@JsonProperty("proxyRetriever") ProxyRetriever proxyRetriever) {
}
}
@@ -1,83 +0,0 @@
/*
* Copyright 2015-2023 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
*
* https://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.cas.jackson2;
import java.util.Collection;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apereo.cas.client.validation.Assertion;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.authentication.CasAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
* Mixin class which helps in deserialize
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken} using
* jackson. Two more dependent classes needs to register along with this mixin class.
* <ol>
* <li>{@link org.springframework.security.cas.jackson2.AssertionImplMixin}</li>
* <li>{@link org.springframework.security.cas.jackson2.AttributePrincipalImplMixin}</li>
* </ol>
*
* <p>
*
* <pre>
* ObjectMapper mapper = new ObjectMapper();
* mapper.registerModule(new CasJackson2Module());
* </pre>
*
* @author Jitendra Singh
* @since 4.2
* @see CasJackson2Module
* @see org.springframework.security.jackson2.SecurityJackson2Modules
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
@JsonIgnoreProperties(ignoreUnknown = true)
class CasAuthenticationTokenMixin {
/**
* Mixin Constructor helps in deserialize {@link CasAuthenticationToken}
* @param keyHash hashCode of provided key to identify if this object made by a given
* {@link CasAuthenticationProvider}
* @param principal typically the UserDetails object (cannot be <code>null</code>)
* @param credentials the service/proxy ticket ID from CAS (cannot be
* <code>null</code>)
* @param authorities the authorities granted to the user (from the
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
* be <code>null</code>)
* @param userDetails the user details (from the
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
* be <code>null</code>)
* @param assertion the assertion returned from the CAS servers. It contains the
* principal and how to obtain a proxy ticket for the user.
*/
@JsonCreator
CasAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash, @JsonProperty("principal") Object principal,
@JsonProperty("credentials") Object credentials,
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
@JsonProperty("userDetails") UserDetails userDetails, @JsonProperty("assertion") Assertion assertion) {
}
}
@@ -1,58 +0,0 @@
/*
* Copyright 2015-2023 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
*
* https://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.cas.jackson2;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.apereo.cas.client.authentication.AttributePrincipalImpl;
import org.apereo.cas.client.validation.AssertionImpl;
import org.springframework.security.cas.authentication.CasAuthenticationToken;
import org.springframework.security.jackson2.SecurityJackson2Modules;
/**
* Jackson module for spring-security-cas. This module register
* {@link AssertionImplMixin}, {@link AttributePrincipalImplMixin} and
* {@link CasAuthenticationTokenMixin}. If no default typing enabled by default then it'll
* enable it because typing info is needed to properly serialize/deserialize objects. In
* order to use this module just add this module into your ObjectMapper configuration.
*
* <pre>
* ObjectMapper mapper = new ObjectMapper();
* mapper.registerModule(new CasJackson2Module());
* </pre> <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list
* of all security modules on the classpath.</b>
*
* @author Jitendra Singh.
* @since 4.2
* @see org.springframework.security.jackson2.SecurityJackson2Modules
*/
public class CasJackson2Module extends SimpleModule {
public CasJackson2Module() {
super(CasJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));
}
@Override
public void setupModule(SetupContext context) {
SecurityJackson2Modules.enableDefaultTyping(context.getOwner());
context.setMixInAnnotations(AssertionImpl.class, AssertionImplMixin.class);
context.setMixInAnnotations(AttributePrincipalImpl.class, AttributePrincipalImplMixin.class);
context.setMixInAnnotations(CasAuthenticationToken.class, CasAuthenticationTokenMixin.class);
}
}
@@ -1,21 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.
*/
/**
* Spring Security support for Apereo's Central Authentication Service
* (<a href="https://github.com/apereo/cas">CAS</a>).
*/
package org.springframework.security.cas;
@@ -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
*
* https://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.cas.userdetails;
import org.apereo.cas.client.validation.Assertion;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
/**
* Abstract class for using the provided CAS assertion to construct a new User object.
* This generally is most useful when combined with a SAML-based response from the CAS
* Server/client.
*
* @author Scott Battaglia
* @since 3.0
*/
public abstract class AbstractCasAssertionUserDetailsService
implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
@Override
public final UserDetails loadUserDetails(final CasAssertionAuthenticationToken token) {
return loadUserDetails(token.getAssertion());
}
/**
* Protected template method for construct a
* {@link org.springframework.security.core.userdetails.UserDetails} via the supplied
* CAS assertion.
* @param assertion the assertion to use to construct the new UserDetails. CANNOT be
* NULL.
* @return the newly constructed UserDetails.
*/
protected abstract UserDetails loadUserDetails(Assertion assertion);
}
@@ -1,87 +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
*
* https://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.cas.userdetails;
import java.util.ArrayList;
import java.util.List;
import org.apereo.cas.client.validation.Assertion;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
/**
* Populates the {@link org.springframework.security.core.GrantedAuthority}s for a user by
* reading a list of attributes that were returned as part of the CAS response. Each
* attribute is read and each value of the attribute is turned into a GrantedAuthority. If
* the attribute has no value then its not added.
*
* @author Scott Battaglia
* @since 3.0
*/
public final class GrantedAuthorityFromAssertionAttributesUserDetailsService
extends AbstractCasAssertionUserDetailsService {
private static final String NON_EXISTENT_PASSWORD_VALUE = "NO_PASSWORD";
private final String[] attributes;
private boolean convertToUpperCase = true;
public GrantedAuthorityFromAssertionAttributesUserDetailsService(final String[] attributes) {
Assert.notNull(attributes, "attributes cannot be null.");
Assert.isTrue(attributes.length > 0, "At least one attribute is required to retrieve roles from.");
this.attributes = attributes;
}
@SuppressWarnings("unchecked")
@Override
protected UserDetails loadUserDetails(final Assertion assertion) {
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for (String attribute : this.attributes) {
Object value = assertion.getPrincipal().getAttributes().get(attribute);
if (value != null) {
if (value instanceof List) {
for (Object o : (List<?>) value) {
grantedAuthorities.add(createSimpleGrantedAuthority(o));
}
}
else {
grantedAuthorities.add(createSimpleGrantedAuthority(value));
}
}
}
return new User(assertion.getPrincipal().getName(), NON_EXISTENT_PASSWORD_VALUE, true, true, true, true,
grantedAuthorities);
}
private SimpleGrantedAuthority createSimpleGrantedAuthority(Object o) {
return new SimpleGrantedAuthority(this.convertToUpperCase ? o.toString().toUpperCase() : o.toString());
}
/**
* Converts the returned attribute values to uppercase values.
* @param convertToUpperCase true if it should convert, false otherwise.
*/
public void setConvertToUpperCase(final boolean convertToUpperCase) {
this.convertToUpperCase = convertToUpperCase;
}
}
@@ -1,151 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.cas.web;
import java.io.IOException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apereo.cas.client.util.CommonUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.util.Assert;
/**
* Used by the <code>ExceptionTranslationFilter</code> to commence authentication via the
* JA-SIG Central Authentication Service (CAS).
* <p>
* The user's browser will be redirected to the JA-SIG CAS enterprise-wide login page.
* This page is specified by the <code>loginUrl</code> property. Once login is complete,
* the CAS login page will redirect to the page indicated by the <code>service</code>
* property. The <code>service</code> is a HTTP URL belonging to the current application.
* The <code>service</code> URL is monitored by the {@link CasAuthenticationFilter}, which
* will validate the CAS login was successful.
*
* @author Ben Alex
* @author Scott Battaglia
*/
public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {
private ServiceProperties serviceProperties;
private String loginUrl;
/**
* Determines whether the Service URL should include the session id for the specific
* user. As of CAS 3.0.5, the session id will automatically be stripped. However,
* older versions of CAS (i.e. CAS 2), do not automatically strip the session
* identifier (this is a bug on the part of the older server implementations), so an
* option to disable the session encoding is provided for backwards compatibility.
*
* By default, encoding is enabled.
*/
private boolean encodeServiceUrlWithSessionId = true;
@Override
public void afterPropertiesSet() {
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
Assert.notNull(this.serviceProperties.getService(), "serviceProperties.getService() cannot be null.");
}
@Override
public final void commence(final HttpServletRequest servletRequest, HttpServletResponse response,
AuthenticationException authenticationException) throws IOException {
String urlEncodedService = createServiceUrl(servletRequest, response);
String redirectUrl = createRedirectUrl(urlEncodedService);
preCommence(servletRequest, response);
new DefaultRedirectStrategy().sendRedirect(servletRequest, response, redirectUrl);
// response.sendRedirect(redirectUrl);
}
/**
* Constructs a new Service Url. The default implementation relies on the CAS client
* to do the bulk of the work.
* @param request the HttpServletRequest
* @param response the HttpServlet Response
* @return the constructed service url. CANNOT be NULL.
*/
protected String createServiceUrl(HttpServletRequest request, HttpServletResponse response) {
return CommonUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), null,
this.serviceProperties.getArtifactParameter(), this.encodeServiceUrlWithSessionId);
}
/**
* Constructs the Url for Redirection to the CAS server. Default implementation relies
* on the CAS client to do the bulk of the work.
* @param serviceUrl the service url that should be included.
* @return the redirect url. CANNOT be NULL.
*/
protected String createRedirectUrl(String serviceUrl) {
return CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), serviceUrl,
this.serviceProperties.isSendRenew(), false);
}
/**
* Template method for you to do your own pre-processing before the redirect occurs.
* @param request the HttpServletRequest
* @param response the HttpServletResponse
*/
protected void preCommence(HttpServletRequest request, HttpServletResponse response) {
}
/**
* The enterprise-wide CAS login URL. Usually something like
* <code>https://www.mycompany.com/cas/login</code>.
* @return the enterprise-wide CAS login URL
*/
public final String getLoginUrl() {
return this.loginUrl;
}
public final ServiceProperties getServiceProperties() {
return this.serviceProperties;
}
public final void setLoginUrl(String loginUrl) {
this.loginUrl = loginUrl;
}
public final void setServiceProperties(ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
/**
* Sets whether to encode the service url with the session id or not.
* @param encodeServiceUrlWithSessionId whether to encode the service url with the
* session id or not.
*/
public final void setEncodeServiceUrlWithSessionId(boolean encodeServiceUrlWithSessionId) {
this.encodeServiceUrlWithSessionId = encodeServiceUrlWithSessionId;
}
/**
* Sets whether to encode the service url with the session id or not.
* @return whether to encode the service url with the session id or not.
*
*/
protected boolean getEncodeServiceUrlWithSessionId() {
return this.encodeServiceUrlWithSessionId;
}
}
@@ -1,386 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.cas.web;
import java.io.IOException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;
import org.apereo.cas.client.util.CommonUtils;
import org.apereo.cas.client.validation.TicketValidator;
import org.springframework.core.log.LogMessage;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasServiceTicketAuthenticationToken;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
/**
* Processes a CAS service ticket, obtains proxy granting tickets, and processes proxy
* tickets.
* <h2>Service Tickets</h2>
* <p>
* A service ticket consists of an opaque ticket string. It arrives at this filter by the
* user's browser successfully authenticating using CAS, and then receiving a HTTP
* redirect to a <code>service</code>. The opaque ticket string is presented in the
* <code>ticket</code> request parameter.
* <p>
* This filter monitors the <code>service</code> URL so it can receive the service ticket
* and process it. By default this filter processes the URL <tt>/login/cas</tt>. When
* processing this URL, the value of {@link ServiceProperties#getService()} is used as the
* <tt>service</tt> when validating the <code>ticket</code>. This means that it is
* important that {@link ServiceProperties#getService()} specifies the same value as the
* <tt>filterProcessesUrl</tt>.
* <p>
* Processing the service ticket involves creating a
* <code>CasServiceTicketAuthenticationToken</code> which uses
* {@link CasServiceTicketAuthenticationToken#CAS_STATEFUL_IDENTIFIER} for the
* <code>principal</code> and the opaque ticket string as the <code>credentials</code>.
* <h2>Obtaining Proxy Granting Tickets</h2>
* <p>
* If specified, the filter can also monitor the <code>proxyReceptorUrl</code>. The filter
* will respond to requests matching this url so that the CAS Server can provide a PGT to
* the filter. Note that in addition to the <code>proxyReceptorUrl</code> a non-null
* <code>proxyGrantingTicketStorage</code> must be provided in order for the filter to
* respond to proxy receptor requests. By configuring a shared
* {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the
* CasAuthenticationFilter one can have the CasAuthenticationFilter handle the proxying
* requirements for CAS.
* <h2>Proxy Tickets</h2>
* <p>
* The filter can process tickets present on any url. This is useful when wanting to
* process proxy tickets. In order for proxy tickets to get processed
* {@link ServiceProperties#isAuthenticateAllArtifacts()} must return <code>true</code>.
* Additionally, if the request is already authenticated, authentication will <b>not</b>
* occur. Last, {@link AuthenticationDetailsSource#buildDetails(Object)} must return a
* {@link ServiceAuthenticationDetails}. This can be accomplished using the
* {@link ServiceAuthenticationDetailsSource}. In this case
* {@link ServiceAuthenticationDetails#getServiceUrl()} will be used for the service url.
* <p>
* Processing the proxy ticket involves creating a
* <code>CasServiceTicketAuthenticationToken</code> which uses
* {@link CasServiceTicketAuthenticationToken#CAS_STATELESS_IDENTIFIER} for the
* <code>principal</code> and the opaque ticket string as the <code>credentials</code>.
* When a proxy ticket is successfully authenticated, the FilterChain continues and the
* <code>authenticationSuccessHandler</code> is not used.
* <h2>Notes about the <code>AuthenticationManager</code></h2>
* <p>
* The configured <code>AuthenticationManager</code> is expected to provide a provider
* that can recognise <code>CasServiceTicketAuthenticationToken</code>s containing this
* special <code>principal</code> name, and process them accordingly by validation with
* the CAS server. Additionally, it should be capable of using the result of
* {@link ServiceAuthenticationDetails#getServiceUrl()} as the service when validating the
* ticket.
* <h2>Example Configuration</h2>
* <p>
* An example configuration that supports service tickets, obtaining proxy granting
* tickets, and proxy tickets is illustrated below:
*
* <pre>
* &lt;b:bean id=&quot;serviceProperties&quot;
* class=&quot;org.springframework.security.cas.ServiceProperties&quot;
* p:service=&quot;https://service.example.com/cas-sample/login/cas&quot;
* p:authenticateAllArtifacts=&quot;true&quot;/&gt;
* &lt;b:bean id=&quot;casEntryPoint&quot;
* class=&quot;org.springframework.security.cas.web.CasAuthenticationEntryPoint&quot;
* p:serviceProperties-ref=&quot;serviceProperties&quot; p:loginUrl=&quot;https://login.example.org/cas/login&quot; /&gt;
* &lt;b:bean id=&quot;casFilter&quot;
* class=&quot;org.springframework.security.cas.web.CasAuthenticationFilter&quot;
* p:authenticationManager-ref=&quot;authManager&quot;
* p:serviceProperties-ref=&quot;serviceProperties&quot;
* p:proxyGrantingTicketStorage-ref=&quot;pgtStorage&quot;
* p:proxyReceptorUrl=&quot;/login/cas/proxyreceptor&quot;&gt;
* &lt;b:property name=&quot;authenticationDetailsSource&quot;&gt;
* &lt;b:bean class=&quot;org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource&quot;/&gt;
* &lt;/b:property&gt;
* &lt;b:property name=&quot;authenticationFailureHandler&quot;&gt;
* &lt;b:bean class=&quot;org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler&quot;
* p:defaultFailureUrl=&quot;/casfailed.jsp&quot;/&gt;
* &lt;/b:property&gt;
* &lt;/b:bean&gt;
* &lt;!--
* NOTE: In a real application you should not use an in memory implementation. You will also want
* to ensure to clean up expired tickets by calling ProxyGrantingTicketStorage.cleanup()
* --&gt;
* &lt;b:bean id=&quot;pgtStorage&quot; class=&quot;org.apereo.cas.client.proxy.ProxyGrantingTicketStorageImpl&quot;/&gt;
* &lt;b:bean id=&quot;casAuthProvider&quot; class=&quot;org.springframework.security.cas.authentication.CasAuthenticationProvider&quot;
* p:serviceProperties-ref=&quot;serviceProperties&quot;
* p:key=&quot;casAuthProviderKey&quot;&gt;
* &lt;b:property name=&quot;authenticationUserDetailsService&quot;&gt;
* &lt;b:bean
* class=&quot;org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper&quot;&gt;
* &lt;b:constructor-arg ref=&quot;userService&quot; /&gt;
* &lt;/b:bean&gt;
* &lt;/b:property&gt;
* &lt;b:property name=&quot;ticketValidator&quot;&gt;
* &lt;b:bean
* class=&quot;org.apereo.cas.client.validation.Cas20ProxyTicketValidator&quot;
* p:acceptAnyProxy=&quot;true&quot;
* p:proxyCallbackUrl=&quot;https://service.example.com/cas-sample/login/cas/proxyreceptor&quot;
* p:proxyGrantingTicketStorage-ref=&quot;pgtStorage&quot;&gt;
* &lt;b:constructor-arg value=&quot;https://login.example.org/cas&quot; /&gt;
* &lt;/b:bean&gt;
* &lt;/b:property&gt;
* &lt;b:property name=&quot;statelessTicketCache&quot;&gt;
* &lt;b:bean class=&quot;org.springframework.security.cas.authentication.EhCacheBasedTicketCache&quot;&gt;
* &lt;b:property name=&quot;cache&quot;&gt;
* &lt;b:bean class=&quot;net.sf.ehcache.Cache&quot;
* init-method=&quot;initialise&quot;
* destroy-method=&quot;dispose&quot;&gt;
* &lt;b:constructor-arg value=&quot;casTickets&quot;/&gt;
* &lt;b:constructor-arg value=&quot;50&quot;/&gt;
* &lt;b:constructor-arg value=&quot;true&quot;/&gt;
* &lt;b:constructor-arg value=&quot;false&quot;/&gt;
* &lt;b:constructor-arg value=&quot;3600&quot;/&gt;
* &lt;b:constructor-arg value=&quot;900&quot;/&gt;
* &lt;/b:bean&gt;
* &lt;/b:property&gt;
* &lt;/b:bean&gt;
* &lt;/b:property&gt;
* &lt;/b:bean&gt;
* </pre>
*
* @author Ben Alex
* @author Rob Winch
*/
public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
/**
* The last portion of the receptor url, i.e. /proxy/receptor
*/
private RequestMatcher proxyReceptorMatcher;
/**
* The backing storage to store ProxyGrantingTicket requests.
*/
private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
private boolean authenticateAllArtifacts;
private AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler();
public CasAuthenticationFilter() {
super("/login/cas");
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
setSecurityContextRepository(new HttpSessionSecurityContextRepository());
}
@Override
protected final void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authResult) throws IOException, ServletException {
boolean continueFilterChain = proxyTicketRequest(serviceTicketRequest(request, response), request);
if (!continueFilterChain) {
super.successfulAuthentication(request, response, chain, authResult);
return;
}
this.logger.debug(
LogMessage.format("Authentication success. Updating SecurityContextHolder to contain: %s", authResult));
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
SecurityContextHolder.setContext(context);
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
chain.doFilter(request, response);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {
// if the request is a proxy request process it and return null to indicate the
// request has been processed
if (proxyReceptorRequest(request)) {
this.logger.debug("Responding to proxy receptor request");
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
return null;
}
String serviceTicket = obtainArtifact(request);
if (serviceTicket == null) {
this.logger.debug("Failed to obtain an artifact (cas ticket)");
serviceTicket = "";
}
boolean serviceTicketRequest = serviceTicketRequest(request, response);
CasServiceTicketAuthenticationToken authRequest = serviceTicketRequest
? CasServiceTicketAuthenticationToken.stateful(serviceTicket)
: CasServiceTicketAuthenticationToken.stateless(serviceTicket);
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* If present, gets the artifact (CAS ticket) from the {@link HttpServletRequest}.
* @param request
* @return if present the artifact from the {@link HttpServletRequest}, else null
*/
protected String obtainArtifact(HttpServletRequest request) {
return request.getParameter(this.artifactParameter);
}
/**
* Overridden to provide proxying capabilities.
*/
@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
final boolean serviceTicketRequest = serviceTicketRequest(request, response);
final boolean result = serviceTicketRequest || proxyReceptorRequest(request)
|| (proxyTicketRequest(serviceTicketRequest, request));
if (this.logger.isDebugEnabled()) {
this.logger.debug("requiresAuthentication = " + result);
}
return result;
}
/**
* Sets the {@link AuthenticationFailureHandler} for proxy requests.
* @param proxyFailureHandler
*/
public final void setProxyAuthenticationFailureHandler(AuthenticationFailureHandler proxyFailureHandler) {
Assert.notNull(proxyFailureHandler, "proxyFailureHandler cannot be null");
this.proxyFailureHandler = proxyFailureHandler;
}
/**
* Wraps the {@link AuthenticationFailureHandler} to distinguish between handling
* proxy ticket authentication failures and service ticket failures.
*/
@Override
public final void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler));
}
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl);
}
public final void setProxyGrantingTicketStorage(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
}
public final void setServiceProperties(final ServiceProperties serviceProperties) {
this.artifactParameter = serviceProperties.getArtifactParameter();
this.authenticateAllArtifacts = serviceProperties.isAuthenticateAllArtifacts();
}
/**
* Indicates if the request is elgible to process a service ticket. This method exists
* for readability.
* @param request
* @param response
* @return
*/
private boolean serviceTicketRequest(HttpServletRequest request, HttpServletResponse response) {
boolean result = super.requiresAuthentication(request, response);
this.logger.debug(LogMessage.format("serviceTicketRequest = %s", result));
return result;
}
/**
* Indicates if the request is elgible to process a proxy ticket.
* @param request
* @return
*/
private boolean proxyTicketRequest(boolean serviceTicketRequest, HttpServletRequest request) {
if (serviceTicketRequest) {
return false;
}
boolean result = this.authenticateAllArtifacts && obtainArtifact(request) != null && !authenticated();
this.logger.debug(LogMessage.format("proxyTicketRequest = %s", result));
return result;
}
/**
* Determines if a user is already authenticated.
* @return
*/
private boolean authenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null && authentication.isAuthenticated()
&& !(authentication instanceof AnonymousAuthenticationToken);
}
/**
* Indicates if the request is elgible to be processed as the proxy receptor.
* @param request
* @return
*/
private boolean proxyReceptorRequest(HttpServletRequest request) {
final boolean result = proxyReceptorConfigured() && this.proxyReceptorMatcher.matches(request);
this.logger.debug(LogMessage.format("proxyReceptorRequest = %s", result));
return result;
}
/**
* Determines if the {@link CasAuthenticationFilter} is configured to handle the proxy
* receptor requests.
* @return
*/
private boolean proxyReceptorConfigured() {
final boolean result = this.proxyGrantingTicketStorage != null && this.proxyReceptorMatcher != null;
this.logger.debug(LogMessage.format("proxyReceptorConfigured = %s", result));
return result;
}
/**
* A wrapper for the AuthenticationFailureHandler that will flex the
* {@link AuthenticationFailureHandler} that is used. The value
* {@link CasAuthenticationFilter#setProxyAuthenticationFailureHandler(AuthenticationFailureHandler)}
* will be used for proxy requests that fail. The value
* {@link CasAuthenticationFilter#setAuthenticationFailureHandler(AuthenticationFailureHandler)}
* will be used for service tickets that fail.
*/
private class CasAuthenticationFailureHandler implements AuthenticationFailureHandler {
private final AuthenticationFailureHandler serviceTicketFailureHandler;
CasAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
Assert.notNull(failureHandler, "failureHandler");
this.serviceTicketFailureHandler = failureHandler;
}
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
if (serviceTicketRequest(request, response)) {
this.serviceTicketFailureHandler.onAuthenticationFailure(request, response, exception);
}
else {
CasAuthenticationFilter.this.proxyFailureHandler.onAuthenticationFailure(request, response, exception);
}
}
}
}
@@ -1,146 +0,0 @@
/*
* Copyright 2011-2023 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
*
* https://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.cas.web.authentication;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Pattern;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
/**
* A default implementation of {@link ServiceAuthenticationDetails} that figures out the
* value for {@link #getServiceUrl()} by inspecting the current {@link HttpServletRequest}
* and using the current URL minus the artifact and the corresponding value.
*
* @author Rob Winch
*/
final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
implements ServiceAuthenticationDetails {
private static final long serialVersionUID = 6192409090610517700L;
private final String serviceUrl;
/**
* Creates a new instance
* @param request the current {@link HttpServletRequest} to obtain the
* {@link #getServiceUrl()} from.
* @param artifactPattern the {@link Pattern} that will be used to clean up the query
* string from containing the artifact name and value. This can be created using
* {@link #createArtifactPattern(String)}.
*/
DefaultServiceAuthenticationDetails(String casService, HttpServletRequest request, Pattern artifactPattern)
throws MalformedURLException {
super(request);
URL casServiceUrl = new URL(casService);
int port = getServicePort(casServiceUrl);
final String query = getQueryString(request, artifactPattern);
this.serviceUrl = UrlUtils.buildFullRequestUrl(casServiceUrl.getProtocol(), casServiceUrl.getHost(), port,
request.getRequestURI(), query);
}
/**
* Returns the current URL minus the artifact parameter and its value, if present.
* @see org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails#getServiceUrl()
*/
@Override
public String getServiceUrl() {
return this.serviceUrl;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj) || !(obj instanceof DefaultServiceAuthenticationDetails)) {
return false;
}
ServiceAuthenticationDetails that = (ServiceAuthenticationDetails) obj;
return this.serviceUrl.equals(that.getServiceUrl());
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + this.serviceUrl.hashCode();
return result;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append(super.toString());
result.append("ServiceUrl: ");
result.append(this.serviceUrl);
return result.toString();
}
/**
* If present, removes the artifactParameterName and the corresponding value from the
* query String.
* @param request
* @return the query String minus the artifactParameterName and the corresponding
* value.
*/
private String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {
final String query = request.getQueryString();
if (query == null) {
return null;
}
String result = artifactPattern.matcher(query).replaceFirst("");
if (result.length() == 0) {
return null;
}
// strip off the trailing & only if the artifact was the first query param
return result.startsWith("&") ? result.substring(1) : result;
}
/**
* Creates a {@link Pattern} that can be passed into the constructor. This allows the
* {@link Pattern} to be reused for every instance of
* {@link DefaultServiceAuthenticationDetails}.
* @param artifactParameterName
* @return
*/
static Pattern createArtifactPattern(String artifactParameterName) {
Assert.hasLength(artifactParameterName, "artifactParameterName is expected to have a length");
return Pattern.compile("&?" + Pattern.quote(artifactParameterName) + "=[^&]*");
}
/**
* Gets the port from the casServiceURL ensuring to return the proper value if the
* default port is being used.
* @param casServiceUrl the casServerUrl to be used (i.e.
* "https://example.com/context/login/cas")
* @return the port that is configured for the casServerUrl
*/
private static int getServicePort(URL casServiceUrl) {
int port = casServiceUrl.getPort();
if (port == -1) {
port = casServiceUrl.getDefaultPort();
}
return port;
}
}
@@ -1,42 +0,0 @@
/*
* Copyright 2011-2023 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
*
* https://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.cas.web.authentication;
import java.io.Serializable;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.core.Authentication;
/**
* In order for the {@link CasAuthenticationProvider} to provide the correct service url
* to authenticate the ticket, the returned value of {@link Authentication#getDetails()}
* should implement this interface when tickets can be sent to any URL rather than only
* {@link ServiceProperties#getService()}.
*
* @author Rob Winch
* @see ServiceAuthenticationDetailsSource
*/
public interface ServiceAuthenticationDetails extends Serializable {
/**
* Gets the absolute service url (i.e. https://example.com/service/).
* @return the service url. Cannot be <code>null</code>.
*/
String getServiceUrl();
}
@@ -1,83 +0,0 @@
/*
* Copyright 2011-2023 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
*
* https://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.cas.web.authentication;
import java.net.MalformedURLException;
import java.util.regex.Pattern;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.util.Assert;
/**
* The {@code AuthenticationDetailsSource} that is set on the
* {@code CasAuthenticationFilter} should return a value that implements
* {@code ServiceAuthenticationDetails} if the application needs to authenticate dynamic
* service urls. The
* {@code ServiceAuthenticationDetailsSource#buildDetails(HttpServletRequest)} creates a
* default {@code ServiceAuthenticationDetails}.
*
* @author Rob Winch
*/
public class ServiceAuthenticationDetailsSource
implements AuthenticationDetailsSource<HttpServletRequest, ServiceAuthenticationDetails> {
private final Pattern artifactPattern;
private ServiceProperties serviceProperties;
/**
* Creates an implementation that uses the specified ServiceProperties and the default
* CAS artifactParameterName.
* @param serviceProperties The ServiceProperties to use to construct the serviceUrl.
*/
public ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties) {
this(serviceProperties, ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
}
/**
* Creates an implementation that uses the specified artifactParameterName
* @param serviceProperties The ServiceProperties to use to construct the serviceUrl.
* @param artifactParameterName the artifactParameterName that is removed from the
* current URL. The result becomes the service url. Cannot be null and cannot be an
* empty String.
*/
public ServiceAuthenticationDetailsSource(ServiceProperties serviceProperties, String artifactParameterName) {
Assert.notNull(serviceProperties, "serviceProperties cannot be null");
this.serviceProperties = serviceProperties;
this.artifactPattern = DefaultServiceAuthenticationDetails.createArtifactPattern(artifactParameterName);
}
/**
* @param context the {@code HttpServletRequest} object.
* @return the {@code ServiceAuthenticationDetails} containing information about the
* current request
*/
@Override
public ServiceAuthenticationDetails buildDetails(HttpServletRequest context) {
try {
return new DefaultServiceAuthenticationDetails(this.serviceProperties.getService(), context,
this.artifactPattern);
}
catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
}
}
@@ -1,21 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.
*/
/**
* Authentication processing mechanisms which respond to the submission of authentication
* credentials using CAS.
*/
package org.springframework.security.cas.web.authentication;
@@ -1,20 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.
*/
/**
* Authenticates standard web browser users via CAS.
*/
package org.springframework.security.cas.web;
@@ -1,45 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.cas.authentication;
import java.util.ArrayList;
import java.util.List;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.AssertionImpl;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
/**
* @author Scott Battaglia
* @since 2.0
*
*/
public abstract class AbstractStatelessTicketCacheTests {
protected CasAuthenticationToken getToken() {
List<String> proxyList = new ArrayList<>();
proxyList.add("https://localhost/newPortal/login/cas");
User user = new User("rod", "password", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"));
final Assertion assertion = new AssertionImpl("rod");
return new CasAuthenticationToken("key", user, "ST-0-ER94xMJmn6pha35CQRoZ",
AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"), user, assertion);
}
}
@@ -1,376 +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
*
* https://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.cas.authentication;
import java.util.HashMap;
import java.util.Map;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.AssertionImpl;
import org.apereo.cas.client.validation.TicketValidator;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* Tests {@link CasAuthenticationProvider}.
*
* @author Ben Alex
* @author Scott Battaglia
*/
@SuppressWarnings("unchecked")
public class CasAuthenticationProviderTests {
private UserDetails makeUserDetails() {
return new User("user", "password", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO"));
}
private UserDetails makeUserDetailsFromAuthoritiesPopulator() {
return new User("user", "password", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_A", "ROLE_B"));
}
private ServiceProperties makeServiceProperties() {
final ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setSendRenew(false);
serviceProperties.setService("http://test.com");
return serviceProperties;
}
@Test
public void statefulAuthenticationIsSuccessful() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setServiceProperties(makeServiceProperties());
cap.setTicketValidator(new MockTicketValidator(true));
cap.afterPropertiesSet();
CasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateful("ST-123");
token.setDetails("details");
Authentication result = cap.authenticate(token);
// Confirm ST-123 was NOT added to the cache
assertThat(cache.getByTicketId("ST-456") == null).isTrue();
if (!(result instanceof CasAuthenticationToken)) {
fail("Should have returned a CasAuthenticationToken");
}
CasAuthenticationToken casResult = (CasAuthenticationToken) result;
assertThat(casResult.getPrincipal()).isEqualTo(makeUserDetailsFromAuthoritiesPopulator());
assertThat(casResult.getCredentials()).isEqualTo("ST-123");
assertThat(casResult.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_A"));
assertThat(casResult.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_B"));
assertThat(casResult.getKeyHash()).isEqualTo(cap.getKey().hashCode());
assertThat(casResult.getDetails()).isEqualTo("details");
// Now confirm the CasAuthenticationToken is automatically re-accepted.
// To ensure TicketValidator not called again, set it to deliver an exception...
cap.setTicketValidator(new MockTicketValidator(false));
Authentication laterResult = cap.authenticate(result);
assertThat(laterResult).isEqualTo(result);
}
@Test
public void statelessAuthenticationIsSuccessful() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
CasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateless("ST-456");
token.setDetails("details");
Authentication result = cap.authenticate(token);
// Confirm ST-456 was added to the cache
assertThat(cache.getByTicketId("ST-456") != null).isTrue();
if (!(result instanceof CasAuthenticationToken)) {
fail("Should have returned a CasAuthenticationToken");
}
assertThat(result.getPrincipal()).isEqualTo(makeUserDetailsFromAuthoritiesPopulator());
assertThat(result.getCredentials()).isEqualTo("ST-456");
assertThat(result.getDetails()).isEqualTo("details");
// Now try to authenticate again. To ensure TicketValidator not
// called again, set it to deliver an exception...
cap.setTicketValidator(new MockTicketValidator(false));
// Previously created CasServiceTicketAuthenticationToken is OK
Authentication newResult = cap.authenticate(token);
assertThat(newResult.getPrincipal()).isEqualTo(makeUserDetailsFromAuthoritiesPopulator());
assertThat(newResult.getCredentials()).isEqualTo("ST-456");
}
@Test
public void authenticateAllNullService() throws Exception {
String serviceUrl = "https://service/context";
ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);
given(details.getServiceUrl()).willReturn(serviceUrl);
TicketValidator validator = mock(TicketValidator.class);
given(validator.validate(any(String.class), any(String.class))).willReturn(new AssertionImpl("rod"));
ServiceProperties serviceProperties = makeServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setTicketValidator(validator);
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
String ticket = "ST-456";
CasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateless(ticket);
Authentication result = cap.authenticate(token);
}
@Test
public void authenticateAllAuthenticationIsSuccessful() throws Exception {
String serviceUrl = "https://service/context";
ServiceAuthenticationDetails details = mock(ServiceAuthenticationDetails.class);
given(details.getServiceUrl()).willReturn(serviceUrl);
TicketValidator validator = mock(TicketValidator.class);
given(validator.validate(any(String.class), any(String.class))).willReturn(new AssertionImpl("rod"));
ServiceProperties serviceProperties = makeServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setTicketValidator(validator);
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
String ticket = "ST-456";
CasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateless(ticket);
Authentication result = cap.authenticate(token);
verify(validator).validate(ticket, serviceProperties.getService());
serviceProperties.setAuthenticateAllArtifacts(true);
result = cap.authenticate(token);
verify(validator, times(2)).validate(ticket, serviceProperties.getService());
token.setDetails(details);
result = cap.authenticate(token);
verify(validator).validate(ticket, serviceUrl);
serviceProperties.setAuthenticateAllArtifacts(false);
serviceProperties.setService(null);
cap.setServiceProperties(serviceProperties);
cap.afterPropertiesSet();
result = cap.authenticate(token);
verify(validator, times(2)).validate(ticket, serviceUrl);
token.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest()));
assertThatIllegalStateException().isThrownBy(() -> cap.authenticate(token));
cap.setServiceProperties(null);
cap.afterPropertiesSet();
assertThatIllegalStateException().isThrownBy(() -> cap.authenticate(token));
}
@Test
public void missingTicketIdIsDetected() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
CasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateful("");
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> cap.authenticate(token));
}
@Test
public void invalidKeyIsDetected() throws Exception {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
CasAuthenticationToken token = new CasAuthenticationToken("WRONG_KEY", makeUserDetails(), "credentials",
AuthorityUtils.createAuthorityList("XX"), makeUserDetails(), assertion);
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> cap.authenticate(token));
}
@Test
public void detectsMissingAuthoritiesPopulator() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
assertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet());
}
@Test
public void detectsMissingKey() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
assertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet());
}
@Test
public void detectsMissingStatelessTicketCache() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
// set this explicitly to null to test failure
cap.setStatelessTicketCache(null);
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
assertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet());
}
@Test
public void detectsMissingTicketValidator() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setServiceProperties(makeServiceProperties());
assertThatIllegalArgumentException().isThrownBy(() -> cap.afterPropertiesSet());
}
@Test
public void gettersAndSettersMatch() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
// TODO disabled because why do we need to expose this?
// assertThat(cap.getUserDetailsService() != null).isTrue();
assertThat(cap.getKey()).isEqualTo("qwerty");
assertThat(cap.getStatelessTicketCache() != null).isTrue();
assertThat(cap.getTicketValidator() != null).isTrue();
}
@Test
public void ignoresClassesItDoesNotSupport() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
TestingAuthenticationToken token = new TestingAuthenticationToken("user", "password", "ROLE_A");
assertThat(cap.supports(TestingAuthenticationToken.class)).isFalse();
// Try it anyway
assertThat(cap.authenticate(token)).isNull();
}
@Test
public void ignoresUsernamePasswordAuthenticationTokensWithoutCasIdentifiersAsPrincipal() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
cap.setStatelessTicketCache(new MockStatelessTicketCache());
cap.setTicketValidator(new MockTicketValidator(true));
cap.setServiceProperties(makeServiceProperties());
cap.afterPropertiesSet();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("some_normal_user",
"password", AuthorityUtils.createAuthorityList("ROLE_A"));
assertThat(cap.authenticate(token)).isNull();
}
@Test
public void supportsRequiredTokens() {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
assertThat(cap.supports(CasServiceTicketAuthenticationToken.class)).isTrue();
assertThat(cap.supports(CasAuthenticationToken.class)).isTrue();
}
private class MockAuthoritiesPopulator implements AuthenticationUserDetailsService {
@Override
public UserDetails loadUserDetails(final Authentication token) throws UsernameNotFoundException {
return makeUserDetailsFromAuthoritiesPopulator();
}
}
private class MockStatelessTicketCache implements StatelessTicketCache {
private Map<String, CasAuthenticationToken> cache = new HashMap<>();
@Override
public CasAuthenticationToken getByTicketId(String serviceTicket) {
return this.cache.get(serviceTicket);
}
@Override
public void putTicketInCache(CasAuthenticationToken token) {
this.cache.put(token.getCredentials().toString(), token);
}
@Override
public void removeTicketFromCache(CasAuthenticationToken token) {
throw new UnsupportedOperationException("mock method not implemented");
}
@Override
public void removeTicketFromCache(String serviceTicket) {
throw new UnsupportedOperationException("mock method not implemented");
}
}
private class MockTicketValidator implements TicketValidator {
private boolean returnTicket;
MockTicketValidator(boolean returnTicket) {
this.returnTicket = returnTicket;
}
@Override
public Assertion validate(final String ticket, final String service) {
if (this.returnTicket) {
return new AssertionImpl("rod");
}
throw new BadCredentialsException("As requested from mock");
}
}
}
@@ -1,158 +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
*
* https://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.cas.authentication;
import java.util.Collections;
import java.util.List;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.AssertionImpl;
import org.junit.jupiter.api.Test;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests {@link CasAuthenticationToken}.
*
* @author Ben Alex
*/
public class CasAuthenticationTokenTests {
private final List<GrantedAuthority> ROLES = AuthorityUtils.createAuthorityList("ROLE_ONE", "ROLE_TWO");
private UserDetails makeUserDetails() {
return makeUserDetails("user");
}
private UserDetails makeUserDetails(final String name) {
return new User(name, "password", true, true, true, true, this.ROLES);
}
@Test
public void testConstructorRejectsNulls() {
Assertion assertion = new AssertionImpl("test");
assertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken(null, makeUserDetails(),
"Password", this.ROLES, makeUserDetails(), assertion));
assertThatIllegalArgumentException().isThrownBy(
() -> new CasAuthenticationToken("key", null, "Password", this.ROLES, makeUserDetails(), assertion));
assertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken("key", makeUserDetails(), null,
this.ROLES, makeUserDetails(), assertion));
assertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken("key", makeUserDetails(),
"Password", this.ROLES, makeUserDetails(), null));
assertThatIllegalArgumentException().isThrownBy(
() -> new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES, null, assertion));
assertThatIllegalArgumentException().isThrownBy(() -> new CasAuthenticationToken("key", makeUserDetails(),
"Password", AuthorityUtils.createAuthorityList("ROLE_1", null), makeUserDetails(), assertion));
}
@Test
public void constructorWhenEmptyKeyThenThrowsException() {
assertThatIllegalArgumentException().isThrownBy(
() -> new CasAuthenticationToken("", "user", "password", Collections.<GrantedAuthority>emptyList(),
new User("user", "password", Collections.<GrantedAuthority>emptyList()), null));
}
@Test
public void testEqualsWhenEqual() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
assertThat(token2).isEqualTo(token1);
}
@Test
public void testGetters() {
// Build the proxy list returned in the ticket from CAS
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
assertThat(token.getKeyHash()).isEqualTo("key".hashCode());
assertThat(token.getPrincipal()).isEqualTo(makeUserDetails());
assertThat(token.getCredentials()).isEqualTo("Password");
assertThat(token.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_ONE"));
assertThat(token.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_TWO"));
assertThat(token.getAssertion()).isEqualTo(assertion);
assertThat(token.getUserDetails().getUsername()).isEqualTo(makeUserDetails().getUsername());
}
@Test
public void testNoArgConstructorDoesntExist() {
assertThatExceptionOfType(NoSuchMethodException.class)
.isThrownBy(() -> CasAuthenticationToken.class.getDeclaredConstructor((Class[]) null));
}
@Test
public void testNotEqualsDueToAbstractParentEqualsCheck() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails("OTHER_NAME"), "Password",
this.ROLES, makeUserDetails(), assertion);
assertThat(!token1.equals(token2)).isTrue();
}
@Test
public void testNotEqualsDueToKey() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("DIFFERENT_KEY", makeUserDetails(), "Password",
this.ROLES, makeUserDetails(), assertion);
assertThat(!token1.equals(token2)).isTrue();
}
@Test
public void testNotEqualsDueToAssertion() {
final Assertion assertion = new AssertionImpl("test");
final Assertion assertion2 = new AssertionImpl("test");
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
CasAuthenticationToken token2 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion2);
assertThat(!token1.equals(token2)).isTrue();
}
@Test
public void testSetAuthenticated() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
assertThat(token.isAuthenticated()).isTrue();
token.setAuthenticated(false);
assertThat(!token.isAuthenticated()).isTrue();
}
@Test
public void testToString() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
String result = token.toString();
assertThat(result.lastIndexOf("Credentials (Service/Proxy Ticket):") != -1).isTrue();
}
}
@@ -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
*
* https://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.cas.authentication;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test cases for the @link {@link NullStatelessTicketCache}
*
* @author Scott Battaglia
*
*/
public class NullStatelessTicketCacheTests extends AbstractStatelessTicketCacheTests {
private StatelessTicketCache cache = new NullStatelessTicketCache();
@Test
public void testGetter() {
assertThat(this.cache.getByTicketId(null)).isNull();
assertThat(this.cache.getByTicketId("test")).isNull();
}
@Test
public void testInsertAndGet() {
final CasAuthenticationToken token = getToken();
this.cache.putTicketInCache(token);
assertThat(this.cache.getByTicketId((String) token.getCredentials())).isNull();
}
}
@@ -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
*
* https://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.cas.authentication;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests
* {@link org.springframework.security.cas.authentication.SpringCacheBasedTicketCache}.
*
* @author Marten Deinum
* @since 3.2
*/
public class SpringCacheBasedTicketCacheTests extends AbstractStatelessTicketCacheTests {
private static CacheManager cacheManager;
@BeforeAll
public static void initCacheManaer() {
cacheManager = new ConcurrentMapCacheManager();
cacheManager.getCache("castickets");
}
@Test
public void testCacheOperation() throws Exception {
SpringCacheBasedTicketCache cache = new SpringCacheBasedTicketCache(cacheManager.getCache("castickets"));
final CasAuthenticationToken token = getToken();
// Check it gets stored in the cache
cache.putTicketInCache(token);
assertThat(cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ")).isEqualTo(token);
// Check it gets removed from the cache
cache.removeTicketFromCache(getToken());
assertThat(cache.getByTicketId("ST-0-ER94xMJmn6pha35CQRoZ")).isNull();
// Check it doesn't return values for null or unknown service tickets
assertThat(cache.getByTicketId(null)).isNull();
assertThat(cache.getByTicketId("UNKNOWN_SERVICE_TICKET")).isNull();
}
@Test
public void testStartupDetectsMissingCache() throws Exception {
assertThatIllegalArgumentException().isThrownBy(() -> new SpringCacheBasedTicketCache(null));
}
}
@@ -1,154 +0,0 @@
/*
* Copyright 2015-2023 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
*
* https://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.cas.jackson2;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apereo.cas.client.authentication.AttributePrincipalImpl;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.AssertionImpl;
import org.json.JSONException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.springframework.security.cas.authentication.CasAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Jitendra Singh
* @since 4.2
*/
public class CasAuthenticationTokenMixinTests {
private static final String KEY = "casKey";
private static final String PASSWORD = "\"1234\"";
private static final Date START_DATE = new Date();
private static final Date END_DATE = new Date();
public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"authority\": \"ROLE_USER\"}";
public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON
+ "]]";
public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", ["
+ AUTHORITY_JSON + "]]";
// @formatter:off
public static final String USER_JSON = "{"
+ "\"@class\": \"org.springframework.security.core.userdetails.User\", "
+ "\"username\": \"admin\","
+ " \"password\": " + PASSWORD + ", "
+ "\"accountNonExpired\": true, "
+ "\"accountNonLocked\": true, "
+ "\"credentialsNonExpired\": true, "
+ "\"enabled\": true, "
+ "\"authorities\": " + AUTHORITIES_SET_JSON
+ "}";
// @formatter:on
private static final String CAS_TOKEN_JSON = "{"
+ "\"@class\": \"org.springframework.security.cas.authentication.CasAuthenticationToken\", "
+ "\"keyHash\": " + KEY.hashCode() + "," + "\"principal\": " + USER_JSON + ", " + "\"credentials\": "
+ PASSWORD + ", " + "\"authorities\": " + AUTHORITIES_ARRAYLIST_JSON + "," + "\"userDetails\": " + USER_JSON
+ "," + "\"authenticated\": true, " + "\"details\": null," + "\"assertion\": {"
+ "\"@class\": \"org.apereo.cas.client.validation.AssertionImpl\", " + "\"principal\": {"
+ "\"@class\": \"org.apereo.cas.client.authentication.AttributePrincipalImpl\", "
+ "\"name\": \"assertName\", " + "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"}, "
+ "\"proxyGrantingTicket\": null, " + "\"proxyRetriever\": null" + "}, "
+ "\"validFromDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], "
+ "\"validUntilDate\": [\"java.util.Date\", " + END_DATE.getTime() + "],"
+ "\"authenticationDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], "
+ "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"},"
+ "\"context\": {\"@class\":\"java.util.HashMap\"}" + "}" + "}";
private static final String CAS_TOKEN_CLEARED_JSON = CAS_TOKEN_JSON.replaceFirst(PASSWORD, "null");
protected ObjectMapper mapper;
@BeforeEach
public void setup() {
this.mapper = new ObjectMapper();
ClassLoader loader = getClass().getClassLoader();
this.mapper.registerModules(SecurityJackson2Modules.getModules(loader));
}
@Test
public void serializeCasAuthenticationTest() throws JsonProcessingException, JSONException {
CasAuthenticationToken token = createCasAuthenticationToken();
String actualJson = this.mapper.writeValueAsString(token);
JSONAssert.assertEquals(CAS_TOKEN_JSON, actualJson, true);
}
@Test
public void serializeCasAuthenticationTestAfterEraseCredentialInvoked()
throws JsonProcessingException, JSONException {
CasAuthenticationToken token = createCasAuthenticationToken();
token.eraseCredentials();
String actualJson = this.mapper.writeValueAsString(token);
JSONAssert.assertEquals(CAS_TOKEN_CLEARED_JSON, actualJson, true);
}
@Test
public void deserializeCasAuthenticationTestAfterEraseCredentialInvoked() throws Exception {
CasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_CLEARED_JSON, CasAuthenticationToken.class);
assertThat(((UserDetails) token.getPrincipal()).getPassword()).isNull();
}
@Test
public void deserializeCasAuthenticationTest() throws IOException {
CasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_JSON, CasAuthenticationToken.class);
assertThat(token).isNotNull();
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);
assertThat(((User) token.getPrincipal()).getUsername()).isEqualTo("admin");
assertThat(((User) token.getPrincipal()).getPassword()).isEqualTo("1234");
assertThat(token.getUserDetails()).isNotNull().isInstanceOf(User.class);
assertThat(token.getAssertion()).isNotNull().isInstanceOf(AssertionImpl.class);
assertThat(token.getKeyHash()).isEqualTo(KEY.hashCode());
assertThat(token.getUserDetails().getAuthorities()).extracting(GrantedAuthority::getAuthority)
.containsOnly("ROLE_USER");
assertThat(token.getAssertion().getAuthenticationDate()).isEqualTo(START_DATE);
assertThat(token.getAssertion().getValidFromDate()).isEqualTo(START_DATE);
assertThat(token.getAssertion().getValidUntilDate()).isEqualTo(END_DATE);
assertThat(token.getAssertion().getPrincipal().getName()).isEqualTo("assertName");
assertThat(token.getAssertion().getAttributes()).hasSize(0);
}
private CasAuthenticationToken createCasAuthenticationToken() {
User principal = new User("admin", "1234", Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
Collection<? extends GrantedAuthority> authorities = Collections
.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
Assertion assertion = new AssertionImpl(new AttributePrincipalImpl("assertName"), START_DATE, END_DATE,
START_DATE, Collections.<String, Object>emptyMap());
return new CasAuthenticationToken(KEY, principal, principal.getPassword(), authorities,
new User("admin", "1234", authorities), assertion);
}
}
@@ -1,63 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.cas.userdetails;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apereo.cas.client.authentication.AttributePrincipal;
import org.apereo.cas.client.validation.Assertion;
import org.junit.jupiter.api.Test;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* @author Luke Taylor
*/
public class GrantedAuthorityFromAssertionAttributesUserDetailsServiceTests {
@Test
public void correctlyExtractsNamedAttributesFromAssertionAndConvertsThemToAuthorities() {
GrantedAuthorityFromAssertionAttributesUserDetailsService uds = new GrantedAuthorityFromAssertionAttributesUserDetailsService(
new String[] { "a", "b", "c", "d" });
uds.setConvertToUpperCase(false);
Assertion assertion = mock(Assertion.class);
AttributePrincipal principal = mock(AttributePrincipal.class);
Map<String, Object> attributes = new HashMap<>();
attributes.put("a", Arrays.asList("role_a1", "role_a2"));
attributes.put("b", "role_b");
attributes.put("c", "role_c");
attributes.put("d", null);
attributes.put("someother", "unused");
given(assertion.getPrincipal()).willReturn(principal);
given(principal.getAttributes()).willReturn(attributes);
given(principal.getName()).willReturn("somebody");
CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, "ticket");
UserDetails user = uds.loadUserDetails(token);
Set<String> roles = AuthorityUtils.authorityListToSet(user.getAuthorities());
assertThat(roles).containsExactlyInAnyOrder("role_a1", "role_a2", "role_b", "role_c");
}
}
@@ -1,98 +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
*
* https://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.cas.web;
import java.net.URLEncoder;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.cas.ServiceProperties;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests {@link CasAuthenticationEntryPoint}.
*
* @author Ben Alex
*/
public class CasAuthenticationEntryPointTests {
@Test
public void testDetectsMissingLoginFormUrl() throws Exception {
CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();
ep.setServiceProperties(new ServiceProperties());
assertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet)
.withMessage("loginUrl must be specified");
}
@Test
public void testDetectsMissingServiceProperties() throws Exception {
CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();
ep.setLoginUrl("https://cas/login");
assertThatIllegalArgumentException().isThrownBy(ep::afterPropertiesSet)
.withMessage("serviceProperties must be specified");
}
@Test
public void testGettersSetters() {
CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();
ep.setLoginUrl("https://cas/login");
assertThat(ep.getLoginUrl()).isEqualTo("https://cas/login");
ep.setServiceProperties(new ServiceProperties());
assertThat(ep.getServiceProperties() != null).isTrue();
}
@Test
public void testNormalOperationWithRenewFalse() throws Exception {
ServiceProperties sp = new ServiceProperties();
sp.setSendRenew(false);
sp.setService("https://mycompany.com/bigWebApp/login/cas");
CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();
ep.setLoginUrl("https://cas/login");
ep.setServiceProperties(sp);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/some_path");
MockHttpServletResponse response = new MockHttpServletResponse();
ep.afterPropertiesSet();
ep.commence(request, response, null);
assertThat(
"https://cas/login?service=" + URLEncoder.encode("https://mycompany.com/bigWebApp/login/cas", "UTF-8"))
.isEqualTo(response.getRedirectedUrl());
}
@Test
public void testNormalOperationWithRenewTrue() throws Exception {
ServiceProperties sp = new ServiceProperties();
sp.setSendRenew(true);
sp.setService("https://mycompany.com/bigWebApp/login/cas");
CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();
ep.setLoginUrl("https://cas/login");
ep.setServiceProperties(sp);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/some_path");
MockHttpServletResponse response = new MockHttpServletResponse();
ep.afterPropertiesSet();
ep.commence(request, response, null);
assertThat("https://cas/login?service="
+ URLEncoder.encode("https://mycompany.com/bigWebApp/login/cas", "UTF-8") + "&renew=true")
.isEqualTo(response.getRedirectedUrl());
}
}
@@ -1,198 +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
*
* https://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.cas.web;
import jakarta.servlet.FilterChain;
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
/**
* Tests {@link CasAuthenticationFilter}.
*
* @author Ben Alex
* @author Rob Winch
*/
public class CasAuthenticationFilterTests {
@AfterEach
public void tearDown() {
SecurityContextHolder.clearContext();
}
@Test
public void testGettersSetters() {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
filter.setProxyReceptorUrl("/someurl");
filter.setServiceProperties(new ServiceProperties());
}
@Test
public void testNormalOperation() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setServletPath("/login/cas");
request.addParameter("ticket", "ST-0-ER94xMJmn6pha35CQRoZ");
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setAuthenticationManager((a) -> a);
assertThat(filter.requiresAuthentication(request, new MockHttpServletResponse())).isTrue();
Authentication result = filter.attemptAuthentication(request, new MockHttpServletResponse());
assertThat(result != null).isTrue();
}
@Test
public void testNullServiceTicketHandledGracefully() throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setAuthenticationManager((a) -> {
throw new BadCredentialsException("Rejected");
});
assertThatExceptionOfType(AuthenticationException.class).isThrownBy(
() -> filter.attemptAuthentication(new MockHttpServletRequest(), new MockHttpServletResponse()));
}
@Test
public void testRequiresAuthenticationFilterProcessUrl() {
String url = "/login/cas";
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl(url);
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath(url);
assertThat(filter.requiresAuthentication(request, response)).isTrue();
}
@Test
public void testRequiresAuthenticationProxyRequest() {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath("/pgtCallback");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyReceptorUrl(request.getServletPath());
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
}
@Test
public void testRequiresAuthenticationAuthAll() {
ServiceProperties properties = new ServiceProperties();
properties.setAuthenticateAllArtifacts(true);
String url = "/login/cas";
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl(url);
filter.setServiceProperties(properties);
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath(url);
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
request.setParameter(properties.getArtifactParameter(), "value");
assertThat(filter.requiresAuthentication(request, response)).isTrue();
SecurityContextHolder.getContext().setAuthentication(new AnonymousAuthenticationToken("key", "principal",
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")));
assertThat(filter.requiresAuthentication(request, response)).isTrue();
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("un", "principal"));
assertThat(filter.requiresAuthentication(request, response)).isTrue();
SecurityContextHolder.getContext()
.setAuthentication(new TestingAuthenticationToken("un", "principal", "ROLE_ANONYMOUS"));
assertThat(filter.requiresAuthentication(request, response)).isFalse();
}
@Test
public void testAuthenticateProxyUrl() throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath("/pgtCallback");
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
filter.setProxyReceptorUrl(request.getServletPath());
assertThat(filter.attemptAuthentication(request, response)).isNull();
}
@Test
public void testDoFilterAuthenticateAll() throws Exception {
AuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class);
AuthenticationManager manager = mock(AuthenticationManager.class);
Authentication authentication = new TestingAuthenticationToken("un", "pwd", "ROLE_USER");
given(manager.authenticate(any(Authentication.class))).willReturn(authentication);
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("ticket", "ST-1-123");
request.setServletPath("/authenticate");
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setServiceProperties(serviceProperties);
filter.setAuthenticationSuccessHandler(successHandler);
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
filter.setAuthenticationManager(manager);
filter.afterPropertiesSet();
filter.doFilter(request, response, chain);
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull()
.withFailMessage("Authentication should not be null");
verify(chain).doFilter(request, response);
verifyNoInteractions(successHandler);
// validate for when the filterProcessUrl matches
filter.setFilterProcessesUrl(request.getServletPath());
SecurityContextHolder.clearContext();
filter.doFilter(request, response, chain);
verifyNoMoreInteractions(chain);
verify(successHandler).onAuthenticationSuccess(request, response, authentication);
}
// SEC-1592
@Test
public void testChainNotInvokedForProxyReceptor() throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
request.setServletPath("/pgtCallback");
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
filter.setProxyReceptorUrl(request.getServletPath());
filter.doFilter(request, response, chain);
verifyNoInteractions(chain);
}
}
@@ -1,67 +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
*
* https://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.cas.web;
import org.junit.jupiter.api.Test;
import org.springframework.security.cas.SamlServiceProperties;
import org.springframework.security.cas.ServiceProperties;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests {@link ServiceProperties}.
*
* @author Ben Alex
*/
public class ServicePropertiesTests {
@Test
public void detectsMissingService() throws Exception {
ServiceProperties sp = new ServiceProperties();
assertThatIllegalArgumentException().isThrownBy(sp::afterPropertiesSet);
}
@Test
public void nullServiceWhenAuthenticateAllTokens() throws Exception {
ServiceProperties sp = new ServiceProperties();
sp.setAuthenticateAllArtifacts(true);
assertThatIllegalArgumentException().isThrownBy(sp::afterPropertiesSet);
sp.setAuthenticateAllArtifacts(false);
assertThatIllegalArgumentException().isThrownBy(sp::afterPropertiesSet);
}
@Test
public void testGettersSetters() throws Exception {
ServiceProperties[] sps = { new ServiceProperties(), new SamlServiceProperties() };
for (ServiceProperties sp : sps) {
sp.setSendRenew(false);
assertThat(sp.isSendRenew()).isFalse();
sp.setSendRenew(true);
assertThat(sp.isSendRenew()).isTrue();
sp.setArtifactParameter("notticket");
assertThat(sp.getArtifactParameter()).isEqualTo("notticket");
sp.setServiceParameter("notservice");
assertThat(sp.getServiceParameter()).isEqualTo("notservice");
sp.setService("https://mycompany.com/service");
assertThat(sp.getService()).isEqualTo("https://mycompany.com/service");
sp.afterPropertiesSet();
}
}
}
@@ -1,132 +0,0 @@
/*
* Copyright 2011-2023 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
*
* https://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.cas.web.authentication;
import java.util.regex.Pattern;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.web.util.UrlUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rob Winch
*/
public class DefaultServiceAuthenticationDetailsTests {
private DefaultServiceAuthenticationDetails details;
private MockHttpServletRequest request;
private Pattern artifactPattern;
private String casServiceUrl;
private ConfigurableApplicationContext context;
@BeforeEach
public void setUp() {
this.casServiceUrl = "https://localhost:8443/j_spring_security_cas";
this.request = new MockHttpServletRequest();
this.request.setScheme("https");
this.request.setServerName("localhost");
this.request.setServerPort(8443);
this.request.setRequestURI("/cas-sample/secure/");
this.artifactPattern = DefaultServiceAuthenticationDetails
.createArtifactPattern(ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER);
}
@AfterEach
public void cleanup() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void getServiceUrlNullQuery() throws Exception {
this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);
assertThat(this.details.getServiceUrl()).isEqualTo(UrlUtils.buildFullRequestUrl(this.request));
}
@Test
public void getServiceUrlTicketOnlyParam() throws Exception {
this.request.setQueryString("ticket=123");
this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);
String serviceUrl = this.details.getServiceUrl();
this.request.setQueryString(null);
assertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request));
}
@Test
public void getServiceUrlTicketFirstMultiParam() throws Exception {
this.request.setQueryString("ticket=123&other=value");
this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);
String serviceUrl = this.details.getServiceUrl();
this.request.setQueryString("other=value");
assertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request));
}
@Test
public void getServiceUrlTicketLastMultiParam() throws Exception {
this.request.setQueryString("other=value&ticket=123");
this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);
String serviceUrl = this.details.getServiceUrl();
this.request.setQueryString("other=value");
assertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request));
}
@Test
public void getServiceUrlTicketMiddleMultiParam() throws Exception {
this.request.setQueryString("other=value&ticket=123&last=this");
this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);
String serviceUrl = this.details.getServiceUrl();
this.request.setQueryString("other=value&last=this");
assertThat(serviceUrl).isEqualTo(UrlUtils.buildFullRequestUrl(this.request));
}
@Test
public void getServiceUrlDoesNotUseHostHeader() throws Exception {
this.casServiceUrl = "https://example.com/j_spring_security_cas";
this.request.setServerName("evil.com");
this.details = new DefaultServiceAuthenticationDetails(this.casServiceUrl, this.request, this.artifactPattern);
assertThat(this.details.getServiceUrl()).isEqualTo("https://example.com/cas-sample/secure/");
}
@Test
public void getServiceUrlDoesNotUseHostHeaderExplicit() {
this.casServiceUrl = "https://example.com/j_spring_security_cas";
this.request.setServerName("evil.com");
ServiceAuthenticationDetails details = loadServiceAuthenticationDetails(
"defaultserviceauthenticationdetails-explicit.xml");
assertThat(details.getServiceUrl()).isEqualTo("https://example.com/cas-sample/secure/");
}
private ServiceAuthenticationDetails loadServiceAuthenticationDetails(String resourceName) {
this.context = new GenericXmlApplicationContext(getClass(), resourceName);
ServiceAuthenticationDetailsSource source = this.context.getBean(ServiceAuthenticationDetailsSource.class);
return source.buildDetails(this.request);
}
}
-15
View File
@@ -1,15 +0,0 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
<root level="${root.level:-WARN}">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="https://example.com/j_spring_security_cas"/>
<property name="sendRenew" value="false"/>
</bean>
<bean id="serviceProperties2"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="https://example2.com/j_spring_security_cas"/>
<property name="sendRenew" value="false"/>
</bean>
<bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource">
<constructor-arg ref="serviceProperties"/>
</bean>
</beans>
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="https://example.com/j_spring_security_cas"/>
<property name="sendRenew" value="false"/>
</bean>
<bean class="org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource"/>
</beans>
+2 -1
View File
@@ -41,7 +41,6 @@ dependencies {
provided 'jakarta.servlet:jakarta.servlet-api'
testImplementation project(':spring-security-aspects')
testImplementation project(':spring-security-cas')
testImplementation project(':spring-security-test')
testImplementation project(path : ':spring-security-core', configuration : 'tests')
testImplementation project(path : ':spring-security-ldap', configuration : 'tests')
@@ -69,6 +68,7 @@ dependencies {
testImplementation 'ldapsdk:ldapsdk:4.1'
testImplementation('net.sourceforge.htmlunit:htmlunit') {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'xml-apis', module: 'xml-apis'
}
testImplementation "org.apache.directory.server:apacheds-core"
testImplementation "org.apache.directory.server:apacheds-core-entry"
@@ -84,6 +84,7 @@ dependencies {
testImplementation "org.mockito:mockito-inline"
testImplementation('org.seleniumhq.selenium:htmlunit-driver') {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'xml-apis', module: 'xml-apis'
}
testImplementation('org.seleniumhq.selenium:selenium-java') {
exclude group: 'commons-logging', module: 'commons-logging'
@@ -95,7 +95,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
if (!namespaceMatchesVersion(element)) {
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
+ "with Spring Security 6.1. Please update your schema declarations to the 6.1 schema.", element);
+ "with Spring Security 6.0. Please update your schema declarations to the 6.0 schema.", element);
}
String name = pc.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(name);
@@ -218,7 +218,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
private boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-security-6\\.1.*.xsd.*")
return schemaLocation.matches("(?m).*spring-security-6\\.0.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*");
}
@@ -221,6 +221,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
if (configs == null) {
return new ArrayList<>();
}
removeFromConfigurersAddedInInitializing(clazz);
return new ArrayList<>(configs);
}
@@ -253,11 +254,16 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
if (configs == null) {
return null;
}
removeFromConfigurersAddedInInitializing(clazz);
Assert.state(configs.size() == 1,
() -> "Only one configurer expected for type " + clazz + ", but got " + configs);
return (C) configs.get(0);
}
private <C extends SecurityConfigurer<O, B>> void removeFromConfigurersAddedInInitializing(Class<C> clazz) {
this.configurersAddedInInitializing.removeIf(clazz::isInstance);
}
/**
* Specifies the {@link ObjectPostProcessor} to use.
* @param objectPostProcessor the {@link ObjectPostProcessor} to use. Cannot be null
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* 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.
@@ -53,9 +53,7 @@ public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
* Return the {@link SecurityBuilder} when done using the {@link SecurityConfigurer}.
* This is useful for method chaining.
* @return the {@link SecurityBuilder} for further customizations
* @deprecated For removal in 7.0. Use the lambda based configuration instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public B and() {
return getBuilder();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -17,9 +17,9 @@
package org.springframework.security.config.annotation.method.configuration;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
@@ -46,7 +46,8 @@ final class Jsr250MethodSecurityConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static Advisor jsr250AuthorizationMethodInterceptor(ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
static MethodInterceptor jsr250AuthorizationMethodInterceptor(
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
ObjectProvider<ObservationRegistry> registryProvider) {
Jsr250AuthorizationManager jsr250 = new Jsr250AuthorizationManager();
@@ -0,0 +1,52 @@
/*
* Copyright 2002-2023 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
*
* https://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.config.annotation.method.configuration;
import org.springframework.aop.Advisor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
class MethodSecurityAdvisorRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registerAsAdvisor("preFilterAuthorization", registry);
registerAsAdvisor("preAuthorizeAuthorization", registry);
registerAsAdvisor("postFilterAuthorization", registry);
registerAsAdvisor("postAuthorizeAuthorization", registry);
registerAsAdvisor("securedAuthorization", registry);
registerAsAdvisor("jsr250Authorization", registry);
}
private void registerAsAdvisor(String prefix, BeanDefinitionRegistry registry) {
String interceptorName = prefix + "MethodInterceptor";
if (!registry.containsBeanDefinition(interceptorName)) {
return;
}
BeanDefinition definition = registry.getBeanDefinition(interceptorName);
if (!(definition instanceof RootBeanDefinition)) {
return;
}
RootBeanDefinition advisor = new RootBeanDefinition((RootBeanDefinition) definition);
advisor.setTargetType(Advisor.class);
registry.registerBeanDefinition(prefix + "Advisor", advisor);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -60,7 +60,8 @@ final class MethodSecuritySelector implements ImportSelector {
private static final class AutoProxyRegistrarSelector extends AdviceModeImportSelector<EnableMethodSecurity> {
private static final String[] IMPORTS = new String[] { AutoProxyRegistrar.class.getName() };
private static final String[] IMPORTS = new String[] { AutoProxyRegistrar.class.getName(),
MethodSecurityAdvisorRegistrar.class.getName() };
private static final String[] ASPECTJ_IMPORTS = new String[] {
MethodSecurityAspectJAutoProxyRegistrar.class.getName() };
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -17,8 +17,8 @@
package org.springframework.security.config.annotation.method.configuration;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.Advisor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationContext;
@@ -52,7 +52,8 @@ final class PrePostMethodSecurityConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static Advisor preFilterAuthorizationMethodInterceptor(ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
static MethodInterceptor preFilterAuthorizationMethodInterceptor(
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
ObjectProvider<SecurityContextHolderStrategy> strategyProvider, ApplicationContext context) {
PreFilterAuthorizationMethodInterceptor preFilter = new PreFilterAuthorizationMethodInterceptor();
@@ -64,7 +65,8 @@ final class PrePostMethodSecurityConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static Advisor preAuthorizeAuthorizationMethodInterceptor(ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
ObjectProvider<AuthorizationEventPublisher> eventPublisherProvider,
@@ -81,7 +83,7 @@ final class PrePostMethodSecurityConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static Advisor postAuthorizeAuthorizationMethodInterceptor(
static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
@@ -99,7 +101,8 @@ final class PrePostMethodSecurityConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static Advisor postFilterAuthorizationMethodInterceptor(ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
static MethodInterceptor postFilterAuthorizationMethodInterceptor(
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
ObjectProvider<SecurityContextHolderStrategy> strategyProvider, ApplicationContext context) {
PostFilterAuthorizationMethodInterceptor postFilter = new PostFilterAuthorizationMethodInterceptor();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 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.
@@ -17,9 +17,9 @@
package org.springframework.security.config.annotation.method.configuration;
import io.micrometer.observation.ObservationRegistry;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
@@ -46,7 +46,8 @@ final class SecuredMethodSecurityConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static Advisor securedAuthorizationMethodInterceptor(ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
static MethodInterceptor securedAuthorizationMethodInterceptor(
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
ObjectProvider<ObservationRegistry> registryProvider) {
SecuredAuthorizationManager secured = new SecuredAuthorizationManager();
SecurityContextHolderStrategy strategy = strategyProvider
@@ -19,8 +19,11 @@ package org.springframework.security.config.annotation.web;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletRegistration;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
@@ -36,6 +39,7 @@ import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
@@ -179,14 +183,47 @@ public abstract class AbstractRequestMatcherRegistry<C> {
* @since 5.8
*/
public C requestMatchers(HttpMethod method, String... patterns) {
List<RequestMatcher> matchers = new ArrayList<>();
if (mvcPresent) {
matchers.addAll(createMvcMatchers(method, patterns));
if (!mvcPresent) {
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
}
else {
matchers.addAll(RequestMatchers.antMatchers(method, patterns));
if (!(this.context instanceof WebApplicationContext)) {
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
}
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
WebApplicationContext context = (WebApplicationContext) this.context;
ServletContext servletContext = context.getServletContext();
if (servletContext == null) {
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
}
Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations();
if (registrations == null) {
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
}
if (!hasDispatcherServlet(registrations)) {
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
}
Assert.isTrue(registrations.size() == 1,
"This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).");
return requestMatchers(createMvcMatchers(method, patterns).toArray(new RequestMatcher[0]));
}
private boolean hasDispatcherServlet(Map<String, ? extends ServletRegistration> registrations) {
if (registrations == null) {
return false;
}
Class<?> dispatcherServlet = ClassUtils.resolveClassName("org.springframework.web.servlet.DispatcherServlet",
null);
for (ServletRegistration registration : registrations.values()) {
try {
Class<?> clazz = Class.forName(registration.getClassName());
if (dispatcherServlet.isAssignableFrom(clazz)) {
return true;
}
}
catch (ClassNotFoundException ex) {
return false;
}
}
return false;
}
/**
@@ -262,12 +299,7 @@ public abstract class AbstractRequestMatcherRegistry<C> {
* @return a {@link List} of {@link AntPathRequestMatcher} instances
*/
static List<RequestMatcher> antMatchers(HttpMethod httpMethod, String... antPatterns) {
String method = (httpMethod != null) ? httpMethod.toString() : null;
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : antPatterns) {
matchers.add(new AntPathRequestMatcher(pattern, method));
}
return matchers;
return Arrays.asList(antMatchersAsArray(httpMethod, antPatterns));
}
/**
@@ -281,6 +313,15 @@ public abstract class AbstractRequestMatcherRegistry<C> {
return antMatchers(null, antPatterns);
}
static RequestMatcher[] antMatchersAsArray(HttpMethod httpMethod, String... antPatterns) {
String method = (httpMethod != null) ? httpMethod.toString() : null;
RequestMatcher[] matchers = new RequestMatcher[antPatterns.length];
for (int index = 0; index < antPatterns.length; index++) {
matchers[index] = new AntPathRequestMatcher(antPatterns[index], method);
}
return matchers;
}
/**
* Create a {@link List} of {@link RegexRequestMatcher} instances.
* @param httpMethod the {@link HttpMethod} to use or {@code null} for any
@@ -73,7 +73,6 @@ import org.springframework.security.config.annotation.web.configurers.oauth2.cli
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer;
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2MetadataConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -285,10 +284,8 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link HeadersConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #headers(Customizer)} instead
* @see HeadersConfigurer
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<HttpSecurity> headers() throws Exception {
return getOrApply(new HeadersConfigurer<>());
}
@@ -401,9 +398,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* on the classpath a {@link HandlerMappingIntrospector} is used.
* @return the {@link CorsConfigurer} for customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #cors(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public CorsConfigurer<HttpSecurity> cors() throws Exception {
return getOrApply(new CorsConfigurer<>());
}
@@ -490,9 +485,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* could return true.
* @return the {@link SessionManagementConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #sessionManagement(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public SessionManagementConfigurer<HttpSecurity> sessionManagement() throws Exception {
return getOrApply(new SessionManagementConfigurer<>());
}
@@ -614,10 +607,8 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link PortMapperConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #portMapper(Customizer)} instead
* @see #requiresChannel()
*/
@Deprecated(since = "6.1", forRemoval = true)
public PortMapperConfigurer<HttpSecurity> portMapper() throws Exception {
return getOrApply(new PortMapperConfigurer<>());
}
@@ -747,9 +738,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* Servlet Container's documentation.
* @return the {@link JeeConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #jee(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public JeeConfigurer<HttpSecurity> jee() throws Exception {
return getOrApply(new JeeConfigurer<>());
}
@@ -860,9 +849,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link X509Configurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #x509(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public X509Configurer<HttpSecurity> x509() throws Exception {
return getOrApply(new X509Configurer<>());
}
@@ -940,9 +927,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link RememberMeConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #rememberMe(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public RememberMeConfigurer<HttpSecurity> rememberMe() throws Exception {
return getOrApply(new RememberMeConfigurer<>());
}
@@ -1086,7 +1071,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #authorizeHttpRequests()} instead
* @deprecated Use {@link #authorizeHttpRequests()} instead
*/
@Deprecated
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
@@ -1201,7 +1186,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* for the {@link ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry}
* @return the {@link HttpSecurity} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #authorizeHttpRequests} instead
* @deprecated Use {@link #authorizeHttpRequests} instead
*/
@Deprecated
public HttpSecurity authorizeRequests(
@@ -1316,10 +1301,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* @return the {@link HttpSecurity} for further customizations
* @throws Exception
* @since 5.6
* @deprecated For removal in 7.0. Use {@link #authorizeHttpRequests(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizeHttpRequests()
throws Exception {
ApplicationContext context = getContext();
@@ -1451,9 +1433,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* when using {@link EnableWebSecurity}.
* @return the {@link RequestCacheConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #requestCache(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public RequestCacheConfigurer<HttpSecurity> requestCache() throws Exception {
return getOrApply(new RequestCacheConfigurer<>());
}
@@ -1504,9 +1484,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* {@link EnableWebSecurity}.
* @return the {@link ExceptionHandlingConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #exceptionHandling(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling() throws Exception {
return getOrApply(new ExceptionHandlingConfigurer<>());
}
@@ -1558,9 +1536,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* automatically applied when using {@link EnableWebSecurity}.
* @return the {@link SecurityContextConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #securityContext(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public SecurityContextConfigurer<HttpSecurity> securityContext() throws Exception {
return getOrApply(new SecurityContextConfigurer<>());
}
@@ -1605,9 +1581,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* {@link EnableWebSecurity}.
* @return the {@link ServletApiConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #servletApi(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServletApiConfigurer<HttpSecurity> servletApi() throws Exception {
return getOrApply(new ServletApiConfigurer<>());
}
@@ -1663,9 +1637,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link CsrfConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #csrf(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
ApplicationContext context = getContext();
return getOrApply(new CsrfConfigurer<>(context));
@@ -1740,9 +1712,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link LogoutConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #logout(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public LogoutConfigurer<HttpSecurity> logout() throws Exception {
return getOrApply(new LogoutConfigurer<>());
}
@@ -1881,9 +1851,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link AnonymousConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #anonymous(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public AnonymousConfigurer<HttpSecurity> anonymous() throws Exception {
return getOrApply(new AnonymousConfigurer<>());
}
@@ -2046,10 +2014,8 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link FormLoginConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #formLogin(Customizer)} instead
* @see FormLoginConfigurer#loginPage(String)
*/
@Deprecated(since = "6.1", forRemoval = true)
public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
return getOrApply(new FormLoginConfigurer<>());
}
@@ -2224,9 +2190,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* @return the {@link Saml2LoginConfigurer} for further customizations
* @throws Exception
* @since 5.2
* @deprecated For removal in 7.0. Use {@link #saml2Login(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public Saml2LoginConfigurer<HttpSecurity> saml2Login() throws Exception {
return getOrApply(new Saml2LoginConfigurer<>());
}
@@ -2456,111 +2420,11 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* @return the {@link Saml2LoginConfigurer} for further customizations
* @throws Exception
* @since 5.6
* @deprecated For removal in 7.0. Use {@link #saml2Logout(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public Saml2LogoutConfigurer<HttpSecurity> saml2Logout() throws Exception {
return getOrApply(new Saml2LogoutConfigurer<>(getContext()));
}
/**
* Configures a SAML 2.0 metadata endpoint that presents relying party configurations
* in an {@code <md:EntityDescriptor>} payload.
*
* <p>
* By default, the endpoints are {@code /saml2/metadata} and
* {@code /saml2/metadata/{registrationId}} though note that also
* {@code /saml2/service-provider-metadata/{registrationId}} is recognized for
* backward compatibility purposes.
*
* <p>
* <h2>Example Configuration</h2>
*
* The following example shows the minimal configuration required, using a
* hypothetical asserting party.
*
* <pre>
* &#064;EnableWebSecurity
* &#064;Configuration
* public class Saml2LogoutSecurityConfig {
* &#064;Bean
* public SecurityFilterChain web(HttpSecurity http) throws Exception {
* http
* .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
* .saml2Metadata(Customizer.withDefaults());
* return http.build();
* }
*
* &#064;Bean
* public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
* RelyingPartyRegistration registration = RelyingPartyRegistrations
* .withMetadataLocation("https://ap.example.org/metadata")
* .registrationId("simple")
* .build();
* return new InMemoryRelyingPartyRegistrationRepository(registration);
* }
* }
* </pre>
* @param saml2MetadataConfigurer the {@link Customizer} to provide more options for
* the {@link Saml2MetadataConfigurer}
* @return the {@link HttpSecurity} for further customizations
* @throws Exception
* @since 6.1
*/
public HttpSecurity saml2Metadata(Customizer<Saml2MetadataConfigurer<HttpSecurity>> saml2MetadataConfigurer)
throws Exception {
saml2MetadataConfigurer.customize(getOrApply(new Saml2MetadataConfigurer<>(getContext())));
return HttpSecurity.this;
}
/**
* Configures a SAML 2.0 metadata endpoint that presents relying party configurations
* in an {@code <md:EntityDescriptor>} payload.
*
* <p>
* By default, the endpoints are {@code /saml2/metadata} and
* {@code /saml2/metadata/{registrationId}} though note that also
* {@code /saml2/service-provider-metadata/{registrationId}} is recognized for
* backward compatibility purposes.
*
* <p>
* <h2>Example Configuration</h2>
*
* The following example shows the minimal configuration required, using a
* hypothetical asserting party.
*
* <pre>
* &#064;EnableWebSecurity
* &#064;Configuration
* public class Saml2LogoutSecurityConfig {
* &#064;Bean
* public SecurityFilterChain web(HttpSecurity http) throws Exception {
* http
* .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
* .saml2Metadata(Customizer.withDefaults());
* return http.build();
* }
*
* &#064;Bean
* public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
* RelyingPartyRegistration registration = RelyingPartyRegistrations
* .withMetadataLocation("https://ap.example.org/metadata")
* .registrationId("simple")
* .build();
* return new InMemoryRelyingPartyRegistrationRepository(registration);
* }
* }
* </pre>
* @return the {@link Saml2MetadataConfigurer} for further customizations
* @throws Exception
* @since 6.1
* @deprecated For removal in 7.0. Use {@link #saml2Metadata(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public Saml2MetadataConfigurer<HttpSecurity> saml2Metadata() throws Exception {
return getOrApply(new Saml2MetadataConfigurer<>(getContext()));
}
/**
* Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0
* Provider. <br>
@@ -2647,7 +2511,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* @return the {@link OAuth2LoginConfigurer} for further customizations
* @throws Exception
* @since 5.0
* @deprecated For removal in 7.0. Use {@link #oauth2Login(Customizer)} instead
* @see <a target="_blank" href=
* "https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code
* Grant</a>
@@ -2657,7 +2520,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* @see org.springframework.security.oauth2.client.registration.ClientRegistration
* @see org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<HttpSecurity> oauth2Login() throws Exception {
return getOrApply(new OAuth2LoginConfigurer<>());
}
@@ -2770,12 +2632,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* @return the {@link OAuth2ClientConfigurer} for further customizations
* @throws Exception
* @since 5.1
* @deprecated For removal in 7.0. Use {@link #oauth2Client(Customizer)} instead
* @see <a target="_blank" href=
* "https://tools.ietf.org/html/rfc6749#section-1.1">OAuth 2.0 Authorization
* Framework</a>
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ClientConfigurer<HttpSecurity> oauth2Client() throws Exception {
OAuth2ClientConfigurer<HttpSecurity> configurer = getOrApply(new OAuth2ClientConfigurer<>());
this.postProcess(configurer);
@@ -2826,13 +2686,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* @return the {@link OAuth2ResourceServerConfigurer} for further customizations
* @throws Exception
* @since 5.1
* @deprecated For removal in 7.0. Use {@link #oauth2ResourceServer(Customizer)}
* instead
* @see <a target="_blank" href=
* "https://tools.ietf.org/html/rfc6749#section-1.1">OAuth 2.0 Authorization
* Framework</a>
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ResourceServerConfigurer<HttpSecurity> oauth2ResourceServer() throws Exception {
OAuth2ResourceServerConfigurer<HttpSecurity> configurer = getOrApply(
new OAuth2ResourceServerConfigurer<>(getContext()));
@@ -2930,9 +2787,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link ChannelSecurityConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #requiresChannel(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry requiresChannel() throws Exception {
ApplicationContext context = getContext();
return getOrApply(new ChannelSecurityConfigurer<>(context)).getRegistry();
@@ -3027,9 +2882,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* </pre>
* @return the {@link HttpBasicConfigurer} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use {@link #httpBasic(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HttpBasicConfigurer<HttpSecurity> httpBasic() throws Exception {
return getOrApply(new HttpBasicConfigurer<>());
}
@@ -3358,9 +3211,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* }
* </pre>
* @return the {@link RequestMatcherConfigurer} for further customizations
* @deprecated For removal in 7.0. Use {@link #securityMatchers(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public RequestMatcherConfigurer securityMatchers() {
return this.requestMatcherConfigurer;
}
@@ -3618,28 +3469,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
/**
* Return the {@link HttpSecurity} for further customizations
* @return the {@link HttpSecurity} for further customizations
* @deprecated Use the lambda based configuration instead. For example: <pre>
* &#064;Configuration
* &#064;EnableWebSecurity
* public class SecurityConfig {
*
* &#064;Bean
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
* http
* .securityMatchers((matchers) -&gt; matchers
* .requestMatchers(&quot;/api/**&quot;)
* )
* .authorizeHttpRequests((authorize) -&gt; authorize
* .anyRequest().hasRole(&quot;USER&quot;)
* )
* .httpBasic(Customizer.withDefaults());
* return http.build();
* }
*
* }
* </pre>
*/
@Deprecated(since = "6.1", forRemoval = true)
public HttpSecurity and() {
return HttpSecurity.this;
}
@@ -46,7 +46,7 @@ import org.springframework.security.web.SecurityFilterChain;
*
* &#064;Bean
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
* http.authorizeHttpRequests().requestMatchers(&quot;/public/**&quot;).permitAll().anyRequest()
* http.authorizeRequests().requestMatchers(&quot;/public/**&quot;).permitAll().anyRequest()
* .hasRole(&quot;USER&quot;).and()
* // Possibly more configuration ...
* .formLogin() // enable form based log in
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -51,8 +51,8 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
* Base class for configuring {@link AbstractAuthenticationFilterConfigurer}. This is
* intended for internal use only.
*
* @param <T> refers to "this" for returning the current configurer
* @param <F> refers to the {@link AbstractAuthenticationProcessingFilter} that is being
* @param T refers to "this" for returning the current configurer
* @param F refers to the {@link AbstractAuthenticationProcessingFilter} that is being
* built
* @author Rob Winch
* @since 3.2
@@ -122,7 +122,7 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
* true. This is a shortcut for calling
* {@link #successHandler(AuthenticationSuccessHandler)}.
* @param defaultSuccessUrl the default success url
* @param alwaysUse true if the {@code defaultSuccessUrl} should be used after
* @param alwaysUse true if the {@code defaultSuccesUrl} should be used after
* authentication despite if a protected page had been previously visited
* @return the {@link FormLoginConfigurer} for additional customization
*/
@@ -380,6 +380,7 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
/**
* Updates the default values for authentication.
* @throws Exception
*/
protected final void updateAuthenticationDefaults() {
if (this.loginProcessingUrl == null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -17,14 +17,11 @@
package org.springframework.security.config.annotation.web.configurers;
import java.util.List;
import java.util.function.Supplier;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
@@ -41,7 +38,6 @@ import org.springframework.security.web.access.intercept.RequestMatcherDelegatin
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
import org.springframework.util.Assert;
import org.springframework.util.function.SingletonSupplier;
/**
* Adds a URL based authorization using {@link AuthorizationManager}.
@@ -60,8 +56,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
private final AuthorizationEventPublisher publisher;
private final Supplier<RoleHierarchy> roleHierarchy;
/**
* Creates an instance.
* @param context the {@link ApplicationContext} to use
@@ -74,8 +68,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
else {
this.publisher = new SpringAuthorizationEventPublisher(context);
}
this.roleHierarchy = SingletonSupplier.of(() -> (context.getBeanNamesForType(RoleHierarchy.class).length > 0)
? context.getBean(RoleHierarchy.class) : new NullRoleHierarchy());
}
/**
@@ -194,25 +186,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
* customizations
* @since 5.7
* @deprecated Permit access to the {@link jakarta.servlet.DispatcherType}
* instead. <pre>
* &#064;Configuration
* &#064;EnableWebSecurity
* public class SecurityConfig {
*
* &#064;Bean
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
* http
* .authorizeHttpRequests((authorize) -&gt; authorize
* .dispatcherTypeMatchers(DispatcherType.ERROR).permitAll()
* // ...
* );
* return http.build();
* }
* }
* </pre>
*/
@Deprecated(since = "6.1", forRemoval = true)
public AuthorizationManagerRequestMatcherRegistry shouldFilterAllDispatcherTypes(boolean shouldFilter) {
this.shouldFilterAllDispatcherTypes = shouldFilter;
return this;
@@ -222,9 +196,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* Return the {@link HttpSecurityBuilder} when done using the
* {@link AuthorizeHttpRequestsConfigurer}. This is useful for method chaining.
* @return the {@link HttpSecurityBuilder} for further customizations
* @deprecated For removal in 7.0. Use the lambda based configuration instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public H and() {
return AuthorizeHttpRequestsConfigurer.this.and();
}
@@ -279,7 +251,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasRole(role)));
return access(AuthorityAuthorizationManager.hasRole(role));
}
/**
@@ -291,7 +263,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry hasAnyRole(String... roles) {
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAnyRole(roles)));
return access(AuthorityAuthorizationManager.hasAnyRole(roles));
}
/**
@@ -301,7 +273,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority)));
return access(AuthorityAuthorizationManager.hasAuthority(authority));
}
/**
@@ -312,13 +284,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* customizations
*/
public AuthorizationManagerRequestMatcherRegistry hasAnyAuthority(String... authorities) {
return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities)));
}
private AuthorityAuthorizationManager<RequestAuthorizationContext> withRoleHierarchy(
AuthorityAuthorizationManager<RequestAuthorizationContext> manager) {
manager.setRoleHierarchy(AuthorizeHttpRequestsConfigurer.this.roleHierarchy.get());
return manager;
return access(AuthorityAuthorizationManager.hasAnyAuthority(authorities));
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -24,7 +24,6 @@ import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
import org.springframework.security.config.annotation.SecurityConfigurer;
@@ -195,10 +194,7 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
* Return the {@link SecurityBuilder} when done using the
* {@link SecurityConfigurer}. This is useful for method chaining.
* @return the type of {@link HttpSecurityBuilder} that is being configured
* @deprecated For removal in 7.0. Use
* {@link HttpSecurity#requiresChannel(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public H and() {
return ChannelSecurityConfigurer.this.and();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -135,9 +135,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* X-Content-Type-Options: nosniff
* </pre>
* @return the {@link ContentTypeOptionsConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #contentTypeOptions(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ContentTypeOptionsConfig contentTypeOptions() {
return this.contentTypeOptions.enable();
}
@@ -168,9 +166,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* >X-XSS-Protection header</a>
* </p>
* @return the {@link XXssConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #xssProtection(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public XXssConfig xssProtection() {
return this.xssProtection.enable();
}
@@ -201,9 +197,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* <li>Expires: 0</li>
* </ul>
* @return the {@link CacheControlConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #cacheControl(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public CacheControlConfig cacheControl() {
return this.cacheControl.enable();
}
@@ -230,10 +224,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* <a href="https://tools.ietf.org/html/rfc6797">HTTP Strict Transport Security
* (HSTS)</a>.
* @return the {@link HstsConfig} for additional customizations
* @deprecated For removal in 7.0. Use
* {@link #httpStrictTransportSecurity(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HstsConfig httpStrictTransportSecurity() {
return this.hsts.enable();
}
@@ -254,9 +245,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Allows customizing the {@link XFrameOptionsHeaderWriter}.
* @return the {@link FrameOptionsConfig} for additional customizations
* @deprecated For removal in 7.0. Use {@link #frameOptions(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public FrameOptionsConfig frameOptions() {
return this.frameOptions.enable();
}
@@ -326,11 +315,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link ContentSecurityPolicyConfig} for additional configuration
* @throws IllegalArgumentException if policyDirectives is null or empty
* @since 4.1
* @deprecated For removal in 7.0. Use {@link #contentSecurityPolicy(Customizer)}
* instead
* @see ContentSecurityPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public ContentSecurityPolicyConfig contentSecurityPolicy(String policyDirectives) {
this.contentSecurityPolicy.writer = new ContentSecurityPolicyHeaderWriter(policyDirectives);
return this.contentSecurityPolicy;
@@ -460,10 +446,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* </pre>
* @return the {@link ReferrerPolicyConfig} for additional configuration
* @since 4.2
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)} instead
* @see ReferrerPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public ReferrerPolicyConfig referrerPolicy() {
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter();
return this.referrerPolicy;
@@ -485,10 +469,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link ReferrerPolicyConfig} for additional configuration
* @throws IllegalArgumentException if policy is null or empty
* @since 4.2
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)} instead
* @see ReferrerPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public ReferrerPolicyConfig referrerPolicy(ReferrerPolicy policy) {
this.referrerPolicy.writer = new ReferrerPolicyHeaderWriter(policy);
return this.referrerPolicy;
@@ -530,8 +512,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link FeaturePolicyConfig} for additional configuration
* @throws IllegalArgumentException if policyDirectives is {@code null} or empty
* @since 5.1
* @deprecated For removal in 7.0. Use {@link #permissionsPolicy(Customizer)} instead.
* @see ObjectPostProcessorConfiguration FeaturePolicyHeaderWriter
* @deprecated Use {@link #permissionsPolicy(Customizer)} instead.
* @seeObjectPostProcessorConfiguration FeaturePolicyHeaderWriter
*/
@Deprecated
public FeaturePolicyConfig featurePolicy(String policyDirectives) {
@@ -555,10 +537,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* </ul>
* @return the {@link PermissionsPolicyConfig} for additional configuration
* @since 5.5
* @deprecated For removal in 7.0. Use {@link #permissionsPolicy(Customizer)} instead
* @see PermissionsPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public PermissionsPolicyConfig permissionsPolicy() {
this.permissionsPolicy.writer = new PermissionsPolicyHeaderWriter();
return this.permissionsPolicy;
@@ -595,11 +575,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* </p>
* @return the {@link CrossOriginOpenerPolicyConfig} for additional confniguration
* @since 5.7
* @deprecated For removal in 7.0. Use {@link #crossOriginOpenerPolicy(Customizer)}
* instead
* @see CrossOriginOpenerPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy() {
this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
return this.crossOriginOpenerPolicy;
@@ -639,11 +616,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* </p>
* @return the {@link CrossOriginEmbedderPolicyConfig} for additional customizations
* @since 5.7
* @deprecated For removal in 7.0. Use {@link #crossOriginEmbedderPolicy(Customizer)}
* instead
* @see CrossOriginEmbedderPolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy() {
this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
return this.crossOriginEmbedderPolicy;
@@ -683,11 +657,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* </p>
* @return the {@link HeadersConfigurer} for additional customizations
* @since 5.7
* @deprecated For removal in 7.0. Use {@link #crossOriginResourcePolicy(Customizer)}
* instead
* @see CrossOriginResourcePolicyHeaderWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginResourcePolicyConfig crossOriginResourcePolicy() {
this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
return this.crossOriginResourcePolicy;
@@ -737,10 +708,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Allows customizing the {@link HeadersConfigurer}
* @return the {@link HeadersConfigurer} for additional customization
* @deprecated For removal in 7.0. Use {@link #contentTypeOptions(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -813,9 +781,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* Allows completing configuration of X-XSS-Protection and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #xssProtection(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -854,9 +820,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* Allows completing configuration of Cache Control and continuing configuration
* of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #cacheControl(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -962,10 +926,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* Allows completing configuration of Strict Transport Security and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #httpStrictTransportSecurity(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -1026,9 +987,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Allows continuing customizing the headers configuration.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #frameOptions(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -1251,10 +1210,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* Allows completing configuration of Content Security Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #contentSecurityPolicy(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -1279,10 +1235,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -1329,10 +1281,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* Allows completing configuration of Permissions Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use {@link #permissionsPolicy(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -1362,10 +1311,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* Allows completing configuration of Cross Origin Opener Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #crossOriginOpenerPolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -1396,10 +1342,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* Allows completing configuration of Cross-Origin-Embedder-Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #crossOriginEmbedderPolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -1430,10 +1373,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* Allows completing configuration of Cross-Origin-Resource-Policy and continuing
* configuration of headers.
* @return the {@link HeadersConfigurer} for additional configuration
* @deprecated For removal in 7.0. Use
* {@link #crossOriginResourcePolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeadersConfigurer<H> and() {
return HeadersConfigurer.this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -36,8 +36,6 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
@@ -77,7 +75,6 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
* </ul>
*
* @author Rob Winch
* @author Evgeniy Cheban
* @since 3.2
*/
public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
@@ -94,8 +91,6 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
private BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
private SecurityContextRepository securityContextRepository;
/**
* Creates a new instance
* @see HttpSecurity#httpBasic()
@@ -147,19 +142,6 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Specifies a custom {@link SecurityContextRepository} to use for basic
* authentication. The default is {@link RequestAttributeSecurityContextRepository}.
* @param securityContextRepository the custom {@link SecurityContextRepository} to
* use
* @return {@link HttpBasicConfigurer} for additional customization
* @since 6.1
*/
public HttpBasicConfigurer<B> securityContextRepository(SecurityContextRepository securityContextRepository) {
this.securityContextRepository = securityContextRepository;
return this;
}
@Override
public void init(B http) {
registerDefaults(http);
@@ -213,9 +195,6 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
if (this.authenticationDetailsSource != null) {
basicAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
}
if (this.securityContextRepository != null) {
basicAuthenticationFilter.setSecurityContextRepository(this.securityContextRepository);
}
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
if (rememberMeServices != null) {
basicAuthenticationFilter.setRememberMeServices(rememberMeServices);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -769,10 +769,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Used to chain back to the {@link SessionManagementConfigurer}
* @return the {@link SessionManagementConfigurer} for further customizations
* @deprecated For removal in 7.0. Use {@link #sessionConcurrency(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public SessionManagementConfigurer<H> and() {
return SessionManagementConfigurer.this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -136,10 +136,7 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
* Returns the {@link AuthorizationCodeGrantConfigurer} for configuring the OAuth 2.0
* Authorization Code Grant.
* @return the {@link AuthorizationCodeGrantConfigurer}
* @deprecated For removal in 7.0. Use {@link #authorizationCodeGrant(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public AuthorizationCodeGrantConfigurer authorizationCodeGrant() {
return this.authorizationCodeGrantConfigurer;
}
@@ -236,10 +233,7 @@ public final class OAuth2ClientConfigurer<B extends HttpSecurityBuilder<B>>
/**
* Returns the {@link OAuth2ClientConfigurer} for further configuration.
* @return the {@link OAuth2ClientConfigurer}
* @deprecated For removal in 7.0. Use {@link #authorizationCodeGrant(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ClientConfigurer<B> and() {
return OAuth2ClientConfigurer.this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -206,10 +206,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
* Returns the {@link AuthorizationEndpointConfig} for configuring the Authorization
* Server's Authorization Endpoint.
* @return the {@link AuthorizationEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #authorizationEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public AuthorizationEndpointConfig authorizationEndpoint() {
return this.authorizationEndpointConfig;
}
@@ -230,9 +227,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
* Returns the {@link TokenEndpointConfig} for configuring the Authorization Server's
* Token Endpoint.
* @return the {@link TokenEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #tokenEndpoint(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public TokenEndpointConfig tokenEndpoint() {
return this.tokenEndpointConfig;
}
@@ -253,10 +248,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
* Returns the {@link RedirectionEndpointConfig} for configuring the Client's
* Redirection Endpoint.
* @return the {@link RedirectionEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #redirectionEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public RedirectionEndpointConfig redirectionEndpoint() {
return this.redirectionEndpointConfig;
}
@@ -277,9 +269,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
* Returns the {@link UserInfoEndpointConfig} for configuring the Authorization
* Server's UserInfo Endpoint.
* @return the {@link UserInfoEndpointConfig}
* @deprecated For removal in 7.0. Use {@link #userInfoEndpoint(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public UserInfoEndpointConfig userInfoEndpoint() {
return this.userInfoEndpointConfig;
}
@@ -600,10 +590,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #authorizationEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
@@ -637,9 +624,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #tokenEndpoint(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
@@ -671,10 +656,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #redirectionEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
@@ -736,10 +718,7 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
/**
* Returns the {@link OAuth2LoginConfigurer} for further configuration.
* @return the {@link OAuth2LoginConfigurer}
* @deprecated For removal in 7.0. Use {@link #userInfoEndpoint(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginConfigurer<B> and() {
return OAuth2LoginConfigurer.this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -198,10 +198,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #jwt(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public JwtConfigurer jwt() {
if (this.jwtConfigurer == null) {
this.jwtConfigurer = new JwtConfigurer(this.context);
@@ -223,10 +219,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #opaqueToken(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OpaqueTokenConfigurer opaqueToken() {
if (this.opaqueTokenConfigurer == null) {
this.opaqueTokenConfigurer = new OpaqueTokenConfigurer(this.context);
@@ -402,10 +394,6 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #jwt(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ResourceServerConfigurer<H> and() {
return OAuth2ResourceServerConfigurer.this;
}
@@ -33,8 +33,9 @@ import org.springframework.security.saml2.provider.service.authentication.Abstra
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.OpenSamlAuthenticationTokenConverter;
import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
import org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter;
@@ -291,6 +292,11 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
}
}
private RelyingPartyRegistrationResolver relyingPartyRegistrationResolver(B http) {
RelyingPartyRegistrationRepository registrations = relyingPartyRegistrationRepository(http);
return new DefaultRelyingPartyRegistrationResolver(registrations);
}
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository(B http) {
if (this.relyingPartyRegistrationRepository == null) {
this.relyingPartyRegistrationRepository = getSharedOrBean(http, RelyingPartyRegistrationRepository.class);
@@ -333,7 +339,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return bean;
}
OpenSaml4AuthenticationRequestResolver openSaml4AuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(
relyingPartyRegistrationRepository(http));
relyingPartyRegistrationResolver(http));
openSaml4AuthenticationRequestResolver
.setRequestMatcher(new AntPathRequestMatcher(this.authenticationRequestUri));
return openSaml4AuthenticationRequestResolver;
@@ -346,14 +352,10 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
AuthenticationConverter authenticationConverterBean = getBeanOrNull(http,
Saml2AuthenticationTokenConverter.class);
if (authenticationConverterBean == null) {
authenticationConverterBean = getBeanOrNull(http, OpenSamlAuthenticationTokenConverter.class);
}
if (authenticationConverterBean == null) {
OpenSamlAuthenticationTokenConverter converter = new OpenSamlAuthenticationTokenConverter(
this.relyingPartyRegistrationRepository);
converter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
converter.setRequestMatcher(createLoginProcessingUrlMatcher(this.loginProcessingUrl));
return converter;
Assert.state(this.loginProcessingUrl.contains("{registrationId}"),
"loginProcessingUrl must contain {registrationId} path variable");
return new Saml2AuthenticationTokenConverter(
new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository));
}
return authenticationConverterBean;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -39,10 +39,11 @@ import org.springframework.security.saml2.provider.service.authentication.logout
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository;
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutRequestResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutResponseResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSamlLogoutRequestValidatorParametersResolver;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestRepository;
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver;
@@ -168,9 +169,7 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Get configurer for SAML 2.0 Logout Request components
* @return the {@link LogoutRequestConfigurer} for further customizations
* @deprecated For removal in 7.0. Use {@link #logoutRequest(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public LogoutRequestConfigurer logoutRequest() {
return this.logoutRequestConfigurer;
}
@@ -190,15 +189,13 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
/**
* Get configurer for SAML 2.0 Logout Response components
* @return the {@link LogoutResponseConfigurer} for further customizations
* @deprecated For removal in 7.0. Use {@link #logoutResponse(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public LogoutResponseConfigurer logoutResponse() {
return this.logoutResponseConfigurer;
}
/**
* Configures SAML 2.0 Logout Response components
* Configures SAML 2.0 Logout Request components
* @param logoutResponseConfigurerCustomizer the {@link Customizer} to provide more
* options for the {@link LogoutResponseConfigurer}
* @return the {@link Saml2LogoutConfigurer} for further customizations
@@ -219,12 +216,17 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
this.logoutHandlers = logout.getLogoutHandlers();
this.logoutSuccessHandler = logout.getLogoutSuccessHandler();
}
RelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);
RelyingPartyRegistrationResolver registrations = relyingPartyRegistrationResolver(http);
http.addFilterBefore(createLogoutRequestProcessingFilter(registrations), CsrfFilter.class);
http.addFilterBefore(createLogoutResponseProcessingFilter(registrations), CsrfFilter.class);
http.addFilterBefore(createRelyingPartyLogoutFilter(registrations), LogoutFilter.class);
}
private RelyingPartyRegistrationResolver relyingPartyRegistrationResolver(H http) {
RelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);
return new DefaultRelyingPartyRegistrationResolver(registrations);
}
private RelyingPartyRegistrationRepository getRelyingPartyRegistrationRepository(H http) {
if (this.relyingPartyRegistrationRepository != null) {
return this.relyingPartyRegistrationRepository;
@@ -240,21 +242,18 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
}
private Saml2LogoutRequestFilter createLogoutRequestProcessingFilter(
RelyingPartyRegistrationRepository registrations) {
RelyingPartyRegistrationResolver registrations) {
LogoutHandler[] logoutHandlers = this.logoutHandlers.toArray(new LogoutHandler[0]);
Saml2LogoutResponseResolver logoutResponseResolver = createSaml2LogoutResponseResolver(registrations);
RequestMatcher requestMatcher = createLogoutRequestMatcher();
OpenSamlLogoutRequestValidatorParametersResolver parameters = new OpenSamlLogoutRequestValidatorParametersResolver(
registrations);
parameters.setRequestMatcher(requestMatcher);
Saml2LogoutRequestFilter filter = new Saml2LogoutRequestFilter(parameters,
Saml2LogoutRequestFilter filter = new Saml2LogoutRequestFilter(registrations,
this.logoutRequestConfigurer.logoutRequestValidator(), logoutResponseResolver, logoutHandlers);
filter.setLogoutRequestMatcher(createLogoutRequestMatcher());
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
return postProcess(filter);
}
private Saml2LogoutResponseFilter createLogoutResponseProcessingFilter(
RelyingPartyRegistrationRepository registrations) {
RelyingPartyRegistrationResolver registrations) {
Saml2LogoutResponseFilter logoutResponseFilter = new Saml2LogoutResponseFilter(registrations,
this.logoutResponseConfigurer.logoutResponseValidator(), this.logoutSuccessHandler);
logoutResponseFilter.setLogoutRequestMatcher(createLogoutResponseMatcher());
@@ -262,7 +261,7 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return postProcess(logoutResponseFilter);
}
private LogoutFilter createRelyingPartyLogoutFilter(RelyingPartyRegistrationRepository registrations) {
private LogoutFilter createRelyingPartyLogoutFilter(RelyingPartyRegistrationResolver registrations) {
LogoutHandler[] logoutHandlers = this.logoutHandlers.toArray(new LogoutHandler[0]);
Saml2RelyingPartyInitiatedLogoutSuccessHandler logoutRequestSuccessHandler = createSaml2LogoutRequestSuccessHandler(
registrations);
@@ -291,15 +290,15 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
}
private Saml2RelyingPartyInitiatedLogoutSuccessHandler createSaml2LogoutRequestSuccessHandler(
RelyingPartyRegistrationRepository registrations) {
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {
Saml2LogoutRequestResolver logoutRequestResolver = this.logoutRequestConfigurer
.logoutRequestResolver(registrations);
.logoutRequestResolver(relyingPartyRegistrationResolver);
return new Saml2RelyingPartyInitiatedLogoutSuccessHandler(logoutRequestResolver);
}
private Saml2LogoutResponseResolver createSaml2LogoutResponseResolver(
RelyingPartyRegistrationRepository registrations) {
return this.logoutResponseConfigurer.logoutResponseResolver(registrations);
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {
return this.logoutResponseConfigurer.logoutResponseResolver(relyingPartyRegistrationResolver);
}
private <C> C getBeanOrNull(Class<C> clazz) {
@@ -375,10 +374,6 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #logoutRequest(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public Saml2LogoutConfigurer<H> and() {
return Saml2LogoutConfigurer.this;
}
@@ -390,11 +385,12 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this.logoutRequestValidator;
}
private Saml2LogoutRequestResolver logoutRequestResolver(RelyingPartyRegistrationRepository registrations) {
private Saml2LogoutRequestResolver logoutRequestResolver(
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {
if (this.logoutRequestResolver != null) {
return this.logoutRequestResolver;
}
return new OpenSaml4LogoutRequestResolver(registrations);
return new OpenSaml4LogoutRequestResolver(relyingPartyRegistrationResolver);
}
}
@@ -447,10 +443,6 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #logoutResponse(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public Saml2LogoutConfigurer<H> and() {
return Saml2LogoutConfigurer.this;
}
@@ -462,9 +454,10 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
return this.logoutResponseValidator;
}
private Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) {
private Saml2LogoutResponseResolver logoutResponseResolver(
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {
if (this.logoutResponseResolver == null) {
return new OpenSaml4LogoutResponseResolver(registrations);
return new OpenSaml4LogoutResponseResolver(relyingPartyRegistrationResolver);
}
return this.logoutResponseResolver;
}
@@ -1,169 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.config.annotation.web.configurers.saml2;
import java.util.function.Function;
import org.springframework.context.ApplicationContext;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver;
import org.springframework.security.saml2.provider.service.metadata.RequestMatcherMetadataResponseResolver;
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.web.Saml2MetadataFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.Assert;
/**
* An {@link AbstractHttpConfigurer} for SAML 2.0 Metadata.
*
* <p>
* SAML 2.0 Metadata provides an application with the capability to publish configuration
* information as a {@code <md:EntityDescriptor>} or {@code <md:EntitiesDescriptor>}.
*
* <p>
* Defaults are provided for all configuration options with the only required
* configuration being a {@link Saml2LoginConfigurer#relyingPartyRegistrationRepository}.
* Alternatively, a {@link RelyingPartyRegistrationRepository} {@code @Bean} may be
* registered instead.
*
* <h2>Security Filters</h2>
*
* The following {@code Filter} is populated:
*
* <ul>
* <li>{@link Saml2MetadataFilter}</li>
* </ul>
*
* <h2>Shared Objects Created</h2>
*
* none
*
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
*
* <ul>
* <li>{@link RelyingPartyRegistrationRepository} (required)</li>
* </ul>
*
* @since 6.1
* @see HttpSecurity#saml2Metadata()
* @see Saml2MetadataFilter
* @see RelyingPartyRegistrationRepository
*/
public class Saml2MetadataConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<Saml2LogoutConfigurer<H>, H> {
private final ApplicationContext context;
private Function<RelyingPartyRegistrationRepository, Saml2MetadataResponseResolver> metadataResponseResolver;
public Saml2MetadataConfigurer(ApplicationContext context) {
this.context = context;
}
/**
* Use this endpoint to request relying party metadata.
*
* <p>
* If you specify a {@code registrationId} placeholder in the URL, then the filter
* will lookup a {@link RelyingPartyRegistration} using that.
*
* <p>
* If there is no {@code registrationId} and your
* {@link RelyingPartyRegistrationRepository} is {code Iterable}, the metadata
* endpoint will try and show all relying parties' metadata in a single
* {@code <md:EntitiesDecriptor} element.
*
* <p>
* If you need a more sophisticated lookup strategy than these, use
* {@link #metadataResponseResolver} instead.
* @param metadataUrl the url to use
* @return the {@link Saml2MetadataConfigurer} for more customizations
*/
public Saml2MetadataConfigurer<H> metadataUrl(String metadataUrl) {
Assert.hasText(metadataUrl, "metadataUrl cannot be empty");
this.metadataResponseResolver = (registrations) -> {
RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(registrations,
new OpenSamlMetadataResolver());
metadata.setRequestMatcher(new AntPathRequestMatcher(metadataUrl));
return metadata;
};
return this;
}
/**
* Use this {@link Saml2MetadataResponseResolver} to parse the request and respond
* with SAML 2.0 metadata.
* @param metadataResponseResolver to use
* @return the {@link Saml2MetadataConfigurer} for more customizations
*/
public Saml2MetadataConfigurer<H> metadataResponseResolver(Saml2MetadataResponseResolver metadataResponseResolver) {
Assert.notNull(metadataResponseResolver, "metadataResponseResolver cannot be null");
this.metadataResponseResolver = (registrations) -> metadataResponseResolver;
return this;
}
public H and() {
return getBuilder();
}
@Override
public void configure(H http) throws Exception {
Saml2MetadataResponseResolver metadataResponseResolver = createMetadataResponseResolver(http);
http.addFilterBefore(new Saml2MetadataFilter(metadataResponseResolver), BasicAuthenticationFilter.class);
}
private Saml2MetadataResponseResolver createMetadataResponseResolver(H http) {
if (this.metadataResponseResolver != null) {
RelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);
return this.metadataResponseResolver.apply(registrations);
}
Saml2MetadataResponseResolver metadataResponseResolver = getBeanOrNull(Saml2MetadataResponseResolver.class);
if (metadataResponseResolver != null) {
return metadataResponseResolver;
}
RelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);
return new RequestMatcherMetadataResponseResolver(registrations, new OpenSamlMetadataResolver());
}
private RelyingPartyRegistrationRepository getRelyingPartyRegistrationRepository(H http) {
Saml2LoginConfigurer<H> login = http.getConfigurer(Saml2LoginConfigurer.class);
if (login != null) {
return login.relyingPartyRegistrationRepository(http);
}
else {
return getBeanOrNull(RelyingPartyRegistrationRepository.class);
}
}
private <C> C getBeanOrNull(Class<C> clazz) {
if (this.context == null) {
return null;
}
if (this.context.getBeanNamesForType(clazz).length == 0) {
return null;
}
return this.context.getBean(clazz);
}
}
@@ -781,8 +781,8 @@ class HttpConfigurationBuilder {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FilterSecurityInterceptor.class);
builder.addPropertyReference("accessDecisionManager", accessManagerId);
builder.addPropertyValue("authenticationManager", authManager);
if ("false".equals(this.httpElt.getAttribute(ATT_ONCE_PER_REQUEST))) {
builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE);
if ("true".equals(this.httpElt.getAttribute(ATT_ONCE_PER_REQUEST))) {
builder.addPropertyValue("observeOncePerRequest", Boolean.TRUE);
}
builder.addPropertyValue("securityMetadataSource", securityMds);
builder.addPropertyValue("securityContextHolderStrategy", this.holderStrategyRef);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2023 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.
@@ -16,6 +16,7 @@
package org.springframework.security.config.web.server;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.HttpMethod;
@@ -23,6 +24,8 @@ import org.springframework.security.web.server.util.matcher.OrServerWebExchangeM
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* @author Rob Winch
@@ -62,7 +65,8 @@ public abstract class AbstractServerWebExchangeMatcherRegistry<T> {
* {@link ServerWebExchangeMatcher}
*/
public T pathMatchers(HttpMethod method, String... antPatterns) {
return matcher(ServerWebExchangeMatchers.pathMatchers(method, antPatterns));
List<PathPattern> pathPatterns = parsePatterns(antPatterns);
return matcher(ServerWebExchangeMatchers.pathMatchers(method, pathPatterns.toArray(new PathPattern[0])));
}
/**
@@ -74,7 +78,19 @@ public abstract class AbstractServerWebExchangeMatcherRegistry<T> {
* {@link ServerWebExchangeMatcher}
*/
public T pathMatchers(String... antPatterns) {
return matcher(ServerWebExchangeMatchers.pathMatchers(antPatterns));
List<PathPattern> pathPatterns = parsePatterns(antPatterns);
return matcher(ServerWebExchangeMatchers.pathMatchers(pathPatterns.toArray(new PathPattern[0])));
}
private List<PathPattern> parsePatterns(String[] antPatterns) {
PathPatternParser parser = getPathPatternParser();
List<PathPattern> pathPatterns = new ArrayList<>(antPatterns.length);
for (String pattern : antPatterns) {
pattern = parser.initFullPathPattern(pattern);
PathPattern pathPattern = parser.parse(pattern);
pathPatterns.add(pathPattern);
}
return pathPatterns;
}
/**
@@ -96,6 +112,10 @@ public abstract class AbstractServerWebExchangeMatcherRegistry<T> {
*/
protected abstract T registerMatcher(ServerWebExchangeMatcher matcher);
protected PathPatternParser getPathPatternParser() {
return PathPatternParser.defaultInstance;
}
/**
* Associates a {@link ServerWebExchangeMatcher} instances
* @param matcher the {@link ServerWebExchangeMatcher} instance
@@ -191,9 +191,11 @@ import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.CorsProcessor;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.DefaultCorsProcessor;
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* A {@link ServerHttpSecurity} is similar to Spring Security's {@code HttpSecurity} but
@@ -421,9 +423,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link HttpsRedirectSpec} to customize
* @deprecated For removal in 7.0. Use {@link #redirectToHttps(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HttpsRedirectSpec redirectToHttps() {
this.httpsRedirectSpec = new HttpsRedirectSpec();
return this.httpsRedirectSpec;
@@ -504,9 +504,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link CsrfSpec} to customize
* @deprecated For removal in 7.0. Use {@link #csrf(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public CsrfSpec csrf() {
if (this.csrf == null) {
this.csrf = new CsrfSpec();
@@ -570,9 +568,7 @@ public class ServerHttpSecurity {
* used instead. If neither has been configured, the Cors configuration will do
* nothing.
* @return the {@link CorsSpec} to customize
* @deprecated For removal in 7.0. Use {@link #cors(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public CorsSpec cors() {
if (this.cors == null) {
this.cors = new CorsSpec();
@@ -614,9 +610,7 @@ public class ServerHttpSecurity {
* </pre>
* @return the {@link AnonymousSpec} to customize
* @since 5.2.0
* @deprecated For removal in 7.0. Use {@link #anonymous(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public AnonymousSpec anonymous() {
if (this.anonymous == null) {
this.anonymous = new AnonymousSpec();
@@ -670,9 +664,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link HttpBasicSpec} to customize
* @deprecated For removal in 7.0. Use {@link #httpBasic(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HttpBasicSpec httpBasic() {
if (this.httpBasic == null) {
this.httpBasic = new HttpBasicSpec();
@@ -724,9 +716,7 @@ public class ServerHttpSecurity {
* </pre>
* @return the {@link PasswordManagementSpec} to customize
* @since 5.6
* @deprecated For removal in 7.0. Use {@link #passwordManagement(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public PasswordManagementSpec passwordManagement() {
if (this.passwordManagement == null) {
this.passwordManagement = new PasswordManagementSpec();
@@ -783,9 +773,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link FormLoginSpec} to customize
* @deprecated For removal in 7.0. Use {@link #formLogin(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public FormLoginSpec formLogin() {
if (this.formLogin == null) {
this.formLogin = new FormLoginSpec();
@@ -846,9 +834,7 @@ public class ServerHttpSecurity {
* {@link ReactivePreAuthenticatedAuthenticationManager} will be used.
* @return the {@link X509Spec} to customize
* @since 5.2
* @deprecated For removal in 7.0. Use {@link #x509(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public X509Spec x509() {
if (this.x509 == null) {
this.x509 = new X509Spec();
@@ -905,9 +891,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link OAuth2LoginSpec} to customize
* @deprecated For removal in 7.0. Use {@link #oauth2Login(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2LoginSpec oauth2Login() {
if (this.oauth2Login == null) {
this.oauth2Login = new OAuth2LoginSpec();
@@ -959,9 +943,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link OAuth2ClientSpec} to customize
* @deprecated For removal in 7.0. Use {@link #oauth2Client(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ClientSpec oauth2Client() {
if (this.client == null) {
this.client = new OAuth2ClientSpec();
@@ -1012,10 +994,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link OAuth2ResourceServerSpec} to customize
* @deprecated For removal in 7.0. Use {@link #oauth2ResourceServer(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ResourceServerSpec oauth2ResourceServer() {
if (this.resourceServer == null) {
this.resourceServer = new OAuth2ResourceServerSpec();
@@ -1087,9 +1066,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link HeaderSpec} to customize
* @deprecated For removal in 7.0. Use {@link #headers(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeaderSpec headers() {
if (this.headers == null) {
this.headers = new HeaderSpec();
@@ -1163,9 +1140,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link ExceptionHandlingSpec} to customize
* @deprecated For removal in 7.0. Use {@link #exceptionHandling(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ExceptionHandlingSpec exceptionHandling() {
if (this.exceptionHandling == null) {
this.exceptionHandling = new ExceptionHandlingSpec();
@@ -1231,9 +1206,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link AuthorizeExchangeSpec} to customize
* @deprecated For removal in 7.0. Use {@link #authorizeExchange(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public AuthorizeExchangeSpec authorizeExchange() {
if (this.authorizeExchange == null) {
this.authorizeExchange = new AuthorizeExchangeSpec();
@@ -1302,9 +1275,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link LogoutSpec} to customize
* @deprecated For removal in 7.0. Use {@link #logout(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public LogoutSpec logout() {
if (this.logout == null) {
this.logout = new LogoutSpec();
@@ -1361,9 +1332,7 @@ public class ServerHttpSecurity {
* }
* </pre>
* @return the {@link RequestCacheSpec} to customize
* @deprecated For removal in 7.0. Use {@link #requestCache(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public RequestCacheSpec requestCache() {
return this.requestCache;
}
@@ -1606,6 +1575,18 @@ public class ServerHttpSecurity {
return null;
}
private <T> T getBeanOrNull(String beanName, Class<T> requiredClass) {
if (this.context == null) {
return null;
}
try {
return this.context.getBean(beanName, requiredClass);
}
catch (Exception ex) {
return null;
}
}
private <T> String[] getBeanNamesForTypeOrEmpty(Class<T> beanClass) {
if (this.context == null) {
return new String[0];
@@ -1626,6 +1607,8 @@ public class ServerHttpSecurity {
*/
public class AuthorizeExchangeSpec extends AbstractServerWebExchangeMatcherRegistry<AuthorizeExchangeSpec.Access> {
private static final String REQUEST_MAPPING_HANDLER_MAPPING_BEAN_NAME = "requestMappingHandlerMapping";
private DelegatingReactiveAuthorizationManager.Builder managerBldr = DelegatingReactiveAuthorizationManager
.builder();
@@ -1633,13 +1616,12 @@ public class ServerHttpSecurity {
private boolean anyExchangeRegistered;
private PathPatternParser pathPatternParser;
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #authorizeExchange(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -1655,6 +1637,22 @@ public class ServerHttpSecurity {
return result;
}
@Override
protected PathPatternParser getPathPatternParser() {
if (this.pathPatternParser != null) {
return this.pathPatternParser;
}
RequestMappingHandlerMapping requestMappingHandlerMapping = getBeanOrNull(
REQUEST_MAPPING_HANDLER_MAPPING_BEAN_NAME, RequestMappingHandlerMapping.class);
if (requestMappingHandlerMapping != null) {
this.pathPatternParser = requestMappingHandlerMapping.getPathPatternParser();
}
if (this.pathPatternParser == null) {
this.pathPatternParser = PathPatternParser.defaultInstance;
}
return this.pathPatternParser;
}
@Override
protected Access registerMatcher(ServerWebExchangeMatcher matcher) {
Assert.state(!this.anyExchangeRegistered, () -> "Cannot register " + matcher
@@ -1837,9 +1835,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated use {@link #redirectToHttps(Customizer)}
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -1917,9 +1913,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #csrf(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -1985,10 +1979,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #exceptionHandling(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -2030,9 +2021,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #requestCache(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -2128,9 +2117,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #httpBasic(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -2203,10 +2190,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}.
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #passwordManagement(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -2364,9 +2348,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #formLogin(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -2490,9 +2472,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #headers(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -2509,9 +2489,7 @@ public class ServerHttpSecurity {
/**
* Configures cache control headers
* @return the {@link CacheSpec} to configure
* @deprecated For removal in 7.0. Use {@link #cache(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public CacheSpec cache() {
return new CacheSpec();
}
@@ -2530,10 +2508,7 @@ public class ServerHttpSecurity {
/**
* Configures content type response headers
* @return the {@link ContentTypeOptionsSpec} to configure
* @deprecated For removal in 7.0. Use {@link #contentTypeOptions(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ContentTypeOptionsSpec contentTypeOptions() {
return new ContentTypeOptionsSpec();
}
@@ -2552,9 +2527,7 @@ public class ServerHttpSecurity {
/**
* Configures frame options response headers
* @return the {@link FrameOptionsSpec} to configure
* @deprecated For removal in 7.0. Use {@link #frameOptions(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public FrameOptionsSpec frameOptions() {
return new FrameOptionsSpec();
}
@@ -2586,9 +2559,7 @@ public class ServerHttpSecurity {
/**
* Configures the Strict Transport Security response headers
* @return the {@link HstsSpec} to configure
* @deprecated For removal in 7.0. Use {@link #hsts(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HstsSpec hsts() {
return new HstsSpec();
}
@@ -2613,9 +2584,7 @@ public class ServerHttpSecurity {
/**
* Configures x-xss-protection response header.
* @return the {@link XssProtectionSpec} to configure
* @deprecated For removal in 7.0. Use {@link #xssProtection(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public XssProtectionSpec xssProtection() {
return new XssProtectionSpec();
}
@@ -2635,10 +2604,7 @@ public class ServerHttpSecurity {
* Configures {@code Content-Security-Policy} response header.
* @param policyDirectives the policy directive(s)
* @return the {@link ContentSecurityPolicySpec} to configure
* @deprecated For removal in 7.0. Use {@link #contentSecurityPolicy(Customizer)}
* instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public ContentSecurityPolicySpec contentSecurityPolicy(String policyDirectives) {
return new ContentSecurityPolicySpec(policyDirectives);
}
@@ -2658,8 +2624,7 @@ public class ServerHttpSecurity {
* Configures {@code Feature-Policy} response header.
* @param policyDirectives the policy
* @return the {@link FeaturePolicySpec} to configure
* @deprecated For removal in 7.0. Use {@link #permissionsPolicy(Customizer)}
* instead.
* @deprecated Use {@link #permissionsPolicy(Customizer)} instead.
*/
@Deprecated
public FeaturePolicySpec featurePolicy(String policyDirectives) {
@@ -2669,10 +2634,7 @@ public class ServerHttpSecurity {
/**
* Configures {@code Permissions-Policy} response header.
* @return the {@link PermissionsPolicySpec} to configure
* @deprecated For removal in 7.0. Use {@link #permissionsPolicy(Customizer)}
* instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public PermissionsPolicySpec permissionsPolicy() {
return new PermissionsPolicySpec();
}
@@ -2692,10 +2654,7 @@ public class ServerHttpSecurity {
* Configures {@code Referrer-Policy} response header.
* @param referrerPolicy the policy to use
* @return the {@link ReferrerPolicySpec} to configure
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)}
* instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public ReferrerPolicySpec referrerPolicy(ReferrerPolicy referrerPolicy) {
return new ReferrerPolicySpec(referrerPolicy);
}
@@ -2703,10 +2662,7 @@ public class ServerHttpSecurity {
/**
* Configures {@code Referrer-Policy} response header.
* @return the {@link ReferrerPolicySpec} to configure
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)}
* instead.
*/
@Deprecated(since = "6.1", forRemoval = true)
public ReferrerPolicySpec referrerPolicy() {
return new ReferrerPolicySpec();
}
@@ -2728,11 +2684,8 @@ public class ServerHttpSecurity {
* Cross-Origin-Opener-Policy</a> header.
* @return the {@link CrossOriginOpenerPolicySpec} to configure
* @since 5.7
* @deprecated For removal in 7.0. Use
* {@link #crossOriginOpenerPolicy(Customizer)} instead.
* @see CrossOriginOpenerPolicyServerHttpHeadersWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginOpenerPolicySpec crossOriginOpenerPolicy() {
return new CrossOriginOpenerPolicySpec();
}
@@ -2757,11 +2710,8 @@ public class ServerHttpSecurity {
* Cross-Origin-Embedder-Policy</a> header.
* @return the {@link CrossOriginEmbedderPolicySpec} to configure
* @since 5.7
* @deprecated For removal in 7.0. Use
* {@link #crossOriginEmbedderPolicy(Customizer)} instead.
* @see CrossOriginEmbedderPolicyServerHttpHeadersWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginEmbedderPolicySpec crossOriginEmbedderPolicy() {
return new CrossOriginEmbedderPolicySpec();
}
@@ -2786,11 +2736,8 @@ public class ServerHttpSecurity {
* Cross-Origin-Resource-Policy</a> header.
* @return the {@link CrossOriginResourcePolicySpec} to configure
* @since 5.7
* @deprecated For removal in 7.0. Use
* {@link #crossOriginResourcePolicy(Customizer)} instead.
* @see CrossOriginResourcePolicyServerHttpHeadersWriter
*/
@Deprecated(since = "6.1", forRemoval = true)
public CrossOriginResourcePolicySpec crossOriginResourcePolicy() {
return new CrossOriginResourcePolicySpec();
}
@@ -2876,10 +2823,7 @@ public class ServerHttpSecurity {
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}
* @return the {@link HeaderSpec} to continue configuring
* @deprecated For removal in 7.0. Use {@link #frameOptions(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
private HeaderSpec and() {
return HeaderSpec.this;
}
@@ -2947,9 +2891,7 @@ public class ServerHttpSecurity {
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}
* @return the {@link HeaderSpec} to continue configuring
* @deprecated For removal in 7.0. Use {@link #hsts(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeaderSpec and() {
return HeaderSpec.this;
}
@@ -3038,10 +2980,7 @@ public class ServerHttpSecurity {
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
* @deprecated For removal in 7.0. Use
* {@link #contentSecurityPolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeaderSpec and() {
return HeaderSpec.this;
}
@@ -3068,10 +3007,7 @@ public class ServerHttpSecurity {
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
* @deprecated For removal in 7.0. Use {@link #featurePolicy(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeaderSpec and() {
return HeaderSpec.this;
}
@@ -3103,10 +3039,7 @@ public class ServerHttpSecurity {
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
* @deprecated For removal in 7.0. Use {@link #permissionsPolicy(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeaderSpec and() {
return HeaderSpec.this;
}
@@ -3143,10 +3076,7 @@ public class ServerHttpSecurity {
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
* @deprecated For removal in 7.0. Use {@link #referrerPolicy(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeaderSpec and() {
return HeaderSpec.this;
}
@@ -3177,10 +3107,7 @@ public class ServerHttpSecurity {
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
* @deprecated For removal in 7.0. Use
* {@link #crossOriginOpenerPolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeaderSpec and() {
return HeaderSpec.this;
}
@@ -3211,10 +3138,7 @@ public class ServerHttpSecurity {
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
* @deprecated For removal in 7.0. Use
* {@link #crossOriginEmbedderPolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeaderSpec and() {
return HeaderSpec.this;
}
@@ -3245,10 +3169,7 @@ public class ServerHttpSecurity {
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}.
* @return the {@link HeaderSpec} to continue configuring
* @deprecated For removal in 7.0. Use
* {@link #crossOriginResourcePolicy(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public HeaderSpec and() {
return HeaderSpec.this;
}
@@ -3324,9 +3245,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #logout(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -3437,9 +3356,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #cors(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -3495,10 +3412,6 @@ public class ServerHttpSecurity {
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #x509(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -3751,9 +3664,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #oauth2Login(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -3952,8 +3863,6 @@ public class ServerHttpSecurity {
private ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository;
private ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;
private ServerRedirectStrategy authorizationRedirectStrategy;
private OAuth2ClientSpec() {
@@ -4048,26 +3957,6 @@ public class ServerHttpSecurity {
return this.authorizationRequestRepository;
}
/**
* Sets the resolver used for resolving {@link OAuth2AuthorizationRequest}'s.
* @param authorizationRequestResolver the resolver used for resolving
* {@link OAuth2AuthorizationRequest}'s
* @return the {@link OAuth2ClientSpec} to customize
* @since 6.1
*/
public OAuth2ClientSpec authorizationRequestResolver(
ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver) {
this.authorizationRequestResolver = authorizationRequestResolver;
return this;
}
private OAuth2AuthorizationRequestRedirectWebFilter getRedirectWebFilter() {
if (this.authorizationRequestResolver != null) {
return new OAuth2AuthorizationRequestRedirectWebFilter(this.authorizationRequestResolver);
}
return new OAuth2AuthorizationRequestRedirectWebFilter(getClientRegistrationRepository());
}
/**
* Sets the redirect strategy for Authorization Endpoint redirect URI.
* @param authorizationRedirectStrategy the redirect strategy
@@ -4088,14 +3977,13 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #oauth2Client(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
protected void configure(ServerHttpSecurity http) {
ReactiveClientRegistrationRepository clientRegistrationRepository = getClientRegistrationRepository();
ServerOAuth2AuthorizedClientRepository authorizedClientRepository = getAuthorizedClientRepository();
ServerAuthenticationConverter authenticationConverter = getAuthenticationConverter();
ReactiveAuthenticationManager authenticationManager = getAuthenticationManager();
@@ -4106,7 +3994,8 @@ public class ServerHttpSecurity {
codeGrantWebFilter.setRequestCache(http.requestCache.requestCache);
}
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = getRedirectWebFilter();
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = new OAuth2AuthorizationRequestRedirectWebFilter(
clientRegistrationRepository);
oauthRedirectFilter.setAuthorizationRequestRepository(getAuthorizationRequestRepository());
oauthRedirectFilter.setAuthorizationRedirectStrategy(getAuthorizationRedirectStrategy());
if (http.requestCache != null) {
@@ -4238,9 +4127,7 @@ public class ServerHttpSecurity {
/**
* Enables JWT Resource Server support.
* @return the {@link JwtSpec} for additional configuration
* @deprecated For removal in 7.0. Use {@link #jwt(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public JwtSpec jwt() {
if (this.jwt == null) {
this.jwt = new JwtSpec();
@@ -4265,9 +4152,7 @@ public class ServerHttpSecurity {
/**
* Enables Opaque Token Resource Server support.
* @return the {@link OpaqueTokenSpec} for additional configuration
* @deprecated For removal in 7.0. Use {@link #opaqueToken(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OpaqueTokenSpec opaqueToken() {
if (this.opaqueToken == null) {
this.opaqueToken = new OpaqueTokenSpec();
@@ -4359,11 +4244,6 @@ public class ServerHttpSecurity {
return new ServerAuthenticationEntryPointFailureHandler(this.entryPoint);
}
/**
* @deprecated For removal in 7.0. Use {@link #oauth2ResourceServer(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -4437,10 +4317,6 @@ public class ServerHttpSecurity {
return this;
}
/**
* @deprecated For removal in 7.0. Use {@link #jwt(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ResourceServerSpec and() {
return OAuth2ResourceServerSpec.this;
}
@@ -4551,10 +4427,7 @@ public class ServerHttpSecurity {
* Allows method chaining to continue configuring the
* {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #opaqueToken(Customizer)}
* instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public OAuth2ResourceServerSpec and() {
return OAuth2ResourceServerSpec.this;
}
@@ -4680,9 +4553,7 @@ public class ServerHttpSecurity {
/**
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
* @return the {@link ServerHttpSecurity} to continue configuring
* @deprecated For removal in 7.0. Use {@link #anonymous(Customizer)} instead
*/
@Deprecated(since = "6.1", forRemoval = true)
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 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.
@@ -51,17 +51,6 @@ class FormLoginDsl {
private var defaultSuccessUrlOption: Pair<String, Boolean>? = null
private var disabled = false
/**
* Disable FormLogin.
*
* @since 6.1
*/
fun disable() {
disabled = true
}
/**
* Grants access to the urls for [failureUrl] as well as for the [HttpSecurityBuilder], the
* [loginPage] and [loginProcessingUrl] for every user.
@@ -95,9 +84,6 @@ class FormLoginDsl {
authenticationSuccessHandler?.also { login.successHandler(authenticationSuccessHandler) }
authenticationFailureHandler?.also { login.failureHandler(authenticationFailureHandler) }
authenticationDetailsSource?.also { login.authenticationDetailsSource(authenticationDetailsSource) }
if (disabled) {
login.disable()
}
}
}
}
@@ -677,43 +677,6 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
this.http.saml2Login(saml2LoginCustomizer)
}
/**
* Configures a SAML 2.0 relying party metadata endpoint.
*
* A [RelyingPartyRegistrationRepository] is required and must be registered with
* the [ApplicationContext] or configured via
* [Saml2Dsl.relyingPartyRegistrationRepository]
*
* Example:
*
* The following example shows the minimal configuration required, using a
* hypothetical asserting party.
*
* ```
* @Configuration
* @EnableWebSecurity
* class SecurityConfig {
*
* @Bean
* fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
* http {
* saml2Login { }
* saml2Metadata { }
* }
* return http.build()
* }
* }
* ```
* @param saml2MetadataConfiguration custom configuration to configure the
* SAML2 relying party metadata endpoint
* @see [Saml2MetadataDsl]
* @since 6.1
*/
fun saml2Metadata(saml2MetadataConfiguration: Saml2MetadataDsl.() -> Unit) {
val saml2MetadataCustomizer = Saml2MetadataDsl().apply(saml2MetadataConfiguration).get()
this.http.saml2Metadata(saml2MetadataCustomizer)
}
/**
* Allows configuring how an anonymous user is represented.
*
@@ -1,52 +0,0 @@
/*
* Copyright 2002-2022 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
*
* https://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.config.annotation.web
import org.springframework.security.authentication.AuthenticationManagerResolver
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.oauth2.resourceserver.JwtDsl
import org.springframework.security.config.annotation.web.oauth2.resourceserver.OpaqueTokenDsl
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver
import org.springframework.security.web.AuthenticationEntryPoint
import org.springframework.security.web.access.AccessDeniedHandler
import jakarta.servlet.http.HttpServletRequest
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2MetadataConfigurer
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver
/**
* A Kotlin DSL to configure [HttpSecurity] SAML 2.0 relying party metadata support using
* idiomatic Kotlin code.
*
* @author Josh Cummings
* @since 6.1
* @property metadataUrl the name of the relying party metadata endpoint; defaults to `/saml2/metadata` and `/saml2/metadata/{registrationId}`
* @property metadataResponseResolver the [Saml2MetadataResponseResolver] to use for resolving the
* metadata request into metadata
*/
@SecurityMarker
class Saml2MetadataDsl {
var metadataUrl: String? = null
var metadataResponseResolver: Saml2MetadataResponseResolver? = null
internal fun get(): (Saml2MetadataConfigurer<HttpSecurity>) -> Unit {
return { saml2Metadata ->
metadataResponseResolver?.also { saml2Metadata.metadataResponseResolver(metadataResponseResolver) }
metadataUrl?.also { saml2Metadata.metadataUrl(metadataUrl) }
}
}
}
@@ -1,5 +1,4 @@
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.1.xsd
http\://www.springframework.org/schema/security/spring-security-6.1.xsd=org/springframework/security/config/spring-security-6.1.xsd
http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.0.xsd
http\://www.springframework.org/schema/security/spring-security-6.0.xsd=org/springframework/security/config/spring-security-6.0.xsd
http\://www.springframework.org/schema/security/spring-security-5.8.xsd=org/springframework/security/config/spring-security-5.8.xsd
http\://www.springframework.org/schema/security/spring-security-5.7.xsd=org/springframework/security/config/spring-security-5.7.xsd
@@ -21,8 +20,7 @@ http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/spri
http\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd
http\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.1.xsd
https\://www.springframework.org/schema/security/spring-security-6.1.xsd=org/springframework/security/config/spring-security-6.1.xsd
https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.0.xsd
https\://www.springframework.org/schema/security/spring-security-6.0.xsd=org/springframework/security/config/spring-security-6.0.xsd
https\://www.springframework.org/schema/security/spring-security-5.8.xsd=org/springframework/security/config/spring-security-5.8.xsd
https\://www.springframework.org/schema/security/spring-security-5.7.xsd=org/springframework/security/config/spring-security-5.7.xsd
@@ -0,0 +1 @@
spring-security-6.0.xsd
@@ -0,0 +1,139 @@
/*
* Copyright 2002-2022 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
*
* https://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.config;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.Servlet;
import jakarta.servlet.ServletRegistration;
import jakarta.servlet.ServletSecurityElement;
import org.springframework.lang.NonNull;
import org.springframework.web.servlet.DispatcherServlet;
public class MockServletContext extends org.springframework.mock.web.MockServletContext {
private final Map<String, ServletRegistration> registrations = new LinkedHashMap<>();
public static MockServletContext mvc() {
MockServletContext servletContext = new MockServletContext();
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class);
return servletContext;
}
@NonNull
@Override
public ServletRegistration.Dynamic addServlet(@NonNull String servletName, Class<? extends Servlet> clazz) {
ServletRegistration.Dynamic dynamic = new MockServletRegistration(servletName, clazz);
this.registrations.put(servletName, dynamic);
return dynamic;
}
@NonNull
@Override
public Map<String, ? extends ServletRegistration> getServletRegistrations() {
return this.registrations;
}
private static class MockServletRegistration implements ServletRegistration.Dynamic {
private final String name;
private final Class<?> clazz;
MockServletRegistration(String name, Class<?> clazz) {
this.name = name;
this.clazz = clazz;
}
@Override
public void setLoadOnStartup(int loadOnStartup) {
}
@Override
public Set<String> setServletSecurity(ServletSecurityElement constraint) {
return null;
}
@Override
public void setMultipartConfig(MultipartConfigElement multipartConfig) {
}
@Override
public void setRunAsRole(String roleName) {
}
@Override
public void setAsyncSupported(boolean isAsyncSupported) {
}
@Override
public Set<String> addMapping(String... urlPatterns) {
return null;
}
@Override
public Collection<String> getMappings() {
return null;
}
@Override
public String getRunAsRole() {
return null;
}
@Override
public String getName() {
return this.name;
}
@Override
public String getClassName() {
return this.clazz.getName();
}
@Override
public boolean setInitParameter(String name, String value) {
return false;
}
@Override
public String getInitParameter(String name) {
return null;
}
@Override
public Set<String> setInitParameters(Map<String, String> initParameters) {
return null;
}
@Override
public Map<String, String> getInitParameters() {
return null;
}
}
}
@@ -30,6 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
@@ -83,6 +84,24 @@ public class AbstractConfiguredSecurityBuilderTests {
verify(DelegateSecurityConfigurer.CONFIGURER).configure(this.builder);
}
@Test
public void buildWhenConfigurerAppliesAndRemoveAnotherConfigurerThenNotConfigured() throws Exception {
ApplyAndRemoveSecurityConfigurer.CONFIGURER = mock(SecurityConfigurer.class);
this.builder.apply(new ApplyAndRemoveSecurityConfigurer());
this.builder.build();
verify(ApplyAndRemoveSecurityConfigurer.CONFIGURER, never()).init(this.builder);
verify(ApplyAndRemoveSecurityConfigurer.CONFIGURER, never()).configure(this.builder);
}
@Test
public void buildWhenConfigurerAppliesAndRemoveAnotherConfigurersThenNotConfigured() throws Exception {
ApplyAndRemoveAllSecurityConfigurer.CONFIGURER = mock(SecurityConfigurer.class);
this.builder.apply(new ApplyAndRemoveAllSecurityConfigurer());
this.builder.build();
verify(ApplyAndRemoveAllSecurityConfigurer.CONFIGURER, never()).init(this.builder);
verify(ApplyAndRemoveAllSecurityConfigurer.CONFIGURER, never()).configure(this.builder);
}
@Test
public void getConfigurerWhenMultipleConfigurersThenThrowIllegalStateException() throws Exception {
TestConfiguredSecurityBuilder builder = new TestConfiguredSecurityBuilder(mock(ObjectPostProcessor.class),
@@ -130,6 +149,32 @@ public class AbstractConfiguredSecurityBuilderTests {
assertThat(builder.getConfigurers(DelegateSecurityConfigurer.class)).hasSize(2);
}
private static class ApplyAndRemoveSecurityConfigurer
extends SecurityConfigurerAdapter<Object, TestConfiguredSecurityBuilder> {
private static SecurityConfigurer<Object, TestConfiguredSecurityBuilder> CONFIGURER;
@Override
public void init(TestConfiguredSecurityBuilder builder) throws Exception {
builder.apply(CONFIGURER);
builder.removeConfigurer(CONFIGURER.getClass());
}
}
private static class ApplyAndRemoveAllSecurityConfigurer
extends SecurityConfigurerAdapter<Object, TestConfiguredSecurityBuilder> {
private static SecurityConfigurer<Object, TestConfiguredSecurityBuilder> CONFIGURER;
@Override
public void init(TestConfiguredSecurityBuilder builder) throws Exception {
builder.apply(CONFIGURER);
builder.removeConfigurers(CONFIGURER.getClass());
}
}
private static class DelegateSecurityConfigurer
extends SecurityConfigurerAdapter<Object, TestConfiguredSecurityBuilder> {
@@ -19,18 +19,22 @@ package org.springframework.security.config.annotation.web;
import java.util.List;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Servlet;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.MockServletContext;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -54,12 +58,15 @@ public class AbstractRequestMatcherRegistryTests {
private TestRequestMatcherRegistry matcherRegistry;
private WebApplicationContext context;
@BeforeEach
public void setUp() {
this.matcherRegistry = new TestRequestMatcherRegistry();
ApplicationContext context = mock(ApplicationContext.class);
given(context.getBean(ObjectPostProcessor.class)).willReturn(NO_OP_OBJECT_POST_PROCESSOR);
this.matcherRegistry.setApplicationContext(context);
this.context = mock(WebApplicationContext.class);
given(this.context.getBean(ObjectPostProcessor.class)).willReturn(NO_OP_OBJECT_POST_PROCESSOR);
given(this.context.getServletContext()).willReturn(MockServletContext.mvc());
this.matcherRegistry.setApplicationContext(this.context);
mockMvcIntrospector(true);
}
@@ -147,6 +154,32 @@ public class AbstractRequestMatcherRegistryTests {
"Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext");
}
@Test
public void requestMatchersWhenNoDispatcherServletThenAntPathRequestMatcherType() {
MockServletContext servletContext = new MockServletContext();
given(this.context.getServletContext()).willReturn(servletContext);
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/**");
assertThat(requestMatchers).isNotEmpty();
assertThat(requestMatchers).hasSize(1);
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
servletContext.addServlet("servletOne", Servlet.class);
servletContext.addServlet("servletTwo", Servlet.class);
requestMatchers = this.matcherRegistry.requestMatchers("/**");
assertThat(requestMatchers).isNotEmpty();
assertThat(requestMatchers).hasSize(1);
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
}
@Test
public void requestMatchersWhenAmbiguousServletsThenException() {
MockServletContext servletContext = new MockServletContext();
given(this.context.getServletContext()).willReturn(servletContext);
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class);
servletContext.addServlet("servletTwo", Servlet.class);
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> this.matcherRegistry.requestMatchers("/**"));
}
private void mockMvcIntrospector(boolean isPresent) {
ApplicationContext context = this.matcherRegistry.getApplicationContext();
given(context.containsBean("mvcHandlerMappingIntrospector")).willReturn(isPresent);
@@ -20,8 +20,6 @@ import java.io.IOException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
@@ -31,7 +29,6 @@ import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
@@ -45,9 +42,6 @@ import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -74,16 +68,6 @@ public class HttpConfigurationTests {
+ " Consider using addFilterBefore or addFilterAfter instead.");
}
// https://github.com/spring-projects/spring-security-javaconfig/issues/104
@Test
public void configureWhenAddFilterCasAuthenticationFilterThenFilterAdded() throws Exception {
CasAuthenticationFilterConfig.CAS_AUTHENTICATION_FILTER = spy(new CasAuthenticationFilter());
this.spring.register(CasAuthenticationFilterConfig.class).autowire();
this.mockMvc.perform(get("/"));
verify(CasAuthenticationFilterConfig.CAS_AUTHENTICATION_FILTER).doFilter(any(ServletRequest.class),
any(ServletResponse.class), any(FilterChain.class));
}
@Test
public void configureWhenConfigIsRequestMatchersJavadocThenAuthorizationApplied() throws Exception {
this.spring.register(RequestMatcherRegistryConfigs.class).autowire();
@@ -123,22 +107,6 @@ public class HttpConfigurationTests {
}
@EnableWebSecurity
static class CasAuthenticationFilterConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.addFilter(CAS_AUTHENTICATION_FILTER);
// @formatter:on
return http.build();
}
static CasAuthenticationFilter CAS_AUTHENTICATION_FILTER;
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
@@ -23,6 +23,7 @@ import java.util.concurrent.Callable;
import com.google.common.net.HttpHeaders;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
@@ -50,6 +51,7 @@ import org.springframework.security.config.annotation.SecurityContextChangedList
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication;
@@ -61,6 +63,8 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.test.web.servlet.RequestCacheResultMatcher;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
@@ -336,6 +340,16 @@ public class HttpSecurityConfigurationTests {
this.mockMvc.perform(get("/"));
}
// gh-13203
@Test
public void disableConfigurerWhenAppliedByAnotherConfigurerThenNotApplied() {
this.spring.register(ApplyCustomDslConfig.class).autowire();
SecurityFilterChain filterChain = this.spring.getContext().getBean(SecurityFilterChain.class);
List<Filter> filters = filterChain.getFilters();
assertThat(filters).doesNotHaveAnyElementsOfTypes(DefaultLoginPageGeneratingFilter.class,
DefaultLogoutPageGeneratingFilter.class);
}
@RestController
static class NameController {
@@ -575,6 +589,31 @@ public class HttpSecurityConfigurationTests {
}
@Configuration
@EnableWebSecurity
static class ApplyCustomDslConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.apply(CustomDsl.customDsl());
return http.build();
}
}
static class CustomDsl extends AbstractHttpConfigurer<CustomDsl, HttpSecurity> {
@Override
public void init(HttpSecurity http) throws Exception {
http.formLogin(FormLoginConfigurer::disable);
}
static CustomDsl customDsl() {
return new CustomDsl();
}
}
static class DefaultConfigurer extends AbstractHttpConfigurer<DefaultConfigurer, HttpSecurity> {
boolean init;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -26,8 +26,6 @@ import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authorization.AuthorizationDecision;
@@ -274,17 +272,6 @@ public class AuthorizeHttpRequestsConfigurerTests {
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
}
@Test
public void getWhenHasRoleUserAndRoleHierarchyConfiguredThenGreaterRoleTakesPrecedence() throws Exception {
this.spring.register(RoleHierarchyUserConfig.class, BasicController.class).autowire();
// @formatter:off
MockHttpServletRequestBuilder requestWithAdmin = get("/")
.with(user("user")
.roles("ADMIN"));
// @formatter:on
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
}
@Test
public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
@@ -783,30 +770,6 @@ public class AuthorizeHttpRequestsConfigurerTests {
}
@Configuration
@EnableWebSecurity
static class RoleHierarchyUserConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
return http
.authorizeHttpRequests((requests) -> requests
.anyRequest().hasRole("USER")
)
.build();
// @formatter:on
}
@Bean
RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
return roleHierarchy;
}
}
@Configuration
@EnableWebSecurity
static class RoleUserOrAdminConfig {
@@ -28,10 +28,10 @@ import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.MockServletContext;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.AuthorityUtils;
@@ -77,7 +77,7 @@ public class AuthorizeRequestsTests {
@BeforeEach
public void setup() {
this.servletContext = spy(new MockServletContext());
this.servletContext = spy(MockServletContext.mvc());
this.request = new MockHttpServletRequest("GET", "");
this.request.setMethod("GET");
this.response = new MockHttpServletResponse();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2022 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.
@@ -34,7 +34,6 @@ import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextChangedListener;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
@@ -43,7 +42,6 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.web.bind.annotation.GetMapping;
@@ -68,7 +66,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*
* @author Rob Winch
* @author Eleftheria Stein
* @author Evgeniy Cheban
*/
@ExtendWith(SpringTestContextExtension.class)
public class HttpBasicConfigurerTests {
@@ -148,15 +145,6 @@ public class HttpBasicConfigurerTests {
verify(listener).securityContextChanged(setAuthentication(UsernamePasswordAuthenticationToken.class));
}
@Test
public void httpBasicWhenUsingCustomSecurityContextRepositoryThenUses() throws Exception {
this.spring.register(CustomSecurityContextRepositoryConfig.class, Users.class, Home.class).autowire();
this.mvc.perform(get("/").with(httpBasic("user", "password"))).andExpect(status().isOk())
.andExpect(content().string("user"));
verify(CustomSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY)
.saveContext(any(SecurityContext.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
}
@Configuration
@EnableWebSecurity
static class ObjectPostProcessorConfig {
@@ -333,24 +321,6 @@ public class HttpBasicConfigurerTests {
}
@Configuration
@EnableWebSecurity
static class CustomSecurityContextRepositoryConfig {
static final SecurityContextRepository SECURITY_CONTEXT_REPOSITORY = mock(SecurityContextRepository.class);
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic()
.securityContextRepository(SECURITY_CONTEXT_REPOSITORY);
// @formatter:on
return http.build();
}
}
@Configuration
static class Users {
@@ -32,7 +32,7 @@ import org.springframework.core.annotation.Order;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.config.MockServletContext;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
@@ -220,7 +220,7 @@ public class HttpSecuritySecurityMatchersTests {
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);
this.context.setServletContext(new MockServletContext());
this.context.setServletContext(MockServletContext.mvc());
this.context.refresh();
this.context.getAutowireCapableBeanFactory().autowireBean(this);
}
@@ -30,8 +30,8 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.MockServletContext;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
@@ -167,7 +167,7 @@ public class UrlAuthorizationConfigurerTests {
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);
this.context.setServletContext(new MockServletContext());
this.context.setServletContext(MockServletContext.mvc());
this.context.refresh();
this.context.getAutowireCapableBeanFactory().autowireBean(this);
}
@@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -90,6 +91,7 @@ import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.atLeastOnce;
@@ -306,9 +308,12 @@ public class Saml2LoginConfigurerTests {
}
@Test
public void saml2LoginWhenLoginProcessingUrlWithoutRegistrationIdAndDefaultAuthenticationConverterThenAutowires()
throws Exception {
this.spring.register(CustomLoginProcessingUrlDefaultAuthenticationConverter.class).autowire();
public void saml2LoginWhenLoginProcessingUrlWithoutRegistrationIdAndDefaultAuthenticationConverterThenValidates() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.spring.register(CustomLoginProcessingUrlDefaultAuthenticationConverter.class)
.autowire())
.havingRootCause().isInstanceOf(IllegalStateException.class)
.withMessage("loginProcessingUrl must contain {registrationId} path variable");
}
@Test
@@ -319,6 +319,34 @@ public class Saml2LogoutConfigurerTests {
verify(getBean(LogoutHandler.class)).logout(any(), any(), any());
}
// gh-12346
@Test
public void saml2LogoutRequestWhenLowercaseEncodingAndDifferentQueryParamOrderThenLogsOutAndSendsLogoutResponse()
throws Exception {
this.spring.register(Saml2LogoutDefaultsConfig.class).autowire();
String apLogoutRequest = "nZFNa4QwEIb/iuQeP6K7dYO6FKQg2B622x56G3WwgiY2E8v239fqCksPPfSWIXmfNw+THC9D73yi\r\n"
+ "oU6rlAWuzxxUtW461abs5fzAY3bMEoKhF6Msdasne8KPCck6c1KRXK9SNhklNVBHUsGAJG0tn+8f\r\n"
+ "SylcX45GW13rnjn5HOwU2KXt3dqRpOeZ0cULDGOPrjat1y8t3gL2zFrGnCJPWXkKcR8KCHY8xmrP\r\n"
+ "Iz868OpOVLwO4wohggagmd8STVgosqBsyoQvBPd3XITnIJaRL8PYjcThjTmvm/f8SXa1lEvY3Nr9\r\n"
+ "LQdEaH6EWAYjR2U7+8W7JvFucRv8aY4X+b/g03zaoCsmu46/FpN9Aw==";
String apLogoutRequestRelayState = "d118dbd5-3853-4268-b3e5-c40fc033fa2f";
String apLogoutRequestSignature = "VZ7rWa5u3hIX60fAQs/gBQZWDP2BAIlCMMrNrTHafoKKj0uXWnuITYLuL8NdsWmyQN0+fqWW4X05+BqiLpL80jHLmQR5RVqqL1EtVv1SpPUna938lgz2sOliuYmfQNj4Bmd+Z5G1K6QhbVrtfb7TQHURjUafzfRm8+jGz3dPjVBrn/rD/umfGoSn6RuWngugcMNL4U0A+JcEh1NSfSYNVz7y+MqlW1UhX2kF86rm97ERCrxay7Gh/bI2f3fJPJ1r+EyLjzrDUkqw5cva3rVlFgEQouMVu35lUJn7SFompW8oTxkI23oc/t+AGZqaBupNITNdjyGCBpfukZ69EZrj8g==";
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("user",
Collections.emptyMap());
principal.setRelyingPartyRegistrationId("get");
Saml2Authentication user = new Saml2Authentication(principal, "response",
AuthorityUtils.createAuthorityList("ROLE_USER"));
MvcResult result = this.mvc
.perform(get("/logout/saml2/slo").param("SAMLRequest", apLogoutRequest)
.param("SigAlg", this.apLogoutRequestSigAlg).param("RelayState", apLogoutRequestRelayState)
.param("Signature", apLogoutRequestSignature)
.with(new SamlQueryStringRequestPostProcessor(true)).with(authentication(user)))
.andExpect(status().isFound()).andReturn();
String location = result.getResponse().getHeader("Location");
assertThat(location).startsWith("https://ap.example.org/logout/saml2/response");
verify(getBean(LogoutHandler.class)).logout(any(), any(), any());
}
@Test
public void saml2LogoutRequestWhenNoRegistrationThen400() throws Exception {
this.spring.register(Saml2LogoutDefaultsConfig.class).autowire();
@@ -1,210 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.config.annotation.web.configurers.saml2;
import com.google.common.net.HttpHeaders;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.saml2.provider.service.metadata.OpenSamlMetadataResolver;
import org.springframework.security.saml2.provider.service.metadata.RequestMatcherMetadataResponseResolver;
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponse;
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver;
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link Saml2MetadataConfigurer}
*/
@ExtendWith(SpringTestContextExtension.class)
public class Saml2MetadataConfigurerTests {
static RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration().build();
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired(required = false)
MockMvc mvc;
@Test
void saml2MetadataRegistrationIdWhenDefaultsThenReturnsMetadata() throws Exception {
this.spring.register(DefaultConfig.class).autowire();
String filename = "saml-" + registration.getRegistrationId() + "-metadata.xml";
this.mvc.perform(get("/saml2/metadata/" + registration.getRegistrationId())).andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString(filename)))
.andExpect(content().string(containsString("md:EntityDescriptor")));
}
@Test
void saml2MetadataRegistrationIdWhenWrongIdThenUnauthorized() throws Exception {
this.spring.register(DefaultConfig.class).autowire();
this.mvc.perform(get("/saml2/metadata/" + registration.getRegistrationId() + "wrong"))
.andExpect(status().isUnauthorized());
}
@Test
void saml2MetadataWhenDefaultsThenReturnsMetadata() throws Exception {
this.spring.register(DefaultConfig.class).autowire();
this.mvc.perform(get("/saml2/metadata")).andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString("-metadata.xml")))
.andExpect(content().string(containsString("md:EntityDescriptor")));
}
@Test
void saml2MetadataWhenMetadataResponseResolverThenUses() throws Exception {
this.spring.register(DefaultConfig.class, MetadataResponseResolverConfig.class).autowire();
Saml2MetadataResponseResolver metadataResponseResolver = this.spring.getContext()
.getBean(Saml2MetadataResponseResolver.class);
given(metadataResponseResolver.resolve(any(HttpServletRequest.class)))
.willReturn(new Saml2MetadataResponse("metadata", "filename"));
this.mvc.perform(get("/saml2/metadata")).andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString("filename")))
.andExpect(content().string(containsString("metadata")));
verify(metadataResponseResolver).resolve(any(HttpServletRequest.class));
}
@Test
void saml2MetadataWhenMetadataResponseResolverDslThenUses() throws Exception {
this.spring.register(MetadataResponseResolverDslConfig.class).autowire();
this.mvc.perform(get("/saml2/metadata")).andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString("filename")))
.andExpect(content().string(containsString("metadata")));
}
@Test
void saml2MetadataWhenMetadataUrlThenUses() throws Exception {
this.spring.register(MetadataUrlConfig.class).autowire();
String filename = "saml-" + registration.getRegistrationId() + "-metadata.xml";
this.mvc.perform(get("/saml/metadata")).andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, containsString(filename)))
.andExpect(content().string(containsString("md:EntityDescriptor")));
this.mvc.perform(get("/saml2/metadata")).andExpect(status().isForbidden());
}
@EnableWebSecurity
@Configuration
@Import(RelyingPartyRegistrationConfig.class)
static class DefaultConfig {
@Bean
SecurityFilterChain filters(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
.saml2Metadata(Customizer.withDefaults());
return http.build();
// @formatter:on
}
}
@EnableWebSecurity
@Configuration
@Import(RelyingPartyRegistrationConfig.class)
static class MetadataUrlConfig {
@Bean
SecurityFilterChain filters(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
.saml2Metadata((saml2) -> saml2.metadataUrl("/saml/metadata"));
return http.build();
// @formatter:on
}
// should ignore
@Bean
Saml2MetadataResponseResolver metadataResponseResolver(RelyingPartyRegistrationRepository registrations) {
return new RequestMatcherMetadataResponseResolver(registrations, new OpenSamlMetadataResolver());
}
}
@EnableWebSecurity
@Configuration
@Import(RelyingPartyRegistrationConfig.class)
static class MetadataResponseResolverDslConfig {
Saml2MetadataResponseResolver metadataResponseResolver = mock(Saml2MetadataResponseResolver.class);
{
given(this.metadataResponseResolver.resolve(any(HttpServletRequest.class)))
.willReturn(new Saml2MetadataResponse("metadata", "filename"));
}
@Bean
SecurityFilterChain filters(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
.saml2Metadata((saml2) -> saml2.metadataResponseResolver(this.metadataResponseResolver));
return http.build();
// @formatter:on
}
}
@Configuration
static class MetadataResponseResolverConfig {
Saml2MetadataResponseResolver metadataResponseResolver = mock(Saml2MetadataResponseResolver.class);
@Bean
Saml2MetadataResponseResolver metadataResponseResolver() {
return this.metadataResponseResolver;
}
}
@Configuration
static class RelyingPartyRegistrationConfig {
RelyingPartyRegistrationRepository registrations = new InMemoryRelyingPartyRegistrationRepository(registration);
@Bean
RelyingPartyRegistrationRepository registrations() {
return this.registrations;
}
}
}

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