diff --git a/.asf.yaml b/.asf.yaml index 36306ad458..7ad465ec65 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -25,12 +25,12 @@ github: issues: true projects: true enabled_merge_buttons: - squash: false - merge: true - rebase: false + squash: false + merge: true + rebase: false notifications: pullrequests: commits@shiro.apache.org commits: commits@shiro.apache.org - issues: issues@shiro.apache.org + issues: issues@shiro.apache.org jira_options: link label worklog diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 793327e005..dca17b94af 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -25,37 +25,37 @@ updates: open-pull-requests-limit: 50 ignore: - dependency-name: "jakarta.platform:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "jakarta.activation:jakarta.activation-api" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "jakarta.annotation:jakarta.annotation-api" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "jakarta.enterprise:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "jakarta.xml.bind:jakarta.xml.bind-api" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "jakarta.servlet.*:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "jakarta.ws.rs:jakarta.ws.rs-api" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "jakarta.validation:jakarta.validation-api" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.glassfish.web:jakarta.servlet.jsp.jstl" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.glassfish.jaxb:jaxb-runtime" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.omnifaces:omnifaces" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.springframework*:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "com.flowlogix:flowlogix-jee" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.eclipse.jetty:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] # Dependencies for Maven - on 1.12.x - package-ecosystem: 'maven' @@ -66,34 +66,34 @@ updates: open-pull-requests-limit: 50 ignore: - dependency-name: "jakarta.servlet.*:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "javax.servlet:javax.servlet-api" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.glassfish.web:jakarta.servlet.jsp.jstl" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.glassfish.jersey.*:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.jasig.cas.client:*" - update-types: ["version-update:semver-minor"] + update-types: [ "version-update:semver-minor" ] - dependency-name: "org.slf4j:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.codehaus.groovy:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "ch.qos.logback:logback-classic" - update-types: ["version-update:semver-minor"] + update-types: [ "version-update:semver-minor" ] - dependency-name: "com.hazelcast:hazelcast" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.aspectj:*" - update-types: ["version-update:semver-patch"] + update-types: [ "version-update:semver-patch" ] - dependency-name: "org.springframework*:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "org.eclipse.jetty:*" - update-types: ["version-update:semver-major"] + update-types: [ "version-update:semver-major" ] - dependency-name: "com.github.mjeanroy:junit-servers-jetty" - update-types: ["version-update:semver-minor"] + update-types: [ "version-update:semver-minor" ] # Dependencies for GitHub Actions - package-ecosystem: 'github-actions' diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d3180e0408..64280a08db 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,29 +5,36 @@ For more details on how to report a vulnerablity see: https://www.apache.org/sec Following this checklist to help us incorporate your contribution quickly and easily: - - [ ] Make sure there is a [GitHub issue](https://github.com/apache/shiro/issues) filed - for the change (usually before you start working on it). Trivial changes like typos do not - require a GitHub issue. Your pull request should address just this issue, without pulling in other changes. - - [ ] Each commit in the pull request should have a meaningful subject line and body. - - [ ] Format the pull request title like `[#XXX] - Fixes bug in SessionManager`, - where you replace `#XXX` with the appropriate GitHub issue. Best practice - is to use the GitHub issue title in the pull request title and in the first line of the commit message. - - [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. - - [ ] add `fixes #XXX` if merging the PR should close a related issue. - - [ ] Run `mvn verify` to make sure basic checks pass. A more thorough check will be performed on your pull request automatically. - - [ ] If you have a group of commits related to the same change, please squash your commits into one and force push your branch using `git rebase -i`. - - [ ] Committers: Make sure a milestone is set on the PR - -Trivial changes like typos do not require a GitHub issue (javadoc, comments...). +- [ ] Make sure there is a [GitHub issue](https://github.com/apache/shiro/issues) filed + for the change (usually before you start working on it). Trivial changes like typos do not + require a GitHub issue. Your pull request should address just this issue, without pulling in other changes. +- [ ] Each commit in the pull request should have a meaningful subject line and body. +- [ ] Format the pull request title like `[#XXX] - Fixes bug in SessionManager`, + where you replace `#XXX` with the appropriate GitHub issue. Best practice + is to use the GitHub issue title in the pull request title and in the first line of the commit message. +- [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. +- [ ] add `fixes #XXX` if merging the PR should close a related issue. +- [ ] Run `mvn verify` to make sure basic checks pass. A more thorough check will be performed on your pull request + automatically. +- [ ] If you have a group of commits related to the same change, please squash your commits into one and force push your + branch using `git rebase -i`. +- [ ] Committers: Make sure a milestone is set on the PR + +Trivial changes like typos do not require a GitHub issue (javadoc, comments...). In this case, just format the pull request title like `[DOC] - Add javadoc in SessionManager`. - -If this is your first contribution, you have to read the [Contribution Guidelines](https://github.com/apache/shiro/blob/master/CONTRIBUTING.md) -If your pull request is about ~20 lines of code you don't need to sign an [Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf) +If this is your first contribution, you have to read +the [Contribution Guidelines](https://github.com/apache/shiro/blob/master/CONTRIBUTING.md) + +If your pull request is about ~20 lines of code you don't need to sign +an [Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf) if you are unsure please ask on the developers list. -To make clear that you license your contribution under the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0) +To make clear that you license your contribution under +the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0) you have to acknowledge this by using the following check-box. - - [ ] I hereby declare this contribution to be licenced under the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0) - - [ ] In any other case, please file an [Apache Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf). +- [ ] I hereby declare this contribution to be licenced under + the [Apache License Version 2.0, January 2004](http://www.apache.org/licenses/LICENSE-2.0) +- [ ] In any other case, please file + an [Apache Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf). diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6c0b305c69..32e62cf10a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -59,45 +59,45 @@ jobs: # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - name: Checkout repository - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - name: Checkout repository + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - - name: Cache local Maven repository - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 + - name: Cache local Maven repository + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 - with: - category: "/language:${{matrix.language}}" + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 97377472bc..c6f2c45abc 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -21,7 +21,7 @@ permissions: contents: read on: - workflow_dispatch: {} + workflow_dispatch: { } push: branches: [ main, '1.11.x', '1.10.x', '1.9.x' ] pull_request: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index ce2bee231c..026404e6b4 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -24,7 +24,7 @@ permissions: pull-requests: write on: - workflow_dispatch: {} + workflow_dispatch: { } schedule: # every day 5min after midnight, UTC. - cron: "5 0 * * *" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3953cf37bb..48bd0cbb88 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,9 @@ Contributing to Apache Shiro Summary ------- -This document covers how to contribute to the Apache Shiro project. These instructions assume you have a GitHub.com account, so if you don't have one you will have to create one. Your proposed code changes will be published to your own fork of the Apache Shiro project and you will submit a Pull Request for your changes to be added. +This document covers how to contribute to the Apache Shiro project. These instructions assume you have a GitHub.com +account, so if you don't have one you will have to create one. Your proposed code changes will be published to your own +fork of the Apache Shiro project and you will submit a Pull Request for your changes to be added. _Lets get started!!!_ @@ -15,7 +17,8 @@ In your browser, navigate to: [https://github.com/apache/shiro](https://github.c Fork the repository by clicking on the 'Fork' button on the top right hand side. The fork will happen and you will be taken to your own fork of the repository. -Copy the Git repository URL by clicking on the clipboard next to the URL on the right hand side of the page under '**HTTPS** clone URL'. +Copy the Git repository URL by clicking on the clipboard next to the URL on the right hand side of the page under '* +*HTTPS** clone URL'. You will paste this URL when doing the following `git clone` command. On your computer, follow these steps to setup a local repository for working on Apache Shiro: @@ -29,15 +32,18 @@ $ git fetch upstream $ git rebase upstream/main ``` - Making changes -------------- -It is important that you create a new branch to make changes on and that you do not change the `main` branch (other than to rebase in changes from `upstream/main`). -In this example I will assume you will be making your changes to a branch called `feature_x`. This `feature_x` branch will be created on your local repository and -will be pushed to your forked repository on GitHub. Once this branch is on your fork you will create a Pull Request for the changes to be added to the Apache Shiro project. +It is important that you create a new branch to make changes on and that you do not change the `main` branch (other than +to rebase in changes from `upstream/main`). +In this example I will assume you will be making your changes to a branch called `feature_x`. This `feature_x` branch +will be created on your local repository and +will be pushed to your forked repository on GitHub. Once this branch is on your fork you will create a Pull Request for +the changes to be added to the Apache Shiro project. -It is best practice to create a new branch each time you want to contribute to the project and only track the changes for that pull request in this branch. +It is best practice to create a new branch each time you want to contribute to the project and only track the changes +for that pull request in this branch. ``` bash $ git checkout -b feature_x @@ -47,16 +53,20 @@ $ git add . $ git commit -a -m "descriptive commit message for your changes" ``` -> The `-b` specifies that you want to create a new branch called `feature_x`. You only specify `-b` the first time you checkout because you are creating a new branch. +> The `-b` specifies that you want to create a new branch called `feature_x`. You only specify `-b` the first time you +> checkout because you are creating a new branch. > Once the `feature_x` branch exists, you can later switch to it with only `git checkout feature_x`. Rebase `feature_x` to include updates from `upstream/main` ------------------------------------------------------------ -It is important that you maintain an up-to-date `main` branch in your local repository. This is done by rebasing in the code changes from `upstream/main` -(the official Apache Shiro project repository) into your local repository. You will want to do this before you start working on a feature as well as right -before you submit your changes as a pull request. I recommend you do this process periodically while you work to make sure you are working off the most recent project code. +It is important that you maintain an up-to-date `main` branch in your local repository. This is done by rebasing in the +code changes from `upstream/main` +(the official Apache Shiro project repository) into your local repository. You will want to do this before you start +working on a feature as well as right +before you submit your changes as a pull request. I recommend you do this process periodically while you work to make +sure you are working off the most recent project code. This process will do the following: @@ -78,10 +88,12 @@ $ git rebase main Make a GitHub Pull Request to contribute your changes ----------------------------------------------------- -When you are happy with your changes and you are ready to contribute them, you will create a Pull Request on GitHub to do so. This is done by pushing your local changes +When you are happy with your changes and you are ready to contribute them, you will create a Pull Request on GitHub to +do so. This is done by pushing your local changes to your forked repository (default remote name is `origin`) and then initiating a pull request on GitHub. -> **IMPORTANT:** Make sure you have rebased your `feature_x` branch to include the latest code from `upstream/main` _before_ you do this. +> **IMPORTANT:** Make sure you have rebased your `feature_x` branch to include the latest code from `upstream/main` +_before_ you do this. ``` bash $ git push origin main @@ -92,22 +104,27 @@ Now that the `feature_x` branch has been pushed to your GitHub repository, you c To initiate the pull request, do the following: -1. In your browser, navigate to your forked repository: [https://github.com/YOUR_ACCOUNT/shiro](https://github.com/YOUR_ACCOUNT/shiro) -2. Click the new button called '**Compare & pull request**' that showed up just above the main area in your forked repository +1. In your browser, navigate to your forked + repository: [https://github.com/YOUR_ACCOUNT/shiro](https://github.com/YOUR_ACCOUNT/shiro) +2. Click the new button called '**Compare & pull request**' that showed up just above the main area in your forked + repository 3. Validate the pull request will be into the upstream `main` and will be from your `feature_x` branch 4. Enter a detailed description of the work you have done and then click '**Send pull request**' -If you are requested to make modifications to your proposed changes, make the changes locally on your `feature_x` branch, re-push the `feature_x` branch to your fork. +If you are requested to make modifications to your proposed changes, make the changes locally on your `feature_x` +branch, re-push the `feature_x` branch to your fork. The existing pull request should automatically pick up the change and update accordingly. Cleaning up after a successful pull request ------------------------------------------- -Once the `feature_x` branch has been committed into the `upstream/main` branch, your local `feature_x` branch and the `origin/feature_x` branch are no longer needed. +Once the `feature_x` branch has been committed into the `upstream/main` branch, your local `feature_x` branch and +the `origin/feature_x` branch are no longer needed. If you want to make additional changes, restart the process with a new branch. -> **IMPORTANT:** Make sure that your changes are in `upstream/main` before you delete your `feature_x` and `origin/feature_x` branches! +> **IMPORTANT:** Make sure that your changes are in `upstream/main` before you delete your `feature_x` +> and `origin/feature_x` branches! You can delete these deprecated branches with the following: diff --git a/LICENSE b/LICENSE index 753842b672..f49a4e16e6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index effbff5373..4d01f468c2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,10 @@ Apache Shiro ============ -[Apache Shiro](http://shiro.apache.org) is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications. +[Apache Shiro](http://shiro.apache.org) is a powerful and easy-to-use Java security framework that performs +authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can +quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise +applications. Documentation and Examples -------------------------- @@ -15,8 +18,9 @@ http://shiro.apache.org Tutorials --------- + * [10 Minute Tutorial](http://shiro.apache.org/10-minute-tutorial.html) -* [Web Application](http://shiro.apache.org/webapp-tutorial.html) +* [Web Application](http://shiro.apache.org/webapp-tutorial.html) License ------- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 58e3501db0..2be485aecd 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,572 +1,572 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -This is not an official release notes document. It exists for Shiro developers -to jot down their notes while working in the source code. These notes will be -combined with Jira’s auto-generated release notes during a release for the -total set. - -########################################################### -# 2.0.0 -########################################################### - -Improvement - - [SHIRO-290] Implement bcrypt and argon2 KDF algorithms - -Backwards Incompatible Changes --------------------------------- - -* Changed default DefaultPasswordService.java algorithm to "Argon2id". -* PasswordService.encryptPassword(Object plaintext) will now throw a NullPointerException on null parameter. - It was never specified how this method would behave. -* Made salt non-nullable. -* Removed methods in PasswordMatcher. - - -########################################################### -# 1.7.1 -########################################################### - -Bug - - [SHIRO-797] - Shiro 1.7.0 is lower than using springboot version 2.0.7 dependency error - - -########################################################### -# 1.7.0 -########################################################### - -Bug - - [SHIRO-767] - org.apache.shiro.util.ClassUtil cannot load the array of Primitive DataType when use undertow as web container - [SHIRO-792] - ShiroWebFilterConfiguration seems to conflict with other FilterRegistrationBean - -New Feature - - [SHIRO-789] - Also add cookie SameSite option to Spring - -Improvement - - [SHIRO-740] - SslFilter with HTTP Strict Transport Security (HSTS) - [SHIRO-794] - Add system property to enable backslash path normalization - [SHIRO-795] - Disable session path rewriting by default - -Task - - [SHIRO-793] - deleteMe cookie should use the defined "sameSite" - - -########################################################### -# 1.6.0 -########################################################### - -Bug - - [SHIRO-610] - Incorrect filterchainResolver in 1.4.0-RC2 - [SHIRO-762] - SecurityUtils.securityManager should be volatile - [SHIRO-766] - ArrayIndexOutOfBoundsException in Base64#decode - -New Feature - - [SHIRO-788] - Add support for Global Filters - -Wish - - [SHIRO-780] - NOTICE files of shiro components don't match NOTICE in source code repository - - -########################################################### -# 1.5.3 -########################################################### - -Bug - - [SHIRO-530] - INI parser does not properly handled backslashes at end of values - [SHIRO-751] - SimplePrincipalMap and SimplePrincipalCollection throw different exceptions for the same problem - [SHIRO-753] - Regression in URI parsing in Shiro 1.5.2 - -Dependency upgrade - - [SHIRO-754] - Upgrade to Apache Commons Codec 1.14 - [SHIRO-755] - Upgrade to Hazelcast 3.12.6 - [SHIRO-756] - Upgrade to Spring 5.2.5.RELEASE and Spring boot 2.2.6.RELEASE - [SHIRO-757] - Upgrade to Htmlunit 2.39.0 - [SHIRO-758] - Upgrade to Jetty 9.4.27.v20200227 - [SHIRO-759] - Upgrade to Karaf 4.2.8 - - -########################################################### -# 1.5.2 -########################################################### - -Bug - - [SHIRO-747] - FirstSuccessfulStrategy doesn't properly short circuit - [SHIRO-749] - shiro-all jar is missing cache package - -Improvement - - [SHIRO-748] - Update Commons Configuration to 2.7 - - -########################################################### -# 1.5.1 -########################################################### - -Bug - - [SHIRO-736] - DefaultCipherInstance is an alias which is not available in every JVM or JCA Provider - [SHIRO-739] - Bean reflection property failed with Enum values - [SHIRO-741] - Matching of / (root) is broken - [SHIRO-742] - fix throw exception when request uri is / - -Dependency upgrade - - [SHIRO-738] - Upgrade to Spring 5.2.3.RELEASE and Spring boot 2.2.4.RELEASE - - -########################################################### -# 1.5.0 -########################################################### - -Notes: this release require a JRE 8 minimum. - -Bug - - [SHIRO-458] - Possible leaked timing information from DefaultPasswordService - [SHIRO-469] - Wrong description of JdbcRealm#setPermissionsQuery - [SHIRO-552] - JdbcRealm in SaltStyle.COLUMN assumes that password column is Base64 but salt column is utf8 bytes - [SHIRO-661] - Add check for the principal of subject whether is null - [SHIRO-682] - fix the potential threat when use "uri = uri + '/' " to bypassed shiro protect - [SHIRO-684] - INI parser keeps escape characters in keys and values - [SHIRO-685] - Potential NullPointerException if PermissionResolver return null/empty string - [SHIRO-687] - Additional Servlet Filters are not available to ShiroFilterFactorBean (unless using XML based beans) - -New Feature - - [SHIRO-694] - Adds BearerToken support - [SHIRO-722] - Add SameSite option to cookies - -Improvement - - [SHIRO-668] - Catch unexpected errors which can lead to oom - [SHIRO-669] - Included a boolean flag in FirstSuccessfulStrategy to break after first successful authentication - [SHIRO-670] - ByteSource Serializable - [SHIRO-681] - Upgrade to compiler Java 8 - [SHIRO-693] - Update plugins - [SHIRO-700] - Minor spring updates - [SHIRO-706] - Switch to Guice4 by default in the build - [SHIRO-709] - Fix Shiro Spring feature - [SHIRO-710] - Update Commons Lang3 + remove older Commons Lang - [SHIRO-711] - Deprecate JavaEnvironment - [SHIRO-712] - Add BasicIniEnvironment - [SHIRO-715] - Remove old JSTL jars - [SHIRO-720] - Update Commons BeanUtils - [SHIRO-724] - Update Jetty, Spring, Spring Boot, Htmlunit dependencies - [SHIRO-726] - Add dynamic import package - [SHIRO-728] - Update Spring Boot to 2.1.10 - [SHIRO-729] - Update Quartz - [SHIRO-730] - Updates the default Cipher mode to GCM in AesCipherService - [SHIRO-731] - Use OWasp Java Encoder to escape user supplied content to the logs - -Test - - [SHIRO-697] - Reduce shiro test logging level to INFO - -Task - - [SHIRO-690] - Validate JDK11 compatibility - [SHIRO-692] - Upgrade and enforce min build maven version to 3.5.0 - [SHIRO-698] - Improve build with maven profile - [SHIRO-734] - Remove Spring-client sample - [SHIRO-735] - Shiro does not support servlet-3.1 void method(@Suspended AsyncResponse) - -Dependency upgrade - - [SHIRO-688] - Upgrade to commons-cli 1.4 - [SHIRO-689] - Upgrade to commons-codec 1.12 - [SHIRO-691] - Upgrade to maven-jar-plugin 3.1.1 - [SHIRO-695] - Update Hazelcast - [SHIRO-696] - Update Jetty - [SHIRO-699] - Fix maven warning for exec-maven-plugin and upgrade to 1.6.0 - [SHIRO-701] - Update logback - [SHIRO-702] - Upgrade to jacoco-maven-plugin 0.8.4 - [SHIRO-703] - Update HSQL - [SHIRO-704] - Update Spring, Spring Boot, Hibernate - [SHIRO-705] - Update Easymock + Powermock - [SHIRO-707] - Misc dependency updates - [SHIRO-716] - Upgrade to commons-codec 1.13 - [SHIRO-717] - Upgrade to maven-pmd-plugin 3.12.0 - [SHIRO-718] - Upgrade to xmlsec 2.1.4 - [SHIRO-719] - Upgrade to Karaf 4.2.6 - -Request - - [SHIRO-723] - Provide Minor Shiro Release that includes CVE-2019-10086 Fix - - -########################################################### -# 1.4.2 -########################################################### - -Bug - - [SHIRO-721] - RememberMe Padding Oracle Vulnerability - -Improvement - - [SHIRO-730] - Updates the default Cipher mode to GCM in AesCipherService - - -########################################################### -# 1.4.1 -########################################################### - -Bug - - [SHIRO-457] - Login without static VM security manager cause exception in debug - [SHIRO-563] - shiro-aspectj karaf feature can't be installed - [SHIRO-624] - OSGI: commons configuration import should be optional - [SHIRO-626] - Bundle symbolic name conflict - [SHIRO-637] - Refresh cached session in HTTP request after user logs out - [SHIRO-650] - Shiro JAX-RS is not an OSGi bundle - [SHIRO-653] - Spring-boot registers shiro filter only on REQUEST dispatcher - [SHIRO-655] - shiro-core has an undesirable runtime OSGi dependency to spring-beans - [SHIRO-658] - Problems building shiro on openjdk-8 on current debian stable (9.6 "stretch") - [SHIRO-660] - Bug in FirstSuccessfulStrategy - [SHIRO-680] - Duplicate Bundle-SymbolicName for Different Shiro Modules - -New Feature - - [SHIRO-638] - Update osgi bundle manifest to support Spring 4.x - -Improvement - - [SHIRO-560] - Shiro-web feature can't be installed in karaf 4.0.4 - [SHIRO-652] - Upgrade Shiro Feature to Karaf 4.x - [SHIRO-664] - Upgrade to Apache pom parent 21 - [SHIRO-665] - Upgrade to maven-bundle-plugin 4.1.0 - [SHIRO-667] - Upgrade to Spring 4.3.22-RELEASE - [SHIRO-672] - Upgrade to jacoco-maven-plugin 0.8.3 - [SHIRO-673] - Upgrade to maven-compiler-plugin 3.8.0 - [SHIRO-674] - Upgrade to maven-dependency-plugin to 3.1.1 - [SHIRO-675] - Upgrade to maven-surefire-plugins 3.0.0-M3 - [SHIRO-676] - Upgrade to maven-jar-plugin 3.1.0 - [SHIRO-677] - Upgrade to versions-maven-plugin 2.7 - [SHIRO-683] - Upgrade to spring-boot 1.5.19.RELEASE - -Task - - [SHIRO-662] - Constant Name Change in AuthenticationRealm - [SHIRO-663] - Clean up pom parent relative path - -Dependency upgrade - - [SHIRO-659] - Upgrade to OWASP dependency-check-maven plugin 4.0.0 - - -########################################################### -# 1.4.0 -########################################################### - -Bug - - [SHIRO-559] - shiro-guice violates the JEE specification - [SHIRO-579] - Permission filter is validating last matched path - [SHIRO-603] - Endless recursion in ShiroSecurityContext.getUserPrincipal() - [SHIRO-605] - ShiroWebModule creates out of order filter chain. - [SHIRO-607] - AuthorizationAttributeSourceAdvisor ignores type-annotations - [SHIRO-608] - Use a ServiceLoader to discover WebEnvironments - [SHIRO-611] - Spring web module does not load correct SessionStorageEvaluator - -Improvement - - [SHIRO-596] - shiro-tools-hasher needs private salt option - [SHIRO-618] - Spring Boot Web Starter- Autoconfiguration for Realm and ShiroFilterChainDefinition - - -########################################################### -# 1.4.0-RC2 -########################################################### - -Bug - - [SHIRO-493] - shiro-guice not working with the guice 4.x - [SHIRO-576] - Commons-beanutils dependency is not security compliant - [SHIRO-586] - Can't Search For Groups In Active Directory Without A System User - [SHIRO-587] - Can't Access Groups If userPrincipalName Doesn't Exist - [SHIRO-591] - Basic Auth Filter permissive mode does NOT work - [SHIRO-592] - ModularRealmAuthenticator causes log spam when one realm throws exception - [SHIRO-593] - Allow for IniWebEnvironment subclasses to specify defaults objects to ReflectionBuilder - [SHIRO-594] - Update Hazelcast version to latest supported version (3.7.2) - [SHIRO-595] - Allow for POST only logout requests - [SHIRO-612] - Need to upgrade BeanUtils to avoid vulnerability - -New Feature - - [SHIRO-501] - Add ability to set system properties in shiro.ini - [SHIRO-589] - Add Servlet 3.x fragment - [SHIRO-590] - Add Spring Boot support / starters - -Improvement - - [SHIRO-296] - Typo fixes - [SHIRO-301] - Call permissionResolver directly in AuthorizingRealm - [SHIRO-392] - Shiro Extension for JAX-RS - [SHIRO-599] - Fix file encoding warnings during maven build and reporting - - -########################################################### -# 1.3.2 -########################################################### - -Bug - - [SHIRO-584] - URL Path matching issue with WebUtils.getPathWithinApplication - - -########################################################### -# 1.3.1 -########################################################### - -Bug - - [SHIRO-577] - Regression - Unable to set custom SessionValidationScheduler - [SHIRO-581] - Improve log message when remember me cipher has changed - - -########################################################### -# 1.3.0 -########################################################### - -Bug - - [SHIRO-373] - Complete CAS remember-me support - [SHIRO-397] - SingleArgumentMethodEventListenerTest fails - [SHIRO-421] - Unable to set long timeouts on HttpServletSession - [SHIRO-435] - SecurityManager is not a singleton in ShiroWebModule - [SHIRO-473] - DefaultAnnotationResolver.getAnnotation throws NullPointerException - [SHIRO-480] - setTarget method in DomainPermission does not set targets - [SHIRO-483] - passwordsMatch() returns false with right plain password-encrypted password in JVM with default locale tr_TR - [SHIRO-502] - OSGi import of com.google.inject in shiro-guice has incorrect version range - [SHIRO-513] - Misleading error message when using custom WebEnvironment - [SHIRO-515] - ExecutorServiceSessionValidationScheduler leaks resources due to improper synchronization - [SHIRO-547] - Use MessageDigest#isEqual() instead of Arrays#equals() for comparing digests - [SHIRO-568] - hash iterations is calculated wrongly in SimpleHash - [SHIRO-570] - SimpleCookie should check the path of the cookie - -New Feature - - [SHIRO-200] - Add ability to configure basic authentication for specific HTTP methods - [SHIRO-395] - Add an Event Bus for event publishing and low-coupling for custom components/plugins. - [SHIRO-412] - Hazelcast-based caching and session clustering - [SHIRO-436] - Add EnvironmentLoader finalizeEnvironment method - -Improvement - - [SHIRO-278] - Rename JndiLdapRealm to DefaultLdapRealm - [SHIRO-300] - WildcardPermission: change visibility of field 'parts' to protected - [SHIRO-361] - HttpServletResponse.encodeURL: only append JSESSIONID when necessary - [SHIRO-428] - AuthorizingRealm "no cache" logging should be at DEBUG level, not INFO, OR is should log only once - [SHIRO-437] - WildcardPermission: conformed toString - [SHIRO-514] - ExecutorServiceSessionValidationScheduler should create threads with a configurable name - [SHIRO-564] - WildcardPermission case-insensitive makes parts collections twice - [SHIRO-566] - CollectionUtils should use Collections wrappers of arrays if possible - -Task - - [SHIRO-208] - Correct JDK 1.5 / 1.6 incompatibilities - [SHIRO-320] - Add an example for using Guice integration. - [SHIRO-571] - Mark shiro-cas deprecated (replaced with buji-pac4j) - - -########################################################### -# 1.2.6 -########################################################### - -Bug - - [SHIRO-545] - JavaEnvironment version getter - [SHIRO-567] - shiro-root-1.2.5.pom uses invalid encoding, fails to parse with Gradle 2.14 - - -########################################################### -# 1.2.5 -########################################################### - -Bug - - [SHIRO-443] - SessionValidationScheduler created multiple times, enabling it is not thread safe - [SHIRO-462] - Authentication exceptions are swallowed - [SHIRO-467] - Authentication exception gets swallowed - [SHIRO-550] - Randomize default remember me cipher - -Improvement - - [SHIRO-504] - Java 8 support - [SHIRO-516] - Explicitly specify the version of aspectjtools to avoid build warning - [SHIRO-562] - WildcardPermission calls String.trim() twice in setParts() - - -########################################################### -# 1.2.4 -########################################################### - -Bug - - [SHIRO-517] - Caused by: java.lang.NoClassDefFoundError: Lcom/google/inject/internal/util/$ImmutableList; - [SHIRO-518] - Shiro-CAS: Security Problem in cas-client-core versions older than 3.3.2 - [SHIRO-556] - https://shiro.apache.org/realm.html appears to link to the javadoc under static/current/apidocs not static/latest - -Improvement - - [SHIRO-332] - Change access level of method 'isPermitted' in org.apache.shiro.realm.AuthorizingRealm (line 461) from private to protected - [SHIRO-496] - Update shiro.guice dependency - [SHIRO-498] - ThreadLocal should not be created when not necessary - - -########################################################### -# 1.2.2 -########################################################### - -Bug: - - [SHIRO-316] - Annotations in samples-aspectj Project Does not Work - [SHIRO-351] - Shiro Native Session implementation cannot extract JSESSIONID From URL if JSESSIONID is URL parameter (not HTTP parameter) - [SHIRO-379] - SimpleAccountRealm concurrency access to roles and users - [SHIRO-380] - runAs feature (still) doesn't work - [SHIRO-387] - EnvironmentLoader destroys wrong environment - [SHIRO-388] - Stackoverflow org.apache.shiro.session.SessionListener.onStop() - [SHIRO-389] - Fix OSGI Exports for shiro-ehcache - [SHIRO-390] - OSGi Import for JSP (javax.servlet.jsp) should be declared optional - [SHIRO-394] - PropertiesRealm reloading not working when loading from file - [SHIRO-399] - Memory leak for invalid sessions - [SHIRO-403] - Trunk will not build under JDK 1.7 due to webstart plugin - [SHIRO-413] - init() method is not called on class that implements org.apache.shiro.util.Initializable - [SHIRO-415] - isLoginAttempt method in BasicHttpAuthenticationFilter class fails if used in any locale other than English - [SHIRO-418] - Javadoc typo in JdbcRealm.SaltStyle - [SHIRO-423] - INI ReflectionBuilder should not wrap reference values - [SHIRO-429] - perms filter parsing is too sensitive to a trailing space - [SHIRO-431] - please use git ignore - [SHIRO-447] - Broken Javadoc links - - -########################################################### -# 1.2.1 -########################################################### - -Bug: - - [SHIRO-341] - ReflectionBuilder has invalid log message format - [SHIRO-342] - Running the example as described at http://shiro.apache.org/10-minute-tutorial.html fails - [SHIRO-344] - runAs feature doesn't work - [SHIRO-350] - Creating a subject should not create a session - [SHIRO-353] - DefaultSecurityManager has invalid SLF4J log instruction - [SHIRO-354] - Authentication cache - [SHIRO-358] - Source Tarball doesn't Build - [SHIRO-363] - PasswordMatcher should support character arrays - [SHIRO-368] - DomainPermission(string, string) constructor sets targets to the same value as actions - [SHIRO-375] - Basic authentication issue when using COLON character - [SHIRO-376] - shiro-cas feature should not depend on shiro-cas - [SHIRO-377] - PropertiesRealm unable to reload Properties - - -########################################################### -# 1.2.0 -########################################################### - -Backwards Incompatible Changes --------------------------------- -- The following org.apache.shiro.mgt.DefaultSecurityManager methods have been removed: - bindPrincipalsToSession(principals, context) - - This logic has been moved into a SubjectDAO concept to allow end-users to control - exactly how the Session may be used for subject state persistence. This allows a - single point of control rather than needing to configure Shiro in multiple places. - - If you overrode this method in Shiro 1.0 or 1.1, please look at the new - org.apache.shiro.mgt.DefaultSubjectDAO implementation, which performs compatible logic. - Documentation for this is covered here: - http://shiro.apache.org/session-management.html#SessionManagement-SessionsandSubjectState - -- The org.apache.shiro.web.session.mgt.ServletContainerSessionManager implementation - (enabled by default for all web applications) no longer subclasses - org.apache.shiro.session.mgt.AbstractSessionManager. AbstractSessionManager existed - originally to consolidate a 'globalSessionTimeout' configuration property for - subclasses. However, the ServletContainerSessionManager has been changed to always - reflect the session configuration from web.xml (per its namesake). Because web.xml - is the definitive source for session timeout configuration, the 'extends' clause - was removed to avoid configuration confusion: if someone attempted to configure - 'globalSessionTimeout' on a ServletContainerSessionManager instance, it would never - be honored. It was better to remove the extends clause to ensure that any - such configuration would fail fast when Shiro starts up to reflect the invalid config. - - -Potential Breaking Changes --------------------------------- -- The org.apache.shiro.web.filter.mgt.FilterChainManager class's - addFilter(String name, Filter filter) semantics have changed. It now no longer - attempts to initialize a filter by default before adding the filter to the chain. - If you ever called this method, you can call the - addFilter(name, filter, true) method to achieve the <= 1.1 behavior. - -- The org.apache.shiro.crypto.SecureRandomNumberGenerator previously defaulted to generating - 128 random _bytes_ each time the nextBytes() method was called. This is too large for most purposes, so the - default has been changed to 16 _bytes_ (which equals 128 bits - what was originally intended). If for some reason - you need more than 16 bytes (128 bits) of randomly generated bits, you will need to configure the - 'defaultNextByteSize' property to match your desired size (in bytes, NOT bits). - -- Shiro's Block Cipher Services (AesCipherService, BlowfishCipherService) have had the following changes: - - 1) The internal Cipher Mode and Streaming Cipher Mode have been changed from CFB to the new default of CBC. - CBC is more commonly used for block ciphers today (e.g. SSL). - If you were using an AES or Blowfish CipherService you will want to revert to the previous defaults in your config - to ensure you can still decrypt previously encrypted data. For example, in code: - - blockCipherService.setMode(OperationMode.CFB); - blockCipherService.setStreamingMode(OperationMode.CFB); - - or, in shiro.ini: - - blockCipherService.modeName = CFB - blockCipherService.streamingModeName = CFB - - 2) The internal Streaming Padding Scheme has been changed from NONE to PKCS5 as PKCS5 is more commonly used. - If you were using an AES or Blowfish CipherService for streaming operations, you will want to revert to the - previous padding scheme default to ensure you can still decrypt previously encrypted data. For example, in code: - - blockCipherService.setStreamingPaddingScheme(PaddingScheme.NONE); - - or, in shiro.ini: - - blockCipherService.streamingPaddingSchemeName = NoPadding - - Note the difference in code vs shiro.ini in this last example: 'NoPadding' is the correct text value, 'NONE' is - the correct Enum value. - -########################################################### -# 1.1.0 -########################################################### - -Backwards Incompatible Changes --------------------------------- -- The org.apache.shiro.web.util.RedirectView class's - appendQueryProperties(StringBuffer targetUrl, Map model, String encodingScheme) - method has been changed to accept a StringBuilder argument instead of a - StringBuffer per SHIRO-191. RedirectView is considered an internal - implementation support class and Shiro end-users should not be affected by this. +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +This is not an official release notes document. It exists for Shiro developers +to jot down their notes while working in the source code. These notes will be +combined with Jira’s auto-generated release notes during a release for the +total set. + +########################################################### +# 2.0.0 +########################################################### + +Improvement + + [SHIRO-290] Implement bcrypt and argon2 KDF algorithms + +Backwards Incompatible Changes +-------------------------------- + +* Changed default DefaultPasswordService.java algorithm to "Argon2id". +* PasswordService.encryptPassword(Object plaintext) will now throw a NullPointerException on null parameter. + It was never specified how this method would behave. +* Made salt non-nullable. +* Removed methods in PasswordMatcher. + + +########################################################### +# 1.7.1 +########################################################### + +Bug + + [SHIRO-797] - Shiro 1.7.0 is lower than using springboot version 2.0.7 dependency error + + +########################################################### +# 1.7.0 +########################################################### + +Bug + + [SHIRO-767] - org.apache.shiro.util.ClassUtil cannot load the array of Primitive DataType when use undertow as web container + [SHIRO-792] - ShiroWebFilterConfiguration seems to conflict with other FilterRegistrationBean + +New Feature + + [SHIRO-789] - Also add cookie SameSite option to Spring + +Improvement + + [SHIRO-740] - SslFilter with HTTP Strict Transport Security (HSTS) + [SHIRO-794] - Add system property to enable backslash path normalization + [SHIRO-795] - Disable session path rewriting by default + +Task + + [SHIRO-793] - deleteMe cookie should use the defined "sameSite" + + +########################################################### +# 1.6.0 +########################################################### + +Bug + + [SHIRO-610] - Incorrect filterchainResolver in 1.4.0-RC2 + [SHIRO-762] - SecurityUtils.securityManager should be volatile + [SHIRO-766] - ArrayIndexOutOfBoundsException in Base64#decode + +New Feature + + [SHIRO-788] - Add support for Global Filters + +Wish + + [SHIRO-780] - NOTICE files of shiro components don't match NOTICE in source code repository + + +########################################################### +# 1.5.3 +########################################################### + +Bug + + [SHIRO-530] - INI parser does not properly handled backslashes at end of values + [SHIRO-751] - SimplePrincipalMap and SimplePrincipalCollection throw different exceptions for the same problem + [SHIRO-753] - Regression in URI parsing in Shiro 1.5.2 + +Dependency upgrade + + [SHIRO-754] - Upgrade to Apache Commons Codec 1.14 + [SHIRO-755] - Upgrade to Hazelcast 3.12.6 + [SHIRO-756] - Upgrade to Spring 5.2.5.RELEASE and Spring boot 2.2.6.RELEASE + [SHIRO-757] - Upgrade to Htmlunit 2.39.0 + [SHIRO-758] - Upgrade to Jetty 9.4.27.v20200227 + [SHIRO-759] - Upgrade to Karaf 4.2.8 + + +########################################################### +# 1.5.2 +########################################################### + +Bug + + [SHIRO-747] - FirstSuccessfulStrategy doesn't properly short circuit + [SHIRO-749] - shiro-all jar is missing cache package + +Improvement + + [SHIRO-748] - Update Commons Configuration to 2.7 + + +########################################################### +# 1.5.1 +########################################################### + +Bug + + [SHIRO-736] - DefaultCipherInstance is an alias which is not available in every JVM or JCA Provider + [SHIRO-739] - Bean reflection property failed with Enum values + [SHIRO-741] - Matching of / (root) is broken + [SHIRO-742] - fix throw exception when request uri is / + +Dependency upgrade + + [SHIRO-738] - Upgrade to Spring 5.2.3.RELEASE and Spring boot 2.2.4.RELEASE + + +########################################################### +# 1.5.0 +########################################################### + +Notes: this release require a JRE 8 minimum. + +Bug + + [SHIRO-458] - Possible leaked timing information from DefaultPasswordService + [SHIRO-469] - Wrong description of JdbcRealm#setPermissionsQuery + [SHIRO-552] - JdbcRealm in SaltStyle.COLUMN assumes that password column is Base64 but salt column is utf8 bytes + [SHIRO-661] - Add check for the principal of subject whether is null + [SHIRO-682] - fix the potential threat when use "uri = uri + '/' " to bypassed shiro protect + [SHIRO-684] - INI parser keeps escape characters in keys and values + [SHIRO-685] - Potential NullPointerException if PermissionResolver return null/empty string + [SHIRO-687] - Additional Servlet Filters are not available to ShiroFilterFactorBean (unless using XML based beans) + +New Feature + + [SHIRO-694] - Adds BearerToken support + [SHIRO-722] - Add SameSite option to cookies + +Improvement + + [SHIRO-668] - Catch unexpected errors which can lead to oom + [SHIRO-669] - Included a boolean flag in FirstSuccessfulStrategy to break after first successful authentication + [SHIRO-670] - ByteSource Serializable + [SHIRO-681] - Upgrade to compiler Java 8 + [SHIRO-693] - Update plugins + [SHIRO-700] - Minor spring updates + [SHIRO-706] - Switch to Guice4 by default in the build + [SHIRO-709] - Fix Shiro Spring feature + [SHIRO-710] - Update Commons Lang3 + remove older Commons Lang + [SHIRO-711] - Deprecate JavaEnvironment + [SHIRO-712] - Add BasicIniEnvironment + [SHIRO-715] - Remove old JSTL jars + [SHIRO-720] - Update Commons BeanUtils + [SHIRO-724] - Update Jetty, Spring, Spring Boot, Htmlunit dependencies + [SHIRO-726] - Add dynamic import package + [SHIRO-728] - Update Spring Boot to 2.1.10 + [SHIRO-729] - Update Quartz + [SHIRO-730] - Updates the default Cipher mode to GCM in AesCipherService + [SHIRO-731] - Use OWasp Java Encoder to escape user supplied content to the logs + +Test + + [SHIRO-697] - Reduce shiro test logging level to INFO + +Task + + [SHIRO-690] - Validate JDK11 compatibility + [SHIRO-692] - Upgrade and enforce min build maven version to 3.5.0 + [SHIRO-698] - Improve build with maven profile + [SHIRO-734] - Remove Spring-client sample + [SHIRO-735] - Shiro does not support servlet-3.1 void method(@Suspended AsyncResponse) + +Dependency upgrade + + [SHIRO-688] - Upgrade to commons-cli 1.4 + [SHIRO-689] - Upgrade to commons-codec 1.12 + [SHIRO-691] - Upgrade to maven-jar-plugin 3.1.1 + [SHIRO-695] - Update Hazelcast + [SHIRO-696] - Update Jetty + [SHIRO-699] - Fix maven warning for exec-maven-plugin and upgrade to 1.6.0 + [SHIRO-701] - Update logback + [SHIRO-702] - Upgrade to jacoco-maven-plugin 0.8.4 + [SHIRO-703] - Update HSQL + [SHIRO-704] - Update Spring, Spring Boot, Hibernate + [SHIRO-705] - Update Easymock + Powermock + [SHIRO-707] - Misc dependency updates + [SHIRO-716] - Upgrade to commons-codec 1.13 + [SHIRO-717] - Upgrade to maven-pmd-plugin 3.12.0 + [SHIRO-718] - Upgrade to xmlsec 2.1.4 + [SHIRO-719] - Upgrade to Karaf 4.2.6 + +Request + + [SHIRO-723] - Provide Minor Shiro Release that includes CVE-2019-10086 Fix + + +########################################################### +# 1.4.2 +########################################################### + +Bug + + [SHIRO-721] - RememberMe Padding Oracle Vulnerability + +Improvement + + [SHIRO-730] - Updates the default Cipher mode to GCM in AesCipherService + + +########################################################### +# 1.4.1 +########################################################### + +Bug + + [SHIRO-457] - Login without static VM security manager cause exception in debug + [SHIRO-563] - shiro-aspectj karaf feature can't be installed + [SHIRO-624] - OSGI: commons configuration import should be optional + [SHIRO-626] - Bundle symbolic name conflict + [SHIRO-637] - Refresh cached session in HTTP request after user logs out + [SHIRO-650] - Shiro JAX-RS is not an OSGi bundle + [SHIRO-653] - Spring-boot registers shiro filter only on REQUEST dispatcher + [SHIRO-655] - shiro-core has an undesirable runtime OSGi dependency to spring-beans + [SHIRO-658] - Problems building shiro on openjdk-8 on current debian stable (9.6 "stretch") + [SHIRO-660] - Bug in FirstSuccessfulStrategy + [SHIRO-680] - Duplicate Bundle-SymbolicName for Different Shiro Modules + +New Feature + + [SHIRO-638] - Update osgi bundle manifest to support Spring 4.x + +Improvement + + [SHIRO-560] - Shiro-web feature can't be installed in karaf 4.0.4 + [SHIRO-652] - Upgrade Shiro Feature to Karaf 4.x + [SHIRO-664] - Upgrade to Apache pom parent 21 + [SHIRO-665] - Upgrade to maven-bundle-plugin 4.1.0 + [SHIRO-667] - Upgrade to Spring 4.3.22-RELEASE + [SHIRO-672] - Upgrade to jacoco-maven-plugin 0.8.3 + [SHIRO-673] - Upgrade to maven-compiler-plugin 3.8.0 + [SHIRO-674] - Upgrade to maven-dependency-plugin to 3.1.1 + [SHIRO-675] - Upgrade to maven-surefire-plugins 3.0.0-M3 + [SHIRO-676] - Upgrade to maven-jar-plugin 3.1.0 + [SHIRO-677] - Upgrade to versions-maven-plugin 2.7 + [SHIRO-683] - Upgrade to spring-boot 1.5.19.RELEASE + +Task + + [SHIRO-662] - Constant Name Change in AuthenticationRealm + [SHIRO-663] - Clean up pom parent relative path + +Dependency upgrade + + [SHIRO-659] - Upgrade to OWASP dependency-check-maven plugin 4.0.0 + + +########################################################### +# 1.4.0 +########################################################### + +Bug + + [SHIRO-559] - shiro-guice violates the JEE specification + [SHIRO-579] - Permission filter is validating last matched path + [SHIRO-603] - Endless recursion in ShiroSecurityContext.getUserPrincipal() + [SHIRO-605] - ShiroWebModule creates out of order filter chain. + [SHIRO-607] - AuthorizationAttributeSourceAdvisor ignores type-annotations + [SHIRO-608] - Use a ServiceLoader to discover WebEnvironments + [SHIRO-611] - Spring web module does not load correct SessionStorageEvaluator + +Improvement + + [SHIRO-596] - shiro-tools-hasher needs private salt option + [SHIRO-618] - Spring Boot Web Starter- Autoconfiguration for Realm and ShiroFilterChainDefinition + + +########################################################### +# 1.4.0-RC2 +########################################################### + +Bug + + [SHIRO-493] - shiro-guice not working with the guice 4.x + [SHIRO-576] - Commons-beanutils dependency is not security compliant + [SHIRO-586] - Can't Search For Groups In Active Directory Without A System User + [SHIRO-587] - Can't Access Groups If userPrincipalName Doesn't Exist + [SHIRO-591] - Basic Auth Filter permissive mode does NOT work + [SHIRO-592] - ModularRealmAuthenticator causes log spam when one realm throws exception + [SHIRO-593] - Allow for IniWebEnvironment subclasses to specify defaults objects to ReflectionBuilder + [SHIRO-594] - Update Hazelcast version to latest supported version (3.7.2) + [SHIRO-595] - Allow for POST only logout requests + [SHIRO-612] - Need to upgrade BeanUtils to avoid vulnerability + +New Feature + + [SHIRO-501] - Add ability to set system properties in shiro.ini + [SHIRO-589] - Add Servlet 3.x fragment + [SHIRO-590] - Add Spring Boot support / starters + +Improvement + + [SHIRO-296] - Typo fixes + [SHIRO-301] - Call permissionResolver directly in AuthorizingRealm + [SHIRO-392] - Shiro Extension for JAX-RS + [SHIRO-599] - Fix file encoding warnings during maven build and reporting + + +########################################################### +# 1.3.2 +########################################################### + +Bug + + [SHIRO-584] - URL Path matching issue with WebUtils.getPathWithinApplication + + +########################################################### +# 1.3.1 +########################################################### + +Bug + + [SHIRO-577] - Regression - Unable to set custom SessionValidationScheduler + [SHIRO-581] - Improve log message when remember me cipher has changed + + +########################################################### +# 1.3.0 +########################################################### + +Bug + + [SHIRO-373] - Complete CAS remember-me support + [SHIRO-397] - SingleArgumentMethodEventListenerTest fails + [SHIRO-421] - Unable to set long timeouts on HttpServletSession + [SHIRO-435] - SecurityManager is not a singleton in ShiroWebModule + [SHIRO-473] - DefaultAnnotationResolver.getAnnotation throws NullPointerException + [SHIRO-480] - setTarget method in DomainPermission does not set targets + [SHIRO-483] - passwordsMatch() returns false with right plain password-encrypted password in JVM with default locale tr_TR + [SHIRO-502] - OSGi import of com.google.inject in shiro-guice has incorrect version range + [SHIRO-513] - Misleading error message when using custom WebEnvironment + [SHIRO-515] - ExecutorServiceSessionValidationScheduler leaks resources due to improper synchronization + [SHIRO-547] - Use MessageDigest#isEqual() instead of Arrays#equals() for comparing digests + [SHIRO-568] - hash iterations is calculated wrongly in SimpleHash + [SHIRO-570] - SimpleCookie should check the path of the cookie + +New Feature + + [SHIRO-200] - Add ability to configure basic authentication for specific HTTP methods + [SHIRO-395] - Add an Event Bus for event publishing and low-coupling for custom components/plugins. + [SHIRO-412] - Hazelcast-based caching and session clustering + [SHIRO-436] - Add EnvironmentLoader finalizeEnvironment method + +Improvement + + [SHIRO-278] - Rename JndiLdapRealm to DefaultLdapRealm + [SHIRO-300] - WildcardPermission: change visibility of field 'parts' to protected + [SHIRO-361] - HttpServletResponse.encodeURL: only append JSESSIONID when necessary + [SHIRO-428] - AuthorizingRealm "no cache" logging should be at DEBUG level, not INFO, OR is should log only once + [SHIRO-437] - WildcardPermission: conformed toString + [SHIRO-514] - ExecutorServiceSessionValidationScheduler should create threads with a configurable name + [SHIRO-564] - WildcardPermission case-insensitive makes parts collections twice + [SHIRO-566] - CollectionUtils should use Collections wrappers of arrays if possible + +Task + + [SHIRO-208] - Correct JDK 1.5 / 1.6 incompatibilities + [SHIRO-320] - Add an example for using Guice integration. + [SHIRO-571] - Mark shiro-cas deprecated (replaced with buji-pac4j) + + +########################################################### +# 1.2.6 +########################################################### + +Bug + + [SHIRO-545] - JavaEnvironment version getter + [SHIRO-567] - shiro-root-1.2.5.pom uses invalid encoding, fails to parse with Gradle 2.14 + + +########################################################### +# 1.2.5 +########################################################### + +Bug + + [SHIRO-443] - SessionValidationScheduler created multiple times, enabling it is not thread safe + [SHIRO-462] - Authentication exceptions are swallowed + [SHIRO-467] - Authentication exception gets swallowed + [SHIRO-550] - Randomize default remember me cipher + +Improvement + + [SHIRO-504] - Java 8 support + [SHIRO-516] - Explicitly specify the version of aspectjtools to avoid build warning + [SHIRO-562] - WildcardPermission calls String.trim() twice in setParts() + + +########################################################### +# 1.2.4 +########################################################### + +Bug + + [SHIRO-517] - Caused by: java.lang.NoClassDefFoundError: Lcom/google/inject/internal/util/$ImmutableList; + [SHIRO-518] - Shiro-CAS: Security Problem in cas-client-core versions older than 3.3.2 + [SHIRO-556] - https://shiro.apache.org/realm.html appears to link to the javadoc under static/current/apidocs not static/latest + +Improvement + + [SHIRO-332] - Change access level of method 'isPermitted' in org.apache.shiro.realm.AuthorizingRealm (line 461) from private to protected + [SHIRO-496] - Update shiro.guice dependency + [SHIRO-498] - ThreadLocal should not be created when not necessary + + +########################################################### +# 1.2.2 +########################################################### + +Bug: + + [SHIRO-316] - Annotations in samples-aspectj Project Does not Work + [SHIRO-351] - Shiro Native Session implementation cannot extract JSESSIONID From URL if JSESSIONID is URL parameter (not HTTP parameter) + [SHIRO-379] - SimpleAccountRealm concurrency access to roles and users + [SHIRO-380] - runAs feature (still) doesn't work + [SHIRO-387] - EnvironmentLoader destroys wrong environment + [SHIRO-388] - Stackoverflow org.apache.shiro.session.SessionListener.onStop() + [SHIRO-389] - Fix OSGI Exports for shiro-ehcache + [SHIRO-390] - OSGi Import for JSP (javax.servlet.jsp) should be declared optional + [SHIRO-394] - PropertiesRealm reloading not working when loading from file + [SHIRO-399] - Memory leak for invalid sessions + [SHIRO-403] - Trunk will not build under JDK 1.7 due to webstart plugin + [SHIRO-413] - init() method is not called on class that implements org.apache.shiro.util.Initializable + [SHIRO-415] - isLoginAttempt method in BasicHttpAuthenticationFilter class fails if used in any locale other than English + [SHIRO-418] - Javadoc typo in JdbcRealm.SaltStyle + [SHIRO-423] - INI ReflectionBuilder should not wrap reference values + [SHIRO-429] - perms filter parsing is too sensitive to a trailing space + [SHIRO-431] - please use git ignore + [SHIRO-447] - Broken Javadoc links + + +########################################################### +# 1.2.1 +########################################################### + +Bug: + + [SHIRO-341] - ReflectionBuilder has invalid log message format + [SHIRO-342] - Running the example as described at http://shiro.apache.org/10-minute-tutorial.html fails + [SHIRO-344] - runAs feature doesn't work + [SHIRO-350] - Creating a subject should not create a session + [SHIRO-353] - DefaultSecurityManager has invalid SLF4J log instruction + [SHIRO-354] - Authentication cache + [SHIRO-358] - Source Tarball doesn't Build + [SHIRO-363] - PasswordMatcher should support character arrays + [SHIRO-368] - DomainPermission(string, string) constructor sets targets to the same value as actions + [SHIRO-375] - Basic authentication issue when using COLON character + [SHIRO-376] - shiro-cas feature should not depend on shiro-cas + [SHIRO-377] - PropertiesRealm unable to reload Properties + + +########################################################### +# 1.2.0 +########################################################### + +Backwards Incompatible Changes +-------------------------------- +- The following org.apache.shiro.mgt.DefaultSecurityManager methods have been removed: + bindPrincipalsToSession(principals, context) + + This logic has been moved into a SubjectDAO concept to allow end-users to control + exactly how the Session may be used for subject state persistence. This allows a + single point of control rather than needing to configure Shiro in multiple places. + + If you overrode this method in Shiro 1.0 or 1.1, please look at the new + org.apache.shiro.mgt.DefaultSubjectDAO implementation, which performs compatible logic. + Documentation for this is covered here: + http://shiro.apache.org/session-management.html#SessionManagement-SessionsandSubjectState + +- The org.apache.shiro.web.session.mgt.ServletContainerSessionManager implementation + (enabled by default for all web applications) no longer subclasses + org.apache.shiro.session.mgt.AbstractSessionManager. AbstractSessionManager existed + originally to consolidate a 'globalSessionTimeout' configuration property for + subclasses. However, the ServletContainerSessionManager has been changed to always + reflect the session configuration from web.xml (per its namesake). Because web.xml + is the definitive source for session timeout configuration, the 'extends' clause + was removed to avoid configuration confusion: if someone attempted to configure + 'globalSessionTimeout' on a ServletContainerSessionManager instance, it would never + be honored. It was better to remove the extends clause to ensure that any + such configuration would fail fast when Shiro starts up to reflect the invalid config. + + +Potential Breaking Changes +-------------------------------- +- The org.apache.shiro.web.filter.mgt.FilterChainManager class's + addFilter(String name, Filter filter) semantics have changed. It now no longer + attempts to initialize a filter by default before adding the filter to the chain. + If you ever called this method, you can call the + addFilter(name, filter, true) method to achieve the <= 1.1 behavior. + +- The org.apache.shiro.crypto.SecureRandomNumberGenerator previously defaulted to generating + 128 random _bytes_ each time the nextBytes() method was called. This is too large for most purposes, so the + default has been changed to 16 _bytes_ (which equals 128 bits - what was originally intended). If for some reason + you need more than 16 bytes (128 bits) of randomly generated bits, you will need to configure the + 'defaultNextByteSize' property to match your desired size (in bytes, NOT bits). + +- Shiro's Block Cipher Services (AesCipherService, BlowfishCipherService) have had the following changes: + + 1) The internal Cipher Mode and Streaming Cipher Mode have been changed from CFB to the new default of CBC. + CBC is more commonly used for block ciphers today (e.g. SSL). + If you were using an AES or Blowfish CipherService you will want to revert to the previous defaults in your config + to ensure you can still decrypt previously encrypted data. For example, in code: + + blockCipherService.setMode(OperationMode.CFB); + blockCipherService.setStreamingMode(OperationMode.CFB); + + or, in shiro.ini: + + blockCipherService.modeName = CFB + blockCipherService.streamingModeName = CFB + + 2) The internal Streaming Padding Scheme has been changed from NONE to PKCS5 as PKCS5 is more commonly used. + If you were using an AES or Blowfish CipherService for streaming operations, you will want to revert to the + previous padding scheme default to ensure you can still decrypt previously encrypted data. For example, in code: + + blockCipherService.setStreamingPaddingScheme(PaddingScheme.NONE); + + or, in shiro.ini: + + blockCipherService.streamingPaddingSchemeName = NoPadding + + Note the difference in code vs shiro.ini in this last example: 'NoPadding' is the correct text value, 'NONE' is + the correct Enum value. + +########################################################### +# 1.1.0 +########################################################### + +Backwards Incompatible Changes +-------------------------------- +- The org.apache.shiro.web.util.RedirectView class's + appendQueryProperties(StringBuffer targetUrl, Map model, String encodingScheme) + method has been changed to accept a StringBuilder argument instead of a + StringBuffer per SHIRO-191. RedirectView is considered an internal + implementation support class and Shiro end-users should not be affected by this. diff --git a/cache/pom.xml b/cache/pom.xml index efbdae8eec..63b35d83ae 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + org.apache.shiro diff --git a/cache/src/main/java/org/apache/shiro/cache/Cache.java b/cache/src/main/java/org/apache/shiro/cache/Cache.java index 2b6f887f29..cea5e03d8c 100644 --- a/cache/src/main/java/org/apache/shiro/cache/Cache.java +++ b/cache/src/main/java/org/apache/shiro/cache/Cache.java @@ -29,6 +29,8 @@ * cache framework's cache instance (e.g. JCache, Ehcache, JCS, OSCache, JBossCache, TerraCotta, Coherence, * GigaSpaces, etc., etc.), allowing a Shiro user to configure any cache mechanism they choose. * + * @param K + * @param V * @since 0.2 */ public interface Cache { @@ -41,7 +43,7 @@ public interface Cache { * @return the cached object or {@code null} if there is no entry for the specified {@code key} * @throws CacheException if there is a problem accessing the underlying cache system */ - public V get(K key) throws CacheException; + V get(K key) throws CacheException; /** * Adds a Cache entry. @@ -51,7 +53,7 @@ public interface Cache { * @return the previous value associated with the given {@code key} or {@code null} if there was previous value * @throws CacheException if there is a problem accessing the underlying cache system */ - public V put(K key, V value) throws CacheException; + V put(K key, V value) throws CacheException; /** * Remove the cache entry corresponding to the specified key. @@ -60,33 +62,33 @@ public interface Cache { * @return the previous value associated with the given {@code key} or {@code null} if there was previous value * @throws CacheException if there is a problem accessing the underlying cache system */ - public V remove(K key) throws CacheException; + V remove(K key) throws CacheException; /** * Clear all entries from the cache. * * @throws CacheException if there is a problem accessing the underlying cache system */ - public void clear() throws CacheException; + void clear() throws CacheException; /** * Returns the number of entries in the cache. * * @return the number of entries in the cache. */ - public int size(); + int size(); /** * Returns a view of all the keys for entries contained in this cache. * * @return a view of all the keys for entries contained in this cache. */ - public Set keys(); + Set keys(); /** * Returns a view of all of the values contained in this cache. * * @return a view of all of the values contained in this cache. */ - public Collection values(); + Collection values(); } diff --git a/cache/src/main/java/org/apache/shiro/cache/CacheException.java b/cache/src/main/java/org/apache/shiro/cache/CacheException.java index e998dfed28..b91cdfa347 100644 --- a/cache/src/main/java/org/apache/shiro/cache/CacheException.java +++ b/cache/src/main/java/org/apache/shiro/cache/CacheException.java @@ -26,8 +26,7 @@ * * @since 0.2 */ -public class CacheException extends ShiroException -{ +public class CacheException extends ShiroException { /** * Creates a new CacheException. diff --git a/cache/src/main/java/org/apache/shiro/cache/CacheManager.java b/cache/src/main/java/org/apache/shiro/cache/CacheManager.java index f50f06fa42..0b96b93071 100644 --- a/cache/src/main/java/org/apache/shiro/cache/CacheManager.java +++ b/cache/src/main/java/org/apache/shiro/cache/CacheManager.java @@ -38,5 +38,5 @@ public interface CacheManager { * @return the Cache with the given name * @throws CacheException if there is an error acquiring the Cache instance. */ - public Cache getCache(String name) throws CacheException; + Cache getCache(String name) throws CacheException; } diff --git a/cache/src/main/java/org/apache/shiro/cache/MapCache.java b/cache/src/main/java/org/apache/shiro/cache/MapCache.java index 06dfde9105..23a4e1641a 100644 --- a/cache/src/main/java/org/apache/shiro/cache/MapCache.java +++ b/cache/src/main/java/org/apache/shiro/cache/MapCache.java @@ -27,6 +27,8 @@ * A MapCache is a {@link Cache Cache} implementation that uses a backing {@link Map} instance to store * and retrieve cached data. * + * @param K + * @param V * @since 1.0 */ public class MapCache implements Cache { diff --git a/config/core/pom.xml b/config/core/pom.xml index c41b941265..4f9e031d5d 100644 --- a/config/core/pom.xml +++ b/config/core/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + org.apache.shiro diff --git a/config/core/src/main/java/org/apache/shiro/config/ConfigurationException.java b/config/core/src/main/java/org/apache/shiro/config/ConfigurationException.java index 2ae352bb95..1fdb5a7bb0 100644 --- a/config/core/src/main/java/org/apache/shiro/config/ConfigurationException.java +++ b/config/core/src/main/java/org/apache/shiro/config/ConfigurationException.java @@ -26,8 +26,7 @@ * * @since 0.9 */ -public class ConfigurationException extends ShiroException -{ +public class ConfigurationException extends ShiroException { /** * Creates a new ConfigurationException. diff --git a/config/core/src/main/java/org/apache/shiro/config/Ini.java b/config/core/src/main/java/org/apache/shiro/config/Ini.java index a42d4a7df4..a329ac516a 100644 --- a/config/core/src/main/java/org/apache/shiro/config/Ini.java +++ b/config/core/src/main/java/org/apache/shiro/config/Ini.java @@ -44,19 +44,41 @@ * * @since 1.0 */ -public class Ini implements Map { +@SuppressWarnings({"checkstyle:MethodCount", "checkstyle:CyclomaticComplexity"}) +public final class Ini implements Map { - private static transient final Logger log = LoggerFactory.getLogger(Ini.class); + /** + * empty string means the first unnamed section + */ + public static final String DEFAULT_SECTION_NAME = ""; - public static final String DEFAULT_SECTION_NAME = ""; //empty string means the first unnamed section + /** + * default charset name. + */ public static final String DEFAULT_CHARSET_NAME = "UTF-8"; + /** + * comment pound. + */ public static final String COMMENT_POUND = "#"; + + /** + * comment semicolon. + */ public static final String COMMENT_SEMICOLON = ";"; + + /** + * section prefix + */ public static final String SECTION_PREFIX = "["; + /** + * section suffix + */ public static final String SECTION_SUFFIX = "]"; - protected static final char ESCAPE_TOKEN = '\\'; + private static final char ESCAPE_TOKEN = '\\'; + + private static final Logger LOGGER = LoggerFactory.getLogger(Ini.class); private final Map sections; @@ -88,7 +110,7 @@ public Ini(Ini defaults) { * are all empty, {@code false} otherwise. * * @return {@code true} if no sections have been configured, or if there are sections, but the sections themselves - * are all empty, {@code false} otherwise. + * are all empty, {@code false} otherwise. */ public boolean isEmpty() { Collection
sections = this.sections.values(); @@ -107,7 +129,7 @@ public boolean isEmpty() { * no sections. * * @return the names of all sections managed by this {@code Ini} instance or an empty collection if there are - * no sections. + * no sections. */ public Set getSectionNames() { return Collections.unmodifiableSet(sections.keySet()); @@ -118,7 +140,7 @@ public Set getSectionNames() { * no sections. * * @return the sections managed by this {@code Ini} instance or an empty collection if there are - * no sections. + * no sections. */ public Collection
getSections() { return Collections.unmodifiableCollection(sections.values()); @@ -165,7 +187,7 @@ public Section removeSection(String sectionName) { private static String cleanName(String sectionName) { String name = StringUtils.clean(sectionName); if (name == null) { - log.trace("Specified name was null or empty. Defaulting to the default section (name = \"\")"); + LOGGER.trace("Specified name was null or empty. Defaulting to the default section (name = \"\")"); name = DEFAULT_SECTION_NAME; } return name; @@ -209,7 +231,7 @@ public String getSectionProperty(String sectionName, String propertyName) { * @param propertyName the name of the property to add * @param defaultValue the default value to return if the section or property do not exist. * @return the value of the specified section property, or the {@code defaultValue} if the section or - * property do not exist. + * property do not exist. */ public String getSectionProperty(String sectionName, String propertyName, String defaultValue) { String value = getSectionProperty(sectionName, propertyName); @@ -297,7 +319,7 @@ public void load(Reader reader) { try { scanner.close(); } catch (Exception e) { - log.debug("Unable to cleanly close the InputStream scanner. Non-critical - ignoring.", e); + LOGGER.debug("Unable to cleanly close the InputStream scanner. Non-critical - ignoring.", e); } } } @@ -315,7 +337,7 @@ public void load(Reader reader) { * [section2] * key2 = value2 * - * + *

* To be merged: *

      * [section1]
@@ -324,7 +346,7 @@ public void load(Reader reader) {
      * [section2]
      * key2 = new value
      *  
- * + *

* Result: *

      * [section1]
@@ -397,8 +419,8 @@ public void load(Scanner scanner) {
 
                 sectionName = newSectionName;
 
-                if (log.isDebugEnabled()) {
-                    log.debug("Parsing " + SECTION_PREFIX + sectionName + SECTION_SUFFIX);
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debug("Parsing " + SECTION_PREFIX + sectionName + SECTION_SUFFIX);
                 }
             } else {
                 //normal line - add it to the existing content buffer:
@@ -501,7 +523,7 @@ public Set> entrySet() {
      * An {@code Ini.Section} is String-key-to-String-value Map, identifiable by a
      * {@link #getName() name} unique within an {@link Ini} instance.
      */
-    public static class Section implements Map {
+    public static final class Section implements Map {
         private final String name;
         private final Map props;
 
@@ -518,17 +540,13 @@ private Section(String name, String sectionContent) {
                 throw new NullPointerException("name");
             }
             this.name = name;
-            Map props;
-            if (StringUtils.hasText(sectionContent) ) {
+            Map props;
+            if (StringUtils.hasText(sectionContent)) {
                 props = toMapProps(sectionContent);
             } else {
-                props = new LinkedHashMap();
-            }
-            if ( props != null ) {
-                this.props = props;
-            } else {
-                this.props = new LinkedHashMap();
+                props = new LinkedHashMap();
             }
+            this.props = props;
         }
 
         private Section(Section defaults) {
@@ -573,21 +591,21 @@ protected static String[] splitKeyValue(String keyValueLine) {
             StringBuilder keyBuffer = new StringBuilder();
             StringBuilder valueBuffer = new StringBuilder();
 
-            boolean buildingKey = true; //we'll build the value next:
+            //we'll build the value next:
+            boolean buildingKey = true;
 
             for (int i = 0; i < line.length(); i++) {
                 char c = line.charAt(i);
 
                 if (buildingKey) {
-                    if (isKeyValueSeparatorChar(c) && !isCharEscaped(line, i) && !isCharEscaped(line, i-1)) {
-                        buildingKey = false;//now start building the value
-                    } else if (!isCharEscaped(line, i)){
+                    if (isKeyValueSeparatorChar(c) && !isCharEscaped(line, i) && !isCharEscaped(line, i - 1)) {
+                        //now start building the value
+                        buildingKey = false;
+                    } else if (!isCharEscaped(line, i)) {
                         keyBuffer.append(c);
                     }
                 } else {
-                    if (valueBuffer.length() == 0 && isKeyValueSeparatorChar(c) && !isCharEscaped(line, i)) {
-                        //swallow the separator chars before we start building the value
-                    } else {
+                    if (valueBuffer.length() != 0 || !isKeyValueSeparatorChar(c) || isCharEscaped(line, i)) {
                         valueBuffer.append(c);
                     }
                 }
@@ -601,9 +619,9 @@ protected static String[] splitKeyValue(String keyValueLine) {
                 throw new IllegalArgumentException(msg);
             }
 
-            log.trace("Discovered key/value pair: {} = {}", key, value);
+            LOGGER.trace("Discovered key/value pair: {} = {}", key, value);
 
-            return new String[]{key, value};
+            return new String[] {key, value};
         }
 
         private static Map toMapProps(String content) {
diff --git a/config/core/src/test/groovy/org/apache/shiro/config/IniTest.groovy b/config/core/src/test/groovy/org/apache/shiro/config/IniTest.groovy
index ab1cddf9bd..c913285b82 100644
--- a/config/core/src/test/groovy/org/apache/shiro/config/IniTest.groovy
+++ b/config/core/src/test/groovy/org/apache/shiro/config/IniTest.groovy
@@ -36,8 +36,8 @@ public class IniTest {
     @Test
     public void testNoSections() {
         String test =
-            "prop1 = value1" + NL +
-                    "prop2 = value2";
+                "prop1 = value1" + NL +
+                        "prop2 = value2";
 
         Ini ini = new Ini();
         ini.load(test);
diff --git a/config/ogdl/pom.xml b/config/ogdl/pom.xml
index dceb2065e1..c853df9154 100644
--- a/config/ogdl/pom.xml
+++ b/config/ogdl/pom.xml
@@ -17,7 +17,8 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
   -->
-
+
 
     
         org.apache.shiro
@@ -29,7 +30,8 @@
     shiro-config-ogdl
     Apache Shiro :: Configuration :: OGDL
     Support for Shiro's Object Graph Definition Language (mostly used in Ini configuration) where
-        declared name/value pairs are interpreted to create an object graph
+        declared name/value pairs are interpreted to create an object graph
+    
     bundle
     
         config.ogdl
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/CommonsInterpolator.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/CommonsInterpolator.java
index bd0c42b267..5b4da74a46 100644
--- a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/CommonsInterpolator.java
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/CommonsInterpolator.java
@@ -55,7 +55,7 @@
  */
 public class CommonsInterpolator implements Interpolator {
 
-    final private ConfigurationInterpolator interpolator;
+    private final ConfigurationInterpolator interpolator;
 
     public CommonsInterpolator() {
         this.interpolator = new ConfigurationInterpolator();
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/Interpolator.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/Interpolator.java
index 879e61f879..84f517ce97 100644
--- a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/Interpolator.java
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/Interpolator.java
@@ -28,6 +28,7 @@ public interface Interpolator {
 
     /**
      * Interpolates value and returns the result.
+     *
      * @param value the source text
      * @return the String result of the interpolation, or value, if there was not change.
      */
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/ReflectionBuilder.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/ReflectionBuilder.java
index f198eba0d6..b931f7b3e4 100644
--- a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/ReflectionBuilder.java
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/ReflectionBuilder.java
@@ -67,11 +67,10 @@
  *
  * @since 0.9
  */
+@SuppressWarnings("checkstyle:MethodCount")
 public class ReflectionBuilder {
 
-    //TODO - complete JavaDoc
-
-    private static final Logger log = LoggerFactory.getLogger(ReflectionBuilder.class);
+    private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionBuilder.class);
 
     private static final String OBJECT_REFERENCE_BEGIN_TOKEN = "$";
     private static final String ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN = "\\$";
@@ -90,6 +89,7 @@ public class ReflectionBuilder {
 
     /**
      * Interpolation allows for ${key} substitution of values.
+     *
      * @since 1.4
      */
     private Interpolator interpolator;
@@ -98,6 +98,7 @@ public class ReflectionBuilder {
      * @since 1.3
      */
     private EventBus eventBus;
+
     /**
      * Keeps track of event subscribers that were automatically registered by this ReflectionBuilder during
      * object construction.  This is used in case a new EventBus is discovered during object graph
@@ -106,20 +107,13 @@ public class ReflectionBuilder {
      *
      * @since 1.3
      */
-    private final Map registeredEventSubscribers;
+    private final Map registeredEventSubscribers;
 
     /**
      * @since 1.4
      */
     private final BeanUtilsBean beanUtilsBean;
 
-    //@since 1.3
-    private Map createDefaultObjectMap() {
-        Map map = new LinkedHashMap();
-        map.put(EVENT_BUS_NAME, new DefaultEventBus());
-        return map;
-    }
-
     private Function alternateObjectSupplier = name -> null;
 
     public ReflectionBuilder() {
@@ -133,9 +127,9 @@ public ReflectionBuilder(Map defaults) {
         beanUtilsBean = new BeanUtilsBean(new ConvertUtilsBean() {
             @Override
             public Object convert(String value, Class clazz) {
-                if (clazz.isEnum()){
+                if (clazz.isEnum()) {
                     return Enum.valueOf(clazz, value);
-                }else{
+                } else {
                     return super.convert(value, clazz);
                 }
             }
@@ -146,12 +140,12 @@ public Object convert(String value, Class clazz) {
         this.interpolator = createInterpolator();
 
         this.objects = createDefaultObjectMap();
-        this.registeredEventSubscribers = new LinkedHashMap();
+        this.registeredEventSubscribers = new LinkedHashMap();
         apply(defaults);
     }
 
     private void apply(Map objects) {
-        if(!isEmpty(objects)) {
+        if (!isEmpty(objects)) {
             this.objects.putAll(objects);
         }
         EventBus found = findEventBus(this.objects);
@@ -159,6 +153,13 @@ private void apply(Map objects) {
         enableEvents(found);
     }
 
+    //@since 1.3
+    private Map createDefaultObjectMap() {
+        Map map = new LinkedHashMap();
+        map.put(EVENT_BUS_NAME, new DefaultEventBus());
+        return map;
+    }
+
     public Map getObjects() {
         return objects;
     }
@@ -183,7 +184,7 @@ private void enableEvents(EventBus eventBus) {
 
         this.eventBus = eventBus;
 
-        for(Map.Entry entry : this.objects.entrySet()) {
+        for (Map.Entry entry : this.objects.entrySet()) {
             enableEventsIfNecessary(entry.getValue(), entry.getKey());
         }
     }
@@ -213,6 +214,7 @@ private boolean isEventSubscriber(Object bean, String name) {
 
     /**
      * Plug in another way to get objects into configuration, ex: CDI
+     *
      * @param alternateObjectSupplier not null (empty lambda ok)
      * @since 2.0
      */
@@ -221,7 +223,7 @@ public void setAlternateObjectSupplier(Function alternateObjectSuppli
     }
 
     //@since 1.3
-    protected EventBus findEventBus(Map objects) {
+    protected EventBus findEventBus(Map objects) {
 
         if (isEmpty(objects)) {
             return null;
@@ -229,14 +231,14 @@ protected EventBus findEventBus(Map objects) {
 
         //prefer a named object first:
         Object value = objects.get(EVENT_BUS_NAME);
-        if (value != null && value instanceof EventBus) {
-            return (EventBus)value;
+        if (value instanceof EventBus) {
+            return (EventBus) value;
         }
 
         //couldn't find a named 'eventBus' EventBus object.  Try to find the first typed value we can:
-        for( Object v : objects.values()) {
+        for (Object v : objects.values()) {
             if (v instanceof EventBus) {
-                return (EventBus)v;
+                return (EventBus) v;
             }
         }
 
@@ -245,7 +247,7 @@ protected EventBus findEventBus(Map objects) {
 
     private boolean applyEventBusIfNecessary(Object value) {
         if (value instanceof EventBusAware) {
-            ((EventBusAware)value).setEventBus(this.eventBus);
+            ((EventBusAware) value).setEventBus(this.eventBus);
             return true;
         }
         return false;
@@ -294,9 +296,11 @@ private String parseBeanId(String lhs) {
                 String rhs = interpolator.interpolate(entry.getValue());
 
                 String beanId = parseBeanId(lhs);
-                if (beanId != null) { //a beanId could be parsed, so the line is a bean instance definition
+                //a beanId could be parsed, so the line is a bean instance definition
+                if (beanId != null) {
                     processor.add(new InstantiationStatement(beanId, rhs));
-                } else { //the line must be a property configuration
+                    //the line must be a property configuration
+                } else {
                     processor.add(new AssignmentStatement(lhs, rhs));
                 }
             }
@@ -319,19 +323,21 @@ public void destroy() {
         final Map immutableObjects = Collections.unmodifiableMap(objects);
 
         //destroy objects in the opposite order they were initialized:
-        List> entries = new ArrayList>(objects.entrySet());
+        List> entries = new ArrayList>(objects.entrySet());
         Collections.reverse(entries);
 
-        for(Map.Entry entry: entries) {
+        for (Map.Entry entry : entries) {
             String id = entry.getKey();
             Object bean = entry.getValue();
 
             //don't destroy the eventbus until the end - we need it to still be 'alive' while publishing destroy events:
-            if (bean != this.eventBus) { //memory equality check (not .equals) on purpose
+            //memory equality check (not .equals) on purpose
+            if (bean != this.eventBus) {
                 LifecycleUtils.destroy(bean);
                 BeanEvent event = new DestroyedBeanEvent(id, bean, immutableObjects);
                 eventBus.publish(event);
-                this.eventBus.unregister(bean); //bean is now destroyed - it should not receive any other events
+                //bean is now destroyed - it should not receive any other events
+                this.eventBus.unregister(bean);
             }
         }
         //only now destroy the event bus:
@@ -342,11 +348,12 @@ protected void createNewInstance(Map objects, String name, Strin
 
         Object currentInstance = objects.get(name);
         if (currentInstance != null) {
-            log.info("An instance with name '{}' already exists.  " +
-                    "Redefining this object as a new instance of type {}", name, value);
+            LOGGER.info("An instance with name '{}' already exists.  "
+                    + "Redefining this object as a new instance of type {}", name, value);
         }
 
-        Object instance;//name with no property, assume right hand side of equals sign is the class name:
+        //name with no property, assume right hand side of equals sign is the class name:
+        Object instance;
         try {
             instance = ClassUtils.newInstance(value);
             if (instance instanceof Nameable) {
@@ -378,8 +385,8 @@ protected void applyProperty(String key, String value, Map objects) {
             }
 
         } else {
-            throw new IllegalArgumentException("All property keys must contain a '.' character. " +
-                    "(e.g. myBean.property = value)  These should already be separated out by buildObjects().");
+            throw new IllegalArgumentException("All property keys must contain a '.' character. "
+                    + "(e.g. myBean.property = value)  These should already be separated out by buildObjects().");
         }
     }
 
@@ -391,9 +398,9 @@ protected void applyGlobalProperty(Map objects, String property, String value) {
                     applyProperty(instance, property, value);
                 }
             } catch (Exception e) {
-                String msg = "Error retrieving property descriptor for instance " +
-                        "of type [" + instance.getClass().getName() + "] " +
-                        "while setting property [" + property + "]";
+                String msg = "Error retrieving property descriptor for instance "
+                        + "of type [" + instance.getClass().getName() + "] "
+                        + "while setting property [" + property + "]";
                 throw new ConfigurationException(msg, e);
             }
         }
@@ -402,14 +409,14 @@ protected void applyGlobalProperty(Map objects, String property, String value) {
     protected void applySingleProperty(Map objects, String name, String property, String value) {
         Object instance = objects.get(name);
         if (property.equals("class")) {
-            throw new IllegalArgumentException("Property keys should not contain 'class' properties since these " +
-                    "should already be separated out by buildObjects().");
+            throw new IllegalArgumentException("Property keys should not contain 'class' properties since these "
+                    + "should already be separated out by buildObjects().");
 
         } else if (instance == null) {
-            String msg = "Configuration error.  Specified object [" + name + "] with property [" +
-                    property + "] without first defining that object's class.  Please first " +
-                    "specify the class property first, e.g. myObject = fully_qualified_class_name " +
-                    "and then define additional properties.";
+            String msg = "Configuration error.  Specified object [" + name + "] with property ["
+                    + property + "] without first defining that object's class.  Please first "
+                    + "specify the class property first, e.g. myObject = fully_qualified_class_name "
+                    + "and then define additional properties.";
             throw new IllegalArgumentException(msg);
 
         } else {
@@ -428,9 +435,9 @@ protected String getId(String referenceToken) {
     protected Object getReferencedObject(String id) {
         Object o = objects != null && !objects.isEmpty() ? objects.get(id) : null;
         if (o == null) {
-            String msg = "The object with id [" + id + "] has not yet been defined and therefore cannot be " +
-                    "referenced.  Please ensure objects are defined in the order in which they should be " +
-                    "created and made available for future reference.";
+            String msg = "The object with id [" + id + "] has not yet been defined and therefore cannot be "
+                    + "referenced.  Please ensure objects are defined in the order in which they should be "
+                    + "created and made available for future reference.";
             throw new UnresolveableReferenceException(msg);
         }
         return o;
@@ -445,7 +452,7 @@ protected String unescapeIfNecessary(String value) {
 
     protected Object resolveReference(String reference) {
         String id = getId(reference);
-        log.debug("Encountered object reference '{}'.  Looking up object with id '{}'", reference, id);
+        LOGGER.debug("Encountered object reference '{}'.  Looking up object with id '{}'", reference, id);
         final Object referencedObject = getReferencedObject(id);
         if (referencedObject instanceof Factory) {
             return ((Factory) referencedObject).getInstance();
@@ -460,8 +467,8 @@ protected boolean isTypedProperty(Object object, String propertyName, Class claz
         try {
             PropertyDescriptor descriptor = beanUtilsBean.getPropertyUtils().getPropertyDescriptor(object, propertyName);
             if (descriptor == null) {
-                String msg = "Property '" + propertyName + "' does not exist for object of " +
-                        "type " + object.getClass().getName() + ".";
+                String msg = "Property '" + propertyName + "' does not exist for object of "
+                        + "type " + object.getClass().getName() + ".";
                 throw new ConfigurationException(msg);
             }
             Class propertyClazz = descriptor.getPropertyType();
@@ -485,7 +492,7 @@ protected Set toSet(String sValue) {
         if (tokens.length == 1 && isReference(tokens[0])) {
             Object reference = resolveReference(tokens[0]);
             if (reference instanceof Set) {
-                return (Set)reference;
+                return (Set) reference;
             }
         }
 
@@ -511,7 +518,7 @@ protected Set toSet(String sValue) {
         if (tokens.length == 1 && isReference(tokens[0])) {
             Object reference = resolveReference(tokens[0]);
             if (reference instanceof Map) {
-                return (Map)reference;
+                return (Map) reference;
             }
         }
 
@@ -519,9 +526,9 @@ protected Set toSet(String sValue) {
         for (String token : tokens) {
             String[] kvPair = StringUtils.split(token, MAP_KEY_VALUE_DELIMITER);
             if (kvPair == null || kvPair.length != 2) {
-                String msg = "Map property value [" + sValue + "] contained key-value pair token [" +
-                        token + "] that does not properly split to a single key and pair.  This must be the " +
-                        "case for all map entries.";
+                String msg = "Map property value [" + sValue + "] contained key-value pair token ["
+                        + token + "] that does not properly split to a single key and pair.  This must be the "
+                        + "case for all map entries.";
                 throw new ConfigurationException(msg);
             }
             mapTokens.put(kvPair[0], kvPair[1]);
@@ -549,7 +556,7 @@ protected Collection toCollection(String sValue) {
         if (tokens.length == 1 && isReference(tokens[0])) {
             Object reference = resolveReference(tokens[0]);
             if (reference instanceof Collection) {
-                return (Collection)reference;
+                return (Collection) reference;
             }
         }
 
@@ -572,7 +579,7 @@ protected List toList(String sValue) {
         if (tokens.length == 1 && isReference(tokens[0])) {
             Object reference = resolveReference(tokens[0]);
             if (reference instanceof List) {
-                return (List)reference;
+                return (List) reference;
             }
         }
 
@@ -615,11 +622,10 @@ protected String checkForNullOrEmptyLiteral(String stringValue) {
             return null;
         }
         //check if the value is the actual literal string 'null' (expected to be wrapped in quotes):
-        if (stringValue.equals("\"null\"")) {
+        if ("\"null\"".equals(stringValue)) {
             return NULL_VALUE_TOKEN;
-        }
-        //or the actual literal string of two quotes '""' (expected to be wrapped in quotes):
-        else if (stringValue.equals("\"\"\"\"")) {
+            //or the actual literal string of two quotes '""' (expected to be wrapped in quotes):
+        } else if ("\"\"\"\"".equals(stringValue)) {
             return EMPTY_STRING_VALUE_TOKEN;
         } else {
             return stringValue;
@@ -641,11 +647,11 @@ protected void applyProperty(Object object, String propertyPath, Object value) {
             //find the end of the map reference:
             mapEnd = propertyPath.indexOf(MAP_PROPERTY_END_TOKEN, mapBegin);
             //find the token in between the [ and the ] (the map/array key or index):
-            keyString = propertyPath.substring(mapBegin+1, mapEnd);
+            keyString = propertyPath.substring(mapBegin + 1, mapEnd);
 
             //find out if there is more path reference to follow.  If not, we're at a terminal of the OGNL expression
-            if (propertyPath.length() > (mapEnd+1)) {
-                remaining = propertyPath.substring(mapEnd+1);
+            if (propertyPath.length() > (mapEnd + 1)) {
+                remaining = propertyPath.substring(mapEnd + 1);
                 if (remaining.startsWith(".")) {
                     remaining = StringUtils.clean(remaining.substring(1));
                 }
@@ -660,7 +666,7 @@ protected void applyProperty(Object object, String propertyPath, Object value) {
             } else {
                 //we're assigning a map or array entry.  Check to see which we should call:
                 if (isTypedProperty(object, mapPropertyPath, Map.class)) {
-                    Map map = (Map)getProperty(object, mapPropertyPath);
+                    Map map = (Map) getProperty(object, mapPropertyPath);
                     Object mapKey = resolveValue(keyString);
                     //noinspection unchecked
                     map.put(mapKey, value);
@@ -675,7 +681,7 @@ protected void applyProperty(Object object, String propertyPath, Object value) {
             //recursively call this method with the remaining property path
             Object referencedValue = null;
             if (isTypedProperty(object, mapPropertyPath, Map.class)) {
-                Map map = (Map)getProperty(object, mapPropertyPath);
+                Map map = (Map) getProperty(object, mapPropertyPath);
                 Object mapKey = resolveValue(keyString);
                 referencedValue = map.get(mapKey);
             } else {
@@ -685,8 +691,8 @@ protected void applyProperty(Object object, String propertyPath, Object value) {
             }
 
             if (referencedValue == null) {
-                throw new ConfigurationException("Referenced map/array value '" + mapPropertyPath + "[" +
-                keyString + "]' does not exist.");
+                throw new ConfigurationException("Referenced map/array value '" + mapPropertyPath + "["
+                        + keyString + "]' does not exist.");
             }
 
             applyProperty(referencedValue, remaining, value);
@@ -695,18 +701,18 @@ protected void applyProperty(Object object, String propertyPath, Object value) {
 
     private void setProperty(Object object, String propertyPath, Object value) {
         try {
-            if (log.isTraceEnabled()) {
-                log.trace("Applying property [{}] value [{}] on object of type [{}]",
-                        new Object[]{propertyPath, value, object.getClass().getName()});
+            if (LOGGER.isTraceEnabled()) {
+                LOGGER.trace("Applying property [{}] value [{}] on object of type [{}]",
+                        new Object[] {propertyPath, value, object.getClass().getName()});
             }
             beanUtilsBean.setProperty(object, propertyPath, value);
         } catch (Exception e) {
-            String msg = "Unable to set property '" + propertyPath + "' with value [" + value + "] on object " +
-                    "of type " + (object != null ? object.getClass().getName() : null) + ".  If " +
-                    "'" + value + "' is a reference to another (previously defined) object, prefix it with " +
-                    "'" + OBJECT_REFERENCE_BEGIN_TOKEN + "' to indicate that the referenced " +
-                    "object should be used as the actual value.  " +
-                    "For example, " + OBJECT_REFERENCE_BEGIN_TOKEN + value;
+            String msg = "Unable to set property '" + propertyPath + "' with value [" + value + "] on object "
+                    + "of type " + (object != null ? object.getClass().getName() : null) + ".  If "
+                    + "'" + value + "' is a reference to another (previously defined) object, prefix it with "
+                    + "'" + OBJECT_REFERENCE_BEGIN_TOKEN + "' to indicate that the referenced "
+                    + "object should be used as the actual value.  "
+                    + "For example, " + OBJECT_REFERENCE_BEGIN_TOKEN + value;
             throw new ConfigurationException(msg, e);
         }
     }
@@ -782,31 +788,32 @@ private Interpolator createInterpolator() {
 
     /**
      * Sets the {@link Interpolator} used when evaluating the right side of the expressions.
+     *
      * @since 1.4
      */
     public void setInterpolator(Interpolator interpolator) {
         this.interpolator = interpolator;
     }
 
-    private class BeanConfigurationProcessor {
+    private final class BeanConfigurationProcessor {
 
         private final List statements = new ArrayList();
         private final List beanConfigurations = new ArrayList();
 
         public void add(Statement statement) {
-
-            statements.add(statement); //we execute bean configuration statements in the order they are declared.
+            //we execute bean configuration statements in the order they are declared.
+            statements.add(statement);
 
             if (statement instanceof InstantiationStatement) {
-                InstantiationStatement is = (InstantiationStatement)statement;
+                InstantiationStatement is = (InstantiationStatement) statement;
                 beanConfigurations.add(new BeanConfiguration(is));
             } else {
-                AssignmentStatement as = (AssignmentStatement)statement;
+                AssignmentStatement as = (AssignmentStatement) statement;
                 //statements always apply to the most recently defined bean configuration with the same name, so we
                 //have to traverse the configuration list starting at the end (most recent elements are appended):
                 boolean addedToConfig = false;
                 String beanName = as.getRootBeanName();
-                for( int i = beanConfigurations.size()-1; i >= 0; i--) {
+                for (int i = beanConfigurations.size() - 1; i >= 0; i--) {
                     BeanConfiguration mostRecent = beanConfigurations.get(i);
                     String mostRecentBeanName = mostRecent.getBeanName();
                     if (beanName.equals(mostRecentBeanName)) {
@@ -828,17 +835,18 @@ public void add(Statement statement) {
 
         public void execute() {
 
-            for( Statement statement : statements) {
+            for (Statement statement : statements) {
 
                 statement.execute();
 
                 BeanConfiguration bd = statement.getBeanConfiguration();
 
-                if (bd.isExecuted()) { //bean is fully configured, no more statements to execute for it:
+                //bean is fully configured, no more statements to execute for it:
+                if (bd.isExecuted()) {
 
                     //bean configured overrides the 'eventBus' bean - replace the existing eventBus with the one configured:
                     if (bd.getBeanName().equals(EVENT_BUS_NAME)) {
-                        EventBus eventBus = (EventBus)bd.getBean();
+                        EventBus eventBus = (EventBus) bd.getBean();
                         enableEvents(eventBus);
                     }
 
@@ -863,7 +871,7 @@ public void execute() {
         }
     }
 
-    private class BeanConfiguration {
+    private final class BeanConfiguration {
 
         private final InstantiationStatement instantiationStatement;
         private final List assignments = new ArrayList();
@@ -886,7 +894,12 @@ public String getBeanName() {
             return this.beanName;
         }
 
-        public boolean isGlobalConfig() { //BeanConfiguration instance representing the global 'shiro.' properties
+        /**
+         * BeanConfiguration instance representing the global 'shiro.' properties
+         *
+         * @return boolean
+         */
+        public boolean isGlobalConfig() {
             // (we should remove this concept).
             return GLOBAL_PROPERTY_PREFIX.equals(getBeanName());
         }
@@ -912,6 +925,7 @@ public Object getBean() {
 
         /**
          * Returns true if all configuration statements have been executed.
+         *
          * @return true if all configuration statements have been executed.
          */
         public boolean isExecuted() {
@@ -983,7 +997,7 @@ public boolean isExecuted() {
         }
     }
 
-    private class InstantiationStatement extends Statement {
+    private final class InstantiationStatement extends Statement {
 
         private InstantiationStatement(String lhs, String rhs) {
             super(lhs, rhs);
@@ -1008,7 +1022,7 @@ protected Object doExecute() {
         }
     }
 
-    private class AssignmentStatement extends Statement {
+    private final class AssignmentStatement extends Statement {
 
         private final String rootBeanName;
 
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/ConfiguredBeanEvent.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/ConfiguredBeanEvent.java
index 2a196a58e4..619620e6a2 100644
--- a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/ConfiguredBeanEvent.java
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/ConfiguredBeanEvent.java
@@ -24,11 +24,11 @@
  * Event triggered when a configured bean has been instantiated and fully configured but right before the bean has been
  * initialized.
  *
- * @since 1.3
  * @see InstantiatedBeanEvent
  * @see org.apache.shiro.lang.util.Initializable Initializable
  * @see InitializedBeanEvent
  * @see DestroyedBeanEvent
+ * @since 1.3
  */
 public class ConfiguredBeanEvent extends BeanEvent {
 
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/DestroyedBeanEvent.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/DestroyedBeanEvent.java
index f4924cb68a..090d985bbe 100644
--- a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/DestroyedBeanEvent.java
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/DestroyedBeanEvent.java
@@ -23,11 +23,11 @@
 /**
  * Event triggered when a configured bean has been destroyed.
  *
- * @since 1.3
  * @see org.apache.shiro.lang.util.Destroyable Destroyable
  * @see InstantiatedBeanEvent
  * @see ConfiguredBeanEvent
  * @see InitializedBeanEvent
+ * @since 1.3
  */
 public class DestroyedBeanEvent extends BeanEvent {
 
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/InitializedBeanEvent.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/InitializedBeanEvent.java
index 44a8dfc930..98d180ba08 100644
--- a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/InitializedBeanEvent.java
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/InitializedBeanEvent.java
@@ -23,11 +23,11 @@
 /**
  * Event triggered when a configured bean has been instantiated, fully configured and initialized.
  *
- * @since 1.3
  * @see org.apache.shiro.lang.util.Initializable Initializable
  * @see InstantiatedBeanEvent
  * @see ConfiguredBeanEvent
  * @see DestroyedBeanEvent
+ * @since 1.3
  */
 public class InitializedBeanEvent extends BeanEvent {
 
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/InstantiatedBeanEvent.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/InstantiatedBeanEvent.java
index b6eec57088..372bf6162d 100644
--- a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/InstantiatedBeanEvent.java
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/InstantiatedBeanEvent.java
@@ -23,11 +23,11 @@
 /**
  * Event triggered when a configured bean has been instantiated but before it is configured or initialized.
  *
- * @since 1.3
  * @see ConfiguredBeanEvent
  * @see InitializedBeanEvent
  * @see DestroyedBeanEvent
  * @see org.apache.shiro.lang.util.Initializable Initializable
+ * @since 1.3
  */
 public class InstantiatedBeanEvent extends BeanEvent {
 
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/LoggingBeanEventListener.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/LoggingBeanEventListener.java
index 24c709954d..3b9ada4546 100644
--- a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/LoggingBeanEventListener.java
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/LoggingBeanEventListener.java
@@ -29,7 +29,7 @@
  */
 public class LoggingBeanEventListener {
 
-    private static final Logger logger = LoggerFactory.getLogger(LoggingBeanEventListener.class);
+    private static final Logger LOGGER = LoggerFactory.getLogger(LoggingBeanEventListener.class);
     private static final String SUFFIX = BeanEvent.class.getSimpleName();
 
     @Subscribe
@@ -37,6 +37,6 @@ public void onEvent(BeanEvent e) {
         String className = e.getClass().getSimpleName();
         int i = className.lastIndexOf(SUFFIX);
         String subclassPrefix = i > 0 ? className.substring(0, i) : className;
-        logger.trace("{} bean '{}' [{}]", new Object[]{subclassPrefix, e.getBeanName(), e.getBean()});
+        LOGGER.trace("{} bean '{}' [{}]", subclassPrefix, e.getBeanName(), e.getBean());
     }
 }
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/package-info.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/package-info.java
new file mode 100644
index 0000000000..e7f1312175
--- /dev/null
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/event/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.shiro.config.ogdl.event;
diff --git a/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/package-info.java b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/package-info.java
new file mode 100644
index 0000000000..daf21d2eb5
--- /dev/null
+++ b/config/ogdl/src/main/java/org/apache/shiro/config/ogdl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.shiro.config.ogdl;
diff --git a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/CompositeBean.groovy b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/CompositeBean.groovy
index ff4d5bb171..0ae0632afb 100644
--- a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/CompositeBean.groovy
+++ b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/CompositeBean.groovy
@@ -34,7 +34,7 @@ class CompositeBean {
     Map compositeBeanMap;
     CompositeBean[] compositeBeanArray;
 
-    public CompositeBean(){}
+    public CompositeBean() {}
 
     public CompositeBean(String name) {
         this.name = name
diff --git a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/InitializableBean.groovy b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/InitializableBean.groovy
index 05b35f0899..cc5d8f3d3d 100644
--- a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/InitializableBean.groovy
+++ b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/InitializableBean.groovy
@@ -28,7 +28,7 @@ class InitializableBean implements Initializable {
 
     private volatile boolean initialized = false;
 
-    public InitializableBean(){}
+    public InitializableBean() {}
 
     public void init() throws ShiroException {
         initialized = true;
diff --git a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/RecordingBeanListener.groovy b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/RecordingBeanListener.groovy
index afb4aa6d5e..b2e8281024 100644
--- a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/RecordingBeanListener.groovy
+++ b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/RecordingBeanListener.groovy
@@ -32,31 +32,36 @@ public class RecordingBeanListener {
     private List destroyedEvents = new ArrayList();
     private List unhandledEvents = new ArrayList();
 
-    @SuppressWarnings("UnusedDeclaration") //used via reflection
+    @SuppressWarnings("UnusedDeclaration")
+    //used via reflection
     @Subscribe
     public void onUnhandledBeanEvent(BeanEvent beanEvent) {
         this.unhandledEvents.add(beanEvent);
     }
 
-    @SuppressWarnings("UnusedDeclaration") //used via reflection
+    @SuppressWarnings("UnusedDeclaration")
+    //used via reflection
     @Subscribe
     public void onInstantiatedBeanEvent(InstantiatedBeanEvent beanEvent) {
         this.instantiateEvents.add(beanEvent);
     }
 
-    @SuppressWarnings("UnusedDeclaration") //used via reflection
+    @SuppressWarnings("UnusedDeclaration")
+    //used via reflection
     @Subscribe
     public void onConfiguredBeanEvent(ConfiguredBeanEvent beanEvent) {
         this.configuredEvents.add(beanEvent);
     }
 
-    @SuppressWarnings("UnusedDeclaration") //used via reflection
+    @SuppressWarnings("UnusedDeclaration")
+    //used via reflection
     @Subscribe
     public void onInitializedBeanEvent(InitializedBeanEvent beanEvent) {
         this.initializedEvents.add(beanEvent);
     }
 
-    @SuppressWarnings("UnusedDeclaration") //used via reflection
+    @SuppressWarnings("UnusedDeclaration")
+    //used via reflection
     @Subscribe
     public void onDestroyedBeanEvent(DestroyedBeanEvent beanEvent) {
         this.destroyedEvents.add(beanEvent);
diff --git a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/ReflectionBuilderTest.groovy b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/ReflectionBuilderTest.groovy
index 2e8e0ec376..86af2eea35 100644
--- a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/ReflectionBuilderTest.groovy
+++ b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/ReflectionBuilderTest.groovy
@@ -134,7 +134,7 @@ class ReflectionBuilderTest {
 
     @Test
     void testWithConfiguredNullValue() {
-        Map defaults = new LinkedHashMap();
+        Map defaults = new LinkedHashMap();
         CompositeBean cBean = new CompositeBean();
         cBean.setSimpleBean(new SimpleBean());
         defaults.put("compositeBean", cBean);
@@ -296,7 +296,7 @@ class ReflectionBuilderTest {
         def set = [new SimpleBean('foo'), new SimpleBean('bar')] as Set
 
         def defs = [
-                compositeBean: 'org.apache.shiro.config.ogdl.CompositeBean',
+                compositeBean                : 'org.apache.shiro.config.ogdl.CompositeBean',
                 'compositeBean.simpleBeanSet': '$set'
         ]
 
@@ -337,7 +337,7 @@ class ReflectionBuilderTest {
         List list = [new SimpleBean('foo'), new SimpleBean('bar')] as List
 
         def defs = [
-                compositeBean: 'org.apache.shiro.config.ogdl.CompositeBean',
+                compositeBean                 : 'org.apache.shiro.config.ogdl.CompositeBean',
                 'compositeBean.simpleBeanList': '$list'
         ]
 
@@ -378,7 +378,7 @@ class ReflectionBuilderTest {
         def c = [new SimpleBean('foo'), new SimpleBean('bar')]
 
         def defs = [
-                compositeBean: 'org.apache.shiro.config.ogdl.CompositeBean',
+                compositeBean                       : 'org.apache.shiro.config.ogdl.CompositeBean',
                 'compositeBean.simpleBeanCollection': '$collection'
         ]
 
@@ -391,7 +391,7 @@ class ReflectionBuilderTest {
         assertNotNull(simpleBeans);
         assertSame c, simpleBeans
         assertEquals(2, simpleBeans.size());
-        def i  = simpleBeans.iterator()
+        def i = simpleBeans.iterator()
         assertEquals 'foo', i.next().name
         assertEquals 'bar', i.next().name
     }
@@ -463,7 +463,7 @@ class ReflectionBuilderTest {
         def map = ['foo': new SimpleBean('foo'), 'bar': new SimpleBean('bar')]
 
         def defs = [
-                compositeBean: 'org.apache.shiro.config.ogdl.CompositeBean',
+                compositeBean                : 'org.apache.shiro.config.ogdl.CompositeBean',
                 'compositeBean.simpleBeanMap': '$map'
         ]
 
@@ -718,8 +718,8 @@ class ReflectionBuilderTest {
     }
 
     void checkType(String instanceName, List events, String name, Class expectedType) {
-        for(BeanEvent event: events) {
-            if(event.getBeanName().equals(name)) {
+        for (BeanEvent event : events) {
+            if (event.getBeanName().equals(name)) {
                 assertTrue(
                         expectedType.isInstance(event.getBean()),
                         "Notification for bean " + name + " did not provide an instance of " + expectedType
diff --git a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/SimpleBean.groovy b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/SimpleBean.groovy
index 8fd9b7f346..b15ffb40df 100644
--- a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/SimpleBean.groovy
+++ b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/SimpleBean.groovy
@@ -26,10 +26,11 @@ class SimpleBean {
     byte[] byteArrayProp;
     List simpleBeans;
     List stringList;
-    Map mapProp = new LinkedHashMap();
+    Map mapProp = new LinkedHashMap();
     SimpleEnum simpleEnum;
 
-    public SimpleBean(){}
+    public SimpleBean() {}
+
     public SimpleBean(String name) {
         this.name = name
     }
diff --git a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/event/BeanEventTest.groovy b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/event/BeanEventTest.groovy
index d784d97dcb..196c86ebf8 100644
--- a/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/event/BeanEventTest.groovy
+++ b/config/ogdl/src/test/groovy/org/apache/shiro/config/ogdl/event/BeanEventTest.groovy
@@ -31,7 +31,7 @@ class BeanEventTest {
     @Test
     void testDefault() {
 
-        def m = [foo: 'bar'] as Map
+        def m = [foo: 'bar'] as Map
         Object o = new Object()
         BeanEvent evt = new MyBeanEvent('baz', o, m)
 
diff --git a/config/pom.xml b/config/pom.xml
index 7b885c4d79..681049c197 100644
--- a/config/pom.xml
+++ b/config/pom.xml
@@ -17,7 +17,8 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
   -->
-
+
 
     4.0.0
 
diff --git a/core/pom.xml b/core/pom.xml
index a81cbf4712..57c0e5b30d 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -17,7 +17,8 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
   -->
-
+
 
     
         org.apache.shiro
@@ -36,7 +37,7 @@
     
         
             
-             
+            
                 org.apache.maven.plugins
                 maven-jar-plugin
                 
diff --git a/core/src/main/java/org/apache/shiro/SecurityUtils.java b/core/src/main/java/org/apache/shiro/SecurityUtils.java
index 058ccf4c5c..71ee87a5db 100644
--- a/core/src/main/java/org/apache/shiro/SecurityUtils.java
+++ b/core/src/main/java/org/apache/shiro/SecurityUtils.java
@@ -107,9 +107,8 @@ public static void setSecurityManager(SecurityManager securityManager) {
      * to calling code in an application. If it is not, it is likely due to a Shiro configuration problem.
      *
      * @return the SecurityManager accessible to the calling code.
-     * @throws UnavailableSecurityManagerException
-     *          if there is no {@code SecurityManager} instance available to the
-     *          calling code, which typically indicates an invalid application configuration.
+     * @throws UnavailableSecurityManagerException if there is no {@code SecurityManager} instance available to the
+     *                                             calling code, which typically indicates an invalid application configuration.
      */
     public static SecurityManager getSecurityManager() throws UnavailableSecurityManagerException {
         SecurityManager securityManager = ThreadContext.getSecurityManager();
@@ -117,9 +116,9 @@ public static SecurityManager getSecurityManager() throws UnavailableSecurityMan
             securityManager = SecurityUtils.securityManager;
         }
         if (securityManager == null) {
-            String msg = "No SecurityManager accessible to the calling code, either bound to the " +
-                    ThreadContext.class.getName() + " or as a vm static singleton.  This is an invalid application " +
-                    "configuration.";
+            String msg = "No SecurityManager accessible to the calling code, either bound to the "
+                    + ThreadContext.class.getName() + " or as a vm static singleton.  This is an invalid application "
+                    + "configuration.";
             throw new UnavailableSecurityManagerException(msg);
         }
         return securityManager;
diff --git a/core/src/main/java/org/apache/shiro/aop/AnnotationHandler.java b/core/src/main/java/org/apache/shiro/aop/AnnotationHandler.java
index 706c56e8c3..d1c567674a 100644
--- a/core/src/main/java/org/apache/shiro/aop/AnnotationHandler.java
+++ b/core/src/main/java/org/apache/shiro/aop/AnnotationHandler.java
@@ -49,7 +49,8 @@ public AnnotationHandler(Class annotationClass) {
     /**
      * Returns the {@link org.apache.shiro.subject.Subject Subject} associated with the currently-executing code.
      * 

- * This default implementation merely calls {@link org.apache.shiro.SecurityUtils#getSubject SecurityUtils.getSubject()}. + * This default implementation merely calls + * {@link org.apache.shiro.SecurityUtils#getSubject SecurityUtils.getSubject()}. * * @return the {@link org.apache.shiro.subject.Subject Subject} associated with the currently-executing code. */ diff --git a/core/src/main/java/org/apache/shiro/aop/AnnotationMethodInterceptor.java b/core/src/main/java/org/apache/shiro/aop/AnnotationMethodInterceptor.java index 9179651dcf..fab7d3cdbc 100644 --- a/core/src/main/java/org/apache/shiro/aop/AnnotationMethodInterceptor.java +++ b/core/src/main/java/org/apache/shiro/aop/AnnotationMethodInterceptor.java @@ -75,7 +75,7 @@ public AnnotationMethodInterceptor(AnnotationHandler handler, AnnotationResolver * an annotation discovered at runtime. * * @return the {@code AnnotationHandler} used to perform authorization behavior based on - * an annotation discovered at runtime. + * an annotation discovered at runtime. */ public AnnotationHandler getHandler() { return handler; @@ -98,7 +98,7 @@ public void setHandler(AnnotationHandler handler) { * perform authorization logic. * * @return the {@code AnnotationResolver} to use to acquire annotations from intercepted - * methods at runtime. + * methods at runtime. * @since 1.1 */ public AnnotationResolver getResolver() { @@ -128,7 +128,7 @@ public void setResolver(AnnotationResolver resolver) { * * @param mi the MethodInvocation for the method being invoked. * @return true if this interceptor supports, that is, should inspect, the specified - * MethodInvocation, false otherwise. + * MethodInvocation, false otherwise. */ public boolean supports(MethodInvocation mi) { return getAnnotation(mi) != null; diff --git a/core/src/main/java/org/apache/shiro/aop/AnnotationResolver.java b/core/src/main/java/org/apache/shiro/aop/AnnotationResolver.java index 7934884503..2e96dff31e 100644 --- a/core/src/main/java/org/apache/shiro/aop/AnnotationResolver.java +++ b/core/src/main/java/org/apache/shiro/aop/AnnotationResolver.java @@ -1,42 +1,42 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.aop; - -import java.lang.annotation.Annotation; - -/** - * Defines an AOP-framework-independent way of determining if an Annotation exists on a Method. - * - * @since 1.1 - */ -public interface AnnotationResolver { - - /** - * Returns an {@link Annotation} instance of the specified type based on the given - * {@link MethodInvocation MethodInvocation} argument, or {@code null} if no annotation - * of that type could be found. First checks the invoked method itself and if not found, - * then the class for the existence of the same annotation. - * - * @param mi the intercepted method to be invoked. - * @param clazz the annotation class of the annotation to find. - * @return the method's annotation of the specified type or {@code null} if no annotation of - * that type could be found. - */ - Annotation getAnnotation(MethodInvocation mi, Class clazz); -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.aop; + +import java.lang.annotation.Annotation; + +/** + * Defines an AOP-framework-independent way of determining if an Annotation exists on a Method. + * + * @since 1.1 + */ +public interface AnnotationResolver { + + /** + * Returns an {@link Annotation} instance of the specified type based on the given + * {@link MethodInvocation MethodInvocation} argument, or {@code null} if no annotation + * of that type could be found. First checks the invoked method itself and if not found, + * then the class for the existence of the same annotation. + * + * @param mi the intercepted method to be invoked. + * @param clazz the annotation class of the annotation to find. + * @return the method's annotation of the specified type or {@code null} if no annotation of + * that type could be found. + */ + Annotation getAnnotation(MethodInvocation mi, Class clazz); +} diff --git a/core/src/main/java/org/apache/shiro/aop/DefaultAnnotationResolver.java b/core/src/main/java/org/apache/shiro/aop/DefaultAnnotationResolver.java index d7fd4aec7a..c020588ce1 100644 --- a/core/src/main/java/org/apache/shiro/aop/DefaultAnnotationResolver.java +++ b/core/src/main/java/org/apache/shiro/aop/DefaultAnnotationResolver.java @@ -1,69 +1,70 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.aop; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; - -/** - * Default {@code AnnotationResolver} implementation that merely inspects the - * {@link MethodInvocation MethodInvocation}'s {@link MethodInvocation#getMethod() target method}, - * and returns {@code targetMethod}.{@link Method#getAnnotation(Class) getAnnotation(class)}. - *

- * Unfortunately Java's default reflection API for Annotations is not very robust, and this logic - * may not be enough - if the incoming method invocation represents a method from an interface, - * this default logic would not discover the annotation if it existed on the method implementation - * directly (as opposed to being defined directly in the interface definition). - *

- * More complex class hierarchy traversal logic is required to exhaust a method's target object's - * classes, parent classes, interfaces and parent interfaces. That logic will likely be added - * to this implementation in due time, but for now, this implementation relies on the JDK's default - * {@link Method#getAnnotation(Class) Method.getAnnotation(class)} logic. - * - * @since 1.1 - */ -public class DefaultAnnotationResolver implements AnnotationResolver { - - /** - * Returns {@code methodInvocation.}{@link org.apache.shiro.aop.MethodInvocation#getMethod() getMethod()}.{@link Method#getAnnotation(Class) getAnnotation(clazz)}. - * - * @param mi the intercepted method to be invoked. - * @param clazz the annotation class to use to find an annotation instance on the method. - * @return the discovered annotation or {@code null} if an annotation instance could not be - * found. - */ - public Annotation getAnnotation(MethodInvocation mi, Class clazz) { - if (mi == null) { - throw new IllegalArgumentException("method argument cannot be null"); - } - Method m = mi.getMethod(); - if (m == null) { - String msg = MethodInvocation.class.getName() + " parameter incorrectly constructed. getMethod() returned null"; - throw new IllegalArgumentException(msg); - - } - Annotation annotation = m.getAnnotation(clazz); - if (annotation == null ) { - Object miThis = mi.getThis(); - //SHIRO-473 - miThis could be null for static methods, just return null - annotation = miThis != null ? miThis.getClass().getAnnotation(clazz) : null; - } - return annotation; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.aop; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +/** + * Default {@code AnnotationResolver} implementation that merely inspects the + * {@link MethodInvocation MethodInvocation}'s {@link MethodInvocation#getMethod() target method}, + * and returns {@code targetMethod}.{@link Method#getAnnotation(Class) getAnnotation(class)}. + *

+ * Unfortunately Java's default reflection API for Annotations is not very robust, and this logic + * may not be enough - if the incoming method invocation represents a method from an interface, + * this default logic would not discover the annotation if it existed on the method implementation + * directly (as opposed to being defined directly in the interface definition). + *

+ * More complex class hierarchy traversal logic is required to exhaust a method's target object's + * classes, parent classes, interfaces and parent interfaces. That logic will likely be added + * to this implementation in due time, but for now, this implementation relies on the JDK's default + * {@link Method#getAnnotation(Class) Method.getAnnotation(class)} logic. + * + * @since 1.1 + */ +public class DefaultAnnotationResolver implements AnnotationResolver { + + /** + * Returns {@code methodInvocation.}{@link org.apache.shiro.aop.MethodInvocation#getMethod() getMethod()}. + * {@link Method#getAnnotation(Class) getAnnotation(clazz)}. + * + * @param mi the intercepted method to be invoked. + * @param clazz the annotation class to use to find an annotation instance on the method. + * @return the discovered annotation or {@code null} if an annotation instance could not be + * found. + */ + public Annotation getAnnotation(MethodInvocation mi, Class clazz) { + if (mi == null) { + throw new IllegalArgumentException("method argument cannot be null"); + } + Method m = mi.getMethod(); + if (m == null) { + String msg = MethodInvocation.class.getName() + " parameter incorrectly constructed. getMethod() returned null"; + throw new IllegalArgumentException(msg); + + } + Annotation annotation = m.getAnnotation(clazz); + if (annotation == null) { + Object miThis = mi.getThis(); + //SHIRO-473 - miThis could be null for static methods, just return null + annotation = miThis != null ? miThis.getClass().getAnnotation(clazz) : null; + } + return annotation; + } +} diff --git a/core/src/main/java/org/apache/shiro/aop/MethodInterceptorSupport.java b/core/src/main/java/org/apache/shiro/aop/MethodInterceptorSupport.java index e21772ecda..2be6b3f1fb 100644 --- a/core/src/main/java/org/apache/shiro/aop/MethodInterceptorSupport.java +++ b/core/src/main/java/org/apache/shiro/aop/MethodInterceptorSupport.java @@ -40,7 +40,8 @@ public MethodInterceptorSupport() { /** * Returns the {@link Subject Subject} associated with the currently-executing code. *

- * This default implementation merely calls {@link org.apache.shiro.SecurityUtils#getSubject SecurityUtils.getSubject()}. + * This default implementation merely calls + * {@link org.apache.shiro.SecurityUtils#getSubject SecurityUtils.getSubject()}. * * @return the {@link org.apache.shiro.subject.Subject Subject} associated with the currently-executing code. */ diff --git a/core/src/main/java/org/apache/shiro/aop/package-info.java b/core/src/main/java/org/apache/shiro/aop/package-info.java index 0daa7749a0..3e9806cc24 100644 --- a/core/src/main/java/org/apache/shiro/aop/package-info.java +++ b/core/src/main/java/org/apache/shiro/aop/package-info.java @@ -23,4 +23,4 @@ * useful for any AOP environment and/or function. Feature-dependent AOP classes (e.g. authorization, * authentication, etc.) will use these classes as their base in their respective packages. */ -package org.apache.shiro.aop; \ No newline at end of file +package org.apache.shiro.aop; diff --git a/core/src/main/java/org/apache/shiro/authc/AbstractAuthenticator.java b/core/src/main/java/org/apache/shiro/authc/AbstractAuthenticator.java index 125929bf72..278dae9010 100644 --- a/core/src/main/java/org/apache/shiro/authc/AbstractAuthenticator.java +++ b/core/src/main/java/org/apache/shiro/authc/AbstractAuthenticator.java @@ -48,7 +48,7 @@ public abstract class AbstractAuthenticator implements Authenticator, LogoutAwar /** * Private class log instance. */ - private static final Logger log = LoggerFactory.getLogger(AbstractAuthenticator.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAuthenticator.class); /*------------------------------------------- | I N S T A N C E V A R I A B L E S | @@ -95,7 +95,7 @@ public void setAuthenticationListeners(Collection listen * attempts. * * @return the {@link AuthenticationListener AuthenticationListener}s that should be notified during authentication - * attempts. + * attempts. */ @SuppressWarnings({"UnusedDeclaration"}) public Collection getAuthenticationListeners() { @@ -191,14 +191,14 @@ public final AuthenticationInfo authenticate(AuthenticationToken token) throws A throw new IllegalArgumentException("Method argument (authentication token) cannot be null."); } - log.trace("Authentication attempt received for token [{}]", token); + LOGGER.trace("Authentication attempt received for token [{}]", token); AuthenticationInfo info; try { info = doAuthenticate(token); if (info == null) { - String msg = "No account information found for authentication token [" + token + "] by this " + - "Authenticator instance. Please check that it is configured correctly."; + String msg = "No account information found for authentication token [" + token + "] by this " + + "Authenticator instance. Please check that it is configured correctly."; throw new AuthenticationException(msg); } } catch (Throwable t) { @@ -209,20 +209,21 @@ public final AuthenticationInfo authenticate(AuthenticationToken token) throws A if (ae == null) { //Exception thrown was not an expected AuthenticationException. Therefore it is probably a little more //severe or unexpected. So, wrap in an AuthenticationException, log to warn, and propagate: - String msg = "Authentication failed for token submission [" + token + "]. Possible unexpected " + - "error? (Typical or expected login exceptions should extend from AuthenticationException)."; + String msg = "Authentication failed for token submission [" + token + "]. Possible unexpected " + + "error? (Typical or expected login exceptions should extend from AuthenticationException)."; ae = new AuthenticationException(msg, t); - if (log.isWarnEnabled()) - log.warn(msg, t); + if (LOGGER.isWarnEnabled()) { + LOGGER.warn(msg, t); + } } try { notifyFailure(token, ae); } catch (Throwable t2) { - if (log.isWarnEnabled()) { - String msg = "Unable to send notification for failed authentication attempt - listener error?. " + - "Please check your AuthenticationListener implementation(s). Logging sending exception " + - "and propagating original AuthenticationException instead..."; - log.warn(msg, t2); + if (LOGGER.isWarnEnabled()) { + String msg = "Unable to send notification for failed authentication attempt - listener error?. " + + "Please check your AuthenticationListener implementation(s). Logging sending exception " + + "and propagating original AuthenticationException instead..."; + LOGGER.warn(msg, t2); } } @@ -230,7 +231,7 @@ public final AuthenticationInfo authenticate(AuthenticationToken token) throws A throw ae; } - log.debug("Authentication successful for token [{}]. Returned account [{}]", token, info); + LOGGER.debug("Authentication successful for token [{}]. Returned account [{}]", token, info); notifySuccess(token, info); @@ -251,7 +252,7 @@ public final AuthenticationInfo authenticate(AuthenticationToken token) throws A * * @param token the authentication token encapsulating the user's login information. * @return an {@code AuthenticationInfo} object encapsulating the user's account information - * important to Shiro. + * important to Shiro. * @throws AuthenticationException if there is a problem logging in the user. */ protected abstract AuthenticationInfo doAuthenticate(AuthenticationToken token) diff --git a/core/src/main/java/org/apache/shiro/authc/AuthenticationException.java b/core/src/main/java/org/apache/shiro/authc/AuthenticationException.java index 18ee77062c..6f4f497fee 100644 --- a/core/src/main/java/org/apache/shiro/authc/AuthenticationException.java +++ b/core/src/main/java/org/apache/shiro/authc/AuthenticationException.java @@ -26,8 +26,7 @@ * * @since 0.1 */ -public class AuthenticationException extends ShiroException -{ +public class AuthenticationException extends ShiroException { /** * Creates a new AuthenticationException. diff --git a/core/src/main/java/org/apache/shiro/authc/AuthenticationInfo.java b/core/src/main/java/org/apache/shiro/authc/AuthenticationInfo.java index 3c20f0a0f7..5184c1a5f7 100644 --- a/core/src/main/java/org/apache/shiro/authc/AuthenticationInfo.java +++ b/core/src/main/java/org/apache/shiro/authc/AuthenticationInfo.java @@ -46,8 +46,9 @@ * preferences. *

*

Please note: Since Shiro sometimes logs authentication operations, please ensure your AuthenticationInfo's - * toString() implementation does not print out account credentials (password, etc.), as these might be viewable to - * someone reading your logs. This is good practice anyway, and account credentials should rarely (if ever) be printed + * toString() implementation does not print out account credentials (password, etc.), + * as these might be viewable to someone reading your logs. + * This is good practice anyway, and account credentials should rarely (if ever) be printed * out for any reason. If you're using Shiro's default implementations of this interface, they only ever print the * account {@link #getPrincipals() principals}, so you do not need to do anything additional.

* diff --git a/core/src/main/java/org/apache/shiro/authc/Authenticator.java b/core/src/main/java/org/apache/shiro/authc/Authenticator.java index ca4df4236d..0bcd93bf71 100644 --- a/core/src/main/java/org/apache/shiro/authc/Authenticator.java +++ b/core/src/main/java/org/apache/shiro/authc/Authenticator.java @@ -62,6 +62,6 @@ public interface Authenticator { * @see ConcurrentAccessException * @see UnknownAccountException */ - public AuthenticationInfo authenticate(AuthenticationToken authenticationToken) + AuthenticationInfo authenticate(AuthenticationToken authenticationToken) throws AuthenticationException; } diff --git a/core/src/main/java/org/apache/shiro/authc/BearerToken.java b/core/src/main/java/org/apache/shiro/authc/BearerToken.java index 51fe909003..2a193e010e 100644 --- a/core/src/main/java/org/apache/shiro/authc/BearerToken.java +++ b/core/src/main/java/org/apache/shiro/authc/BearerToken.java @@ -20,13 +20,13 @@ /** - * A {@link AuthenticationToken} that contains an a Bearer token or API key, typically received via an HTTP {@code Authorization} header. This - * class also implements the {@link org.apache.shiro.authc.HostAuthenticationToken HostAuthenticationToken} interface to retain the host name - * or IP address location from where the authentication attempt is occurring. + * A {@link AuthenticationToken} that contains a Bearer token or API key, typically received via an HTTP + * {@code Authorization} header. This class also implements the + * {@link org.apache.shiro.authc.HostAuthenticationToken HostAuthenticationToken} interface to + * retain the host name or IP address location from where the authentication attempt is occurring. * * @see RFC 2617 * @see OAuth2 Authorization Request Header Field - * * @since 1.5 */ public class BearerToken implements HostAuthenticationToken { diff --git a/core/src/main/java/org/apache/shiro/authc/HostAuthenticationToken.java b/core/src/main/java/org/apache/shiro/authc/HostAuthenticationToken.java index 475b9f70f5..599b318b41 100644 --- a/core/src/main/java/org/apache/shiro/authc/HostAuthenticationToken.java +++ b/core/src/main/java/org/apache/shiro/authc/HostAuthenticationToken.java @@ -36,9 +36,9 @@ public interface HostAuthenticationToken extends AuthenticationToken { * {@code ServletRequest.getRemoteHost()} value. * * @return the fully qualified name of the client from where the - * authentication attempt originates or the String representation - * of the client's IP address is hostname resolution is not - * available or disabled. + * authentication attempt originates or the String representation + * of the client's IP address is hostname resolution is not + * available or disabled. */ String getHost(); } diff --git a/core/src/main/java/org/apache/shiro/authc/LogoutAware.java b/core/src/main/java/org/apache/shiro/authc/LogoutAware.java index d686bfcd3d..30196fa70f 100644 --- a/core/src/main/java/org/apache/shiro/authc/LogoutAware.java +++ b/core/src/main/java/org/apache/shiro/authc/LogoutAware.java @@ -37,5 +37,5 @@ public interface LogoutAware { * * @param principals the identifying principals of the Subject logging out. */ - public void onLogout(PrincipalCollection principals); + void onLogout(PrincipalCollection principals); } diff --git a/core/src/main/java/org/apache/shiro/authc/RememberMeAuthenticationToken.java b/core/src/main/java/org/apache/shiro/authc/RememberMeAuthenticationToken.java index 88da10dd57..28aaff1cdd 100644 --- a/core/src/main/java/org/apache/shiro/authc/RememberMeAuthenticationToken.java +++ b/core/src/main/java/org/apache/shiro/authc/RememberMeAuthenticationToken.java @@ -36,7 +36,7 @@ public interface RememberMeAuthenticationToken extends AuthenticationToken { * across sessions, {@code false} otherwise. * * @return {@code true} if the submitting user wishes their identity (principal(s)) to be remembered - * across sessions, {@code false} otherwise. + * across sessions, {@code false} otherwise. */ boolean isRememberMe(); diff --git a/core/src/main/java/org/apache/shiro/authc/SaltedAuthenticationInfo.java b/core/src/main/java/org/apache/shiro/authc/SaltedAuthenticationInfo.java index f490385c05..003f022645 100644 --- a/core/src/main/java/org/apache/shiro/authc/SaltedAuthenticationInfo.java +++ b/core/src/main/java/org/apache/shiro/authc/SaltedAuthenticationInfo.java @@ -1,48 +1,47 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.authc; - -import org.apache.shiro.lang.util.ByteSource; - -/** - * Interface representing account information that may use a salt when hashing credentials. This interface - * exists primarily to support environments that hash user credentials (e.g. passwords). - *

- * Salts should typically be generated from a secure pseudo-random number generator so they are effectively - * impossible to guess. The salt value should be safely stored along side the account information to ensure - * it is maintained along with the account's credentials. - *

- * This interface exists as a way for Shiro to acquire that salt so it can correctly perform - * {@link org.apache.shiro.authc.credential.CredentialsMatcher credentials matching} during login attempts. - * See the {@link org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher} JavaDoc for - * more information on hashing credentials with salts. - * - * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher - * - * @since 1.1 - */ -public interface SaltedAuthenticationInfo extends AuthenticationInfo { - - /** - * Returns the salt used to salt the account's credentials or {@code null} if no salt was used. - * - * @return the salt used to salt the account's credentials or {@code null} if no salt was used. - */ - ByteSource getCredentialsSalt(); -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.authc; + +import org.apache.shiro.lang.util.ByteSource; + +/** + * Interface representing account information that may use a salt when hashing credentials. This interface + * exists primarily to support environments that hash user credentials (e.g. passwords). + *

+ * Salts should typically be generated from a secure pseudo-random number generator so they are effectively + * impossible to guess. The salt value should be safely stored along side the account information to ensure + * it is maintained along with the account's credentials. + *

+ * This interface exists as a way for Shiro to acquire that salt so it can correctly perform + * {@link org.apache.shiro.authc.credential.CredentialsMatcher credentials matching} during login attempts. + * See the {@link org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher} JavaDoc for + * more information on hashing credentials with salts. + * + * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher + * @since 1.1 + */ +public interface SaltedAuthenticationInfo extends AuthenticationInfo { + + /** + * Returns the salt used to salt the account's credentials or {@code null} if no salt was used. + * + * @return the salt used to salt the account's credentials or {@code null} if no salt was used. + */ + ByteSource getCredentialsSalt(); +} diff --git a/core/src/main/java/org/apache/shiro/authc/SimpleAccount.java b/core/src/main/java/org/apache/shiro/authc/SimpleAccount.java index e65e07c87f..790668b6bf 100644 --- a/core/src/main/java/org/apache/shiro/authc/SimpleAccount.java +++ b/core/src/main/java/org/apache/shiro/authc/SimpleAccount.java @@ -81,7 +81,8 @@ public SimpleAccount() { * @param realmName the name of the realm that accesses this account data */ public SimpleAccount(Object principal, Object credentials, String realmName) { - this(principal instanceof PrincipalCollection ? (PrincipalCollection) principal : new SimplePrincipalCollection(principal, realmName), credentials); + this(principal instanceof PrincipalCollection + ? (PrincipalCollection) principal : new SimplePrincipalCollection(principal, realmName), credentials); } /** @@ -95,6 +96,7 @@ public SimpleAccount(Object principal, Object credentials, String realmName) { * @see org.apache.shiro.authc.credential.HashedCredentialsMatcher HashedCredentialsMatcher * @since 1.1 */ + @SuppressWarnings("checkstyle:LineLength") public SimpleAccount(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) { this(principal instanceof PrincipalCollection ? (PrincipalCollection) principal : new SimplePrincipalCollection(principal, realmName), hashedCredentials, credentialsSalt); @@ -162,6 +164,7 @@ public SimpleAccount(PrincipalCollection principals, Object credentials, Set roleNames, Set permissions) { this.authcInfo = new SimpleAuthenticationInfo(new SimplePrincipalCollection(principal, realmName), credentials); this.authzInfo = new SimpleAuthorizationInfo(roleNames); @@ -179,6 +182,7 @@ public SimpleAccount(Object principal, Object credentials, String realmName, Set * @param roleNames the names of the roles assigned to this account. * @param permissions the permissions assigned to this account directly (not those assigned to any of the realms). */ + @SuppressWarnings("checkstyle:LineLength") public SimpleAccount(Collection principals, Object credentials, String realmName, Set roleNames, Set permissions) { this.authcInfo = new SimpleAuthenticationInfo(new SimplePrincipalCollection(principals, realmName), credentials); this.authzInfo = new SimpleAuthorizationInfo(roleNames); @@ -253,7 +257,7 @@ public void setCredentials(Object credentials) { * was used or credentials were not hashed at all. * * @return the salt used to hash this Account's credentials (e.g. for password hashing), or {@code null} if no salt - * was used or credentials were not hashed at all. + * was used or credentials were not hashed at all. * @since 1.1 */ public ByteSource getCredentialsSalt() { @@ -467,7 +471,7 @@ public int hashCode() { * * @param o the object to test for equality. * @return true if the specified object is also a {@link SimpleAccount SimpleAccount} and its - * {@link #getPrincipals() principals} are equal to this object's principals, false otherwise. + * {@link #getPrincipals() principals} are equal to this object's principals, false otherwise. */ public boolean equals(Object o) { if (o == this) { @@ -491,4 +495,4 @@ public String toString() { return getPrincipals() != null ? getPrincipals().toString() : "empty"; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/apache/shiro/authc/SimpleAuthenticationInfo.java b/core/src/main/java/org/apache/shiro/authc/SimpleAuthenticationInfo.java index 8ab6ae9b3e..8cae62fbe2 100644 --- a/core/src/main/java/org/apache/shiro/authc/SimpleAuthenticationInfo.java +++ b/core/src/main/java/org/apache/shiro/authc/SimpleAuthenticationInfo.java @@ -165,7 +165,7 @@ public void setCredentials(Object credentials) { * use for salts. Therefore it doesn't make sense to 'merge' salts in a multi-realm scenario. * * @return the salt used to hash the credentials, or {@code null} if no salt was used or credentials were not - * hashed at all. + * hashed at all. * @since 1.1 */ @Override @@ -196,7 +196,7 @@ public void setCredentialsSalt(ByteSource salt) { * @param info the AuthenticationInfo to add into this instance. */ @Override - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "checkstyle:NPathComplexity"}) public void merge(AuthenticationInfo info) { if (info == null || info.getPrincipals() == null || info.getPrincipals().isEmpty()) { return; @@ -254,7 +254,7 @@ public void merge(AuthenticationInfo info) { * * @param o the object to compare for equality. * @return true if the Object argument is an instanceof SimpleAuthenticationInfo and - * its {@link #getPrincipals() principals} are equal to this instance's principals, false otherwise. + * its {@link #getPrincipals() principals} are equal to this instance's principals, false otherwise. */ @Override public boolean equals(Object o) { diff --git a/core/src/main/java/org/apache/shiro/authc/UsernamePasswordToken.java b/core/src/main/java/org/apache/shiro/authc/UsernamePasswordToken.java index 23768f2166..21d76c56ec 100644 --- a/core/src/main/java/org/apache/shiro/authc/UsernamePasswordToken.java +++ b/core/src/main/java/org/apache/shiro/authc/UsernamePasswordToken.java @@ -65,7 +65,7 @@ public class UsernamePasswordToken implements HostAuthenticationToken, RememberM * Whether or not 'rememberMe' should be enabled for the corresponding login attempt; * default is false */ - private boolean rememberMe = false; + private boolean rememberMe; /** * The location from where the login attempt occurs, or null if not known or explicitly @@ -279,7 +279,7 @@ public Object getCredentials() { *

(Shiro's default Authenticator allows null hosts to support localhost and proxy server environments).

* * @return the host from where the authentication attempt occurs, or null if it is unknown or - * explicitly omitted. + * explicitly omitted. * @since 1.0 */ public String getHost() { @@ -305,7 +305,7 @@ public void setHost(String host) { * across sessions, false otherwise. Unless overridden, this value is false by default. * * @return true if the submitting user wishes their identity (principal(s)) to be remembered - * across sessions, false otherwise (false by default). + * across sessions, false otherwise (false by default). * @since 0.9 */ public boolean isRememberMe() { @@ -352,7 +352,7 @@ public void clear() { * that might be widely viewable). * * @return the String representation of the UsernamePasswordToken, omitting - * the password. + * the password. */ public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/core/src/main/java/org/apache/shiro/authc/credential/AllowAllCredentialsMatcher.java b/core/src/main/java/org/apache/shiro/authc/credential/AllowAllCredentialsMatcher.java index f16638fb2b..9984068570 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/AllowAllCredentialsMatcher.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/AllowAllCredentialsMatcher.java @@ -33,8 +33,8 @@ public class AllowAllCredentialsMatcher implements CredentialsMatcher { /** * Returns true always no matter what the method arguments are. * - * @param token the token submitted for authentication. - * @param info the account being verified for access + * @param token the token submitted for authentication. + * @param info the account being verified for access * @return true always. */ public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { diff --git a/core/src/main/java/org/apache/shiro/authc/credential/CredentialsMatcher.java b/core/src/main/java/org/apache/shiro/authc/credential/CredentialsMatcher.java index 58b7514b83..89c9dd93c7 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/CredentialsMatcher.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/CredentialsMatcher.java @@ -21,7 +21,6 @@ import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; - /** * Interface implemented by classes that can determine if an AuthenticationToken's provided * credentials matches a corresponding account's credentials stored in the system. @@ -44,11 +43,11 @@ public interface CredentialsMatcher { * Returns {@code true} if the provided token credentials match the stored account credentials, * {@code false} otherwise. * - * @param token the {@code AuthenticationToken} submitted during the authentication attempt - * @param info the {@code AuthenticationInfo} stored in the system. + * @param token the {@code AuthenticationToken} submitted during the authentication attempt + * @param info the {@code AuthenticationInfo} stored in the system. * @return {@code true} if the provided token credentials match the stored account credentials, - * {@code false} otherwise. + * {@code false} otherwise. */ boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info); -} \ No newline at end of file +} diff --git a/core/src/main/java/org/apache/shiro/authc/credential/DefaultPasswordService.java b/core/src/main/java/org/apache/shiro/authc/credential/DefaultPasswordService.java index 25c0898a59..73fd341362 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/DefaultPasswordService.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/DefaultPasswordService.java @@ -47,15 +47,21 @@ */ public class DefaultPasswordService implements HashingPasswordService { + /** + * default hash algorithm. + */ public static final String DEFAULT_HASH_ALGORITHM = "argon2id"; - private static final Logger log = LoggerFactory.getLogger(DefaultPasswordService.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPasswordService.class); private HashService hashService; private HashFormat hashFormat; private HashFormatFactory hashFormatFactory; - private volatile boolean hashFormatWarned; //used to avoid excessive log noise + /** + * used to avoid excessive log noise + */ + private volatile boolean hashFormatWarned; /** * Constructs a new PasswordService with a default hash service and the default @@ -122,13 +128,13 @@ protected void checkHashFormatDurability() { HashFormat format = this.hashFormat; - if (!(format instanceof ParsableHashFormat) && log.isWarnEnabled()) { - String msg = "The configured hashFormat instance [" + format.getClass().getName() + "] is not a " + - ParsableHashFormat.class.getName() + " implementation. This is " + - "required if you wish to support backwards compatibility for saved password checking (almost " + - "always desirable). Without a " + ParsableHashFormat.class.getSimpleName() + " instance, " + - "any hashService configuration changes will break previously hashed/saved passwords."; - log.warn(msg); + if (!(format instanceof ParsableHashFormat) && LOGGER.isWarnEnabled()) { + String msg = "The configured hashFormat instance [" + format.getClass().getName() + "] is not a " + + ParsableHashFormat.class.getName() + " implementation. This is " + + "required if you wish to support backwards compatibility for saved password checking (almost " + + "always desirable). Without a " + ParsableHashFormat.class.getSimpleName() + " instance, " + + "any hashService configuration changes will break previously hashed/saved passwords."; + LOGGER.warn(msg); this.hashFormatWarned = true; } } diff --git a/core/src/main/java/org/apache/shiro/authc/credential/HashedCredentialsMatcher.java b/core/src/main/java/org/apache/shiro/authc/credential/HashedCredentialsMatcher.java index 4e73810602..0ba10d6464 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/HashedCredentialsMatcher.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/HashedCredentialsMatcher.java @@ -7,7 +7,7 @@ * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,7 +36,7 @@ * before being compared to those in the {@code AuthenticationInfo} from the data store. *

* Credential hashing is one of the most common security techniques when safeguarding a user's private credentials - * (passwords, keys, etc). Most developers never want to store their users' credentials in plain form, viewable by + * (passwords, keys, etc.). Most developers never want to store their users' credentials in plain form, viewable by * anyone, so they often hash the users' credentials before they are saved in the data store. *

* This class (and its subclasses) function as follows: @@ -55,7 +55,7 @@ * "Why add salt?" and 6 "Hardening against the attacker's attack").

*

Real World Case Study

* In April 2010, some public Atlassian Jira and Confluence - * installations (Apache Software Foundation, Codehaus, etc) were the target of account attacks and user accounts + * installations (Apache Software Foundation, Codehaus, etc.) were the target of account attacks and user accounts * were compromised. The reason? Jira and Confluence at the time did not salt user passwords and attackers were * able to use dictionary attacks to compromise user accounts (Atlassian has since * @@ -71,7 +71,7 @@ * {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getSalt(AuthenticationToken)} method. This however * could constitute a security hole since ideally salts should never be obtained based on what a user can submit. * User-submitted salt mechanisms are much more susceptible to dictionary attacks and SHOULD NOT be - * used in secure systems. Instead salts should ideally be a secure randomly-generated number that is generated when + * used in secure systems. Instead, salts should ideally be a secure randomly-generated number that is generated when * the user account is created. The secure number should never be disseminated to the user and always kept private * by the application. *

Shiro 1.1

@@ -136,19 +136,21 @@ public HashedCredentialsMatcher() { this.hashAlgorithm = null; this.hashSalted = false; this.hashIterations = 1; - this.storedCredentialsHexEncoded = true; //false means Base64-encoded + //false means Base64-encoded + this.storedCredentialsHexEncoded = true; } /** * Creates an instance using the specified {@link #getHashAlgorithmName() hashAlgorithmName} to hash submitted * credentials. + * * @param hashAlgorithmName the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} * to use when performing hashes for credentials matching. * @since 1.1 */ public HashedCredentialsMatcher(String hashAlgorithmName) { this(); - if (!StringUtils.hasText(hashAlgorithmName) ) { + if (!StringUtils.hasText(hashAlgorithmName)) { throw new IllegalArgumentException("hashAlgorithmName cannot be null or empty."); } this.hashAlgorithm = hashAlgorithmName; @@ -159,7 +161,7 @@ public HashedCredentialsMatcher(String hashAlgorithmName) { * when performing hashes for credentials matching. * * @return the {@code Hash} {@link org.apache.shiro.crypto.hash.Hash#getAlgorithmName() algorithmName} to use - * when performing hashes for credentials matching. + * when performing hashes for credentials matching. * @since 1.1 */ public String getHashAlgorithmName() { @@ -187,7 +189,7 @@ public void setHashAlgorithmName(String hashAlgorithmName) { * easier. * * @return {@code true} if the system's stored credential hash is Hex encoded, {@code false} if it - * is Base64 encoded. Default is {@code true} + * is Base64 encoded. Default is {@code true} */ public boolean isStoredCredentialsHexEncoded() { return storedCredentialsHexEncoded; @@ -210,6 +212,7 @@ public void setStoredCredentialsHexEncoded(boolean storedCredentialsHexEncoded) this.storedCredentialsHexEncoded = storedCredentialsHexEncoded; } + @SuppressWarnings("checkstyle:LineLength") /** * Returns {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing, * {@code false} if it should not be salted. @@ -219,19 +222,19 @@ public void setStoredCredentialsHexEncoded(boolean storedCredentialsHexEncoded) * The default value is {@code false}. * * @return {@code true} if a submitted {@code AuthenticationToken}'s credentials should be salted when hashing, - * {@code false} if it should not be salted. + * {@code false} if it should not be salted. * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo} - * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its - * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value. - * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return - * {@code SaltedAuthenticationInfo} instances, but it is highly recommended that {@code Realm} implementations - * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} - * instances as soon as possible. - *

- * This is because salts should always be obtained from the stored account information and - * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for - * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user - * are almost impossible to break. This method will be removed in Shiro 2.0. + * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its + * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value. + * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return + * {@code SaltedAuthenticationInfo} instances, but it is highly recommended that {@code Realm} implementations + * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} + * instances as soon as possible. + *

+ * This is because salts should always be obtained from the stored account information and + * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for + * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user + * are almost impossible to break. This method will be removed in Shiro 2.0. */ @Deprecated public boolean isHashSalted() { @@ -239,25 +242,25 @@ public boolean isHashSalted() { } /** - * Sets whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing. + * Sets whether to salt a submitted {@code AuthenticationToken}'s credentials when hashing. *

* If enabled, the salt used will be obtained via the {@link #getSalt(org.apache.shiro.authc.AuthenticationToken) getCredentialsSalt} method. *

* The default value is {@code false}. * - * @param hashSalted whether or not to salt a submitted {@code AuthenticationToken}'s credentials when hashing. + * @param hashSalted whether to salt a submitted {@code AuthenticationToken}'s credentials when hashing. * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo} - * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its - * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value. - * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return - * {@code SaltedAuthenticationInfo} instances, but it is highly recommended that {@code Realm} implementations - * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} - * instances as soon as possible. - *

- * This is because salts should always be obtained from the stored account information and - * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for - * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user - * are almost impossible to break. This method will be removed in Shiro 2.0. + * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its + * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value. + * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return + * {@code SaltedAuthenticationInfo} instances, but it is highly recommended that {@code Realm} implementations + * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} + * instances as soon as possible. + *

+ * This is because salts should always be obtained from the stored account information and + * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for + * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user + * are almost impossible to break. This method will be removed in Shiro 2.0. */ @Deprecated public void setHashSalted(boolean hashSalted) { @@ -271,7 +274,7 @@ public void setHashSalted(boolean hashSalted) { * Unless overridden, the default value is {@code 1}, meaning a normal hash execution will occur. * * @return the number of times a submitted {@code AuthenticationToken}'s credentials will be hashed before - * comparing to the credentials stored in the system. + * comparing to the credentials stored in the system. */ public int getHashIterations() { return hashIterations; @@ -296,26 +299,27 @@ public void setHashIterations(int hashIterations) { } } + @SuppressWarnings("checkstyle:LineLength") /** * Returns a salt value used to hash the token's credentials. *

* This default implementation merely returns {@code token.getPrincipal()}, effectively using the user's - * identity (username, user id, etc) as the salt, a most common technique. If you wish to provide the + * identity (username, user id, etc.) as the salt, a most common technique. If you wish to provide the * authentication token's salt another way, you may override this method. * * @param token the AuthenticationToken submitted during the authentication attempt. * @return a salt value to use to hash the authentication token's credentials. * @deprecated since Shiro 1.1. Hash salting is now expected to be based on if the {@link AuthenticationInfo} - * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its - * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value. - * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return - * {@code SaltedAuthenticationInfo} instances, but it is highly recommended that {@code Realm} implementations - * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} - * instances as soon as possible.

- * This is because salts should always be obtained from the stored account information and - * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for - * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user - * are almost impossible to break. This method will be removed in Shiro 2.0. + * returned from the {@code Realm} is a {@link SaltedAuthenticationInfo} instance and its + * {@link org.apache.shiro.authc.SaltedAuthenticationInfo#getCredentialsSalt() getCredentialsSalt()} method returns a non-null value. + * This method and the 1.0 behavior still exists for backwards compatibility if the {@code Realm} does not return + * {@code SaltedAuthenticationInfo} instances, but it is highly recommended that {@code Realm} implementations + * that support hashed credentials start returning {@link SaltedAuthenticationInfo SaltedAuthenticationInfo} + * instances as soon as possible.

+ * This is because salts should always be obtained from the stored account information and + * never be interpreted based on user/Subject-entered data. User-entered data is easier to compromise for + * attackers, whereas account-unique (and secure randomly-generated) salts never disseminated to the end-user + * are almost impossible to break. This method will be removed in Shiro 2.0. */ @Deprecated protected Object getSalt(AuthenticationToken token) { @@ -372,7 +376,7 @@ protected Object getCredentials(AuthenticationInfo info) { * @param token the {@code AuthenticationToken} submitted during the authentication attempt. * @param info the {@code AuthenticationInfo} stored in the system matching the token principal * @return {@code true} if the provided token credentials hash match to the stored account credentials hash, - * {@code false} otherwise + * {@code false} otherwise * @since 1.1 */ @Override @@ -424,8 +428,8 @@ protected Object hashProvidedCredentials(AuthenticationToken token, Authenticati private String assertHashAlgorithmName() throws IllegalStateException { String hashAlgorithmName = getHashAlgorithmName(); if (hashAlgorithmName == null) { - String msg = "Required 'hashAlgorithmName' property has not been set. This is required to execute " + - "the hashing algorithm."; + String msg = "Required 'hashAlgorithmName' property has not been set. This is required to execute " + + "the hashing algorithm."; throw new IllegalStateException(msg); } return hashAlgorithmName; @@ -447,6 +451,7 @@ protected Hash hashProvidedCredentials(Object credentials, Object salt, int hash return new SimpleHash(hashAlgorithmName, credentials, requireNonNull(salt, "salt cannot be null."), hashIterations); } + @SuppressWarnings("checkstyle:LineLength") /** * Returns a new, uninitialized instance, without its byte array set. Used as a utility method in the * {@link SimpleCredentialsMatcher#getCredentials(org.apache.shiro.authc.AuthenticationInfo) getCredentials(AuthenticationInfo)} implementation. diff --git a/core/src/main/java/org/apache/shiro/authc/credential/HashingPasswordService.java b/core/src/main/java/org/apache/shiro/authc/credential/HashingPasswordService.java index bdde4f90dc..f333aee8b8 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/HashingPasswordService.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/HashingPasswordService.java @@ -79,13 +79,13 @@ public interface HashingPasswordService extends PasswordService { * as they can be cleared/nulled-out after use. Any argument type supported by * {@link ByteSource.Util#isCompatible(Object)} is valid. * - * @param plaintext a raw/plaintext password submitted by an end user/Subject. - * @param savedPasswordHash the previously hashed password known to be associated with an account. - * This value is expected to have been previously generated from the - * {@link #hashPassword(Object) hashPassword} method (typically - * when the account is created or the account's password is reset). + * @param plaintext a raw/plaintext password submitted by an end user/Subject. + * @param savedPasswordHash the previously hashed password known to be associated with an account. + * This value is expected to have been previously generated from the + * {@link #hashPassword(Object) hashPassword} method (typically + * when the account is created or the account's password is reset). * @return {@code true} if the {@code plaintext} password matches the existing {@code savedPasswordHash}, - * {@code false} otherwise. + * {@code false} otherwise. */ boolean passwordsMatch(Object plaintext, Hash savedPasswordHash); } diff --git a/core/src/main/java/org/apache/shiro/authc/credential/PasswordMatcher.java b/core/src/main/java/org/apache/shiro/authc/credential/PasswordMatcher.java index dd60a850b4..240fc0eac8 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/PasswordMatcher.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/PasswordMatcher.java @@ -50,11 +50,11 @@ public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo assertStoredCredentialsType(storedCredentials); if (storedCredentials instanceof Hash) { - Hash hashedPassword = (Hash)storedCredentials; + Hash hashedPassword = (Hash) storedCredentials; return hashedPassword.matchesPassword(ByteSource.Util.bytes(submittedPassword)); } //otherwise they are a String (asserted in the 'assertStoredCredentialsType' method call above): - String formatted = (String)storedCredentials; + String formatted = (String) storedCredentials; return service.passwordsMatch(submittedPassword, formatted); } @@ -76,8 +76,8 @@ private void assertStoredCredentialsType(Object credentials) { return; } - String msg = "Stored account credentials are expected to be either a " + - Hash.class.getName() + " instance or a formatted hash String."; + String msg = "Stored account credentials are expected to be either a " + + Hash.class.getName() + " instance or a formatted hash String."; throw new IllegalArgumentException(msg); } @@ -85,7 +85,7 @@ protected Object getStoredPassword(AuthenticationInfo storedAccountInfo) { Object stored = storedAccountInfo != null ? storedAccountInfo.getCredentials() : null; //fix for https://issues.apache.org/jira/browse/SHIRO-363 if (stored instanceof char[]) { - stored = new String((char[])stored); + stored = new String((char[]) stored); } return stored; } diff --git a/core/src/main/java/org/apache/shiro/authc/credential/PasswordService.java b/core/src/main/java/org/apache/shiro/authc/credential/PasswordService.java index 60cc9b79ec..6cc125a4d1 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/PasswordService.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/PasswordService.java @@ -140,7 +140,7 @@ public interface PasswordService { * {@link #encryptPassword(Object) encryptPassword} method (typically * when the account is created or the account's password is reset). * @return {@code true} if the {@code submittedPlaintext} password matches the existing {@code saved} password, - * {@code false} otherwise. + * {@code false} otherwise. * @see ByteSource.Util#isCompatible(Object) */ boolean passwordsMatch(Object submittedPlaintext, String encrypted); diff --git a/core/src/main/java/org/apache/shiro/authc/credential/Sha256CredentialsMatcher.java b/core/src/main/java/org/apache/shiro/authc/credential/Sha256CredentialsMatcher.java index be84edc955..76bd73765a 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/Sha256CredentialsMatcher.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/Sha256CredentialsMatcher.java @@ -18,8 +18,6 @@ */ package org.apache.shiro.authc.credential; -import org.apache.shiro.crypto.hash.AbstractHash; -import org.apache.shiro.crypto.hash.Hash; import org.apache.shiro.crypto.hash.Sha256Hash; @@ -29,7 +27,7 @@ * * @since 0.9 * @deprecated since 1.1 - use the HashedCredentialsMatcher directly and set its - * {@link HashedCredentialsMatcher#setHashAlgorithmName(String) hashAlgorithmName} property. + * {@link HashedCredentialsMatcher#setHashAlgorithmName(String) hashAlgorithmName} property. */ public class Sha256CredentialsMatcher extends HashedCredentialsMatcher { diff --git a/core/src/main/java/org/apache/shiro/authc/credential/Sha384CredentialsMatcher.java b/core/src/main/java/org/apache/shiro/authc/credential/Sha384CredentialsMatcher.java index ec4757853a..8b736a16eb 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/Sha384CredentialsMatcher.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/Sha384CredentialsMatcher.java @@ -18,8 +18,6 @@ */ package org.apache.shiro.authc.credential; -import org.apache.shiro.crypto.hash.AbstractHash; -import org.apache.shiro.crypto.hash.Hash; import org.apache.shiro.crypto.hash.Sha384Hash; @@ -29,7 +27,7 @@ * * @since 0.9 * @deprecated since 1.1 - use the HashedCredentialsMatcher directly and set its - * {@link HashedCredentialsMatcher#setHashAlgorithmName(String) hashAlgorithmName} property. + * {@link HashedCredentialsMatcher#setHashAlgorithmName(String) hashAlgorithmName} property. */ public class Sha384CredentialsMatcher extends HashedCredentialsMatcher { diff --git a/core/src/main/java/org/apache/shiro/authc/credential/Sha512CredentialsMatcher.java b/core/src/main/java/org/apache/shiro/authc/credential/Sha512CredentialsMatcher.java index 5989b23d24..797317ef70 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/Sha512CredentialsMatcher.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/Sha512CredentialsMatcher.java @@ -18,8 +18,6 @@ */ package org.apache.shiro.authc.credential; -import org.apache.shiro.crypto.hash.AbstractHash; -import org.apache.shiro.crypto.hash.Hash; import org.apache.shiro.crypto.hash.Sha512Hash; @@ -29,7 +27,7 @@ * * @since 0.9 * @deprecated since 1.1 - use the HashedCredentialsMatcher directly and set its - * {@link HashedCredentialsMatcher#setHashAlgorithmName(String) hashAlgorithmName} property. + * {@link HashedCredentialsMatcher#setHashAlgorithmName(String) hashAlgorithmName} property. */ public class Sha512CredentialsMatcher extends HashedCredentialsMatcher { diff --git a/core/src/main/java/org/apache/shiro/authc/credential/SimpleCredentialsMatcher.java b/core/src/main/java/org/apache/shiro/authc/credential/SimpleCredentialsMatcher.java index c8132c990d..b3a09ab5bf 100644 --- a/core/src/main/java/org/apache/shiro/authc/credential/SimpleCredentialsMatcher.java +++ b/core/src/main/java/org/apache/shiro/authc/credential/SimpleCredentialsMatcher.java @@ -41,7 +41,7 @@ */ public class SimpleCredentialsMatcher extends CodecSupport implements CredentialsMatcher { - private static final Logger log = LoggerFactory.getLogger(SimpleCredentialsMatcher.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleCredentialsMatcher.class); /** * Returns the {@code token}'s credentials. @@ -92,15 +92,15 @@ protected Object getCredentials(AuthenticationInfo info) { * @return {@code true} if the {@code tokenCredentials} are equal to the {@code accountCredentials}. */ protected boolean equals(Object tokenCredentials, Object accountCredentials) { - if (log.isDebugEnabled()) { - log.debug("Performing credentials equality check for tokenCredentials of type [" + - tokenCredentials.getClass().getName() + " and accountCredentials of type [" + - accountCredentials.getClass().getName() + "]"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Performing credentials equality check for tokenCredentials of type [" + + tokenCredentials.getClass().getName() + " and accountCredentials of type [" + + accountCredentials.getClass().getName() + "]"); } if (isByteSource(tokenCredentials) && isByteSource(accountCredentials)) { - if (log.isDebugEnabled()) { - log.debug("Both credentials arguments can be easily converted to byte arrays. Performing " + - "array equals comparison"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Both credentials arguments can be easily converted to byte arrays. Performing " + + "array equals comparison"); } byte[] tokenBytes = toBytes(tokenCredentials); byte[] accountBytes = toBytes(accountCredentials); @@ -115,13 +115,13 @@ protected boolean equals(Object tokenCredentials, Object accountCredentials) { * (via {@link #getCredentials(AuthenticationToken) getCredentials(token)}) * and then the {@code account}'s credentials * (via {@link #getCredentials(org.apache.shiro.authc.AuthenticationInfo) getCredentials(account)}) and then passes both of - * them to the {@link #equals(Object,Object) equals(tokenCredentials, accountCredentials)} method for equality + * them to the {@link #equals(Object, Object) equals(tokenCredentials, accountCredentials)} method for equality * comparison. * * @param token the {@code AuthenticationToken} submitted during the authentication attempt. * @param info the {@code AuthenticationInfo} stored in the system matching the token principal. * @return {@code true} if the provided token credentials are equal to the stored account credentials, - * {@code false} otherwise + * {@code false} otherwise */ public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { Object tokenCredentials = getCredentials(token); diff --git a/core/src/main/java/org/apache/shiro/authc/pam/AbstractAuthenticationStrategy.java b/core/src/main/java/org/apache/shiro/authc/pam/AbstractAuthenticationStrategy.java index 55b7a20522..28ebc64985 100644 --- a/core/src/main/java/org/apache/shiro/authc/pam/AbstractAuthenticationStrategy.java +++ b/core/src/main/java/org/apache/shiro/authc/pam/AbstractAuthenticationStrategy.java @@ -18,7 +18,11 @@ */ package org.apache.shiro.authc.pam; -import org.apache.shiro.authc.*; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.MergableAuthenticationInfo; +import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.realm.Realm; import java.util.Collection; @@ -32,6 +36,7 @@ */ public abstract class AbstractAuthenticationStrategy implements AuthenticationStrategy { + @SuppressWarnings("checkstyle:LineLength") /** * Simply returns new {@link org.apache.shiro.authc.SimpleAuthenticationInfo SimpleAuthenticationInfo}();, which supports * aggregating account data across realms. @@ -40,6 +45,7 @@ public AuthenticationInfo beforeAllAttempts(Collection realms, return new SimpleAuthenticationInfo(); } + @SuppressWarnings("checkstyle:LineLength") /** * Simply returns the aggregate method argument, without modification. */ @@ -51,7 +57,9 @@ public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, * Base implementation that will aggregate the specified singleRealmInfo into the * aggregateInfo and then returns the aggregate. Can be overridden by subclasses for custom behavior. */ - public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException { + public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, + AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, + Throwable t) throws AuthenticationException { AuthenticationInfo info; if (singleRealmInfo == null) { info = aggregateInfo; @@ -78,15 +86,16 @@ public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, A * {@link org.apache.shiro.authc.MergableAuthenticationInfo MergableAuthenticationInfo} is not desired for some reason. */ protected AuthenticationInfo merge(AuthenticationInfo info, AuthenticationInfo aggregate) { - if( aggregate instanceof MergableAuthenticationInfo ) { - ((MergableAuthenticationInfo)aggregate).merge(info); + if (aggregate instanceof MergableAuthenticationInfo) { + ((MergableAuthenticationInfo) aggregate).merge(info); return aggregate; } else { - throw new IllegalArgumentException( "Attempt to merge authentication info from multiple realms, but aggregate " + - "AuthenticationInfo is not of type MergableAuthenticationInfo." ); + throw new IllegalArgumentException("Attempt to merge authentication info from multiple realms, but aggregate " + + "AuthenticationInfo is not of type MergableAuthenticationInfo."); } } + @SuppressWarnings("checkstyle:LineLength") /** * Simply returns the aggregate argument without modification. Can be overridden for custom behavior. */ diff --git a/core/src/main/java/org/apache/shiro/authc/pam/AllSuccessfulStrategy.java b/core/src/main/java/org/apache/shiro/authc/pam/AllSuccessfulStrategy.java index a6dbbfe8d3..05b7fb9ba9 100644 --- a/core/src/main/java/org/apache/shiro/authc/pam/AllSuccessfulStrategy.java +++ b/core/src/main/java/org/apache/shiro/authc/pam/AllSuccessfulStrategy.java @@ -40,9 +40,12 @@ */ public class AllSuccessfulStrategy extends AbstractAuthenticationStrategy { - /** Private class log instance. */ - private static final Logger log = LoggerFactory.getLogger(AllSuccessfulStrategy.class); + /** + * Private class log instance. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(AllSuccessfulStrategy.class); + @SuppressWarnings("checkstyle:LineLength") /** * Because all realms in this strategy must complete successfully, this implementation ensures that the given * Realm {@link org.apache.shiro.realm.Realm#supports(org.apache.shiro.authc.AuthenticationToken) supports} the given @@ -50,12 +53,13 @@ public class AllSuccessfulStrategy extends AbstractAuthenticationStrategy { * {@link UnsupportedTokenException UnsupportedTokenException} to end the authentication * process immediately. If the realm does support the token, the info argument is returned immediately. */ - public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException { + public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, + AuthenticationInfo info) throws AuthenticationException { if (!realm.supports(token)) { - String msg = "Realm [" + realm + "] of type [" + realm.getClass().getName() + "] does not support " + - " the submitted AuthenticationToken [" + token + "]. The [" + getClass().getName() + - "] implementation requires all configured realm(s) to support and be able to process the submitted " + - "AuthenticationToken."; + String msg = "Realm [" + realm + "] of type [" + realm.getClass().getName() + "] does not support " + + " the submitted AuthenticationToken [" + token + "]. The [" + getClass().getName() + + "] implementation requires all configured realm(s) to support and be able to process the submitted " + + "AuthenticationToken."; throw new UnsupportedTokenException(msg); } @@ -72,6 +76,7 @@ public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, * realm did in fact authenticate successfully * */ + @SuppressWarnings("checkstyle:LineLength") public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo info, AuthenticationInfo aggregate, Throwable t) throws AuthenticationException { if (t != null) { @@ -79,21 +84,21 @@ public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, A //propagate: throw ((AuthenticationException) t); } else { - String msg = "Unable to acquire account data from realm [" + realm + "]. The [" + - getClass().getName() + " implementation requires all configured realm(s) to operate successfully " + - "for a successful authentication."; + String msg = "Unable to acquire account data from realm [" + realm + "]. The [" + + getClass().getName() + " implementation requires all configured realm(s) to operate successfully " + + "for a successful authentication."; throw new AuthenticationException(msg, t); } } if (info == null) { - String msg = "Realm [" + realm + "] could not find any associated account data for the submitted " + - "AuthenticationToken [" + token + "]. The [" + getClass().getName() + "] implementation requires " + - "all configured realm(s) to acquire valid account data for a submitted token during the " + - "log-in process."; + String msg = "Realm [" + realm + "] could not find any associated account data for the submitted " + + "AuthenticationToken [" + token + "]. The [" + getClass().getName() + "] implementation requires " + + "all configured realm(s) to acquire valid account data for a submitted token during the " + + "log-in process."; throw new UnknownAccountException(msg); } - log.debug("Account successfully authenticated using realm [{}]", realm); + LOGGER.debug("Account successfully authenticated using realm [{}]", realm); // If non-null account is returned, then the realm was able to authenticate the // user - so merge the account with any accumulated before: diff --git a/core/src/main/java/org/apache/shiro/authc/pam/AtLeastOneSuccessfulStrategy.java b/core/src/main/java/org/apache/shiro/authc/pam/AtLeastOneSuccessfulStrategy.java index aa201d73d2..1278ea7310 100644 --- a/core/src/main/java/org/apache/shiro/authc/pam/AtLeastOneSuccessfulStrategy.java +++ b/core/src/main/java/org/apache/shiro/authc/pam/AtLeastOneSuccessfulStrategy.java @@ -51,13 +51,14 @@ private static boolean isEmpty(PrincipalCollection pc) { * is not null, and if either is null, throws an AuthenticationException to indicate * that none of the realms authenticated successfully. */ + @SuppressWarnings("checkstyle:LineLength") public AuthenticationInfo afterAllAttempts(AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException { //we know if one or more were able to successfully authenticate if the aggregated account object does not //contain null or empty data: if (aggregate == null || isEmpty(aggregate.getPrincipals())) { - throw new AuthenticationException("Authentication token of type [" + token.getClass() + "] " + - "could not be authenticated by any configured realms. Please ensure that at least one realm can " + - "authenticate these tokens."); + throw new AuthenticationException("Authentication token of type [" + token.getClass() + "] " + + "could not be authenticated by any configured realms. Please ensure that at least one realm can " + + "authenticate these tokens."); } return aggregate; diff --git a/core/src/main/java/org/apache/shiro/authc/pam/AuthenticationStrategy.java b/core/src/main/java/org/apache/shiro/authc/pam/AuthenticationStrategy.java index e816e74a0e..622bfb8f64 100644 --- a/core/src/main/java/org/apache/shiro/authc/pam/AuthenticationStrategy.java +++ b/core/src/main/java/org/apache/shiro/authc/pam/AuthenticationStrategy.java @@ -56,8 +56,10 @@ public interface AuthenticationStrategy { * @return an empty AuthenticationInfo object that will populated with data from multiple realms. * @throws AuthenticationException if the strategy implementation does not wish the Authentication attempt to execute. */ + @SuppressWarnings("checkstyle:LineLength") AuthenticationInfo beforeAllAttempts(Collection realms, AuthenticationToken token) throws AuthenticationException; + @SuppressWarnings("checkstyle:LineLength") /** * Method invoked by the ModularAuthenticator just prior to the realm being consulted for account data, * allowing pre-authentication-attempt logic for that realm only. @@ -70,13 +72,13 @@ public interface AuthenticationStrategy { * @param token the {@code AuthenticationToken} submitted for the subject attempting system log-in. * @param aggregate the aggregated AuthenticationInfo object being used across the multi-realm authentication attempt * @return the AuthenticationInfo object that will be presented to further realms in the authentication process - returning - * the {@code aggregate} method argument is the normal case if no special action needs to be taken. - * @throws org.apache.shiro.authc.AuthenticationException - * an exception thrown by the Strategy implementation if it wishes the login - * process for the associated subject (user) to stop immediately. + * the {@code aggregate} method argument is the normal case if no special action needs to be taken. + * @throws org.apache.shiro.authc.AuthenticationException an exception thrown by the Strategy implementation if it wishes the login + * process for the associated subject (user) to stop immediately. */ AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException; + @SuppressWarnings("checkstyle:LineLength") /** * Method invoked by the ModularAuthenticator just after the given realm has been consulted for authentication, * allowing post-authentication-attempt logic for that realm only. @@ -91,13 +93,16 @@ public interface AuthenticationStrategy { * @param aggregateInfo the aggregate info representing all realms in a multi-realm environment. * @param t the Throwable thrown by the Realm during the attempt, or {@code null} if the method returned normally. * @return the AuthenticationInfo object that will be presented to further realms in the authentication process - returning - * the {@code aggregateAccount} method argument is the normal case if no special action needs to be taken. + * the {@code aggregateAccount} method argument is the normal case if no special action needs to be taken. * @throws AuthenticationException an exception thrown by the Strategy implementation if it wishes the login process * for the associated subject (user) to stop immediately. */ - AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) + AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, + AuthenticationInfo singleRealmInfo, + AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException; + @SuppressWarnings("checkstyle:LineLength") /** * Method invoked by the ModularAuthenticator signifying that all of its configured Realms have been consulted * for account data, allowing post-processing after all realms have completed. diff --git a/core/src/main/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategy.java b/core/src/main/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategy.java index 3d332f1cee..98907aa9ba 100644 --- a/core/src/main/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategy.java +++ b/core/src/main/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategy.java @@ -39,27 +39,28 @@ public class FirstSuccessfulStrategy extends AbstractAuthenticationStrategy { private boolean stopAfterFirstSuccess; - public void setStopAfterFirstSuccess (boolean stopAfterFirstSuccess ) { - - this.stopAfterFirstSuccess = stopAfterFirstSuccess ; + public void setStopAfterFirstSuccess(boolean stopAfterFirstSuccess) { + this.stopAfterFirstSuccess = stopAfterFirstSuccess; } public boolean getStopAfterFirstSuccess() { - return stopAfterFirstSuccess ; + return stopAfterFirstSuccess; } /** * Returns {@code null} immediately, relying on this class's {@link #merge merge} implementation to return * only the first {@code info} object it encounters, ignoring all subsequent ones. */ + @SuppressWarnings("checkstyle:LineLength") public AuthenticationInfo beforeAllAttempts(Collection realms, AuthenticationToken token) throws AuthenticationException { return null; } + @SuppressWarnings("checkstyle:LineLength") /** - * Throws ShortCircuitIterationException if stopAfterFirstSuccess is set and authentication is - * successful with a previously consulted realm. + * Throws ShortCircuitIterationException if stopAfterFirstSuccess is set and authentication is + * successful with a previously consulted realm. * Returns the aggregate method argument, without modification * otherwise. */ @@ -70,8 +71,6 @@ public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, return aggregate; } - - private static boolean isEmpty(PrincipalCollection pc) { return pc == null || pc.isEmpty(); } diff --git a/core/src/main/java/org/apache/shiro/authc/pam/ModularRealmAuthenticator.java b/core/src/main/java/org/apache/shiro/authc/pam/ModularRealmAuthenticator.java index 6e8cbc058f..c59ea6d2c7 100644 --- a/core/src/main/java/org/apache/shiro/authc/pam/ModularRealmAuthenticator.java +++ b/core/src/main/java/org/apache/shiro/authc/pam/ModularRealmAuthenticator.java @@ -18,7 +18,12 @@ */ package org.apache.shiro.authc.pam; -import org.apache.shiro.authc.*; +import org.apache.shiro.authc.AbstractAuthenticator; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.LogoutAware; +import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.realm.Realm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.CollectionUtils; @@ -63,10 +68,7 @@ */ public class ModularRealmAuthenticator extends AbstractAuthenticator { - /*-------------------------------------------- - | C O N S T A N T S | - ============================================*/ - private static final Logger log = LoggerFactory.getLogger(ModularRealmAuthenticator.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ModularRealmAuthenticator.class); /*-------------------------------------------- | I N S T A N C E V A R I A B L E S | @@ -156,8 +158,8 @@ public void setAuthenticationStrategy(AuthenticationStrategy authenticationStrat protected void assertRealmsConfigured() throws IllegalStateException { Collection realms = getRealms(); if (CollectionUtils.isEmpty(realms)) { - String msg = "Configuration error: No realms have been configured! One or more realms must be " + - "present to execute an authentication attempt."; + String msg = "Configuration error: No realms have been configured! One or more realms must be " + + "present to execute an authentication attempt."; throw new IllegalStateException(msg); } } @@ -172,15 +174,15 @@ protected void assertRealmsConfigured() throws IllegalStateException { */ protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) { if (!realm.supports(token)) { - String msg = "Realm [" + realm + "] does not support authentication token [" + - token + "]. Please ensure that the appropriate Realm implementation is " + - "configured correctly or that the realm accepts AuthenticationTokens of this type."; + String msg = "Realm [" + realm + "] does not support authentication token [" + + token + "]. Please ensure that the appropriate Realm implementation is " + + "configured correctly or that the realm accepts AuthenticationTokens of this type."; throw new UnsupportedTokenException(msg); } AuthenticationInfo info = realm.getAuthenticationInfo(token); if (info == null) { - String msg = "Realm [" + realm + "] was unable to find account data for the " + - "submitted AuthenticationToken [" + token + "]."; + String msg = "Realm [" + realm + "] was unable to find account data for the " + + "submitted AuthenticationToken [" + token + "]."; throw new UnknownAccountException(msg); } return info; @@ -193,7 +195,7 @@ protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, Authentica * @param realms the multiple realms configured on this Authenticator instance. * @param token the submitted AuthenticationToken representing the subject's (user's) log-in principals and credentials. * @return an aggregated AuthenticationInfo instance representing account data across all the successfully - * consulted realms. + * consulted realms. */ protected AuthenticationInfo doMultiRealmAuthentication(Collection realms, AuthenticationToken token) { @@ -201,8 +203,8 @@ protected AuthenticationInfo doMultiRealmAuthentication(Collection realms AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token); - if (log.isTraceEnabled()) { - log.trace("Iterating through {} realms for PAM authentication", realms.size()); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Iterating through {} realms for PAM authentication", realms.size()); } for (Realm realm : realms) { @@ -210,14 +212,14 @@ protected AuthenticationInfo doMultiRealmAuthentication(Collection realms try { aggregate = strategy.beforeAttempt(realm, token, aggregate); } catch (ShortCircuitIterationException shortCircuitSignal) { - // Break from continuing with subsequent realms on receiving + // Break from continuing with subsequent realms on receiving // short circuit signal from strategy break; } if (realm.supports(token)) { - log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm); + LOGGER.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm); AuthenticationInfo info = null; Throwable t = null; @@ -225,16 +227,16 @@ protected AuthenticationInfo doMultiRealmAuthentication(Collection realms info = realm.getAuthenticationInfo(token); } catch (Throwable throwable) { t = throwable; - if (log.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:"; - log.debug(msg, t); + LOGGER.debug(msg, t); } } aggregate = strategy.afterAttempt(realm, token, info, aggregate, t); } else { - log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token); + LOGGER.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token); } } diff --git a/core/src/main/java/org/apache/shiro/authc/pam/ShortCircuitIterationException.java b/core/src/main/java/org/apache/shiro/authc/pam/ShortCircuitIterationException.java index 0522b24bdf..4f9d6fa948 100644 --- a/core/src/main/java/org/apache/shiro/authc/pam/ShortCircuitIterationException.java +++ b/core/src/main/java/org/apache/shiro/authc/pam/ShortCircuitIterationException.java @@ -20,13 +20,12 @@ import org.apache.shiro.authc.AuthenticationException; - /** * Exception thrown during the authentication process using - * {@link org.apache.shiro.authc.pam.FirstSuccessfulStrategy}, with - * stopAfterFirstSuccess set. - * This is a signal to short circuit the authentication from proceeding - * with subsequent {@link org.apache.shiro.realm.Realm Realm}s + * {@link org.apache.shiro.authc.pam.FirstSuccessfulStrategy}, with + * stopAfterFirstSuccess set. + * This is a signal to short circuit the authentication from proceeding + * with subsequent {@link org.apache.shiro.realm.Realm Realm}s * after a first successful authentication. * * @see org.apache.shiro.authc.pam.AuthenticationStrategy diff --git a/core/src/main/java/org/apache/shiro/authc/pam/package-info.java b/core/src/main/java/org/apache/shiro/authc/pam/package-info.java index e6b3dc4a16..a8abac1aea 100644 --- a/core/src/main/java/org/apache/shiro/authc/pam/package-info.java +++ b/core/src/main/java/org/apache/shiro/authc/pam/package-info.java @@ -16,17 +16,19 @@ * specific language governing permissions and limitations * under the License. */ + /** * Support for PAM, or Pluggable Authentication Modules, which is * the capability to authenticate a user against multiple configurable (pluggable) modules (Shiro * calls these {@link org.apache.shiro.realm.Realm Realm}s). *

- * The primary class of interest here is the {@link org.apache.shiro.authc.pam.ModularRealmAuthenticator ModularRealmAuthenticator} - * which is an Authenticator implementation that coordinates authentication attempts across + * The primary class of interest here is the + * {@link org.apache.shiro.authc.pam.ModularRealmAuthenticator ModularRealmAuthenticator} + * * which is an Authenticator implementation that coordinates authentication attempts across * one or more Realm instances. *

* How the ModularRealmAuthenticator actually coordinates this behavior is configurable based on your * application's needs using an injectable - * {@link AuthenticationStrategy}. + * {@link org.apache.shiro.authc.pam.AuthenticationStrategy}. */ package org.apache.shiro.authc.pam; diff --git a/core/src/main/java/org/apache/shiro/authz/AuthorizationException.java b/core/src/main/java/org/apache/shiro/authz/AuthorizationException.java index 317decf93c..3887fb30b3 100644 --- a/core/src/main/java/org/apache/shiro/authz/AuthorizationException.java +++ b/core/src/main/java/org/apache/shiro/authz/AuthorizationException.java @@ -26,8 +26,7 @@ * * @since 0.1 */ -public class AuthorizationException extends ShiroException -{ +public class AuthorizationException extends ShiroException { /** * Creates a new AuthorizationException. diff --git a/core/src/main/java/org/apache/shiro/authz/Authorizer.java b/core/src/main/java/org/apache/shiro/authz/Authorizer.java index 9316af113e..7f77ba6769 100644 --- a/core/src/main/java/org/apache/shiro/authz/Authorizer.java +++ b/core/src/main/java/org/apache/shiro/authz/Authorizer.java @@ -57,7 +57,7 @@ public interface Authorizer { * @param principals the application-specific subject/user identifier. * @param permission the String representation of a Permission that is being checked. * @return true if the corresponding Subject/user is permitted, false otherwise. - * @see #isPermitted(PrincipalCollection principals,Permission permission) + * @see #isPermitted(PrincipalCollection principals, Permission permission) * @since 0.9 */ boolean isPermitted(PrincipalCollection principals, String permission); @@ -85,9 +85,9 @@ public interface Authorizer { * @param subjectPrincipal the application-specific subject/user identifier. * @param permissions the String representations of the Permissions that are being checked. * @return an array of booleans whose indices correspond to the index of the - * permissions in the given list. A true value at an index indicates the user is permitted for - * for the associated Permission string in the list. A false value at an index - * indicates otherwise. + * permissions in the given list. A true value at an index indicates the user is permitted for + * for the associated Permission string in the list. A false value at an index + * indicates otherwise. * @since 0.9 */ boolean[] isPermitted(PrincipalCollection subjectPrincipal, String... permissions); @@ -106,9 +106,9 @@ public interface Authorizer { * @param subjectPrincipal the application-specific subject/user identifier. * @param permissions the permissions that are being checked. * @return an array of booleans whose indices correspond to the index of the - * permissions in the given list. A true value at an index indicates the user is permitted for - * for the associated Permission object in the list. A false value at an index - * indicates otherwise. + * permissions in the given list. A true value at an index indicates the user is permitted for + * for the associated Permission object in the list. A false value at an index + * indicates otherwise. */ boolean[] isPermitted(PrincipalCollection subjectPrincipal, List permissions); @@ -122,7 +122,7 @@ public interface Authorizer { * @param subjectPrincipal the application-specific subject/user identifier. * @param permissions the String representations of the Permissions that are being checked. * @return true if the user has all of the specified permissions, false otherwise. - * @see #isPermittedAll(PrincipalCollection,Collection) + * @see #isPermittedAll(PrincipalCollection, Collection) * @since 0.9 */ boolean isPermittedAll(PrincipalCollection subjectPrincipal, String... permissions); @@ -151,8 +151,7 @@ public interface Authorizer { * * @param subjectPrincipal the application-specific subject/user identifier. * @param permission the String representation of the Permission to check. - * @throws AuthorizationException - * if the user does not have the permission. + * @throws AuthorizationException if the user does not have the permission. * @since 0.9 */ void checkPermission(PrincipalCollection subjectPrincipal, String permission) throws AuthorizationException; @@ -164,8 +163,7 @@ public interface Authorizer { * * @param subjectPrincipal the application-specific subject/user identifier. * @param permission the Permission to check. - * @throws AuthorizationException - * if the user does not have the permission. + * @throws AuthorizationException if the user does not have the permission. */ void checkPermission(PrincipalCollection subjectPrincipal, Permission permission) throws AuthorizationException; @@ -173,7 +171,7 @@ public interface Authorizer { * Ensures the corresponding Subject/user * {@link Permission#implies(Permission) implies} all of the * specified permission strings. - * + *

* If the subject's existing associated permissions do not * {@link Permission#implies(Permission) imply} all of the given permissions, * an {@link AuthorizationException} will be thrown. @@ -192,7 +190,7 @@ public interface Authorizer { * Ensures the corresponding Subject/user * {@link Permission#implies(Permission) implies} all of the * specified permission strings. - * + *

* If the subject's existing associated permissions do not * {@link Permission#implies(Permission) imply} all of the given permissions, * an {@link AuthorizationException} will be thrown. @@ -222,8 +220,8 @@ public interface Authorizer { * @param subjectPrincipal the application-specific subject/user identifier. * @param roleIdentifiers the application-specific role identifiers to check (usually role ids or role names). * @return an array of booleans whose indices correspond to the index of the - * roles in the given identifiers. A true value indicates the user has the - * role at that index. False indicates the user does not have the role at that index. + * roles in the given identifiers. A true value indicates the user has the + * role at that index. False indicates the user does not have the role at that index. */ boolean[] hasRoles(PrincipalCollection subjectPrincipal, List roleIdentifiers); @@ -242,8 +240,7 @@ public interface Authorizer { * * @param subjectPrincipal the application-specific subject/user identifier. * @param roleIdentifier the application-specific role identifier (usually a role id or role name ). - * @throws AuthorizationException - * if the user does not have the role. + * @throws AuthorizationException if the user does not have the role. */ void checkRole(PrincipalCollection subjectPrincipal, String roleIdentifier) throws AuthorizationException; @@ -253,11 +250,11 @@ public interface Authorizer { * * @param subjectPrincipal the application-specific subject/user identifier. * @param roleIdentifiers the application-specific role identifiers to check (usually role ids or role names). - * @throws AuthorizationException - * if the user does not have all of the specified roles. + * @throws AuthorizationException if the user does not have all of the specified roles. */ void checkRoles(PrincipalCollection subjectPrincipal, Collection roleIdentifiers) throws AuthorizationException; + @SuppressWarnings("checkstyle:LineLength") /** * Same as {@link #checkRoles(org.apache.shiro.subject.PrincipalCollection, java.util.Collection) * checkRoles(PrincipalCollection subjectPrincipal, Collection<String> roleIdentifiers)} but doesn't require a collection @@ -267,12 +264,9 @@ public interface Authorizer { * * @param subjectPrincipal the application-specific subject/user identifier. * @param roleIdentifiers the application-specific role identifiers to check (usually role ids or role names). - * @throws AuthorizationException - * if the user does not have all of the specified roles. - * - * @since 1.1.0 + * @throws AuthorizationException if the user does not have all of the specified roles. + * @since 1.1.0 */ void checkRoles(PrincipalCollection subjectPrincipal, String... roleIdentifiers) throws AuthorizationException; - -} +} diff --git a/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java b/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java index 4a120246b5..78f439108f 100644 --- a/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java +++ b/core/src/main/java/org/apache/shiro/authz/ModularRealmAuthorizer.java @@ -95,13 +95,14 @@ public void setRealms(Collection realms) { * if all realm instances will each configure their own permission resolver. * * @return the PermissionResolver to be used on all configured realms, or nullall of the wrapped realms that * implement the {@link org.apache.shiro.authz.permission.PermissionResolverAware PermissionResolverAware} interface. @@ -120,7 +121,8 @@ public void setPermissionResolver(PermissionResolver permissionResolver) { /** * Sets the internal {@link #getPermissionResolver} on any internal configured - * {@link #getRealms Realms} that implement the {@link org.apache.shiro.authz.permission.PermissionResolverAware PermissionResolverAware} interface. + * {@link #getRealms Realms} that implement + * the {@link org.apache.shiro.authz.permission.PermissionResolverAware PermissionResolverAware}interface. *

* This method is called after setting a permissionResolver on this ModularRealmAuthorizer via the * {@link #setPermissionResolver(org.apache.shiro.authz.permission.PermissionResolver) setPermissionResolver} method. @@ -147,13 +149,14 @@ protected void applyPermissionResolverToRealms() { * if all realm instances will each configure their own permission resolver. * * @return the RolePermissionResolver to be used on all configured realms, or nullall of the wrapped realms that * implement the {@link org.apache.shiro.authz.permission.RolePermissionResolverAware PermissionResolverAware} interface. @@ -173,7 +176,8 @@ public void setRolePermissionResolver(RolePermissionResolver rolePermissionResol /** * Sets the internal {@link #getRolePermissionResolver} on any internal configured - * {@link #getRealms Realms} that implement the {@link org.apache.shiro.authz.permission.RolePermissionResolverAware RolePermissionResolverAware} interface. + * {@link #getRealms Realms} that implement the + * {@link org.apache.shiro.authz.permission.RolePermissionResolverAware RolePermissionResolverAware} interface. *

* This method is called after setting a rolePermissionResolver on this ModularRealmAuthorizer via the * {@link #setRolePermissionResolver(org.apache.shiro.authz.permission.RolePermissionResolver) setRolePermissionResolver} method. @@ -205,8 +209,8 @@ protected void applyRolePermissionResolverToRealms() { protected void assertRealmsConfigured() throws IllegalStateException { Collection realms = getRealms(); if (realms == null || realms.isEmpty()) { - String msg = "Configuration error: No realms have been configured! One or more realms must be " + - "present to execute an authorization operation."; + String msg = "Configuration error: No realms have been configured! One or more realms must be " + + "present to execute an authorization operation."; throw new IllegalStateException(msg); } } @@ -219,7 +223,9 @@ protected void assertRealmsConfigured() throws IllegalStateException { public boolean isPermitted(PrincipalCollection principals, String permission) { assertRealmsConfigured(); for (Realm realm : getRealms()) { - if (!(realm instanceof Authorizer)) continue; + if (!(realm instanceof Authorizer)) { + continue; + } if (((Authorizer) realm).isPermitted(principals, permission)) { return true; } @@ -235,7 +241,9 @@ public boolean isPermitted(PrincipalCollection principals, String permission) { public boolean isPermitted(PrincipalCollection principals, Permission permission) { assertRealmsConfigured(); for (Realm realm : getRealms()) { - if (!(realm instanceof Authorizer)) continue; + if (!(realm instanceof Authorizer)) { + continue; + } if (((Authorizer) realm).isPermitted(principals, permission)) { return true; } @@ -348,6 +356,7 @@ public void checkPermissions(PrincipalCollection principals, String... permissio } } + @SuppressWarnings("checkstyle:LineLength") /** * If !{@link #isPermitted(org.apache.shiro.subject.PrincipalCollection, Permission) isPermitted(permission)} for * all the given Permissions, throws @@ -370,7 +379,9 @@ public void checkPermissions(PrincipalCollection principals, Collection roles) throws AuthorizationException { //SHIRO-234 - roles.toArray() -> roles.toArray(new String[roles.size()]) - if (roles != null && !roles.isEmpty()) checkRoles(principals, roles.toArray(new String[roles.size()])); + if (roles != null && !roles.isEmpty()) { + checkRoles(principals, roles.toArray(new String[roles.size()])); + } } /** diff --git a/core/src/main/java/org/apache/shiro/authz/Permission.java b/core/src/main/java/org/apache/shiro/authz/Permission.java index 23c42c775a..eb680fc340 100644 --- a/core/src/main/java/org/apache/shiro/authz/Permission.java +++ b/core/src/main/java/org/apache/shiro/authz/Permission.java @@ -79,7 +79,7 @@ public interface Permission { * * @param p the permission to check for behavior/functionality comparison. * @return {@code true} if this current instance implies all the functionality and/or resource access - * described by the specified {@code Permission} argument, {@code false} otherwise. + * described by the specified {@code Permission} argument, {@code false} otherwise. */ boolean implies(Permission p); } diff --git a/core/src/main/java/org/apache/shiro/authz/SimpleAuthorizationInfo.java b/core/src/main/java/org/apache/shiro/authz/SimpleAuthorizationInfo.java index 70ebef972f..6fe99ff554 100644 --- a/core/src/main/java/org/apache/shiro/authz/SimpleAuthorizationInfo.java +++ b/core/src/main/java/org/apache/shiro/authz/SimpleAuthorizationInfo.java @@ -54,6 +54,7 @@ public SimpleAuthorizationInfo() { /** * Creates a new instance with the specified roles and no permissions. + * * @param roles the roles assigned to the realm account. */ public SimpleAuthorizationInfo(Set roles) { @@ -66,6 +67,7 @@ public Set getRoles() { /** * Sets the roles assigned to the account. + * * @param roles the roles assigned to the account. */ public void setRoles(Set roles) { @@ -75,6 +77,7 @@ public void setRoles(Set roles) { /** * Adds (assigns) a role to those associated with the account. If the account doesn't yet have any roles, a * new roles collection (a Set) will be created automatically. + * * @param role the role to add to those associated with the account. */ public void addRole(String role) { @@ -87,6 +90,7 @@ public void addRole(String role) { /** * Adds (assigns) multiple roles to those associated with the account. If the account doesn't yet have any roles, a * new roles collection (a Set) will be created automatically. + * * @param roles the roles to add to those associated with the account. */ public void addRoles(Collection roles) { @@ -114,6 +118,7 @@ public void setStringPermissions(Set stringPermissions) { /** * Adds (assigns) a permission to those directly associated with the account. If the account doesn't yet have any * direct permissions, a new permission collection (a Set<String>) will be created automatically. + * * @param permission the permission to add to those directly assigned to the account. */ public void addStringPermission(String permission) { @@ -126,6 +131,7 @@ public void addStringPermission(String permission) { /** * Adds (assigns) multiple permissions to those associated directly with the account. If the account doesn't yet * have any string-based permissions, a new permissions collection (a Set<String>) will be created automatically. + * * @param permissions the permissions to add to those associated directly with the account. */ public void addStringPermissions(Collection permissions) { @@ -153,6 +159,7 @@ public void setObjectPermissions(Set objectPermissions) { /** * Adds (assigns) a permission to those directly associated with the account. If the account doesn't yet have any * direct permissions, a new permission collection (a Set<{@link Permission Permission}>) will be created automatically. + * * @param permission the permission to add to those directly assigned to the account. */ public void addObjectPermission(Permission permission) { @@ -166,6 +173,7 @@ public void addObjectPermission(Permission permission) { * Adds (assigns) multiple permissions to those associated directly with the account. If the account doesn't yet * have any object-based permissions, a new permissions collection (a Set<{@link Permission Permission}>) * will be created automatically. + * * @param permissions the permissions to add to those associated directly with the account. */ public void addObjectPermissions(Collection permissions) { diff --git a/core/src/main/java/org/apache/shiro/authz/SimpleRole.java b/core/src/main/java/org/apache/shiro/authz/SimpleRole.java index 73f65ae3d8..8dcdd876bc 100644 --- a/core/src/main/java/org/apache/shiro/authz/SimpleRole.java +++ b/core/src/main/java/org/apache/shiro/authz/SimpleRole.java @@ -31,7 +31,7 @@ */ public class SimpleRole implements Serializable { - protected String name = null; + protected String name; protected Set permissions; public SimpleRole() { diff --git a/core/src/main/java/org/apache/shiro/authz/annotation/Logical.java b/core/src/main/java/org/apache/shiro/authz/annotation/Logical.java index cf2977303a..ed08c1a635 100644 --- a/core/src/main/java/org/apache/shiro/authz/annotation/Logical.java +++ b/core/src/main/java/org/apache/shiro/authz/annotation/Logical.java @@ -1,29 +1,36 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.authz.annotation; - -/** - * An enum for specifying a logical operation that can be used for - * interpreting authorization annotations - * - * @since 1.1.0 - */ -public enum Logical { - AND, OR -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.authz.annotation; + +/** + * An enum for specifying a logical operation that can be used for + * interpreting authorization annotations + * + * @since 1.1.0 + */ +public enum Logical { + /** + * AND + */ + AND, + /** + * OR + */ + OR +} diff --git a/core/src/main/java/org/apache/shiro/authz/annotation/RequiresAuthentication.java b/core/src/main/java/org/apache/shiro/authz/annotation/RequiresAuthentication.java index 7216a03d92..1f84ddb209 100644 --- a/core/src/main/java/org/apache/shiro/authz/annotation/RequiresAuthentication.java +++ b/core/src/main/java/org/apache/shiro/authz/annotation/RequiresAuthentication.java @@ -24,6 +24,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +@SuppressWarnings("checkstyle:LineLength") /** * Requires the current Subject to have been authenticated during their current session for the annotated * class/instance/method to be accessed or invoked. This is more restrictive than the @@ -38,7 +39,6 @@ * * @see RequiresUser * @see RequiresGuest - * * @since 0.9.0 */ @Target({ElementType.TYPE, ElementType.METHOD}) diff --git a/core/src/main/java/org/apache/shiro/authz/annotation/RequiresGuest.java b/core/src/main/java/org/apache/shiro/authz/annotation/RequiresGuest.java index f16241490c..23c441c14f 100644 --- a/core/src/main/java/org/apache/shiro/authz/annotation/RequiresGuest.java +++ b/core/src/main/java/org/apache/shiro/authz/annotation/RequiresGuest.java @@ -35,7 +35,6 @@ * * @see RequiresAuthentication * @see RequiresUser - * * @since 0.9.0 */ @Target({ElementType.TYPE, ElementType.METHOD}) diff --git a/core/src/main/java/org/apache/shiro/authz/annotation/RequiresPermissions.java b/core/src/main/java/org/apache/shiro/authz/annotation/RequiresPermissions.java index 50b5c25b26..b363da9749 100644 --- a/core/src/main/java/org/apache/shiro/authz/annotation/RequiresPermissions.java +++ b/core/src/main/java/org/apache/shiro/authz/annotation/RequiresPermissions.java @@ -54,12 +54,12 @@ * to determine if the user is allowed to invoke the code protected by this annotation. */ String[] value(); - + /** * The logical operation for the permission checks in case multiple roles are specified. AND is the default + * * @since 1.1.0 */ - Logical logical() default Logical.AND; + Logical logical() default Logical.AND; } - diff --git a/core/src/main/java/org/apache/shiro/authz/annotation/RequiresRoles.java b/core/src/main/java/org/apache/shiro/authz/annotation/RequiresRoles.java index 016cd864cb..afc147847c 100644 --- a/core/src/main/java/org/apache/shiro/authz/annotation/RequiresRoles.java +++ b/core/src/main/java/org/apache/shiro/authz/annotation/RequiresRoles.java @@ -25,7 +25,7 @@ import java.lang.annotation.Target; /** - * Requires the currently executing {@link org.apache.shiro.subject.Subject Subject} to have all of the + * Requires the currently executing {@link org.apache.shiro.subject.Subject Subject} to have all of the * specified roles. If they do not have the role(s), the method will not be executed and * an {@link org.apache.shiro.authz.AuthorizationException AuthorizationException} is thrown. *

@@ -61,10 +61,11 @@ * invocation to be allowed. */ String[] value(); - + /** * The logical operation for the permission check in case multiple roles are specified. AND is the default + * * @since 1.1.0 */ - Logical logical() default Logical.AND; + Logical logical() default Logical.AND; } diff --git a/core/src/main/java/org/apache/shiro/authz/annotation/RequiresUser.java b/core/src/main/java/org/apache/shiro/authz/annotation/RequiresUser.java index 853056cf2c..20048b8ab4 100644 --- a/core/src/main/java/org/apache/shiro/authz/annotation/RequiresUser.java +++ b/core/src/main/java/org/apache/shiro/authz/annotation/RequiresUser.java @@ -43,7 +43,6 @@ * * @see RequiresAuthentication * @see RequiresGuest - * * @since 0.9.0 */ @Target({ElementType.TYPE, ElementType.METHOD}) diff --git a/core/src/main/java/org/apache/shiro/authz/annotation/package-info.java b/core/src/main/java/org/apache/shiro/authz/annotation/package-info.java index 3fa8d23a7f..df90e4de94 100644 --- a/core/src/main/java/org/apache/shiro/authz/annotation/package-info.java +++ b/core/src/main/java/org/apache/shiro/authz/annotation/package-info.java @@ -19,10 +19,6 @@ /** * Annotations used to restrict which classes, instances, or methods may be accessed or invoked depending on the * caller's access abilities or authentication state. - * - * Since 1.1, all core annotations were extends to accept Target ElementType.TYPE in addition to ElementType.METHOD + * Since 1.1, all core annotations were extends to accept Target ElementType.TYPE in addition to ElementType.METHOD */ package org.apache.shiro.authz.annotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; - diff --git a/core/src/main/java/org/apache/shiro/authz/aop/AnnotationsAuthorizingMethodInterceptor.java b/core/src/main/java/org/apache/shiro/authz/aop/AnnotationsAuthorizingMethodInterceptor.java index f9bd41a5de..69b63b9bec 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/AnnotationsAuthorizingMethodInterceptor.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/AnnotationsAuthorizingMethodInterceptor.java @@ -45,7 +45,7 @@ public abstract class AnnotationsAuthorizingMethodInterceptor extends Authorizin protected Collection methodInterceptors; /** - * Default no-argument constructor that defaults the + * Default no-argument constructor that defaults the * {@link #methodInterceptors methodInterceptors} attribute to contain two interceptors by default - the * {@link RoleAnnotationMethodInterceptor RoleAnnotationMethodInterceptor} and the * {@link PermissionAnnotationMethodInterceptor PermissionAnnotationMethodInterceptor} to @@ -68,6 +68,7 @@ public AnnotationsAuthorizingMethodInterceptor() { * {@link RoleAnnotationMethodInterceptor RoleAnnotationMethodInterceptor} and a * {@link PermissionAnnotationMethodInterceptor PermissionAnnotationMethodInterceptor} to * support role and permission annotations automatically. + * * @return the method interceptors to execute for the annotated method. */ public Collection getMethodInterceptors() { @@ -76,6 +77,7 @@ public Collection getMethodInterceptors( /** * Sets the method interceptors to execute for the annotated method. + * * @param methodInterceptors the method interceptors to execute for the annotated method. * @see #getMethodInterceptors() */ diff --git a/core/src/main/java/org/apache/shiro/authz/aop/AuthenticatedAnnotationHandler.java b/core/src/main/java/org/apache/shiro/authz/aop/AuthenticatedAnnotationHandler.java index d45d740526..2d13b0d002 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/AuthenticatedAnnotationHandler.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/AuthenticatedAnnotationHandler.java @@ -40,17 +40,18 @@ public AuthenticatedAnnotationHandler() { super(RequiresAuthentication.class); } + @SuppressWarnings("checkstyle:LineLength") /** * Ensures that the calling Subject is authenticated, and if not, throws an * {@link org.apache.shiro.authz.UnauthenticatedException UnauthenticatedException} indicating the method is not allowed to be executed. * * @param a the annotation to inspect * @throws org.apache.shiro.authz.UnauthenticatedException if the calling Subject has not yet - * authenticated. + * authenticated. */ public void assertAuthorized(Annotation a) throws UnauthenticatedException { - if (a instanceof RequiresAuthentication && !getSubject().isAuthenticated() ) { - throw new UnauthenticatedException( "The current Subject is not authenticated. Access denied." ); + if (a instanceof RequiresAuthentication && !getSubject().isAuthenticated()) { + throw new UnauthenticatedException("The current Subject is not authenticated. Access denied."); } } } diff --git a/core/src/main/java/org/apache/shiro/authz/aop/AuthorizingAnnotationMethodInterceptor.java b/core/src/main/java/org/apache/shiro/authz/aop/AuthorizingAnnotationMethodInterceptor.java index 0728eca723..26d02dbad6 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/AuthorizingAnnotationMethodInterceptor.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/AuthorizingAnnotationMethodInterceptor.java @@ -30,27 +30,26 @@ * * @since 0.1 */ -public abstract class AuthorizingAnnotationMethodInterceptor extends AnnotationMethodInterceptor -{ - +public abstract class AuthorizingAnnotationMethodInterceptor extends AnnotationMethodInterceptor { + /** * Constructor that ensures the internal handler is set which will be used to perform the * authorization assertion checks when a supported annotation is encountered. - * @param handler the internal handler used to perform authorization assertion checks when a - * supported annotation is encountered. + * + * @param handler the internal handler used to perform authorization assertion checks when a + * supported annotation is encountered. */ - public AuthorizingAnnotationMethodInterceptor( AuthorizingAnnotationHandler handler ) { + public AuthorizingAnnotationMethodInterceptor(AuthorizingAnnotationHandler handler) { super(handler); } /** - * * @param handler * @param resolver * @since 1.1 */ - public AuthorizingAnnotationMethodInterceptor( AuthorizingAnnotationHandler handler, - AnnotationResolver resolver) { + public AuthorizingAnnotationMethodInterceptor(AuthorizingAnnotationHandler handler, + AnnotationResolver resolver) { super(handler, resolver); } @@ -59,15 +58,17 @@ public AuthorizingAnnotationMethodInterceptor( AuthorizingAnnotationHandler hand * {@link #assertAuthorized(org.apache.shiro.aop.MethodInvocation) assertAuthorized} method first. * * @param methodInvocation the method invocation to check for authorization prior to allowing it to proceed/execute. - * @return the return value from the method invocation (the value of {@link org.apache.shiro.aop.MethodInvocation#proceed() MethodInvocation.proceed()}). + * @return the return value from the method invocation + * (the value of {@link org.apache.shiro.aop.MethodInvocation#proceed() MethodInvocation.proceed()}). * @throws org.apache.shiro.authz.AuthorizationException if the MethodInvocation is not allowed to proceed. - * @throws Throwable if any other error occurs. + * @throws Throwable if any other error occurs. */ public Object invoke(MethodInvocation methodInvocation) throws Throwable { assertAuthorized(methodInvocation); return methodInvocation.proceed(); } + @SuppressWarnings("checkstyle:LineLength") /** * Ensures the calling Subject is authorized to execute the specified MethodInvocation. *

@@ -81,14 +82,15 @@ public Object invoke(MethodInvocation methodInvocation) throws Throwable { */ public void assertAuthorized(MethodInvocation mi) throws AuthorizationException { try { - ((AuthorizingAnnotationHandler)getHandler()).assertAuthorized(getAnnotation(mi)); - } - catch(AuthorizationException ae) { - // Annotation handler doesn't know why it was called, so add the information here if possible. - // Don't wrap the exception here since we don't want to mask the specific exception, such as - // UnauthenticatedException etc. - if (ae.getCause() == null) ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod())); + ((AuthorizingAnnotationHandler) getHandler()).assertAuthorized(getAnnotation(mi)); + } catch (AuthorizationException ae) { + // Annotation handler doesn't know why it was called, so add the information here if possible. + // Don't wrap the exception here since we don't want to mask the specific exception, such as + // UnauthenticatedException etc. + if (ae.getCause() == null) { + ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod())); + } throw ae; - } + } } } diff --git a/core/src/main/java/org/apache/shiro/authz/aop/AuthorizingMethodInterceptor.java b/core/src/main/java/org/apache/shiro/authz/aop/AuthorizingMethodInterceptor.java index 08b310aad2..c4fc450cb6 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/AuthorizingMethodInterceptor.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/AuthorizingMethodInterceptor.java @@ -42,6 +42,7 @@ public Object invoke(MethodInvocation methodInvocation) throws Throwable { /** * Asserts that the specified MethodInvocation is allowed to continue by performing any necessary authorization * (access control) checks first. + * * @param methodInvocation the MethodInvocation to invoke. * @throws AuthorizationException if the methodInvocation should not be allowed to continue/execute. */ diff --git a/core/src/main/java/org/apache/shiro/authz/aop/DenyAllAnnotationHandler.java b/core/src/main/java/org/apache/shiro/authz/aop/DenyAllAnnotationHandler.java index c60b4a4911..73e087a601 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/DenyAllAnnotationHandler.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/DenyAllAnnotationHandler.java @@ -35,7 +35,7 @@ public class DenyAllAnnotationHandler extends AuthorizingAnnotationHandler { /** * Default no-argument constructor that ensures this interceptor looks for - * + *

* {@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest} annotations in a method * declaration. */ diff --git a/core/src/main/java/org/apache/shiro/authz/aop/GuestAnnotationHandler.java b/core/src/main/java/org/apache/shiro/authz/aop/GuestAnnotationHandler.java index 9c1816236c..c74e86b67f 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/GuestAnnotationHandler.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/GuestAnnotationHandler.java @@ -24,7 +24,7 @@ import org.apache.shiro.authz.UnauthenticatedException; import org.apache.shiro.authz.annotation.RequiresGuest; - +@SuppressWarnings("checkstyle:LineLength") /** * Checks to see if a @{@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest} annotation * is declared, and if so, ensures the calling Subject does not @@ -38,7 +38,7 @@ public class GuestAnnotationHandler extends AuthorizingAnnotationHandler { /** * Default no-argument constructor that ensures this interceptor looks for - * + *

* {@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest} annotations in a method * declaration. */ @@ -53,14 +53,13 @@ public GuestAnnotationHandler() { * AuthorizingException will be thrown indicating that execution is not allowed to continue. * * @param a the annotation to check for one or more roles - * @throws org.apache.shiro.authz.AuthorizationException - * if the calling Subject is not a "guest". + * @throws org.apache.shiro.authz.AuthorizationException if the calling Subject is not a "guest". */ public void assertAuthorized(Annotation a) throws AuthorizationException { if (a instanceof RequiresGuest && getSubject().getPrincipal() != null) { - throw new UnauthenticatedException("Attempting to perform a guest-only operation. The current Subject is " + - "not a guest (they have been authenticated or remembered from a previous login). Access " + - "denied."); + throw new UnauthenticatedException("Attempting to perform a guest-only operation. The current Subject is " + + "not a guest (they have been authenticated or remembered from a previous login). Access " + + "denied."); } } } diff --git a/core/src/main/java/org/apache/shiro/authz/aop/GuestAnnotationMethodInterceptor.java b/core/src/main/java/org/apache/shiro/authz/aop/GuestAnnotationMethodInterceptor.java index 8828acbd83..a89409bbf0 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/GuestAnnotationMethodInterceptor.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/GuestAnnotationMethodInterceptor.java @@ -20,6 +20,7 @@ import org.apache.shiro.aop.AnnotationResolver; +@SuppressWarnings("checkstyle:LineLength") /** * Checks to see if a @{@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest} annotation * is declared, and if so, ensures the calling Subject does not diff --git a/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationHandler.java b/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationHandler.java index a463123ad7..d943a28c77 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationHandler.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationHandler.java @@ -21,7 +21,6 @@ import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresPermissions; -import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.subject.Subject; import java.lang.annotation.Annotation; @@ -54,17 +53,19 @@ protected String[] getAnnotationValue(Annotation a) { return rpAnnotation.value(); } + @SuppressWarnings("checkstyle:LineLength") /** * Ensures that the calling Subject has the Annotation's specified permissions, and if not, throws an * AuthorizingException indicating access is denied. * * @param a the RequiresPermission annotation being inspected to check for one or more permissions - * @throws org.apache.shiro.authz.AuthorizationException - * if the calling Subject does not have the permission(s) necessary to - * continue access or execution. + * @throws org.apache.shiro.authz.AuthorizationException if the calling Subject does not have the permission(s) necessary to + * continue access or execution. */ public void assertAuthorized(Annotation a) throws AuthorizationException { - if (!(a instanceof RequiresPermissions)) return; + if (!(a instanceof RequiresPermissions)) { + return; + } RequiresPermissions rpAnnotation = (RequiresPermissions) a; String[] perms = getAnnotationValue(a); @@ -88,8 +89,10 @@ public void assertAuthorized(Annotation a) throws AuthorizationException { } } // Cause the exception if none of the role match, note that the exception message will be a bit misleading - if (!hasAtLeastOnePermission) getSubject().checkPermission(perms[0]); - + if (!hasAtLeastOnePermission) { + getSubject().checkPermission(perms[0]); + } + } } } diff --git a/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationMethodInterceptor.java b/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationMethodInterceptor.java index c4f8082641..1a20e486cc 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationMethodInterceptor.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/PermissionAnnotationMethodInterceptor.java @@ -20,9 +20,11 @@ import org.apache.shiro.aop.AnnotationResolver; +@SuppressWarnings("checkstyle:LineLength") /** * Checks to see if a @{@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotation is declared, and if so, performs * a permission check to see if the calling Subject is allowed to call the method. + * * @since 0.9 */ public class PermissionAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor { @@ -37,7 +39,7 @@ public class PermissionAnnotationMethodInterceptor extends AuthorizingAnnotation * {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} annotations in a method declaration. */ public PermissionAnnotationMethodInterceptor() { - super( new PermissionAnnotationHandler() ); + super(new PermissionAnnotationHandler()); } /** @@ -45,7 +47,7 @@ public PermissionAnnotationMethodInterceptor() { * @since 1.1 */ public PermissionAnnotationMethodInterceptor(AnnotationResolver resolver) { - super( new PermissionAnnotationHandler(), resolver); + super(new PermissionAnnotationHandler(), resolver); } /* diff --git a/core/src/main/java/org/apache/shiro/authz/aop/PermitAllAnnotationHandler.java b/core/src/main/java/org/apache/shiro/authz/aop/PermitAllAnnotationHandler.java index 71a61e74cd..ca51e9a4e7 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/PermitAllAnnotationHandler.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/PermitAllAnnotationHandler.java @@ -44,5 +44,6 @@ public PermitAllAnnotationHandler() { * @param a the annotation to check for one or more roles */ @Override - public void assertAuthorized(Annotation a) { } + public void assertAuthorized(Annotation a) { + } } diff --git a/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationHandler.java b/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationHandler.java index db54424428..d827ce807d 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationHandler.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationHandler.java @@ -25,6 +25,7 @@ import java.lang.annotation.Annotation; import java.util.Arrays; +@SuppressWarnings("checkstyle:LineLength") /** * Checks to see if a @{@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles} annotation is declared, and if so, performs * a role check to see if the calling Subject is allowed to proceed. @@ -41,17 +42,19 @@ public RoleAnnotationHandler() { super(RequiresRoles.class); } + @SuppressWarnings("checkstyle:LineLength") /** * Ensures that the calling Subject has the Annotation's specified roles, and if not, throws an * AuthorizingException indicating that access is denied. * * @param a the RequiresRoles annotation to use to check for one or more roles - * @throws org.apache.shiro.authz.AuthorizationException - * if the calling Subject does not have the role(s) necessary to - * proceed. + * @throws org.apache.shiro.authz.AuthorizationException if the calling Subject does not have the role(s) necessary to + * proceed. */ public void assertAuthorized(Annotation a) throws AuthorizationException { - if (!(a instanceof RequiresRoles)) return; + if (!(a instanceof RequiresRoles)) { + return; + } RequiresRoles rrAnnotation = (RequiresRoles) a; String[] roles = rrAnnotation.value(); @@ -67,9 +70,15 @@ public void assertAuthorized(Annotation a) throws AuthorizationException { if (Logical.OR.equals(rrAnnotation.logical())) { // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first boolean hasAtLeastOneRole = false; - for (String role : roles) if (getSubject().hasRole(role)) hasAtLeastOneRole = true; + for (String role : roles) { + if (getSubject().hasRole(role)) { + hasAtLeastOneRole = true; + } + } // Cause the exception if none of the role match, note that the exception message will be a bit misleading - if (!hasAtLeastOneRole) getSubject().checkRole(roles[0]); + if (!hasAtLeastOneRole) { + getSubject().checkRole(roles[0]); + } } } diff --git a/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationMethodInterceptor.java b/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationMethodInterceptor.java index a03404248b..2401e81d9c 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationMethodInterceptor.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/RoleAnnotationMethodInterceptor.java @@ -35,7 +35,7 @@ public class RoleAnnotationMethodInterceptor extends AuthorizingAnnotationMethod * {@link RequiresRoles RequiresRoles} annotations in a method declaration. */ public RoleAnnotationMethodInterceptor() { - super( new RoleAnnotationHandler() ); + super(new RoleAnnotationHandler()); } /** diff --git a/core/src/main/java/org/apache/shiro/authz/aop/RolesAllowedAnnotationHandler.java b/core/src/main/java/org/apache/shiro/authz/aop/RolesAllowedAnnotationHandler.java index 467fc80a91..745d90e173 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/RolesAllowedAnnotationHandler.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/RolesAllowedAnnotationHandler.java @@ -30,6 +30,7 @@ * @since 2.0 */ public class RolesAllowedAnnotationHandler extends AuthorizingAnnotationHandler { + /** * Default no-argument constructor that ensures this handler looks for * {@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles} annotations. @@ -38,18 +39,20 @@ public RolesAllowedAnnotationHandler() { super(RolesAllowed.class); } + @SuppressWarnings("checkstyle:LineLength") /** * Ensures that the calling Subject has one of the Annotation's specified roles, and if not, throws an * AuthorizingException indicating that access is denied. * * @param a the RolesAllowed annotation to use to check for one or more roles - * @throws org.apache.shiro.authz.AuthorizationException - * if the calling Subject does not have the role necessary to - * proceed. + * @throws org.apache.shiro.authz.AuthorizationException if the calling Subject does not have the role necessary to + * proceed. */ @Override public void assertAuthorized(Annotation a) throws AuthorizationException { - if (!(a instanceof RolesAllowed)) return; + if (!(a instanceof RolesAllowed)) { + return; + } RolesAllowed raAnnotation = (RolesAllowed) a; String[] roles = raAnnotation.value(); @@ -63,8 +66,14 @@ public void assertAuthorized(Annotation a) throws AuthorizationException { // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first boolean hasAtLeastOneRole = false; - for (String role : roles) if (getSubject().hasRole(role)) hasAtLeastOneRole = true; + for (String role : roles) { + if (getSubject().hasRole(role)) { + hasAtLeastOneRole = true; + } + } // Cause the exception if none of the role match, note that the exception message will be a bit misleading - if (!hasAtLeastOneRole) getSubject().checkRole(roles[0]); + if (!hasAtLeastOneRole) { + getSubject().checkRole(roles[0]); + } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/apache/shiro/authz/aop/UserAnnotationHandler.java b/core/src/main/java/org/apache/shiro/authz/aop/UserAnnotationHandler.java index d48ca81911..a3ebe94fb9 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/UserAnnotationHandler.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/UserAnnotationHandler.java @@ -24,7 +24,7 @@ import org.apache.shiro.authz.UnauthenticatedException; import org.apache.shiro.authz.annotation.RequiresUser; - +@SuppressWarnings("checkstyle:LineLength") /** * Checks to see if a @{@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser} annotation * is declared, and if so, ensures the calling Subject is either @@ -39,13 +39,14 @@ public class UserAnnotationHandler extends AuthorizingAnnotationHandler { /** * Default no-argument constructor that ensures this handler looks for - * + *

* {@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser} annotations. */ public UserAnnotationHandler() { super(RequiresUser.class); } + @SuppressWarnings("checkstyle:LineLength") /** * Ensures that the calling Subject is a user, that is, they are either * {@link org.apache.shiro.subject.Subject#isAuthenticated() authenticated} or remembered via remember @@ -53,14 +54,13 @@ public UserAnnotationHandler() { * AuthorizingException indicating access is not allowed. * * @param a the RequiresUser annotation to check - * @throws org.apache.shiro.authz.AuthorizationException - * if the calling Subject is not authenticated or remembered via rememberMe services. + * @throws org.apache.shiro.authz.AuthorizationException if the calling Subject is not authenticated or remembered via rememberMe services. */ public void assertAuthorized(Annotation a) throws AuthorizationException { if (a instanceof RequiresUser && getSubject().getPrincipal() == null) { - throw new UnauthenticatedException("Attempting to perform a user-only operation. The current Subject is " + - "not a user (they haven't been authenticated or remembered from a previous login). " + - "Access denied."); + throw new UnauthenticatedException("Attempting to perform a user-only operation. The current Subject is " + + "not a user (they haven't been authenticated or remembered from a previous login). " + + "Access denied."); } } } diff --git a/core/src/main/java/org/apache/shiro/authz/aop/UserAnnotationMethodInterceptor.java b/core/src/main/java/org/apache/shiro/authz/aop/UserAnnotationMethodInterceptor.java index 2b680ef0a0..3c5532b28f 100644 --- a/core/src/main/java/org/apache/shiro/authz/aop/UserAnnotationMethodInterceptor.java +++ b/core/src/main/java/org/apache/shiro/authz/aop/UserAnnotationMethodInterceptor.java @@ -20,6 +20,7 @@ import org.apache.shiro.aop.AnnotationResolver; +@SuppressWarnings("checkstyle:LineLength") /** * Checks to see if a @{@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser} annotation * is declared, and if so, ensures the calling Subject is either @@ -34,16 +35,15 @@ public class UserAnnotationMethodInterceptor extends AuthorizingAnnotationMethod /** * Default no-argument constructor that ensures this interceptor looks for - * + *

* {@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser} annotations in a method * declaration. */ public UserAnnotationMethodInterceptor() { - super( new UserAnnotationHandler() ); + super(new UserAnnotationHandler()); } /** - * * @param resolver * @since 1.1 */ diff --git a/core/src/main/java/org/apache/shiro/authz/permission/DomainPermission.java b/core/src/main/java/org/apache/shiro/authz/permission/DomainPermission.java index cfdc0b2dea..154b511b99 100644 --- a/core/src/main/java/org/apache/shiro/authz/permission/DomainPermission.java +++ b/core/src/main/java/org/apache/shiro/authz/permission/DomainPermission.java @@ -32,12 +32,12 @@ */ public class DomainPermission extends WildcardPermission { + private static final long serialVersionUID = 1L; + private String domain; private Set actions; private Set targets; - private static final long serialVersionUID = 1l; - /** * Creates a domain permission with *all* actions for *all* targets; */ diff --git a/core/src/main/java/org/apache/shiro/authz/permission/InvalidPermissionStringException.java b/core/src/main/java/org/apache/shiro/authz/permission/InvalidPermissionStringException.java index d7c19403f5..5888319096 100644 --- a/core/src/main/java/org/apache/shiro/authz/permission/InvalidPermissionStringException.java +++ b/core/src/main/java/org/apache/shiro/authz/permission/InvalidPermissionStringException.java @@ -27,10 +27,9 @@ * * @since 0.9 */ -public class InvalidPermissionStringException extends ShiroException -{ +public class InvalidPermissionStringException extends ShiroException { - private String permissionString; + private final String permissionString; /** * Constructs a new exception with the given message and permission string. diff --git a/core/src/main/java/org/apache/shiro/authz/permission/PermissionResolver.java b/core/src/main/java/org/apache/shiro/authz/permission/PermissionResolver.java index 1714c39359..7fa1df20ad 100644 --- a/core/src/main/java/org/apache/shiro/authz/permission/PermissionResolver.java +++ b/core/src/main/java/org/apache/shiro/authz/permission/PermissionResolver.java @@ -20,6 +20,7 @@ import org.apache.shiro.authz.Permission; +@SuppressWarnings("checkstyle:LineLength") /** * A {@code PermissionResolver} resolves a String value and converts it into a * {@link org.apache.shiro.authz.Permission Permission} instance. @@ -54,8 +55,7 @@ public interface PermissionResolver { * * @param permissionString the String representation of a permission. * @return A Permission object that can be used internally to determine a subject's permissions. - * @throws InvalidPermissionStringException - * if the permission string is not valid for this resolver. + * @throws InvalidPermissionStringException if the permission string is not valid for this resolver. */ Permission resolvePermission(String permissionString); diff --git a/core/src/main/java/org/apache/shiro/authz/permission/PermissionResolverAware.java b/core/src/main/java/org/apache/shiro/authz/permission/PermissionResolverAware.java index 9ea76c2338..fa1e1c76b2 100644 --- a/core/src/main/java/org/apache/shiro/authz/permission/PermissionResolverAware.java +++ b/core/src/main/java/org/apache/shiro/authz/permission/PermissionResolverAware.java @@ -36,5 +36,5 @@ public interface PermissionResolverAware { * * @param pr the PermissionResolver being set. */ - public void setPermissionResolver(PermissionResolver pr); + void setPermissionResolver(PermissionResolver pr); } diff --git a/core/src/main/java/org/apache/shiro/authz/permission/RolePermissionResolver.java b/core/src/main/java/org/apache/shiro/authz/permission/RolePermissionResolver.java index 68e3cdb69e..5ff3d14bcf 100644 --- a/core/src/main/java/org/apache/shiro/authz/permission/RolePermissionResolver.java +++ b/core/src/main/java/org/apache/shiro/authz/permission/RolePermissionResolver.java @@ -28,7 +28,6 @@ *

* In some cases a {@link org.apache.shiro.realm.Realm} my only be able to return a list of roles. This * component allows an application to resolve the roles into permissions. - * */ public interface RolePermissionResolver { diff --git a/core/src/main/java/org/apache/shiro/authz/permission/RolePermissionResolverAware.java b/core/src/main/java/org/apache/shiro/authz/permission/RolePermissionResolverAware.java index be9d841e03..f8c4621d91 100644 --- a/core/src/main/java/org/apache/shiro/authz/permission/RolePermissionResolverAware.java +++ b/core/src/main/java/org/apache/shiro/authz/permission/RolePermissionResolverAware.java @@ -36,5 +36,5 @@ public interface RolePermissionResolverAware { * * @param rpr the RolePermissionResolver being set. */ - public void setRolePermissionResolver(RolePermissionResolver rpr); + void setRolePermissionResolver(RolePermissionResolver rpr); } diff --git a/core/src/main/java/org/apache/shiro/authz/permission/WildcardPermission.java b/core/src/main/java/org/apache/shiro/authz/permission/WildcardPermission.java index 2eeaffbf90..f4c91e35be 100644 --- a/core/src/main/java/org/apache/shiro/authz/permission/WildcardPermission.java +++ b/core/src/main/java/org/apache/shiro/authz/permission/WildcardPermission.java @@ -123,6 +123,7 @@ public class WildcardPermission implements Permission, Serializable { /*-------------------------------------------- | C O N S T R U C T O R S | ============================================*/ + /** * Default no-arg constructor for subclasses only - end-user developers instantiating Permission instances must * provide a wildcard string at a minimum, since Permission instances are immutable once instantiated. @@ -147,6 +148,7 @@ protected void setParts(String wildcardString) { setParts(wildcardString, DEFAULT_CASE_SENSITIVE); } + @SuppressWarnings("checkstyle:LineLength") protected void setParts(String wildcardString, boolean caseSensitive) { wildcardString = StringUtils.clean(wildcardString); @@ -165,7 +167,8 @@ protected void setParts(String wildcardString, boolean caseSensitive) { Set subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN)); if (subparts.isEmpty()) { - throw new IllegalArgumentException("Wildcard string cannot contain parts with only dividers. Make sure permission strings are properly formatted."); + throw new IllegalArgumentException( + "Wildcard string cannot contain parts with only dividers. Make sure permission strings are properly formatted."); } this.parts.add(subparts); } @@ -184,8 +187,9 @@ protected List> getParts() { /** * Sets the pre-split String parts of this WildcardPermission. - * @since 1.3.0 + * * @param parts pre-split String parts. + * @since 1.3.0 */ protected void setParts(List> parts) { this.parts = parts; @@ -238,7 +242,7 @@ public String toString() { buffer.append(PART_DIVIDER_TOKEN); } Iterator partIt = part.iterator(); - while(partIt.hasNext()) { + while (partIt.hasNext()) { buffer.append(partIt.next()); if (partIt.hasNext()) { buffer.append(SUBPART_DIVIDER_TOKEN); diff --git a/core/src/main/java/org/apache/shiro/authz/permission/WildcardPermissionResolver.java b/core/src/main/java/org/apache/shiro/authz/permission/WildcardPermissionResolver.java index 0adffada7c..6bfb2d3a5f 100644 --- a/core/src/main/java/org/apache/shiro/authz/permission/WildcardPermissionResolver.java +++ b/core/src/main/java/org/apache/shiro/authz/permission/WildcardPermissionResolver.java @@ -29,19 +29,20 @@ */ public class WildcardPermissionResolver implements PermissionResolver { boolean caseSensitive; - + /** * Constructor to specify case sensitivity for the resolved permissions. + * * @param caseSensitive true if permissions should be case sensitive. */ public WildcardPermissionResolver(boolean caseSensitive) { - this.caseSensitive=caseSensitive; + this.caseSensitive = caseSensitive; } /** - * Default constructor. + * Default constructor. * Equivalent to calling WildcardPermissionResolver(false) - * + * * @see WildcardPermissionResolver#WildcardPermissionResolver(boolean) */ public WildcardPermissionResolver() { @@ -50,26 +51,29 @@ public WildcardPermissionResolver() { /** * Set the case sensitivity of the resolved Wildcard permissions. + * * @param state the caseSensitive flag state for resolved permissions. */ public void setCaseSensitive(boolean state) { this.caseSensitive = state; } + /** * Return true if this resolver produces case sensitive permissions. + * * @return true if this resolver produces case sensitive permissions. */ public boolean isCaseSensitive() { return caseSensitive; } - + /** * Returns a new {@link WildcardPermission WildcardPermission} instance constructed based on the specified * permissionString. * * @param permissionString the permission string to convert to a {@link Permission Permission} instance. * @return a new {@link WildcardPermission WildcardPermission} instance constructed based on the specified - * permissionString + * permissionString */ public Permission resolvePermission(String permissionString) { return new WildcardPermission(permissionString, caseSensitive); diff --git a/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareExecutor.java b/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareExecutor.java index 78c8424292..d7e0769fd8 100644 --- a/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareExecutor.java +++ b/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareExecutor.java @@ -95,7 +95,7 @@ public void setTargetExecutor(Executor targetExecutor) { * {@code SecurityUtils}.{@link SecurityUtils#getSubject() getSubject()}. * * @return the currently Subject instance that should be associated with Runnable or Callable instances before - * being dispatched to the target {@code Executor} instance. + * being dispatched to the target {@code Executor} instance. */ protected Subject getSubject() { return SecurityUtils.getSubject(); diff --git a/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareExecutorService.java b/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareExecutorService.java index 07be8bdcf8..52fa28104c 100644 --- a/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareExecutorService.java +++ b/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareExecutorService.java @@ -23,7 +23,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.concurrent.*; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * {@code ExecutorService} implementation that will automatically first associate any argument @@ -40,7 +46,8 @@ * Consider this code that could be repeated in many places across an application: *

  * {@link Callable Callable} applicationWork = //instantiate or acquire Callable from somewhere
- * {@link Subject Subject} subject = {@link org.apache.shiro.SecurityUtils SecurityUtils}.{@link org.apache.shiro.SecurityUtils#getSubject() getSubject()};
+ * {@link Subject Subject} subject = {@link org.apache.shiro.SecurityUtils SecurityUtils}.
+ * {@link org.apache.shiro.SecurityUtils#getSubject() getSubject()};
  * {@link Callable Callable} work = subject.{@link Subject#associateWith(Callable) associateWith(applicationWork)};
  * {@link ExecutorService anExecutorService}.{@link ExecutorService#submit(Callable) submit(work)};
  * 
@@ -78,8 +85,8 @@ public void setTargetExecutorService(ExecutorService targetExecutorService) { @Override public void setTargetExecutor(Executor targetExecutor) { if (!(targetExecutor instanceof ExecutorService)) { - String msg = "The " + getClass().getName() + " implementation only accepts " + - ExecutorService.class.getName() + " target instances."; + String msg = "The " + getClass().getName() + " implementation only accepts " + + ExecutorService.class.getName() + " target instances."; throw new IllegalArgumentException(msg); } super.setTargetExecutor(targetExecutor); diff --git a/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareScheduledExecutorService.java b/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareScheduledExecutorService.java index 9013ec8275..227604b086 100644 --- a/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareScheduledExecutorService.java +++ b/core/src/main/java/org/apache/shiro/concurrent/SubjectAwareScheduledExecutorService.java @@ -18,7 +18,12 @@ */ package org.apache.shiro.concurrent; -import java.util.concurrent.*; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; /** * Same concept as the {@link SubjectAwareExecutorService} but additionally supports the @@ -47,8 +52,8 @@ public void setTargetScheduledExecutorService(ScheduledExecutorService targetSch @Override public void setTargetExecutor(Executor targetExecutor) { if (!(targetExecutor instanceof ScheduledExecutorService)) { - String msg = "The " + getClass().getName() + " implementation only accepts " + - ScheduledExecutorService.class.getName() + " target instances."; + String msg = "The " + getClass().getName() + " implementation only accepts " + + ScheduledExecutorService.class.getName() + " target instances."; throw new IllegalArgumentException(msg); } super.setTargetExecutorService((ScheduledExecutorService) targetExecutor); @@ -57,8 +62,8 @@ public void setTargetExecutor(Executor targetExecutor) { @Override public void setTargetExecutorService(ExecutorService targetExecutorService) { if (!(targetExecutorService instanceof ScheduledExecutorService)) { - String msg = "The " + getClass().getName() + " implementation only accepts " + - ScheduledExecutorService.class.getName() + " target instances."; + String msg = "The " + getClass().getName() + " implementation only accepts " + + ScheduledExecutorService.class.getName() + " target instances."; throw new IllegalArgumentException(msg); } super.setTargetExecutorService(targetExecutorService); diff --git a/core/src/main/java/org/apache/shiro/concurrent/package-info.java b/core/src/main/java/org/apache/shiro/concurrent/package-info.java index 9163cdd0c5..4d11d40b98 100644 --- a/core/src/main/java/org/apache/shiro/concurrent/package-info.java +++ b/core/src/main/java/org/apache/shiro/concurrent/package-info.java @@ -20,9 +20,9 @@ * {@link java.util.concurrent.Executor Executor}, {@link java.util.concurrent.ExecutorService ExecutorService}, * and {@link java.util.concurrent.ScheduledExecutorService ScheduledExecutorService} implementations for transparent * {@link org.apache.shiro.subject.Subject Subject} association with threads in an asynchronous execution environment. - * - * @see SubjectAwareExecutor - * @see SubjectAwareExecutorService - * @see SubjectAwareScheduledExecutorService + * + * @see org.apache.shiro.concurrent.SubjectAwareExecutor + * @see org.apache.shiro.concurrent.SubjectAwareExecutorService + * @see org.apache.shiro.concurrent.SubjectAwareScheduledExecutorService */ -package org.apache.shiro.concurrent; \ No newline at end of file +package org.apache.shiro.concurrent; diff --git a/core/src/main/java/org/apache/shiro/dao/package-info.java b/core/src/main/java/org/apache/shiro/dao/package-info.java index d2798cd4c4..e91e186717 100644 --- a/core/src/main/java/org/apache/shiro/dao/package-info.java +++ b/core/src/main/java/org/apache/shiro/dao/package-info.java @@ -23,4 +23,4 @@ * * @since 1.2 */ -package org.apache.shiro.dao; \ No newline at end of file +package org.apache.shiro.dao; diff --git a/core/src/main/java/org/apache/shiro/env/BasicIniEnvironment.java b/core/src/main/java/org/apache/shiro/env/BasicIniEnvironment.java index 280809ffa5..c4abba0b9b 100644 --- a/core/src/main/java/org/apache/shiro/env/BasicIniEnvironment.java +++ b/core/src/main/java/org/apache/shiro/env/BasicIniEnvironment.java @@ -19,6 +19,7 @@ package org.apache.shiro.env; import java.util.function.Function; + import org.apache.shiro.config.Ini; import org.apache.shiro.ini.IniSecurityManagerFactory; diff --git a/core/src/main/java/org/apache/shiro/env/DefaultEnvironment.java b/core/src/main/java/org/apache/shiro/env/DefaultEnvironment.java index 45b3433906..c243db88b3 100644 --- a/core/src/main/java/org/apache/shiro/env/DefaultEnvironment.java +++ b/core/src/main/java/org/apache/shiro/env/DefaultEnvironment.java @@ -73,13 +73,13 @@ public DefaultEnvironment(Map seed) { * {@link #lookupSecurityManager()} method is provided as an alternative. * * @return the application's {@code SecurityManager} instance accessible in the backing map using the - * {@link #getSecurityManagerName() securityManagerName} property as the lookup key. + * {@link #getSecurityManagerName() securityManagerName} property as the lookup key. */ public SecurityManager getSecurityManager() throws IllegalStateException { SecurityManager securityManager = lookupSecurityManager(); if (securityManager == null) { - throw new IllegalStateException("No SecurityManager found in Environment. This is an invalid " + - "environment state."); + throw new IllegalStateException("No SecurityManager found in Environment. This is an invalid " + + "environment state."); } return securityManager; } @@ -107,7 +107,7 @@ protected SecurityManager lookupSecurityManager() { * instance. Unless set otherwise, the default is {@code securityManager}. * * @return the name of the {@link SecurityManager} instance in the backing map. Used as a key to lookup the - * instance. + * instance. */ public String getSecurityManagerName() { return securityManagerName; @@ -129,7 +129,7 @@ public void setSecurityManagerName(String securityManagerName) { * * @return the live (modifiable) internal objects collection. */ - public Map getObjects() { + public Map getObjects() { return this.objects; } @@ -147,10 +147,11 @@ public T getObject(String name, Class requiredType) throws RequiredTypeEx return null; } if (!requiredType.isInstance(o)) { - String msg = "Object named '" + name + "' (of type [" + o.getClass().getName() + "]) is not of required type [" + requiredType.getName() + "]."; + String msg = "Object named '" + name + "' (of type [" + o.getClass().getName() + "]) is not of required type [" + + requiredType.getName() + "]."; throw new RequiredTypeException(msg); } - return (T)o; + return (T) o; } public void setObject(String name, Object instance) { diff --git a/core/src/main/java/org/apache/shiro/env/NamedObjectEnvironment.java b/core/src/main/java/org/apache/shiro/env/NamedObjectEnvironment.java index 1d6aa349b8..e428adcdee 100644 --- a/core/src/main/java/org/apache/shiro/env/NamedObjectEnvironment.java +++ b/core/src/main/java/org/apache/shiro/env/NamedObjectEnvironment.java @@ -29,12 +29,12 @@ public interface NamedObjectEnvironment extends Environment { * Returns the object in Shiro's environment with the specified name and type or {@code null} if * no object with that name was found. * - * @param name the assigned name of the object. + * @param name the assigned name of the object. * @param requiredType the class to which the discovered object must be assignable. - * @param the type of the class - * @throws RequiredTypeException if the discovered object does not equal, extend, or implement the specified class. + * @param the type of the class * @return the object in Shiro's environment with the specified name (of the specified type) or {@code null} if * no object with that name was found. + * @throws RequiredTypeException if the discovered object does not equal, extend, or implement the specified class. */ T getObject(String name, Class requiredType) throws RequiredTypeException; } diff --git a/core/src/main/java/org/apache/shiro/env/package-info.java b/core/src/main/java/org/apache/shiro/env/package-info.java index 6ad3bd3f34..a725a1a7b6 100644 --- a/core/src/main/java/org/apache/shiro/env/package-info.java +++ b/core/src/main/java/org/apache/shiro/env/package-info.java @@ -17,9 +17,10 @@ * under the License. */ /** - * Concepts used to represent Shiro's aggregate state in an application. An {@link Environment} instance represents + * Concepts used to represent Shiro's aggregate state in an application. + * An {@link org.apache.shiro.env.Environment} instance represents * everything Shiro needs to function in an application. * - * @see Environment + * @see org.apache.shiro.env.Environment */ -package org.apache.shiro.env; \ No newline at end of file +package org.apache.shiro.env; diff --git a/core/src/main/java/org/apache/shiro/ini/IniFactorySupport.java b/core/src/main/java/org/apache/shiro/ini/IniFactorySupport.java index b83477e524..4fb2370bd5 100644 --- a/core/src/main/java/org/apache/shiro/ini/IniFactorySupport.java +++ b/core/src/main/java/org/apache/shiro/ini/IniFactorySupport.java @@ -32,15 +32,19 @@ * Base support class for {@link Factory} implementations that generate their instance(s) based on * {@link Ini} configuration. * + * @param T * @since 1.0 * @deprecated use Shiro's {@code Environment} mechanisms instead. */ @Deprecated public abstract class IniFactorySupport extends AbstractFactory { + /** + * default ini resource path. + */ public static final String DEFAULT_INI_RESOURCE_PATH = "classpath:shiro.ini"; - private static transient final Logger log = LoggerFactory.getLogger(IniFactorySupport.class); + private static final Logger LOGGER = LoggerFactory.getLogger(IniFactorySupport.class); private Ini ini; @@ -64,6 +68,7 @@ public void setIni(Ini ini) { /** * Returns a mapping of String to bean representing the default set of object used by the factory. * These beans can be used by this factory in conjunction with objects parsed from the INI configuration. + * * @return A Map of default objects, or null. * @since 1.4 */ @@ -74,6 +79,7 @@ public void setIni(Ini ini) { /** * Sets the default objects used by this factory. These defaults may be used in conjunction with the INI * configuration. + * * @param defaultBeans String to object mapping used for default configuration in this factory. * @since 1.4 */ @@ -86,16 +92,16 @@ public void setDefaults(Map defaultBeans) { * the file does not exist. * * @return a new Ini instance created from the default {@code classpath:shiro.ini} file, or {@code null} if - * the file does not exist. + * the file does not exist. */ public static Ini loadDefaultClassPathIni() { Ini ini = null; if (ResourceUtils.resourceExists(DEFAULT_INI_RESOURCE_PATH)) { - log.debug("Found shiro.ini at the root of the classpath."); + LOGGER.debug("Found shiro.ini at the root of the classpath."); ini = new Ini(); ini.loadFromPath(DEFAULT_INI_RESOURCE_PATH); if (CollectionUtils.isEmpty(ini)) { - log.warn("shiro.ini found at the root of the classpath, but it did not contain any data."); + LOGGER.warn("shiro.ini found at the root of the classpath, but it did not contain any data."); } } return ini; @@ -115,7 +121,7 @@ public static Ini loadDefaultClassPathIni() { protected Ini resolveIni() { Ini ini = getIni(); if (CollectionUtils.isEmpty(ini)) { - log.debug("Null or empty Ini instance. Falling back to the default {} file.", DEFAULT_INI_RESOURCE_PATH); + LOGGER.debug("Null or empty Ini instance. Falling back to the default {} file.", DEFAULT_INI_RESOURCE_PATH); ini = loadDefaultClassPathIni(); } return ini; @@ -137,20 +143,20 @@ public T createInstance() { T instance; if (CollectionUtils.isEmpty(ini)) { - log.debug("No populated Ini available. Creating a default instance."); + LOGGER.debug("No populated Ini available. Creating a default instance."); instance = createDefaultInstance(); if (instance == null) { - String msg = getClass().getName() + " implementation did not return a default instance in " + - "the event of a null/empty Ini configuration. This is required to support the " + - "Factory interface. Please check your implementation."; + String msg = getClass().getName() + " implementation did not return a default instance in " + + "the event of a null/empty Ini configuration. This is required to support the " + + "Factory interface. Please check your implementation."; throw new IllegalStateException(msg); } } else { - log.debug("Creating instance from Ini [" + ini + "]"); + LOGGER.debug("Creating instance from Ini [" + ini + "]"); instance = createInstance(ini); if (instance == null) { - String msg = getClass().getName() + " implementation did not return a constructed instance from " + - "the createInstance(Ini) method implementation."; + String msg = getClass().getName() + " implementation did not return a constructed instance from " + + "the createInstance(Ini) method implementation."; throw new IllegalStateException(msg); } } @@ -158,7 +164,18 @@ public T createInstance() { return instance; } + /** + * create instance. + * + * @param ini ini + * @return T + */ protected abstract T createInstance(Ini ini); + /** + * create default instance. + * + * @return T + */ protected abstract T createDefaultInstance(); } diff --git a/core/src/main/java/org/apache/shiro/ini/IniSecurityManagerFactory.java b/core/src/main/java/org/apache/shiro/ini/IniSecurityManagerFactory.java index 171292a985..328dce68bc 100644 --- a/core/src/main/java/org/apache/shiro/ini/IniSecurityManagerFactory.java +++ b/core/src/main/java/org/apache/shiro/ini/IniSecurityManagerFactory.java @@ -50,12 +50,22 @@ @Deprecated public class IniSecurityManagerFactory extends IniFactorySupport { + /** + * main section name. + */ public static final String MAIN_SECTION_NAME = "main"; + /** + * security manager name. + */ public static final String SECURITY_MANAGER_NAME = "securityManager"; + + /** + * ini realm name. + */ public static final String INI_REALM_NAME = "iniRealm"; - private static transient final Logger log = LoggerFactory.getLogger(IniSecurityManagerFactory.class); + private static final Logger LOGGER = LoggerFactory.getLogger(IniSecurityManagerFactory.class); private ReflectionBuilder builder; @@ -81,7 +91,7 @@ public IniSecurityManagerFactory(String iniResourcePath) { } public void destroy() { - if(getReflectionBuilder() != null) { + if (getReflectionBuilder() != null) { getReflectionBuilder().destroy(); } } @@ -127,8 +137,8 @@ protected boolean isAutoApplyRealms(SecurityManager securityManager) { RealmSecurityManager realmSecurityManager = (RealmSecurityManager) securityManager; Collection realms = realmSecurityManager.getRealms(); if (!CollectionUtils.isEmpty(realms)) { - log.info("Realms have been explicitly set on the SecurityManager instance - auto-setting of " + - "realms will not occur."); + LOGGER.info("Realms have been explicitly set on the SecurityManager instance - auto-setting of " + + "realms will not occur."); autoApply = false; } } @@ -214,9 +224,9 @@ private Collection getRealms(Map instances) { if (existingName == null || existingName.startsWith(realm.getClass().getName())) { if (realm instanceof Nameable) { ((Nameable) realm).setName(name); - log.debug("Applied name '{}' to Nameable realm instance {}", name, realm); + LOGGER.debug("Applied name '{}' to Nameable realm instance {}", name, realm); } else { - log.info("Realm does not implement the {} interface. Configured name will not be applied.", + LOGGER.info("Realm does not implement the {} interface. Configured name will not be applied.", Nameable.class.getName()); } } @@ -232,8 +242,8 @@ private void assertRealmSecurityManager(SecurityManager securityManager) { throw new NullPointerException("securityManager instance cannot be null"); } if (!(securityManager instanceof RealmSecurityManager)) { - String msg = "securityManager instance is not a " + RealmSecurityManager.class.getName() + - " instance. This is required to access or configure realms on the instance."; + String msg = "securityManager instance is not a " + RealmSecurityManager.class.getName() + + " instance. This is required to access or configure realms on the instance."; throw new ConfigurationException(msg); } } @@ -250,13 +260,13 @@ protected void applyRealmsToSecurityManager(Collection realms, SecurityMa * * @param ini the Ini instance to inspect for account data resulting in an implicitly created realm. * @return {@code true} if the Ini contains account data and a {@code Realm} should be implicitly - * {@link #createRealm(Ini) created} to reflect the account data, {@code false} if no realm should be - * implicitly created. + * {@link #createRealm(Ini) created} to reflect the account data, {@code false} if no realm should be + * implicitly created. */ protected boolean shouldImplicitlyCreateRealm(Ini ini) { - return !CollectionUtils.isEmpty(ini) && - (!CollectionUtils.isEmpty(ini.getSection(IniRealm.ROLES_SECTION_NAME)) || - !CollectionUtils.isEmpty(ini.getSection(IniRealm.USERS_SECTION_NAME))); + return !CollectionUtils.isEmpty(ini) + && (!CollectionUtils.isEmpty(ini.getSection(IniRealm.ROLES_SECTION_NAME)) + || !CollectionUtils.isEmpty(ini.getSection(IniRealm.USERS_SECTION_NAME))); } /** @@ -269,12 +279,14 @@ protected Realm createRealm(Ini ini) { //IniRealm realm = new IniRealm(ini); changed to support SHIRO-322 IniRealm realm = new IniRealm(); realm.setName(INI_REALM_NAME); - realm.setIni(ini); //added for SHIRO-322 + //added for SHIRO-322 + realm.setIni(ini); return realm; } /** * Returns the ReflectionBuilder instance used to create SecurityManagers object graph. + * * @return ReflectionBuilder instance used to create SecurityManagers object graph. * @since 1.4 */ @@ -285,6 +297,7 @@ public ReflectionBuilder getReflectionBuilder() { /** * Sets the ReflectionBuilder that will be used to create the SecurityManager based on the contents of * the Ini configuration. + * * @param builder The ReflectionBuilder used to parse the Ini configuration. * @since 1.4 */ @@ -292,4 +305,4 @@ public ReflectionBuilder getReflectionBuilder() { public void setReflectionBuilder(ReflectionBuilder builder) { this.builder = builder; } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/apache/shiro/ini/package-info.java b/core/src/main/java/org/apache/shiro/ini/package-info.java new file mode 100644 index 0000000000..100556138b --- /dev/null +++ b/core/src/main/java/org/apache/shiro/ini/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Concepts used to represent Shiro's aggregate state in an application. + * An {@link org.apache.shiro.env.Environment} instance represents + * everything Shiro needs to function in an application. + * + * @see org.apache.shiro.env.Environment + */ +package org.apache.shiro.ini; diff --git a/core/src/main/java/org/apache/shiro/jndi/JndiLocator.java b/core/src/main/java/org/apache/shiro/jndi/JndiLocator.java index 1701e73bbe..60dfad4414 100644 --- a/core/src/main/java/org/apache/shiro/jndi/JndiLocator.java +++ b/core/src/main/java/org/apache/shiro/jndi/JndiLocator.java @@ -42,16 +42,16 @@ public class JndiLocator { /** - * Private class log. + * JNDI prefix used in a J2EE container */ - private static final Logger log = LoggerFactory.getLogger(JndiLocator.class); + public static final String CONTAINER_PREFIX = "java:comp/env/"; /** - * JNDI prefix used in a J2EE container + * Private class log. */ - public static final String CONTAINER_PREFIX = "java:comp/env/"; + private static final Logger LOGGER = LoggerFactory.getLogger(JndiLocator.class); - private boolean resourceRef = false; + private boolean resourceRef; private JndiTemplate jndiTemplate = new JndiTemplate(); @@ -141,20 +141,19 @@ protected Object lookup(String jndiName, Class requiredType) throws NamingExcept Object jndiObject; try { jndiObject = getJndiTemplate().lookup(convertedName, requiredType); - } - catch (NamingException ex) { + } catch (NamingException ex) { if (!convertedName.equals(jndiName)) { // Try fallback to originally specified name... - if (log.isDebugEnabled()) { - log.debug("Converted JNDI name [" + convertedName + - "] not found - trying original name [" + jndiName + "]. " + ex); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Converted JNDI name [" + convertedName + + "] not found - trying original name [" + jndiName + "]. " + ex); } jndiObject = getJndiTemplate().lookup(jndiName, requiredType); } else { throw ex; } } - log.debug("Located object with JNDI name '{}'", convertedName); + LOGGER.debug("Located object with JNDI name '{}'", convertedName); return jndiObject; } diff --git a/core/src/main/java/org/apache/shiro/jndi/JndiObjectFactory.java b/core/src/main/java/org/apache/shiro/jndi/JndiObjectFactory.java index a67bf30393..2fa72bb442 100644 --- a/core/src/main/java/org/apache/shiro/jndi/JndiObjectFactory.java +++ b/core/src/main/java/org/apache/shiro/jndi/JndiObjectFactory.java @@ -24,6 +24,7 @@ /** * A factory implementation intended to be used to look up objects in jndi. + * * @param * @since 1.2 */ @@ -34,7 +35,7 @@ public class JndiObjectFactory extends JndiLocator implements Factory { public T getInstance() { try { - if(requiredType != null) { + if (requiredType != null) { return requiredType.cast(this.lookup(resourceName, requiredType)); } else { return (T) this.lookup(resourceName); diff --git a/core/src/main/java/org/apache/shiro/jndi/JndiTemplate.java b/core/src/main/java/org/apache/shiro/jndi/JndiTemplate.java index a9870518b1..4600a802f6 100644 --- a/core/src/main/java/org/apache/shiro/jndi/JndiTemplate.java +++ b/core/src/main/java/org/apache/shiro/jndi/JndiTemplate.java @@ -44,11 +44,13 @@ */ public class JndiTemplate { - private static final Logger log = LoggerFactory.getLogger(JndiTemplate.class); + private static final Logger LOGGER = LoggerFactory.getLogger(JndiTemplate.class); private Properties environment; - /** Create a new JndiTemplate instance. */ + /** + * Create a new JndiTemplate instance. + */ public JndiTemplate() { } @@ -91,12 +93,11 @@ public Object execute(JndiCallback contextCallback) throws NamingException { Context ctx = createInitialContext(); try { return contextCallback.doInContext(ctx); - } - finally { + } finally { try { ctx.close(); } catch (NamingException ex) { - log.debug("Could not close JNDI InitialContext", ex); + LOGGER.debug("Could not close JNDI InitialContext", ex); } } } @@ -115,7 +116,7 @@ protected Context createInitialContext() throws NamingException { Hashtable icEnv = null; if (env != null) { icEnv = new Hashtable(env.size()); - for (Enumeration en = env.propertyNames(); en.hasMoreElements();) { + for (Enumeration en = env.propertyNames(); en.hasMoreElements(); ) { String key = (String) en.nextElement(); icEnv.put(key, env.getProperty(key)); } @@ -128,12 +129,12 @@ protected Context createInitialContext() throws NamingException { * * @param name the JNDI name of the object * @return object found (cannot be null; if a not so well-behaved - * JNDI implementations returns null, a NamingException gets thrown) + * JNDI implementations returns null, a NamingException gets thrown) * @throws NamingException if there is no object with the given * name bound to JNDI */ public Object lookup(final String name) throws NamingException { - log.debug("Looking up JNDI object with name '{}'", name); + LOGGER.debug("Looking up JNDI object with name '{}'", name); return execute(new JndiCallback() { public Object doInContext(Context ctx) throws NamingException { Object located = ctx.lookup(name); @@ -155,16 +156,16 @@ public Object doInContext(Context ctx) throws NamingException { * if the value is Object.class, this method will succeed whatever * the class of the returned instance. * @return object found (cannot be null; if a not so well-behaved - * JNDI implementations returns null, a NamingException gets thrown) + * JNDI implementations returns null, a NamingException gets thrown) * @throws NamingException if there is no object with the given * name bound to JNDI */ public Object lookup(String name, Class requiredType) throws NamingException { Object jndiObject = lookup(name); if (requiredType != null && !requiredType.isInstance(jndiObject)) { - String msg = "Jndi object acquired under name '" + name + "' is of type [" + - jndiObject.getClass().getName() + "] and not assignable to the required type [" + - requiredType.getName() + "]."; + String msg = "Jndi object acquired under name '" + name + "' is of type [" + + jndiObject.getClass().getName() + "] and not assignable to the required type [" + + requiredType.getName() + "]."; throw new NamingException(msg); } return jndiObject; @@ -178,7 +179,7 @@ public Object lookup(String name, Class requiredType) throws NamingException { * @throws NamingException thrown by JNDI, mostly name already bound */ public void bind(final String name, final Object object) throws NamingException { - log.debug("Binding JNDI object with name '{}'", name); + LOGGER.debug("Binding JNDI object with name '{}'", name); execute(new JndiCallback() { public Object doInContext(Context ctx) throws NamingException { ctx.bind(name, object); @@ -196,7 +197,7 @@ public Object doInContext(Context ctx) throws NamingException { * @throws NamingException thrown by JNDI */ public void rebind(final String name, final Object object) throws NamingException { - log.debug("Rebinding JNDI object with name '{}'", name); + LOGGER.debug("Rebinding JNDI object with name '{}'", name); execute(new JndiCallback() { public Object doInContext(Context ctx) throws NamingException { ctx.rebind(name, object); @@ -212,7 +213,7 @@ public Object doInContext(Context ctx) throws NamingException { * @throws NamingException thrown by JNDI, mostly name not found */ public void unbind(final String name) throws NamingException { - log.debug("Unbinding JNDI object with name '{}'", name); + LOGGER.debug("Unbinding JNDI object with name '{}'", name); execute(new JndiCallback() { public Object doInContext(Context ctx) throws NamingException { ctx.unbind(name); diff --git a/core/src/main/java/org/apache/shiro/ldap/package-info.java b/core/src/main/java/org/apache/shiro/ldap/package-info.java index 48ccc3b6c5..bef3f3780d 100644 --- a/core/src/main/java/org/apache/shiro/ldap/package-info.java +++ b/core/src/main/java/org/apache/shiro/ldap/package-info.java @@ -17,10 +17,9 @@ * under the License. */ -package org.apache.shiro.ldap; - /** * Support for accessing
LDAP data sources. * * @since 1.2 - */ \ No newline at end of file + */ +package org.apache.shiro.ldap; diff --git a/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java b/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java index 0d1b13d58d..0dcf38c69b 100644 --- a/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java +++ b/core/src/main/java/org/apache/shiro/mgt/AbstractRememberMeManager.java @@ -18,7 +18,9 @@ */ package org.apache.shiro.mgt; +import java.util.Objects; import java.util.function.Supplier; + import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; @@ -68,7 +70,7 @@ public abstract class AbstractRememberMeManager implements RememberMeManager { /** * private inner log instance. */ - private static final Logger log = LoggerFactory.getLogger(AbstractRememberMeManager.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRememberMeManager.class); /** * Serializer to use for converting PrincipalCollection instances to/from byte arrays @@ -78,7 +80,8 @@ public abstract class AbstractRememberMeManager implements RememberMeManager { /** * Cipher to use for encrypting/decrypting serialized byte arrays for added security */ - private CipherService cipherService = new AesCipherService();; + private CipherService cipherService = new AesCipherService(); + ; /** * Cipher encryption key to use with the Cipher when encrypting data @@ -117,7 +120,7 @@ public AbstractRememberMeManager(Supplier keySupplier) { * {@link org.apache.shiro.lang.io.DefaultSerializer}. * * @return the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for - * persistent remember me storage. + * persistent remember me storage. */ public Serializer getSerializer() { return serializer; @@ -143,7 +146,7 @@ public void setSerializer(Serializer serializer) { * Unless overridden by the {@link #setCipherService} method, the default instance is an {@link AesCipherService}. * * @return the {@code Cipher} to use for encrypting and decrypting serialized identity data to prevent easy - * inspection of Subject identity data + * inspection of Subject identity data */ public CipherService getCipherService() { return cipherService; @@ -273,15 +276,16 @@ public void setCipherKey(byte[] cipherKey) { * @return true if remember me services should be performed as a result of the successful authentication attempt. */ protected boolean isRememberMe(AuthenticationToken token) { - return token != null && (token instanceof RememberMeAuthenticationToken) && - ((RememberMeAuthenticationToken) token).isRememberMe(); + return Objects.nonNull(token) && (token instanceof RememberMeAuthenticationToken) + && ((RememberMeAuthenticationToken) token).isRememberMe(); } /** * Reacts to the successful login attempt by first always {@link #forgetIdentity(Subject) forgetting} any previously * stored identity. Then if the {@code token} * {@link #isRememberMe(org.apache.shiro.authc.AuthenticationToken) is a RememberMe} token, the associated identity - * will be {@link #rememberIdentity(org.apache.shiro.subject.Subject, org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.authc.AuthenticationInfo) remembered} + * will be {@link #rememberIdentity(org.apache.shiro.subject.Subject, org.apache.shiro.authc.AuthenticationToken, + * org.apache.shiro.authc.AuthenticationInfo) remembered} * for later retrieval during a new user session. * * @param subject the subject for which the principals are being remembered. @@ -296,9 +300,9 @@ public void onSuccessfulLogin(Subject subject, AuthenticationToken token, Authen if (isRememberMe(token)) { rememberIdentity(subject, token, info); } else { - if (log.isDebugEnabled()) { - log.debug("AuthenticationToken did not indicate RememberMe is requested. " + - "RememberMe functionality will not be executed for corresponding account."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("AuthenticationToken did not indicate RememberMe is requested. " + + "RememberMe functionality will not be executed for corresponding account."); } } } @@ -414,7 +418,7 @@ public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext * is being used to construct a {@link Subject} instance. To be used to assist with data * lookup. * @return the previously persisted serialized identity, or {@code null} if there is no available data for the - * Subject. + * Subject. */ protected abstract byte[] getRememberedSerializedIdentity(SubjectContext subjectContext); @@ -451,15 +455,16 @@ protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectCont * is being used to construct a {@link Subject} instance. * @return nothing - the original {@code RuntimeException} is propagated in all cases. */ + @SuppressWarnings("checkstyle:LineLength") protected PrincipalCollection onRememberedPrincipalFailure(RuntimeException e, SubjectContext context) { - if (log.isWarnEnabled()) { - String message = "There was a failure while trying to retrieve remembered principals. This could be due to a " + - "configuration problem or corrupted principals. This could also be due to a recently " + - "changed encryption key, if you are using a shiro.ini file, this property would be " + - "'securityManager.rememberMeManager.cipherKey' see: http://shiro.apache.org/web.html#Web-RememberMeServices. " + - "The remembered identity will be forgotten and not used for this request."; - log.warn(message); + if (LOGGER.isWarnEnabled()) { + String message = "There was a failure while trying to retrieve remembered principals. This could be due to a " + + "configuration problem or corrupted principals. This could also be due to a recently " + + "changed encryption key, if you are using a shiro.ini file, this property would be " + + "'securityManager.rememberMeManager.cipherKey' see: http://shiro.apache.org/web.html#Web-RememberMeServices. " + + "The remembered identity will be forgotten and not used for this request."; + LOGGER.warn(message); } forgetIdentity(context); //propagate - security manager implementation will handle and warn appropriately diff --git a/core/src/main/java/org/apache/shiro/mgt/AuthenticatingSecurityManager.java b/core/src/main/java/org/apache/shiro/mgt/AuthenticatingSecurityManager.java index c1a7cdb535..a455e0898d 100644 --- a/core/src/main/java/org/apache/shiro/mgt/AuthenticatingSecurityManager.java +++ b/core/src/main/java/org/apache/shiro/mgt/AuthenticatingSecurityManager.java @@ -65,7 +65,7 @@ public AuthenticatingSecurityManager() { * {@link org.apache.shiro.authc.pam.ModularRealmAuthenticator ModularRealmAuthenticator}. * * @return the delegate Authenticator instance that this SecurityManager uses to perform all - * authentication operations. + * authentication operations. */ public Authenticator getAuthenticator() { return authenticator; diff --git a/core/src/main/java/org/apache/shiro/mgt/AuthorizingSecurityManager.java b/core/src/main/java/org/apache/shiro/mgt/AuthorizingSecurityManager.java index e2555d8bf9..daf2d27664 100644 --- a/core/src/main/java/org/apache/shiro/mgt/AuthorizingSecurityManager.java +++ b/core/src/main/java/org/apache/shiro/mgt/AuthorizingSecurityManager.java @@ -145,6 +145,7 @@ public void checkPermissions(PrincipalCollection principals, String... permissio this.authorizer.checkPermissions(principals, permissions); } + @SuppressWarnings("checkstyle:LineLength") public void checkPermissions(PrincipalCollection principals, Collection permissions) throws AuthorizationException { this.authorizer.checkPermissions(principals, permissions); } @@ -168,8 +169,9 @@ public void checkRole(PrincipalCollection principals, String role) throws Author public void checkRoles(PrincipalCollection principals, Collection roles) throws AuthorizationException { this.authorizer.checkRoles(principals, roles); } - + public void checkRoles(PrincipalCollection principals, String... roles) throws AuthorizationException { this.authorizer.checkRoles(principals, roles); - } + } + } diff --git a/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java b/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java index 80219d4c68..d83f971fc8 100644 --- a/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java +++ b/core/src/main/java/org/apache/shiro/mgt/CachingSecurityManager.java @@ -46,6 +46,7 @@ public abstract class CachingSecurityManager implements SecurityManager, Destroy /** * The EventBus to use to use to publish and receive events of interest during Shiro's lifecycle. + * * @since 1.3 */ private EventBus eventBus; @@ -111,7 +112,7 @@ public EventBus getEventBus() { * eventBus is available. * * @param eventBus the EventBus used by this {@code SecurityManager} and potentially any of its - * children components. + * children components. * @since 1.3 */ public void setEventBus(EventBus eventBus) { @@ -124,7 +125,7 @@ public void setEventBus(EventBus eventBus) { */ protected void applyEventBusToCacheManager() { if (this.eventBus != null && this.cacheManager != null && this.cacheManager instanceof EventBusAware) { - ((EventBusAware)this.cacheManager).setEventBus(this.eventBus); + ((EventBusAware) this.cacheManager).setEventBus(this.eventBus); } } diff --git a/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java b/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java index 9c59218fd7..fb2962ebf1 100644 --- a/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java +++ b/core/src/main/java/org/apache/shiro/mgt/DefaultSecurityManager.java @@ -72,9 +72,10 @@ * * @since 0.2 */ +@SuppressWarnings("checkstyle:MethodCount") public class DefaultSecurityManager extends SessionsSecurityManager { - private static final Logger log = LoggerFactory.getLogger(DefaultSecurityManager.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSecurityManager.class); protected RememberMeManager rememberMeManager; protected SubjectDAO subjectDAO; @@ -118,6 +119,7 @@ public SubjectFactory getSubjectFactory() { return subjectFactory; } + @SuppressWarnings("checkstyle:LineLength") /** * Sets the {@code SubjectFactory} responsible for creating {@link Subject} instances exposed to the application. * @@ -133,7 +135,7 @@ public void setSubjectFactory(SubjectFactory subjectFactory) { * implementation is a {@link DefaultSubjectDAO}. * * @return the {@code SubjectDAO} responsible for persisting Subject state, typically used after login or when an - * Subject identity is discovered (e.g. after RememberMe services). + * Subject identity is discovered (e.g. after RememberMe services). * @see DefaultSubjectDAO * @since 1.2 */ @@ -174,7 +176,7 @@ protected SubjectContext createSubjectContext() { * @param info the {@code AuthenticationInfo} of a newly authenticated user. * @param existing the existing {@code Subject} instance that initiated the authentication attempt * @return the {@code Subject} instance that represents the context and session data for the newly - * authenticated subject. + * authenticated subject. */ protected Subject createSubject(AuthenticationToken token, AuthenticationInfo info, Subject existing) { SubjectContext context = createSubjectContext(); @@ -210,18 +212,18 @@ protected void rememberMeSuccessfulLogin(AuthenticationToken token, Authenticati try { rmm.onSuccessfulLogin(subject, token, info); } catch (Exception e) { - if (log.isWarnEnabled()) { - String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + - "] threw an exception during onSuccessfulLogin. RememberMe services will not be " + - "performed for account [" + info + "]."; - log.warn(msg, e); + if (LOGGER.isWarnEnabled()) { + String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + + "] threw an exception during onSuccessfulLogin. RememberMe services will not be " + + "performed for account [" + info + "]."; + LOGGER.warn(msg, e); } } } else { - if (log.isTraceEnabled()) { - log.trace("This " + getClass().getName() + " instance does not have a " + - "[" + RememberMeManager.class.getName() + "] instance configured. RememberMe services " + - "will not be performed for account [" + info + "]."); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("This " + getClass().getName() + " instance does not have a " + + "[" + RememberMeManager.class.getName() + "] instance configured. RememberMe services " + + "will not be performed for account [" + info + "]."); } } } @@ -232,11 +234,11 @@ protected void rememberMeFailedLogin(AuthenticationToken token, AuthenticationEx try { rmm.onFailedLogin(subject, token, ex); } catch (Exception e) { - if (log.isWarnEnabled()) { - String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + - "] threw an exception during onFailedLogin for AuthenticationToken [" + - token + "]."; - log.warn(msg, e); + if (LOGGER.isWarnEnabled()) { + String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + + "] threw an exception during onFailedLogin for AuthenticationToken [" + + token + "]."; + LOGGER.warn(msg, e); } } } @@ -248,11 +250,11 @@ protected void rememberMeLogout(Subject subject) { try { rmm.onLogout(subject); } catch (Exception e) { - if (log.isWarnEnabled()) { - String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + - "] threw an exception during onLogout for subject with principals [" + - (subject != null ? subject.getPrincipals() : null) + "]"; - log.warn(msg, e); + if (LOGGER.isWarnEnabled()) { + String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + + "] threw an exception during onLogout for subject with principals [" + + (subject != null ? subject.getPrincipals() : null) + "]"; + LOGGER.warn(msg, e); } } } @@ -277,12 +279,13 @@ public Subject login(Subject subject, AuthenticationToken token) throws Authenti try { onFailedLogin(token, ae, subject); } catch (Exception e) { - if (log.isInfoEnabled()) { - log.info("onFailedLogin method threw an " + - "exception. Logging and propagating original AuthenticationException.", e); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("onFailedLogin method threw an " + + "exception. Logging and propagating original AuthenticationException.", e); } } - throw ae; //propagate + //propagate + throw ae; } Subject loggedIn = createSubject(token, info, subject); @@ -414,10 +417,10 @@ protected void delete(Subject subject) { @SuppressWarnings({"unchecked"}) protected SubjectContext ensureSecurityManager(SubjectContext context) { if (context.resolveSecurityManager() != null) { - log.trace("Context already contains a SecurityManager instance. Returning."); + LOGGER.trace("Context already contains a SecurityManager instance. Returning."); return context; } - log.trace("No SecurityManager found in context. Adding self reference."); + LOGGER.trace("No SecurityManager found in context. Adding self reference."); context.setSecurityManager(this); return context; } @@ -438,19 +441,19 @@ protected SubjectContext ensureSecurityManager(SubjectContext context) { @SuppressWarnings({"unchecked"}) protected SubjectContext resolveSession(SubjectContext context) { if (context.resolveSession() != null) { - log.debug("Context already contains a session. Returning."); + LOGGER.debug("Context already contains a session. Returning."); return context; } try { - //Context couldn't resolve it directly, let's see if we can since we have direct access to + //Context couldn't resolve it directly, let's see if we can since we have direct access to //the session manager: Session session = resolveContextSession(context); if (session != null) { context.setSession(session); } } catch (InvalidSessionException e) { - log.debug("Resolved SubjectContext context session is invalid. Ignoring and creating an anonymous " + - "(session-less) Subject instance.", e); + LOGGER.debug("Resolved SubjectContext context session is invalid. Ignoring and creating an anonymous " + + "(session-less) Subject instance.", e); } return context; } @@ -496,13 +499,13 @@ protected SubjectContext resolvePrincipals(SubjectContext context) { PrincipalCollection principals = context.resolvePrincipals(); if (isEmpty(principals)) { - log.trace("No identity (PrincipalCollection) found in the context. Looking for a remembered identity."); + LOGGER.trace("No identity (PrincipalCollection) found in the context. Looking for a remembered identity."); principals = getRememberedIdentity(context); if (!isEmpty(principals)) { - log.debug("Found remembered PrincipalCollection. Adding to the context to be used " + - "for subject construction by the SubjectFactory."); + LOGGER.debug("Found remembered PrincipalCollection. Adding to the context to be used " + + "for subject construction by the SubjectFactory."); context.setPrincipals(principals); @@ -522,7 +525,7 @@ protected SubjectContext resolvePrincipals(SubjectContext context) { // bindPrincipalsToSession(principals, context); } else { - log.trace("No remembered identity found. Returning original context."); + LOGGER.trace("No remembered identity found. Returning original context."); } } @@ -555,8 +558,8 @@ public void logout(Subject subject) { PrincipalCollection principals = subject.getPrincipals(); if (principals != null && !principals.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("Logging out subject with primary principal {}", principals.getPrimaryPrincipal()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Logging out subject with primary principal {}", principals.getPrimaryPrincipal()); } Authenticator authc = getAuthenticator(); if (authc instanceof LogoutAware) { @@ -567,18 +570,18 @@ public void logout(Subject subject) { try { delete(subject); } catch (Exception e) { - if (log.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { String msg = "Unable to cleanly unbind Subject. Ignoring (logging out)."; - log.debug(msg, e); + LOGGER.debug(msg, e); } } finally { try { stopSession(subject); } catch (Exception e) { - if (log.isDebugEnabled()) { - String msg = "Unable to cleanly stop Session for Subject [" + subject.getPrincipal() + "] " + - "Ignoring (logging out)."; - log.debug(msg, e); + if (LOGGER.isDebugEnabled()) { + String msg = "Unable to cleanly stop Session for Subject [" + subject.getPrincipal() + "] " + + "Ignoring (logging out)."; + LOGGER.debug(msg, e); } } } @@ -612,10 +615,10 @@ protected PrincipalCollection getRememberedIdentity(SubjectContext subjectContex try { return rmm.getRememberedPrincipals(subjectContext); } catch (Exception e) { - if (log.isWarnEnabled()) { - String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + - "] threw an exception during getRememberedPrincipals()."; - log.warn(msg, e); + if (LOGGER.isWarnEnabled()) { + String msg = "Delegate RememberMeManager instance of type [" + rmm.getClass().getName() + + "] threw an exception during getRememberedPrincipals()."; + LOGGER.warn(msg, e); } } } diff --git a/core/src/main/java/org/apache/shiro/mgt/DefaultSessionStorageEvaluator.java b/core/src/main/java/org/apache/shiro/mgt/DefaultSessionStorageEvaluator.java index b720ac6cdf..b36080e96b 100644 --- a/core/src/main/java/org/apache/shiro/mgt/DefaultSessionStorageEvaluator.java +++ b/core/src/main/java/org/apache/shiro/mgt/DefaultSessionStorageEvaluator.java @@ -73,7 +73,7 @@ public boolean isSessionStorageEnabled(Subject subject) { * authenticate on every request). * * @return {@code true} if any Subject's {@code Session} may be used to persist that {@code Subject}'s state, - * {@code false} otherwise. + * {@code false} otherwise. */ public boolean isSessionStorageEnabled() { return sessionStorageEnabled; diff --git a/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectDAO.java b/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectDAO.java index 06b4e463ab..354517369b 100644 --- a/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectDAO.java +++ b/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectDAO.java @@ -80,7 +80,7 @@ */ public class DefaultSubjectDAO implements SubjectDAO { - private static final Logger log = LoggerFactory.getLogger(DefaultSubjectDAO.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSubjectDAO.class); /** * Evaluator that determines if a Subject's session may be used to store the Subject's own state. @@ -112,7 +112,7 @@ protected boolean isSessionStorageEnabled(Subject subject) { * the Subject's session. The default instance is a {@link DefaultSessionStorageEvaluator}. * * @return the {@code SessionStorageEvaluator} that will determine if a {@code Subject}'s state may be persisted in - * the Subject's session. + * the Subject's session. * @see DefaultSessionStorageEvaluator */ public SessionStorageEvaluator getSessionStorageEvaluator() { @@ -145,8 +145,8 @@ public Subject save(Subject subject) { if (isSessionStorageEnabled(subject)) { saveToSession(subject); } else { - log.trace("Session storage of subject state for Subject [{}] has been disabled: identity and " + - "authentication state are expected to be initialized on every request or invocation.", subject); + LOGGER.trace("Session storage of subject state for Subject [{}] has been disabled: identity and " + + "authentication state are expected to be initialized on every request or invocation.", subject); } return subject; @@ -191,7 +191,7 @@ protected void mergePrincipals(Subject subject) { try { Field field = DelegatingSubject.class.getDeclaredField("principals"); field.setAccessible(true); - currentPrincipals = (PrincipalCollection)field.get(subject); + currentPrincipals = (PrincipalCollection) field.get(subject); } catch (Exception e) { throw new IllegalStateException("Unable to access DelegatingSubject principals property.", e); } diff --git a/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectFactory.java b/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectFactory.java index 7e41abbcdf..618a44146b 100644 --- a/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectFactory.java +++ b/core/src/main/java/org/apache/shiro/mgt/DefaultSubjectFactory.java @@ -26,7 +26,8 @@ /** - * Default {@link SubjectFactory SubjectFactory} implementation that creates {@link org.apache.shiro.subject.support.DelegatingSubject DelegatingSubject} + * Default {@link SubjectFactory SubjectFactory} implementation that creates + * {@link org.apache.shiro.subject.support.DelegatingSubject DelegatingSubject} * instances. * * @since 1.0 @@ -49,7 +50,7 @@ public Subject createSubject(SubjectContext context) { /** * @deprecated since 1.2 - override {@link #createSubject(org.apache.shiro.subject.SubjectContext)} directly if you - * need to instantiate a custom {@link Subject} class. + * need to instantiate a custom {@link Subject} class. */ @Deprecated protected Subject newSubjectInstance(PrincipalCollection principals, boolean authenticated, String host, diff --git a/core/src/main/java/org/apache/shiro/mgt/RealmSecurityManager.java b/core/src/main/java/org/apache/shiro/mgt/RealmSecurityManager.java index aaca9d8b22..891bb1554d 100644 --- a/core/src/main/java/org/apache/shiro/mgt/RealmSecurityManager.java +++ b/core/src/main/java/org/apache/shiro/mgt/RealmSecurityManager.java @@ -139,14 +139,15 @@ protected void applyEventBusToRealms() { EventBus eventBus = getEventBus(); Collection realms = getRealms(); if (eventBus != null && realms != null && !realms.isEmpty()) { - for(Realm realm : realms) { + for (Realm realm : realms) { if (realm instanceof EventBusAware) { - ((EventBusAware)realm).setEventBus(eventBus); + ((EventBusAware) realm).setEventBus(eventBus); } } } } + @SuppressWarnings("checkstyle:LineLength") /** * Simply calls {@link #applyCacheManagerToRealms() applyCacheManagerToRealms()} to allow the * newly set {@link org.apache.shiro.cache.CacheManager CacheManager} to be propagated to the internal collection of Realm diff --git a/core/src/main/java/org/apache/shiro/mgt/SessionStorageEvaluator.java b/core/src/main/java/org/apache/shiro/mgt/SessionStorageEvaluator.java index ebdd9dfed6..8a062b213f 100644 --- a/core/src/main/java/org/apache/shiro/mgt/SessionStorageEvaluator.java +++ b/core/src/main/java/org/apache/shiro/mgt/SessionStorageEvaluator.java @@ -54,8 +54,8 @@ public interface SessionStorageEvaluator { * * @param subject the {@code Subject} for which session state persistence may be enabled * @return {@code true} if the specified {@code Subject}'s - * {@link org.apache.shiro.subject.Subject#getSession() session} may be used to persist that Subject's - * state, {@code false} otherwise. + * {@link org.apache.shiro.subject.Subject#getSession() session} may be used to persist that Subject's + * state, {@code false} otherwise. * @see Subject#getSession() * @see Subject#getSession(boolean) */ diff --git a/core/src/main/java/org/apache/shiro/mgt/SessionsSecurityManager.java b/core/src/main/java/org/apache/shiro/mgt/SessionsSecurityManager.java index abe60ece28..3f9dfd3f73 100644 --- a/core/src/main/java/org/apache/shiro/mgt/SessionsSecurityManager.java +++ b/core/src/main/java/org/apache/shiro/mgt/SessionsSecurityManager.java @@ -97,6 +97,7 @@ public SessionManager getSessionManager() { return this.sessionManager; } + @SuppressWarnings("checkstyle:LineLength") /** * Calls {@link org.apache.shiro.mgt.AuthorizingSecurityManager#afterCacheManagerSet() super.afterCacheManagerSet()} and then immediately calls * {@link #applyCacheManagerToSessionManager() applyCacheManagerToSessionManager()} to ensure the @@ -144,7 +145,7 @@ protected void applyCacheManagerToSessionManager() { protected void applyEventBusToSessionManager() { EventBus eventBus = getEventBus(); if (eventBus != null && this.sessionManager instanceof EventBusAware) { - ((EventBusAware)this.sessionManager).setEventBus(eventBus); + ((EventBusAware) this.sessionManager).setEventBus(eventBus); } } diff --git a/core/src/main/java/org/apache/shiro/realm/AuthenticatingRealm.java b/core/src/main/java/org/apache/shiro/realm/AuthenticatingRealm.java index 54d3963a1d..be0395352a 100644 --- a/core/src/main/java/org/apache/shiro/realm/AuthenticatingRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/AuthenticatingRealm.java @@ -112,9 +112,7 @@ */ public abstract class AuthenticatingRealm extends CachingRealm implements Initializable { - //TODO - complete JavaDoc - - private static final Logger log = LoggerFactory.getLogger(AuthenticatingRealm.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticatingRealm.class); private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger(); @@ -191,7 +189,7 @@ public AuthenticatingRealm(CacheManager cacheManager, CredentialsMatcher matcher * value is a {@link org.apache.shiro.authc.credential.SimpleCredentialsMatcher SimpleCredentialsMatcher} instance. * * @return the CredentialsMatcher used during an authentication attempt to verify submitted - * credentials with those stored in the system. + * credentials with those stored in the system. */ public CredentialsMatcher getCredentialsMatcher() { return credentialsMatcher; @@ -264,7 +262,7 @@ public void setAuthenticationCache(Cache authenticat * set. * * @return a {@link Cache} instance to use for authentication caching, or {@code null} if no cache has been - * set. + * set. * @see #setAuthenticationCache(org.apache.shiro.cache.Cache) * @see #isAuthenticationCachingEnabled() * @since 1.2 @@ -284,7 +282,7 @@ public Cache getAuthenticationCache() { * of this page in the class-level JavaDoc. * * @return the name of a {@link Cache} to lookup from any available {@link #getCacheManager() cacheManager} if - * a cache is not explicitly configured via {@link #setAuthenticationCache(org.apache.shiro.cache.Cache)}. + * a cache is not explicitly configured via {@link #setAuthenticationCache(org.apache.shiro.cache.Cache)}. * @see #isAuthenticationCachingEnabled() * @since 1.2 */ @@ -455,13 +453,13 @@ private Cache getAuthenticationCacheLazy() { if (this.authenticationCache == null) { - log.trace("No authenticationCache instance set. Checking for a cacheManager..."); + LOGGER.trace("No authenticationCache instance set. Checking for a cacheManager..."); CacheManager cacheManager = getCacheManager(); if (cacheManager != null) { String cacheName = getAuthenticationCacheName(); - log.debug("CacheManager [{}] configured. Building authentication cache '{}'", cacheManager, cacheName); + LOGGER.debug("CacheManager [{}] configured. Building authentication cache '{}'", cacheManager, cacheName); this.authenticationCache = cacheManager.getCache(cacheName); } } @@ -475,7 +473,7 @@ private Cache getAuthenticationCacheLazy() { * * @param token the token submitted during the authentication attempt. * @return any cached AuthenticationInfo corresponding to the specified token or {@code null} if there currently - * isn't any cached data. + * isn't any cached data. * @since 1.2 */ private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token) { @@ -483,19 +481,20 @@ private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token Cache cache = getAvailableAuthenticationCache(); if (cache != null && token != null) { - log.trace("Attempting to retrieve the AuthenticationInfo from cache."); + LOGGER.trace("Attempting to retrieve the AuthenticationInfo from cache."); Object key = getAuthenticationCacheKey(token); info = cache.get(key); if (info == null) { - log.trace("No AuthorizationInfo found in cache for key [{}]", key); + LOGGER.trace("No AuthorizationInfo found in cache for key [{}]", key); } else { - log.trace("Found cached AuthorizationInfo for key [{}]", key); + LOGGER.trace("Found cached AuthorizationInfo for key [{}]", key); } } return info; } + @SuppressWarnings("checkstyle:LineLength") /** * Caches the specified info if authentication caching * {@link #isAuthenticationCachingEnabled(org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.authc.AuthenticationInfo) isEnabled} @@ -507,7 +506,7 @@ private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token */ private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, AuthenticationInfo info) { if (!isAuthenticationCachingEnabled(token, info)) { - log.debug("AuthenticationInfo caching is disabled for info [{}]. Submitted token: [{}].", info, token); + LOGGER.debug("AuthenticationInfo caching is disabled for info [{}]. Submitted token: [{}].", info, token); //return quietly, caching is disabled for this token/info pair: return; } @@ -516,7 +515,7 @@ private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, Authen if (cache != null) { Object key = getAuthenticationCacheKey(token); cache.put(key, info); - log.trace("Cached AuthenticationInfo for continued authentication. key=[{}], value=[{}].", key, info); + LOGGER.trace("Cached AuthenticationInfo for continued authentication. key=[{}], value=[{}].", key, info); } } @@ -532,13 +531,14 @@ private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, Authen * @param info the {@code AuthenticationInfo} acquired from data source lookup via * {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)} * @return {@code true} if authentication caching should be utilized based on the specified - * {@link AuthenticationToken} and/or {@link AuthenticationInfo}, {@code false} otherwise. + * {@link AuthenticationToken} and/or {@link AuthenticationInfo}, {@code false} otherwise. * @since 1.2 */ protected boolean isAuthenticationCachingEnabled(AuthenticationToken token, AuthenticationInfo info) { return isAuthenticationCachingEnabled(); } + @SuppressWarnings("checkstyle:LineLength") /** * This implementation functions as follows: *
    @@ -560,7 +560,7 @@ protected boolean isAuthenticationCachingEnabled(AuthenticationToken token, Auth * * @param token the submitted account principal and credentials. * @return the AuthenticationInfo corresponding to the given {@code token}, or {@code null} if no - * AuthenticationInfo could be found. + * AuthenticationInfo could be found. * @throws AuthenticationException if authentication failed. */ public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { @@ -569,18 +569,18 @@ public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) if (info == null) { //otherwise not cached, perform the lookup: info = doGetAuthenticationInfo(token); - log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info); + LOGGER.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info); if (token != null && info != null) { cacheAuthenticationInfoIfPossible(token, info); } } else { - log.debug("Using cached authentication info [{}] to perform credentials matching.", info); + LOGGER.debug("Using cached authentication info [{}] to perform credentials matching.", info); } if (info != null) { assertCredentialsMatch(token, info); } else { - log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token); + LOGGER.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token); } return info; @@ -603,9 +603,9 @@ protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationI throw new IncorrectCredentialsException(msg); } } else { - throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " + - "credentials during authentication. If you do not wish for credentials to be examined, you " + - "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance."); + throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " + + "credentials during authentication. If you do not wish for credentials to be examined, you " + + "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance."); } } @@ -706,7 +706,7 @@ protected void clearCachedAuthenticationInfo(PrincipalCollection principals) { * * @param token the authentication token containing the user's principal and credentials. * @return an {@link AuthenticationInfo} object containing account data resulting from the - * authentication ONLY if the lookup is successful (i.e. account exists and is valid, etc.) + * authentication ONLY if the lookup is successful (i.e. account exists and is valid, etc.) * @throws AuthenticationException if there is an error acquiring data or performing * realm-specific authentication logic for the specified token */ diff --git a/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java b/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java index 347e7e4ece..fd9f94555b 100644 --- a/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java @@ -19,8 +19,16 @@ package org.apache.shiro.realm; import org.apache.shiro.authc.credential.CredentialsMatcher; -import org.apache.shiro.authz.*; -import org.apache.shiro.authz.permission.*; +import org.apache.shiro.authz.AuthorizationException; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.Authorizer; +import org.apache.shiro.authz.Permission; +import org.apache.shiro.authz.UnauthorizedException; +import org.apache.shiro.authz.permission.PermissionResolver; +import org.apache.shiro.authz.permission.PermissionResolverAware; +import org.apache.shiro.authz.permission.RolePermissionResolver; +import org.apache.shiro.authz.permission.RolePermissionResolverAware; +import org.apache.shiro.authz.permission.WildcardPermissionResolver; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.subject.PrincipalCollection; @@ -30,10 +38,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; - /** * An {@code AuthorizingRealm} extends the {@code AuthenticatingRealm}'s capabilities by adding Authorization * (access control) support. @@ -47,21 +61,21 @@ * you are of course free to subclass the {@link AuthenticatingRealm AuthenticatingRealm} directly instead and * implement the remaining Realm interface methods directly. You might do this if you want have better control * over how the Role and Permission checks occur for your specific data source. However, using AuthorizationInfo - * (and its default implementation {@link org.apache.shiro.authz.SimpleAuthorizationInfo SimpleAuthorizationInfo}) is sufficient in the large + * (and its default implementation {@link org.apache.shiro.authz.SimpleAuthorizationInfo SimpleAuthorizationInfo}) + * is sufficient in the large * majority of Realm cases. * * @see org.apache.shiro.authz.SimpleAuthorizationInfo * @since 0.2 */ +@SuppressWarnings({"checkstyle:MethodCount", "checkstyle:LineLength"}) public abstract class AuthorizingRealm extends AuthenticatingRealm implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware { - //TODO - complete JavaDoc - /*------------------------------------------- | C O N S T A N T S | ============================================*/ - private static final Logger log = LoggerFactory.getLogger(AuthorizingRealm.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizingRealm.class); /** * The default suffix appended to the realm name for caching AuthorizationInfo instances. @@ -102,8 +116,12 @@ public AuthorizingRealm(CredentialsMatcher matcher) { public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) { super(); - if (cacheManager != null) setCacheManager(cacheManager); - if (matcher != null) setCredentialsMatcher(matcher); + if (cacheManager != null) { + setCacheManager(cacheManager); + } + if (matcher != null) { + setCredentialsMatcher(matcher); + } this.authorizationCachingEnabled = true; this.permissionResolver = new WildcardPermissionResolver(); @@ -179,7 +197,9 @@ public PermissionResolver getPermissionResolver() { } public void setPermissionResolver(PermissionResolver permissionResolver) { - if (permissionResolver == null) throw new IllegalArgumentException("Null PermissionResolver is not allowed"); + if (permissionResolver == null) { + throw new IllegalArgumentException("Null PermissionResolver is not allowed"); + } this.permissionResolver = permissionResolver; } @@ -231,23 +251,23 @@ private Cache getAuthorizationCacheLazy() { if (this.authorizationCache == null) { - if (log.isDebugEnabled()) { - log.debug("No authorizationCache instance set. Checking for a cacheManager..."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("No authorizationCache instance set. Checking for a cacheManager..."); } CacheManager cacheManager = getCacheManager(); if (cacheManager != null) { String cacheName = getAuthorizationCacheName(); - if (log.isDebugEnabled()) { - log.debug("CacheManager [" + cacheManager + "] has been configured. Building " + - "authorization cache named [" + cacheName + "]"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("CacheManager [" + cacheManager + "] has been configured. Building " + + "authorization cache named [" + cacheName + "]"); } this.authorizationCache = cacheManager.getCache(cacheName); } else { - if (log.isDebugEnabled()) { - log.debug("No cache or cacheManager properties have been set. Authorization cache cannot " + - "be obtained."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("No cache or cacheManager properties have been set. Authorization cache cannot " + + "be obtained."); } } } @@ -306,7 +326,7 @@ private Cache getAvailableAuthorizationCache() { * @param principals the corresponding Subject's identifying principals with which to look up the Subject's * {@code AuthorizationInfo}. * @return the authorization information for the account associated with the specified {@code principals}, - * or {@code null} if no account could be found. + * or {@code null} if no account could be found. */ protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) { @@ -316,22 +336,22 @@ protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) AuthorizationInfo info = null; - if (log.isTraceEnabled()) { - log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]"); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Retrieving AuthorizationInfo for principals [" + principals + "]"); } Cache cache = getAvailableAuthorizationCache(); if (cache != null) { - if (log.isTraceEnabled()) { - log.trace("Attempting to retrieve the AuthorizationInfo from cache."); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Attempting to retrieve the AuthorizationInfo from cache."); } Object key = getAuthorizationCacheKey(principals); info = cache.get(key); - if (log.isTraceEnabled()) { + if (LOGGER.isTraceEnabled()) { if (info == null) { - log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]"); + LOGGER.trace("No AuthorizationInfo found in cache for principals [" + principals + "]"); } else { - log.trace("AuthorizationInfo found in cache for principals [" + principals + "]"); + LOGGER.trace("AuthorizationInfo found in cache for principals [" + principals + "]"); } } } @@ -342,8 +362,8 @@ protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) info = doGetAuthorizationInfo(principals); // If the info is not null and the cache has been created, then cache the authorization info. if (info != null && cache != null) { - if (log.isTraceEnabled()) { - log.trace("Caching authorization info for principals: [" + principals + "]."); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Caching authorization info for principals: [" + principals + "]."); } Object key = getAuthorizationCacheKey(principals); cache.put(key, info); diff --git a/core/src/main/java/org/apache/shiro/realm/CachingRealm.java b/core/src/main/java/org/apache/shiro/realm/CachingRealm.java index 4f9fffedee..fbe47cf3ba 100644 --- a/core/src/main/java/org/apache/shiro/realm/CachingRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/CachingRealm.java @@ -47,9 +47,7 @@ */ public abstract class CachingRealm implements Realm, Nameable, CacheManagerAware, LogoutAware { - private static final Logger log = LoggerFactory.getLogger(CachingRealm.class); - - //TODO - complete JavaDoc + private static final Logger LOGGER = LoggerFactory.getLogger(CachingRealm.class); private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger(); @@ -78,7 +76,7 @@ public CachingRealm() { * caching is disabled. * * @return the CacheManager used for data caching to reduce EIS round trips, or null if - * caching is disabled. + * caching is disabled. */ public CacheManager getCacheManager() { return this.cacheManager; @@ -105,7 +103,7 @@ public void setCacheManager(CacheManager cacheManager) { * manage account data in memory already lookups would already be as efficient as possible. * * @return {@code true} if caching will be globally enabled if a {@link CacheManager} has been - * configured, {@code false} otherwise + * configured, {@code false} otherwise */ public boolean isCachingEnabled() { return cachingEnabled; @@ -168,7 +166,7 @@ private static boolean isEmpty(PrincipalCollection pc) { protected void clearCache(PrincipalCollection principals) { if (!isEmpty(principals)) { doClearCache(principals); - log.trace("Cleared cache entries for account with principals [{}]", principals); + LOGGER.trace("Cleared cache entries for account with principals [{}]", principals); } } @@ -196,7 +194,7 @@ protected void doClearCache(PrincipalCollection principals) { * * @param principals the PrincipalCollection holding all principals (from all realms) associated with a single Subject. * @return the 'primary' principal attributed to this particular realm, or the fallback 'master' principal if it - * exists, or if not {@code null}. + * exists, or if not {@code null}. * @since 1.2 */ protected Object getAvailablePrincipal(PrincipalCollection principals) { diff --git a/core/src/main/java/org/apache/shiro/realm/Realm.java b/core/src/main/java/org/apache/shiro/realm/Realm.java index 3889fa6c77..52c348275c 100644 --- a/core/src/main/java/org/apache/shiro/realm/Realm.java +++ b/core/src/main/java/org/apache/shiro/realm/Realm.java @@ -51,7 +51,8 @@ * practically any application environment. * *

    Most users will not implement the Realm interface directly, but will extend one of the subclasses, - * {@link org.apache.shiro.realm.AuthenticatingRealm AuthenticatingRealm} or {@link org.apache.shiro.realm.AuthorizingRealm}, greatly reducing the effort required + * {@link org.apache.shiro.realm.AuthenticatingRealm AuthenticatingRealm} or + * {@link org.apache.shiro.realm.AuthorizingRealm}, greatly reducing the effort required * to implement a Realm from scratch.

    * * @see org.apache.shiro.realm.CachingRealm CachingRealm @@ -80,10 +81,11 @@ public interface Realm { * * @param token the AuthenticationToken submitted for the authentication attempt * @return true if this realm can/will authenticate Subjects represented by specified token, - * false otherwise. + * false otherwise. */ boolean supports(AuthenticationToken token); + @SuppressWarnings("checkstyle:LineLength") /** * Returns an account's authentication-specific information for the specified token, * or null if no account could be found based on the token. @@ -95,10 +97,9 @@ public interface Realm { * * @param token the application-specific representation of an account principal and credentials. * @return the authentication information for the account associated with the specified token, - * or null if no account could be found. - * @throws org.apache.shiro.authc.AuthenticationException - * if there is an error obtaining or constructing an AuthenticationInfo object based on the - * specified token or implementation-specific login behavior fails. + * or null if no account could be found. + * @throws org.apache.shiro.authc.AuthenticationException if there is an error obtaining or constructing an AuthenticationInfo object based on the + * specified token or implementation-specific login behavior fails. */ AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException; diff --git a/core/src/main/java/org/apache/shiro/realm/RealmFactory.java b/core/src/main/java/org/apache/shiro/realm/RealmFactory.java index b360a5b432..2c35b6e0af 100644 --- a/core/src/main/java/org/apache/shiro/realm/RealmFactory.java +++ b/core/src/main/java/org/apache/shiro/realm/RealmFactory.java @@ -46,7 +46,7 @@ public interface RealmFactory { * {@link java.util.Iterator Iterator} determines the order in which Realms are used. * * @return the Collection of Realms that the application's SecurityManager will use - * for security data access. + * for security data access. */ Collection getRealms(); diff --git a/core/src/main/java/org/apache/shiro/realm/SimpleAccountRealm.java b/core/src/main/java/org/apache/shiro/realm/SimpleAccountRealm.java index 2542b04516..e9347da53f 100644 --- a/core/src/main/java/org/apache/shiro/realm/SimpleAccountRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/SimpleAccountRealm.java @@ -50,17 +50,24 @@ */ public class SimpleAccountRealm extends AuthorizingRealm { - //TODO - complete JavaDoc - protected final Map users; //username-to-SimpleAccount - protected final Map roles; //roleName-to-SimpleRole - protected final ReadWriteLock USERS_LOCK; - protected final ReadWriteLock ROLES_LOCK; + /** + * username-to-SimpleAccount. + */ + protected final Map users; + + /** + * roleName-to-SimpleRole. + */ + protected final Map roles; + + protected final ReadWriteLock usersLock; + protected final ReadWriteLock rolesLock; public SimpleAccountRealm() { this.users = new LinkedHashMap(); this.roles = new LinkedHashMap(); - USERS_LOCK = new ReentrantReadWriteLock(); - ROLES_LOCK = new ReentrantReadWriteLock(); + usersLock = new ReentrantReadWriteLock(); + rolesLock = new ReentrantReadWriteLock(); //SimpleAccountRealms are memory-only realms - no need for an additional cache mechanism since we're //already as memory-efficient as we can be: setCachingEnabled(false); @@ -72,11 +79,11 @@ public SimpleAccountRealm(String name) { } protected SimpleAccount getUser(String username) { - USERS_LOCK.readLock().lock(); + usersLock.readLock().lock(); try { return this.users.get(username); } finally { - USERS_LOCK.readLock().unlock(); + usersLock.readLock().unlock(); } } @@ -104,20 +111,20 @@ protected String getUsername(PrincipalCollection principals) { protected void add(SimpleAccount account) { String username = getUsername(account); - USERS_LOCK.writeLock().lock(); + usersLock.writeLock().lock(); try { this.users.put(username, account); } finally { - USERS_LOCK.writeLock().unlock(); + usersLock.writeLock().unlock(); } } protected SimpleRole getRole(String rolename) { - ROLES_LOCK.readLock().lock(); + rolesLock.readLock().lock(); try { return roles.get(rolename); } finally { - ROLES_LOCK.readLock().unlock(); + rolesLock.readLock().unlock(); } } @@ -130,11 +137,11 @@ public void addRole(String name) { } protected void add(SimpleRole role) { - ROLES_LOCK.writeLock().lock(); + rolesLock.writeLock().lock(); try { roles.put(role.getName(), role); } finally { - ROLES_LOCK.writeLock().unlock(); + rolesLock.writeLock().unlock(); } } @@ -176,11 +183,11 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = getUsername(principals); - USERS_LOCK.readLock().lock(); + usersLock.readLock().lock(); try { return this.users.get(username); } finally { - USERS_LOCK.readLock().unlock(); + usersLock.readLock().unlock(); } } -} \ No newline at end of file +} diff --git a/core/src/main/java/org/apache/shiro/realm/activedirectory/ActiveDirectoryRealm.java b/core/src/main/java/org/apache/shiro/realm/activedirectory/ActiveDirectoryRealm.java index a24ec248d9..2bf28669d3 100644 --- a/core/src/main/java/org/apache/shiro/realm/activedirectory/ActiveDirectoryRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/activedirectory/ActiveDirectoryRealm.java @@ -39,7 +39,12 @@ import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.LdapContext; -import java.util.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Set; /** @@ -52,13 +57,11 @@ */ public class ActiveDirectoryRealm extends AbstractLdapRealm { - //TODO - complete JavaDoc - /*-------------------------------------------- | C O N S T A N T S | ============================================*/ - private static final Logger log = LoggerFactory.getLogger(ActiveDirectoryRealm.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ActiveDirectoryRealm.class); private static final String ROLE_NAMES_DELIMETER = ","; @@ -85,7 +88,7 @@ public void setGroupRolesMap(Map groupRolesMap) { | M E T H O D S | ============================================*/ - + @SuppressWarnings("checkstyle:LineLength") /** * Builds an {@link AuthenticationInfo} object by querying the active directory LDAP context for the * specified username. This method binds to the LDAP server using the provided username and password - @@ -133,7 +136,8 @@ protected AuthenticationInfo buildAuthenticationInfo(String username, char[] pas * @return the AuthorizationInfo for the given Subject principal. * @throws NamingException if an error occurs when searching the LDAP server. */ - protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals, LdapContextFactory ldapContextFactory) throws NamingException { + protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals, + LdapContextFactory ldapContextFactory) throws NamingException { String username = (String) getAvailablePrincipal(principals); @@ -155,6 +159,7 @@ protected AuthorizationInfo buildAuthorizationInfo(Set roleNames) { return new SimpleAuthorizationInfo(roleNames); } + @SuppressWarnings("checkstyle:LineLength") protected Set getRoleNamesForUser(String username, LdapContext ldapContext) throws NamingException { Set roleNames; roleNames = new LinkedHashSet(); @@ -167,15 +172,15 @@ protected Set getRoleNamesForUser(String username, LdapContext ldapConte userPrincipalName += principalSuffix; } - Object[] searchArguments = new Object[]{userPrincipalName}; + Object[] searchArguments = new Object[] {userPrincipalName}; NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments, searchControls); while (answer.hasMoreElements()) { SearchResult sr = (SearchResult) answer.next(); - if (log.isDebugEnabled()) { - log.debug("Retrieving group names for user [" + sr.getName() + "]"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Retrieving group names for user [" + sr.getName() + "]"); } Attributes attrs = sr.getAttributes(); @@ -189,8 +194,8 @@ protected Set getRoleNamesForUser(String username, LdapContext ldapConte Collection groupNames = LdapUtils.getAllAttributeValues(attr); - if (log.isDebugEnabled()) { - log.debug("Groups found for user [" + username + "]: " + groupNames); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Groups found for user [" + username + "]: " + groupNames); } Collection rolesForGroups = getRoleNamesForGroups(groupNames); @@ -218,8 +223,8 @@ protected Collection getRoleNamesForGroups(Collection groupNames if (strRoleNames != null) { for (String roleName : strRoleNames.split(ROLE_NAMES_DELIMETER)) { - if (log.isDebugEnabled()) { - log.debug("User is member of group [" + groupName + "] so adding role [" + roleName + "]"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("User is member of group [" + groupName + "] so adding role [" + roleName + "]"); } roleNames.add(roleName); diff --git a/core/src/main/java/org/apache/shiro/realm/jdbc/JdbcRealm.java b/core/src/main/java/org/apache/shiro/realm/jdbc/JdbcRealm.java index 8388ae0ec1..a2e9ca87f6 100644 --- a/core/src/main/java/org/apache/shiro/realm/jdbc/JdbcRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/jdbc/JdbcRealm.java @@ -46,7 +46,7 @@ import java.util.LinkedHashSet; import java.util.Set; - +@SuppressWarnings("checkstyle:LineLength") /** * Realm that allows authentication and authorization via JDBC calls. The default queries suggest a potential schema * for retrieving the user's password for authentication, and querying for a user's roles and permissions. The @@ -55,7 +55,7 @@ * If the default implementation * of authentication and authorization cannot handle your schema, this class can be subclassed and the * appropriate methods overridden. (usually {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)}, - * {@link #getRoleNamesForUser(java.sql.Connection,String)}, and/or {@link #getPermissions(java.sql.Connection,String,java.util.Collection)} + * {@link #getRoleNamesForUser(java.sql.Connection, String)}, and/or {@link #getPermissions(java.sql.Connection, String, java.util.Collection)} *

    * This realm supports caching by extending from {@link org.apache.shiro.realm.AuthorizingRealm}. * @@ -72,10 +72,11 @@ public class JdbcRealm extends AuthorizingRealm { * The default query used to retrieve account data for the user. */ protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?"; - + /** * The default query used to retrieve account data for the user when {@link #saltStyle} is COLUMN. */ + @SuppressWarnings("checkstyle:LineLength") protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?"; /** @@ -88,17 +89,17 @@ public class JdbcRealm extends AuthorizingRealm { */ protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?"; - private static final Logger log = LoggerFactory.getLogger(JdbcRealm.class); - + private static final Logger LOGGER = LoggerFactory.getLogger(JdbcRealm.class); + /** * Password hash salt configuration.

      - *
    • NO_SALT - password hashes are not salted.
    • - *
    • CRYPT - password hashes are stored in unix crypt format.
    • - *
    • COLUMN - salt is in a separate column in the database.
    • - *
    • EXTERNAL - salt is not stored in the database. {@link #getSaltForUser(String)} will be called - * to get the salt
    + *
  1. NO_SALT - password hashes are not salted.
  2. + *
  3. CRYPT - password hashes are stored in unix crypt format.
  4. + *
  5. COLUMN - salt is in a separate column in the database.
  6. + *
  7. EXTERNAL - salt is not stored in the database. {@link #getSaltForUser(String)} will be called + * to get the salt
  8. */ - public enum SaltStyle {NO_SALT, CRYPT, COLUMN, EXTERNAL}; + public enum SaltStyle { NO_SALT, CRYPT, COLUMN, EXTERNAL } /*-------------------------------------------- | I N S T A N C E V A R I A B L E S | @@ -111,8 +112,8 @@ public enum SaltStyle {NO_SALT, CRYPT, COLUMN, EXTERNAL}; protected String permissionsQuery = DEFAULT_PERMISSIONS_QUERY; - protected boolean permissionsLookupEnabled = false; - + protected boolean permissionsLookupEnabled; + protected SaltStyle saltStyle = SaltStyle.NO_SALT; protected boolean saltIsBase64Encoded = true; @@ -124,7 +125,7 @@ public enum SaltStyle {NO_SALT, CRYPT, COLUMN, EXTERNAL}; /*-------------------------------------------- | A C C E S S O R S / M O D I F I E R S | ============================================*/ - + /** * Sets the datasource that should be used to retrieve connections used by this realm. * @@ -139,7 +140,7 @@ public void setDataSource(DataSource dataSource) { * implementation, this query must take the user's username as a single parameter and return a single result * with the user's password as the first column. If you require a solution that does not match this query * structure, you can override {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)} or - * just {@link #getPasswordForUser(java.sql.Connection,String)} + * just {@link #getPasswordForUser(java.sql.Connection, String)} * * @param authenticationQuery the query to use for authentication. * @see #DEFAULT_AUTHENTICATION_QUERY @@ -153,7 +154,7 @@ public void setAuthenticationQuery(String authenticationQuery) { * implementation, this query must take the user's username as a single parameter and return a row * per role with a single column containing the role name. If you require a solution that does not match this query * structure, you can override {@link #doGetAuthorizationInfo(PrincipalCollection)} or just - * {@link #getRoleNamesForUser(java.sql.Connection,String)} + * {@link #getRoleNamesForUser(java.sql.Connection, String)} * * @param userRolesQuery the query to use for retrieving a user's roles. * @see #DEFAULT_USER_ROLES_QUERY @@ -168,7 +169,7 @@ public void setUserRolesQuery(String userRolesQuery) { * per permission with a single column, containing the permission. * If you require a solution that does not match this query * structure, you can override {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} or just - * {@link #getPermissions(java.sql.Connection,String,java.util.Collection)}

    + * {@link #getPermissions(java.sql.Connection, String, java.util.Collection)}

    *

    * Permissions are only retrieved if you set {@link #permissionsLookupEnabled} to true. Otherwise, * this query is ignored. @@ -191,10 +192,10 @@ public void setPermissionsQuery(String permissionsQuery) { public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) { this.permissionsLookupEnabled = permissionsLookupEnabled; } - + /** * Sets the salt style. See {@link #saltStyle}. - * + * * @param saltStyle new SaltStyle to set. */ public void setSaltStyle(SaltStyle saltStyle) { @@ -237,21 +238,23 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) String password = null; String salt = null; switch (saltStyle) { - case NO_SALT: - password = getPasswordForUser(conn, username)[0]; - break; - case CRYPT: - // TODO: separate password and hash from getPasswordForUser[0] - throw new ConfigurationException("Not implemented yet"); - //break; - case COLUMN: - String[] queryResults = getPasswordForUser(conn, username); - password = queryResults[0]; - salt = queryResults[1]; - break; - case EXTERNAL: - password = getPasswordForUser(conn, username)[0]; - salt = getSaltForUser(username); + case NO_SALT: + password = getPasswordForUser(conn, username)[0]; + break; + case CRYPT: + // TODO: separate password and hash from getPasswordForUser[0] + throw new ConfigurationException("Not implemented yet"); + //break; + case COLUMN: + String[] queryResults = getPasswordForUser(conn, username); + password = queryResults[0]; + salt = queryResults[1]; + break; + case EXTERNAL: + password = getPasswordForUser(conn, username)[0]; + salt = getSaltForUser(username); + break; + default: } if (password == null) { @@ -259,19 +262,19 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) } info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName()); - + if (salt != null) { - if (saltStyle == SaltStyle.COLUMN && saltIsBase64Encoded) { + if (saltStyle == SaltStyle.COLUMN && saltIsBase64Encoded) { info.setCredentialsSalt(ByteSource.Util.bytes(Base64.decode(salt))); - } else { + } else { info.setCredentialsSalt(ByteSource.Util.bytes(salt)); - } + } } } catch (SQLException e) { final String message = "There was a SQL error while authenticating user [" + username + "]"; - if (log.isErrorEnabled()) { - log.error(message, e); + if (LOGGER.isErrorEnabled()) { + LOGGER.error(message, e); } // Rethrow any SQL errors as an authentication exception @@ -283,21 +286,22 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) return info; } + @SuppressWarnings("checkstyle:LineLength") private String[] getPasswordForUser(Connection conn, String username) throws SQLException { String[] result; boolean returningSeparatedSalt = false; switch (saltStyle) { - case NO_SALT: - case CRYPT: - case EXTERNAL: - result = new String[1]; - break; - default: - result = new String[2]; - returningSeparatedSalt = true; + case NO_SALT: + case CRYPT: + case EXTERNAL: + result = new String[1]; + break; + default: + result = new String[2]; + returningSeparatedSalt = true; } - + PreparedStatement ps = null; ResultSet rs = null; try { @@ -361,8 +365,8 @@ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal } catch (SQLException e) { final String message = "There was a SQL error while authorizing user [" + username + "]"; - if (log.isErrorEnabled()) { - log.error(message, e); + if (LOGGER.isErrorEnabled()) { + LOGGER.error(message, e); } // Rethrow any SQL errors as an authorization exception @@ -397,8 +401,8 @@ protected Set getRoleNamesForUser(Connection conn, String username) thro if (roleName != null) { roleNames.add(roleName); } else { - if (log.isWarnEnabled()) { - log.warn("Null role name found while retrieving role names for user [" + username + "]"); + if (LOGGER.isWarnEnabled()) { + LOGGER.warn("Null role name found while retrieving role names for user [" + username + "]"); } } } @@ -443,7 +447,7 @@ protected Set getPermissions(Connection conn, String username, Collectio return permissions; } - + protected String getSaltForUser(String username) { return username; } diff --git a/core/src/main/java/org/apache/shiro/realm/jdbc/package-info.java b/core/src/main/java/org/apache/shiro/realm/jdbc/package-info.java index af7be66ef1..40c469f043 100644 --- a/core/src/main/java/org/apache/shiro/realm/jdbc/package-info.java +++ b/core/src/main/java/org/apache/shiro/realm/jdbc/package-info.java @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ + /** - * Realms that acquire security data from an RDBMS (Relational Database Management System) using the + * Realms that acquire security data from an RDBMS (Relational Database Management System) using the * JDBC API. */ package org.apache.shiro.realm.jdbc; diff --git a/core/src/main/java/org/apache/shiro/realm/jndi/JndiRealmFactory.java b/core/src/main/java/org/apache/shiro/realm/jndi/JndiRealmFactory.java index 45144d3251..6ca774fb35 100644 --- a/core/src/main/java/org/apache/shiro/realm/jndi/JndiRealmFactory.java +++ b/core/src/main/java/org/apache/shiro/realm/jndi/JndiRealmFactory.java @@ -40,7 +40,7 @@ */ public class JndiRealmFactory extends JndiLocator implements RealmFactory { - Collection jndiNames = null; + Collection jndiNames; /** * Returns the JNDI names that will be used to look up Realm(s) from JNDI. @@ -80,8 +80,8 @@ public void setJndiNames(Collection jndiNames) { public void setJndiNames(String commaDelimited) throws IllegalStateException { String arg = StringUtils.clean(commaDelimited); if (arg == null) { - String msg = "One or more comma-delimited jndi names must be specified for the " + - getClass().getName() + " to locate Realms."; + String msg = "One or more comma-delimited jndi names must be specified for the " + + getClass().getName() + " to locate Realms."; throw new IllegalStateException(msg); } String[] names = StringUtils.tokenizeToStringArray(arg, ","); @@ -101,8 +101,8 @@ public void setJndiNames(String commaDelimited) throws IllegalStateException { public Collection getRealms() throws IllegalStateException { Collection jndiNames = getJndiNames(); if (jndiNames == null || jndiNames.isEmpty()) { - String msg = "One or more jndi names must be specified for the " + - getClass().getName() + " to locate Realms."; + String msg = "One or more jndi names must be specified for the " + + getClass().getName() + " to locate Realms."; throw new IllegalStateException(msg); } List realms = new ArrayList(jndiNames.size()); diff --git a/core/src/main/java/org/apache/shiro/realm/jndi/package-info.java b/core/src/main/java/org/apache/shiro/realm/jndi/package-info.java index 4aa3f9317f..fcd9cb1dd0 100644 --- a/core/src/main/java/org/apache/shiro/realm/jndi/package-info.java +++ b/core/src/main/java/org/apache/shiro/realm/jndi/package-info.java @@ -17,5 +17,6 @@ * under the License. */ /** - * Support for acquiring Realms from JNDI, particularly useful for configuring Shiro in JEE or EJB environments. */ + * Support for acquiring Realms from JNDI, particularly useful for configuring Shiro in JEE or EJB environments. + */ package org.apache.shiro.realm.jndi; diff --git a/core/src/main/java/org/apache/shiro/realm/ldap/AbstractLdapRealm.java b/core/src/main/java/org/apache/shiro/realm/ldap/AbstractLdapRealm.java index 7909e89855..475993e09e 100644 --- a/core/src/main/java/org/apache/shiro/realm/ldap/AbstractLdapRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/ldap/AbstractLdapRealm.java @@ -37,8 +37,8 @@ * list as well.

    * *

    Implementations would need to implement the - * {@link #queryForAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken ,LdapContextFactory)} and - * {@link #queryForAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection ,LdapContextFactory)} abstract methods.

    + * {@link #queryForAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken, LdapContextFactory)} and + * {@link #queryForAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection, LdapContextFactory)} abstract methods.

    * *

    By default, this implementation will create an instance of {@link JndiLdapContextFactory} to use for * creating LDAP connections using the principalSuffix, searchBase, url, systemUsername, and systemPassword properties @@ -46,19 +46,17 @@ * sufficient. If more customized connections are needed, you should inject a custom {@link LdapContextFactory}, which * will cause these properties specified on the realm to be ignored.

    * - * @see #queryForAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken , LdapContextFactory) - * @see #queryForAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection , LdapContextFactory) + * @see #queryForAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken, LdapContextFactory) + * @see #queryForAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection, LdapContextFactory) * @since 0.1 */ public abstract class AbstractLdapRealm extends AuthorizingRealm { - //TODO - complete JavaDoc - /*-------------------------------------------- | C O N S T A N T S | ============================================*/ - private static final Logger log = LoggerFactory.getLogger(AbstractLdapRealm.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLdapRealm.class); /*-------------------------------------------- | I N S T A N C E V A R I A B L E S | @@ -69,26 +67,26 @@ public abstract class AbstractLdapRealm extends AuthorizingRealm { * AD Example: * User's Principal Name be "John.Doe" * User's E-Mail Address be "John.Doe@example.com" - * * For the example below, set: - * realm.principalSuffix = @example.com - * + * realm.principalSuffix = @example.com * Only then, "John.Doe" and also "John.Doe@example.com" can authorize against groups */ - protected String principalSuffix = null; + protected String principalSuffix; - protected String searchBase = null; + protected String searchBase; - protected String url = null; + protected String url; - protected String systemUsername = null; + protected String systemUsername; - protected String systemPassword = null; + protected String systemPassword; - //SHIRO-115 - prevent potential code injection: + /** + * SHIRO-115 - prevent potential code injection. + */ protected String searchFilter = "(&(objectClass=*)(userPrincipalName={0}))"; - private LdapContextFactory ldapContextFactory = null; + private LdapContextFactory ldapContextFactory; /*-------------------------------------------- | C O N S T R U C T O R S | @@ -177,8 +175,8 @@ protected void onInit() { private LdapContextFactory ensureContextFactory() { if (this.ldapContextFactory == null) { - if (log.isDebugEnabled()) { - log.debug("No LdapContextFactory specified - creating a default instance."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("No LdapContextFactory specified - creating a default instance."); } JndiLdapContextFactory defaultFactory = new JndiLdapContextFactory(); @@ -230,7 +228,9 @@ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal * @return an {@link AuthenticationInfo} instance containing information retrieved from the LDAP server. * @throws NamingException if any LDAP errors occur during the search. */ - protected abstract AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, LdapContextFactory ldapContextFactory) throws NamingException; + @SuppressWarnings("checkstyle:LineLength") + protected abstract AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, LdapContextFactory ldapContextFactory) + throws NamingException; /** @@ -243,6 +243,8 @@ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal * @return an {@link AuthorizationInfo} instance containing information retrieved from the LDAP server. * @throws NamingException if any LDAP errors occur during the search. */ - protected abstract AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principal, LdapContextFactory ldapContextFactory) throws NamingException; + @SuppressWarnings("checkstyle:LineLength") + protected abstract AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principal, LdapContextFactory ldapContextFactory) + throws NamingException; } diff --git a/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapRealm.java b/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapRealm.java index 75f477e44e..ed01701e29 100644 --- a/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapRealm.java @@ -1,430 +1,430 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.realm.ldap; - -import org.apache.shiro.authc.AuthenticationException; -import org.apache.shiro.authc.AuthenticationInfo; -import org.apache.shiro.authc.AuthenticationToken; -import org.apache.shiro.authc.SimpleAuthenticationInfo; -import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher; -import org.apache.shiro.authz.AuthorizationException; -import org.apache.shiro.authz.AuthorizationInfo; -import org.apache.shiro.ldap.UnsupportedAuthenticationMechanismException; -import org.apache.shiro.realm.AuthorizingRealm; -import org.apache.shiro.subject.PrincipalCollection; -import org.apache.shiro.lang.util.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.naming.AuthenticationNotSupportedException; -import javax.naming.NamingException; -import javax.naming.ldap.LdapContext; - -/** - * An LDAP {@link org.apache.shiro.realm.Realm Realm} implementation utilizing Sun's/Oracle's - * JNDI API as an LDAP API. This is - * Shiro's default implementation for supporting LDAP, as using the JNDI API has been a common approach for Java LDAP - * support for many years. - *

    - * This realm implementation and its backing {@link JndiLdapContextFactory} should cover 99% of all Shiro-related LDAP - * authentication and authorization needs. However, if it does not suit your needs, you might want to look into - * creating your own realm using an alternative, perhaps more robust, LDAP communication API, such as the - * Apache LDAP API. - *

    Authentication

    - * During an authentication attempt, if the submitted {@code AuthenticationToken}'s - * {@link org.apache.shiro.authc.AuthenticationToken#getPrincipal() principal} is a simple username, but the - * LDAP directory expects a complete User Distinguished Name (User DN) to establish a connection, the - * {@link #setUserDnTemplate(String) userDnTemplate} property must be configured. If not configured, - * the property will pass the simple username directly as the User DN, which is often incorrect in most LDAP - * environments (maybe Microsoft ActiveDirectory being the exception). - *

    Authorization

    - * By default, authorization is effectively disabled due to the default - * {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} implementation returning {@code null}. - * If you wish to perform authorization based on an LDAP schema, you must subclass this one - * and override that method to reflect your organization's data model. - *

    Configuration

    - * This class primarily provides the {@link #setUserDnTemplate(String) userDnTemplate} property to allow you to specify - * the your LDAP server's User DN format. Most other configuration is performed via the nested - * {@link LdapContextFactory contextFactory} property. - *

    - * For example, defining this realm in Shiro .ini: - *

    - * [main]
    - * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
    - * ldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=com
    - * ldapRealm.contextFactory.url = ldap://ldapHost:389
    - * ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
    - * ldapRealm.contextFactory.environment[some.obscure.jndi.key] = some value
    - * ...
    - * 
    - * The default {@link #setContextFactory contextFactory} instance is a {@link JndiLdapContextFactory}. See that - * class's JavaDoc for more information on configuring the LDAP connection as well as specifying JNDI environment - * properties as necessary. - * - * @see JndiLdapContextFactory - * - * @since 1.3 - */ -public class DefaultLdapRealm extends AuthorizingRealm { - - private static final Logger log = LoggerFactory.getLogger(DefaultLdapRealm.class); - - //The zero index currently means nothing, but could be utilized in the future for other substitution techniques. - private static final String USERDN_SUBSTITUTION_TOKEN = "{0}"; - - private String userDnPrefix; - private String userDnSuffix; - - /*-------------------------------------------- - | I N S T A N C E V A R I A B L E S | - ============================================*/ - /** - * The LdapContextFactory instance used to acquire {@link javax.naming.ldap.LdapContext LdapContext}'s at runtime - * to acquire connections to the LDAP directory to perform authentication attempts and authorization queries. - */ - private LdapContextFactory contextFactory; - - /*-------------------------------------------- - | C O N S T R U C T O R S | - ============================================*/ - - /** - * Default no-argument constructor that defaults the internal {@link LdapContextFactory} instance to a - * {@link JndiLdapContextFactory}. - */ - public DefaultLdapRealm() { - //Credentials Matching is not necessary - the LDAP directory will do it automatically: - setCredentialsMatcher(new AllowAllCredentialsMatcher()); - //Any Object principal and Object credentials may be passed to the LDAP provider, so accept any token: - setAuthenticationTokenClass(AuthenticationToken.class); - this.contextFactory = new JndiLdapContextFactory(); - } - - /*-------------------------------------------- - | A C C E S S O R S / M O D I F I E R S | - ============================================*/ - - /** - * Returns the User DN prefix to use when building a runtime User DN value or {@code null} if no - * {@link #getUserDnTemplate() userDnTemplate} has been configured. If configured, this value is the text that - * occurs before the {@link #USERDN_SUBSTITUTION_TOKEN} in the {@link #getUserDnTemplate() userDnTemplate} value. - * - * @return the the User DN prefix to use when building a runtime User DN value or {@code null} if no - * {@link #getUserDnTemplate() userDnTemplate} has been configured. - */ - protected String getUserDnPrefix() { - return userDnPrefix; - } - - /** - * Returns the User DN suffix to use when building a runtime User DN value. or {@code null} if no - * {@link #getUserDnTemplate() userDnTemplate} has been configured. If configured, this value is the text that - * occurs after the {@link #USERDN_SUBSTITUTION_TOKEN} in the {@link #getUserDnTemplate() userDnTemplate} value. - * - * @return the User DN suffix to use when building a runtime User DN value or {@code null} if no - * {@link #getUserDnTemplate() userDnTemplate} has been configured. - */ - protected String getUserDnSuffix() { - return userDnSuffix; - } - - /*-------------------------------------------- - | M E T H O D S | - ============================================*/ - - /** - * Sets the User Distinguished Name (DN) template to use when creating User DNs at runtime. A User DN is an LDAP - * fully-qualified unique user identifier which is required to establish a connection with the LDAP - * directory to authenticate users and query for authorization information. - *

    Usage

    - * User DN formats are unique to the LDAP directory's schema, and each environment differs - you will need to - * specify the format corresponding to your directory. You do this by specifying the full User DN as normal, but - * but you use a {@code {0}} placeholder token in the string representing the location where the - * user's submitted principal (usually a username or uid) will be substituted at runtime. - *

    - * For example, if your directory - * uses an LDAP {@code uid} attribute to represent usernames, the User DN for the {@code jsmith} user may look like - * this: - *

    - *

    uid=jsmith,ou=users,dc=mycompany,dc=com
    - *

    - * in which case you would set this property with the following template value: - *

    - *

    uid={0},ou=users,dc=mycompany,dc=com
    - *

    - * If no template is configured, the raw {@code AuthenticationToken} - * {@link AuthenticationToken#getPrincipal() principal} will be used as the LDAP principal. This is likely - * incorrect as most LDAP directories expect a fully-qualified User DN as opposed to the raw uid or username. So, - * ensure you set this property to match your environment! - * - * @param template the User Distinguished Name template to use for runtime substitution - * @throws IllegalArgumentException if the template is null, empty, or does not contain the - * {@code {0}} substitution token. - * @see LdapContextFactory#getLdapContext(Object,Object) - */ - public void setUserDnTemplate(String template) throws IllegalArgumentException { - if (!StringUtils.hasText(template)) { - String msg = "User DN template cannot be null or empty."; - throw new IllegalArgumentException(msg); - } - int index = template.indexOf(USERDN_SUBSTITUTION_TOKEN); - if (index < 0) { - String msg = "User DN template must contain the '" + - USERDN_SUBSTITUTION_TOKEN + "' replacement token to understand where to " + - "insert the runtime authentication principal."; - throw new IllegalArgumentException(msg); - } - String prefix = template.substring(0, index); - String suffix = template.substring(prefix.length() + USERDN_SUBSTITUTION_TOKEN.length()); - if (log.isDebugEnabled()) { - log.debug("Determined user DN prefix [{}] and suffix [{}]", prefix, suffix); - } - this.userDnPrefix = prefix; - this.userDnSuffix = suffix; - } - - /** - * Returns the User Distinguished Name (DN) template to use when creating User DNs at runtime - see the - * {@link #setUserDnTemplate(String) setUserDnTemplate} JavaDoc for a full explanation. - * - * @return the User Distinguished Name (DN) template to use when creating User DNs at runtime. - */ - public String getUserDnTemplate() { - return getUserDn(USERDN_SUBSTITUTION_TOKEN); - } - - /** - * Returns the LDAP User Distinguished Name (DN) to use when acquiring an - * {@link javax.naming.ldap.LdapContext LdapContext} from the {@link LdapContextFactory}. - *

    - * If the the {@link #getUserDnTemplate() userDnTemplate} property has been set, this implementation will construct - * the User DN by substituting the specified {@code principal} into the configured template. If the - * {@link #getUserDnTemplate() userDnTemplate} has not been set, the method argument will be returned directly - * (indicating that the submitted authentication token principal is the User DN). - * - * @param principal the principal to substitute into the configured {@link #getUserDnTemplate() userDnTemplate}. - * @return the constructed User DN to use at runtime when acquiring an {@link javax.naming.ldap.LdapContext}. - * @throws IllegalArgumentException if the method argument is null or empty - * @throws IllegalStateException if the {@link #getUserDnTemplate userDnTemplate} has not been set. - * @see LdapContextFactory#getLdapContext(Object, Object) - */ - protected String getUserDn(String principal) throws IllegalArgumentException, IllegalStateException { - if (!StringUtils.hasText(principal)) { - throw new IllegalArgumentException("User principal cannot be null or empty for User DN construction."); - } - String prefix = getUserDnPrefix(); - String suffix = getUserDnSuffix(); - if (prefix == null && suffix == null) { - log.debug("userDnTemplate property has not been configured, indicating the submitted " + - "AuthenticationToken's principal is the same as the User DN. Returning the method argument " + - "as is."); - return principal; - } - - int prefixLength = prefix != null ? prefix.length() : 0; - int suffixLength = suffix != null ? suffix.length() : 0; - StringBuilder sb = new StringBuilder(prefixLength + principal.length() + suffixLength); - if (prefixLength > 0) { - sb.append(prefix); - } - sb.append(principal); - if (suffixLength > 0) { - sb.append(suffix); - } - return sb.toString(); - } - - /** - * Sets the LdapContextFactory instance used to acquire connections to the LDAP directory during authentication - * attempts and authorization queries. Unless specified otherwise, the default is a {@link JndiLdapContextFactory} - * instance. - * - * @param contextFactory the LdapContextFactory instance used to acquire connections to the LDAP directory during - * authentication attempts and authorization queries - */ - @SuppressWarnings({"UnusedDeclaration"}) - public void setContextFactory(LdapContextFactory contextFactory) { - this.contextFactory = contextFactory; - } - - /** - * Returns the LdapContextFactory instance used to acquire connections to the LDAP directory during authentication - * attempts and authorization queries. Unless specified otherwise, the default is a {@link JndiLdapContextFactory} - * instance. - * - * @return the LdapContextFactory instance used to acquire connections to the LDAP directory during - * authentication attempts and authorization queries - */ - public LdapContextFactory getContextFactory() { - return this.contextFactory; - } - - /*-------------------------------------------- - | M E T H O D S | - ============================================*/ - - /** - * Delegates to {@link #queryForAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken, LdapContextFactory)}, - * wrapping any {@link NamingException}s in a Shiro {@link AuthenticationException} to satisfy the parent method - * signature. - * - * @param token the authentication token containing the user's principal and credentials. - * @return the {@link AuthenticationInfo} acquired after a successful authentication attempt - * @throws AuthenticationException if the authentication attempt fails or if a - * {@link NamingException} occurs. - */ - protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { - AuthenticationInfo info; - try { - info = queryForAuthenticationInfo(token, getContextFactory()); - } catch (AuthenticationNotSupportedException e) { - String msg = "Unsupported configured authentication mechanism"; - throw new UnsupportedAuthenticationMechanismException(msg, e); - } catch (javax.naming.AuthenticationException e) { - throw new AuthenticationException("LDAP authentication failed.", e); - } catch (NamingException e) { - String msg = "LDAP naming error while attempting to authenticate user."; - throw new AuthenticationException(msg, e); - } - - return info; - } - - - protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { - AuthorizationInfo info; - try { - info = queryForAuthorizationInfo(principals, getContextFactory()); - } catch (NamingException e) { - String msg = "LDAP naming error while attempting to retrieve authorization for user [" + principals + "]."; - throw new AuthorizationException(msg, e); - } - - return info; - } - - /** - * Returns the principal to use when creating the LDAP connection for an authentication attempt. - *

    - * This implementation uses a heuristic: it checks to see if the specified token's - * {@link AuthenticationToken#getPrincipal() principal} is a {@code String}, and if so, - * {@link #getUserDn(String) converts it} from what is - * assumed to be a raw uid or username {@code String} into a User DN {@code String}. Almost all LDAP directories - * expect the authentication connection to present a User DN and not an unqualified username or uid. - *

    - * If the token's {@code principal} is not a String, it is assumed to already be in the format supported by the - * underlying {@link LdapContextFactory} implementation and the raw principal is returned directly. - * - * @param token the {@link AuthenticationToken} submitted during the authentication process - * @return the User DN or raw principal to use to acquire the LdapContext. - * @see LdapContextFactory#getLdapContext(Object, Object) - */ - protected Object getLdapPrincipal(AuthenticationToken token) { - Object principal = token.getPrincipal(); - if (principal instanceof String) { - String sPrincipal = (String) principal; - return getUserDn(sPrincipal); - } - return principal; - } - - /** - * This implementation opens an LDAP connection using the token's - * {@link #getLdapPrincipal(org.apache.shiro.authc.AuthenticationToken) discovered principal} and provided - * {@link AuthenticationToken#getCredentials() credentials}. If the connection opens successfully, the - * authentication attempt is immediately considered successful and a new - * {@link AuthenticationInfo} instance is - * {@link #createAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken, Object, Object, javax.naming.ldap.LdapContext) created} - * and returned. If the connection cannot be opened, either because LDAP authentication failed or some other - * JNDI problem, an {@link NamingException} will be thrown. - * - * @param token the submitted authentication token that triggered the authentication attempt. - * @param ldapContextFactory factory used to retrieve LDAP connections. - * @return an {@link AuthenticationInfo} instance representing the authenticated user's information. - * @throws NamingException if any LDAP errors occur. - */ - protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, - LdapContextFactory ldapContextFactory) - throws NamingException { - - Object principal = token.getPrincipal(); - Object credentials = token.getCredentials(); - - log.debug("Authenticating user '{}' through LDAP", principal); - - principal = getLdapPrincipal(token); - - LdapContext ctx = null; - try { - ctx = ldapContextFactory.getLdapContext(principal, credentials); - //context was opened successfully, which means their credentials were valid. Return the AuthenticationInfo: - return createAuthenticationInfo(token, principal, credentials, ctx); - } finally { - LdapUtils.closeContext(ctx); - } - } - - /** - * Returns the {@link AuthenticationInfo} resulting from a Subject's successful LDAP authentication attempt. - *

    - * This implementation ignores the {@code ldapPrincipal}, {@code ldapCredentials}, and the opened - * {@code ldapContext} arguments and merely returns an {@code AuthenticationInfo} instance mirroring the - * submitted token's principal and credentials. This is acceptable because this method is only ever invoked after - * a successful authentication attempt, which means the provided principal and credentials were correct, and can - * be used directly to populate the (now verified) {@code AuthenticationInfo}. - *

    - * Subclasses however are free to override this method for more advanced construction logic. - * - * @param token the submitted {@code AuthenticationToken} that resulted in a successful authentication - * @param ldapPrincipal the LDAP principal used when creating the LDAP connection. Unlike the token's - * {@link AuthenticationToken#getPrincipal() principal}, this value is usually a constructed - * User DN and not a simple username or uid. The exact value is depending on the - * configured - * - * LDAP authentication mechanism in use. - * @param ldapCredentials the LDAP credentials used when creating the LDAP connection. - * @param ldapContext the LdapContext created that resulted in a successful authentication. It can be used - * further by subclasses for more complex operations. It does not need to be closed - - * it will be closed automatically after this method returns. - * @return the {@link AuthenticationInfo} resulting from a Subject's successful LDAP authentication attempt. - * @throws NamingException if there was any problem using the {@code LdapContext} - */ - @SuppressWarnings({"UnusedDeclaration"}) - protected AuthenticationInfo createAuthenticationInfo(AuthenticationToken token, Object ldapPrincipal, - Object ldapCredentials, LdapContext ldapContext) - throws NamingException { - return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName()); - } - - - /** - * Method that should be implemented by subclasses to build an - * {@link AuthorizationInfo} object by querying the LDAP context for the - * specified principal.

    - * - * @param principals the principals of the Subject whose AuthenticationInfo should be queried from the LDAP server. - * @param ldapContextFactory factory used to retrieve LDAP connections. - * @return an {@link AuthorizationInfo} instance containing information retrieved from the LDAP server. - * @throws NamingException if any LDAP errors occur during the search. - */ - protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals, - LdapContextFactory ldapContextFactory) throws NamingException { - return null; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.realm.ldap; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher; +import org.apache.shiro.authz.AuthorizationException; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.ldap.UnsupportedAuthenticationMechanismException; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.lang.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.naming.AuthenticationNotSupportedException; +import javax.naming.NamingException; +import javax.naming.ldap.LdapContext; + +/** + * An LDAP {@link org.apache.shiro.realm.Realm Realm} implementation utilizing Sun's/Oracle's + * JNDI API as an LDAP API. This is + * Shiro's default implementation for supporting LDAP, as using the JNDI API has been a common approach for Java LDAP + * support for many years. + *

    + * This realm implementation and its backing {@link JndiLdapContextFactory} should cover 99% of all Shiro-related LDAP + * authentication and authorization needs. However, if it does not suit your needs, you might want to look into + * creating your own realm using an alternative, perhaps more robust, LDAP communication API, such as the + * Apache LDAP API. + *

    Authentication

    + * During an authentication attempt, if the submitted {@code AuthenticationToken}'s + * {@link org.apache.shiro.authc.AuthenticationToken#getPrincipal() principal} is a simple username, but the + * LDAP directory expects a complete User Distinguished Name (User DN) to establish a connection, the + * {@link #setUserDnTemplate(String) userDnTemplate} property must be configured. If not configured, + * the property will pass the simple username directly as the User DN, which is often incorrect in most LDAP + * environments (maybe Microsoft ActiveDirectory being the exception). + *

    Authorization

    + * By default, authorization is effectively disabled due to the default + * {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} implementation returning {@code null}. + * If you wish to perform authorization based on an LDAP schema, you must subclass this one + * and override that method to reflect your organization's data model. + *

    Configuration

    + * This class primarily provides the {@link #setUserDnTemplate(String) userDnTemplate} property to allow you to specify + * the your LDAP server's User DN format. Most other configuration is performed via the nested + * {@link LdapContextFactory contextFactory} property. + *

    + * For example, defining this realm in Shiro .ini: + *

    + * [main]
    + * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
    + * ldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=com
    + * ldapRealm.contextFactory.url = ldap://ldapHost:389
    + * ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
    + * ldapRealm.contextFactory.environment[some.obscure.jndi.key] = some value
    + * ...
    + * 
    + * The default {@link #setContextFactory contextFactory} instance is a {@link JndiLdapContextFactory}. See that + * class's JavaDoc for more information on configuring the LDAP connection as well as specifying JNDI environment + * properties as necessary. + * + * @see JndiLdapContextFactory + * @since 1.3 + */ +public class DefaultLdapRealm extends AuthorizingRealm { + + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLdapRealm.class); + + //The zero index currently means nothing, but could be utilized in the future for other substitution techniques. + private static final String USERDN_SUBSTITUTION_TOKEN = "{0}"; + + private String userDnPrefix; + private String userDnSuffix; + + /*-------------------------------------------- + | I N S T A N C E V A R I A B L E S | + ============================================*/ + /** + * The LdapContextFactory instance used to acquire {@link javax.naming.ldap.LdapContext LdapContext}'s at runtime + * to acquire connections to the LDAP directory to perform authentication attempts and authorization queries. + */ + private LdapContextFactory contextFactory; + + /*-------------------------------------------- + | C O N S T R U C T O R S | + ============================================*/ + + /** + * Default no-argument constructor that defaults the internal {@link LdapContextFactory} instance to a + * {@link JndiLdapContextFactory}. + */ + public DefaultLdapRealm() { + //Credentials Matching is not necessary - the LDAP directory will do it automatically: + setCredentialsMatcher(new AllowAllCredentialsMatcher()); + //Any Object principal and Object credentials may be passed to the LDAP provider, so accept any token: + setAuthenticationTokenClass(AuthenticationToken.class); + this.contextFactory = new JndiLdapContextFactory(); + } + + /*-------------------------------------------- + | A C C E S S O R S / M O D I F I E R S | + ============================================*/ + + /** + * Returns the User DN prefix to use when building a runtime User DN value or {@code null} if no + * {@link #getUserDnTemplate() userDnTemplate} has been configured. If configured, this value is the text that + * occurs before the {@link #USERDN_SUBSTITUTION_TOKEN} in the {@link #getUserDnTemplate() userDnTemplate} value. + * + * @return the the User DN prefix to use when building a runtime User DN value or {@code null} if no + * {@link #getUserDnTemplate() userDnTemplate} has been configured. + */ + protected String getUserDnPrefix() { + return userDnPrefix; + } + + /** + * Returns the User DN suffix to use when building a runtime User DN value. or {@code null} if no + * {@link #getUserDnTemplate() userDnTemplate} has been configured. If configured, this value is the text that + * occurs after the {@link #USERDN_SUBSTITUTION_TOKEN} in the {@link #getUserDnTemplate() userDnTemplate} value. + * + * @return the User DN suffix to use when building a runtime User DN value or {@code null} if no + * {@link #getUserDnTemplate() userDnTemplate} has been configured. + */ + protected String getUserDnSuffix() { + return userDnSuffix; + } + + /*-------------------------------------------- + | M E T H O D S | + ============================================*/ + + /** + * Sets the User Distinguished Name (DN) template to use when creating User DNs at runtime. A User DN is an LDAP + * fully-qualified unique user identifier which is required to establish a connection with the LDAP + * directory to authenticate users and query for authorization information. + *

    Usage

    + * User DN formats are unique to the LDAP directory's schema, and each environment differs - you will need to + * specify the format corresponding to your directory. You do this by specifying the full User DN as normal, but + * but you use a {@code {0}} placeholder token in the string representing the location where the + * user's submitted principal (usually a username or uid) will be substituted at runtime. + *

    + * For example, if your directory + * uses an LDAP {@code uid} attribute to represent usernames, the User DN for the {@code jsmith} user may look like + * this: + *

    + *

    uid=jsmith,ou=users,dc=mycompany,dc=com
    + *

    + * in which case you would set this property with the following template value: + *

    + *

    uid={0},ou=users,dc=mycompany,dc=com
    + *

    + * If no template is configured, the raw {@code AuthenticationToken} + * {@link AuthenticationToken#getPrincipal() principal} will be used as the LDAP principal. This is likely + * incorrect as most LDAP directories expect a fully-qualified User DN as opposed to the raw uid or username. So, + * ensure you set this property to match your environment! + * + * @param template the User Distinguished Name template to use for runtime substitution + * @throws IllegalArgumentException if the template is null, empty, or does not contain the + * {@code {0}} substitution token. + * @see LdapContextFactory#getLdapContext(Object, Object) + */ + public void setUserDnTemplate(String template) throws IllegalArgumentException { + if (!StringUtils.hasText(template)) { + String msg = "User DN template cannot be null or empty."; + throw new IllegalArgumentException(msg); + } + int index = template.indexOf(USERDN_SUBSTITUTION_TOKEN); + if (index < 0) { + String msg = "User DN template must contain the '" + + USERDN_SUBSTITUTION_TOKEN + "' replacement token to understand where to " + + "insert the runtime authentication principal."; + throw new IllegalArgumentException(msg); + } + String prefix = template.substring(0, index); + String suffix = template.substring(prefix.length() + USERDN_SUBSTITUTION_TOKEN.length()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Determined user DN prefix [{}] and suffix [{}]", prefix, suffix); + } + this.userDnPrefix = prefix; + this.userDnSuffix = suffix; + } + + /** + * Returns the User Distinguished Name (DN) template to use when creating User DNs at runtime - see the + * {@link #setUserDnTemplate(String) setUserDnTemplate} JavaDoc for a full explanation. + * + * @return the User Distinguished Name (DN) template to use when creating User DNs at runtime. + */ + public String getUserDnTemplate() { + return getUserDn(USERDN_SUBSTITUTION_TOKEN); + } + + /** + * Returns the LDAP User Distinguished Name (DN) to use when acquiring an + * {@link javax.naming.ldap.LdapContext LdapContext} from the {@link LdapContextFactory}. + *

    + * If the the {@link #getUserDnTemplate() userDnTemplate} property has been set, this implementation will construct + * the User DN by substituting the specified {@code principal} into the configured template. If the + * {@link #getUserDnTemplate() userDnTemplate} has not been set, the method argument will be returned directly + * (indicating that the submitted authentication token principal is the User DN). + * + * @param principal the principal to substitute into the configured {@link #getUserDnTemplate() userDnTemplate}. + * @return the constructed User DN to use at runtime when acquiring an {@link javax.naming.ldap.LdapContext}. + * @throws IllegalArgumentException if the method argument is null or empty + * @throws IllegalStateException if the {@link #getUserDnTemplate userDnTemplate} has not been set. + * @see LdapContextFactory#getLdapContext(Object, Object) + */ + protected String getUserDn(String principal) throws IllegalArgumentException, IllegalStateException { + if (!StringUtils.hasText(principal)) { + throw new IllegalArgumentException("User principal cannot be null or empty for User DN construction."); + } + String prefix = getUserDnPrefix(); + String suffix = getUserDnSuffix(); + if (prefix == null && suffix == null) { + LOGGER.debug("userDnTemplate property has not been configured, indicating the submitted " + + "AuthenticationToken's principal is the same as the User DN. Returning the method argument " + + "as is."); + return principal; + } + + int prefixLength = prefix != null ? prefix.length() : 0; + int suffixLength = suffix != null ? suffix.length() : 0; + StringBuilder sb = new StringBuilder(prefixLength + principal.length() + suffixLength); + if (prefixLength > 0) { + sb.append(prefix); + } + sb.append(principal); + if (suffixLength > 0) { + sb.append(suffix); + } + return sb.toString(); + } + + /** + * Sets the LdapContextFactory instance used to acquire connections to the LDAP directory during authentication + * attempts and authorization queries. Unless specified otherwise, the default is a {@link JndiLdapContextFactory} + * instance. + * + * @param contextFactory the LdapContextFactory instance used to acquire connections to the LDAP directory during + * authentication attempts and authorization queries + */ + @SuppressWarnings({"UnusedDeclaration"}) + public void setContextFactory(LdapContextFactory contextFactory) { + this.contextFactory = contextFactory; + } + + /** + * Returns the LdapContextFactory instance used to acquire connections to the LDAP directory during authentication + * attempts and authorization queries. Unless specified otherwise, the default is a {@link JndiLdapContextFactory} + * instance. + * + * @return the LdapContextFactory instance used to acquire connections to the LDAP directory during + * authentication attempts and authorization queries + */ + public LdapContextFactory getContextFactory() { + return this.contextFactory; + } + + /*-------------------------------------------- + | M E T H O D S | + ============================================*/ + + /** + * Delegates to {@link #queryForAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken, LdapContextFactory)}, + * wrapping any {@link NamingException}s in a Shiro {@link AuthenticationException} to satisfy the parent method + * signature. + * + * @param token the authentication token containing the user's principal and credentials. + * @return the {@link AuthenticationInfo} acquired after a successful authentication attempt + * @throws AuthenticationException if the authentication attempt fails or if a + * {@link NamingException} occurs. + */ + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { + AuthenticationInfo info; + try { + info = queryForAuthenticationInfo(token, getContextFactory()); + } catch (AuthenticationNotSupportedException e) { + String msg = "Unsupported configured authentication mechanism"; + throw new UnsupportedAuthenticationMechanismException(msg, e); + } catch (javax.naming.AuthenticationException e) { + throw new AuthenticationException("LDAP authentication failed.", e); + } catch (NamingException e) { + String msg = "LDAP naming error while attempting to authenticate user."; + throw new AuthenticationException(msg, e); + } + + return info; + } + + + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + AuthorizationInfo info; + try { + info = queryForAuthorizationInfo(principals, getContextFactory()); + } catch (NamingException e) { + String msg = "LDAP naming error while attempting to retrieve authorization for user [" + principals + "]."; + throw new AuthorizationException(msg, e); + } + + return info; + } + + /** + * Returns the principal to use when creating the LDAP connection for an authentication attempt. + *

    + * This implementation uses a heuristic: it checks to see if the specified token's + * {@link AuthenticationToken#getPrincipal() principal} is a {@code String}, and if so, + * {@link #getUserDn(String) converts it} from what is + * assumed to be a raw uid or username {@code String} into a User DN {@code String}. Almost all LDAP directories + * expect the authentication connection to present a User DN and not an unqualified username or uid. + *

    + * If the token's {@code principal} is not a String, it is assumed to already be in the format supported by the + * underlying {@link LdapContextFactory} implementation and the raw principal is returned directly. + * + * @param token the {@link AuthenticationToken} submitted during the authentication process + * @return the User DN or raw principal to use to acquire the LdapContext. + * @see LdapContextFactory#getLdapContext(Object, Object) + */ + protected Object getLdapPrincipal(AuthenticationToken token) { + Object principal = token.getPrincipal(); + if (principal instanceof String) { + String sPrincipal = (String) principal; + return getUserDn(sPrincipal); + } + return principal; + } + + @SuppressWarnings("checkstyle:LineLength") + /** + * This implementation opens an LDAP connection using the token's + * {@link #getLdapPrincipal(org.apache.shiro.authc.AuthenticationToken) discovered principal} and provided + * {@link AuthenticationToken#getCredentials() credentials}. If the connection opens successfully, the + * authentication attempt is immediately considered successful and a new + * {@link AuthenticationInfo} instance is + * {@link #createAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken, Object, Object, javax.naming.ldap.LdapContext) created} + * and returned. If the connection cannot be opened, either because LDAP authentication failed or some other + * JNDI problem, an {@link NamingException} will be thrown. + * + * @param token the submitted authentication token that triggered the authentication attempt. + * @param ldapContextFactory factory used to retrieve LDAP connections. + * @return an {@link AuthenticationInfo} instance representing the authenticated user's information. + * @throws NamingException if any LDAP errors occur. + */ + protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, + LdapContextFactory ldapContextFactory) + throws NamingException { + + Object principal = token.getPrincipal(); + Object credentials = token.getCredentials(); + + LOGGER.debug("Authenticating user '{}' through LDAP", principal); + + principal = getLdapPrincipal(token); + + LdapContext ctx = null; + try { + ctx = ldapContextFactory.getLdapContext(principal, credentials); + //context was opened successfully, which means their credentials were valid. Return the AuthenticationInfo: + return createAuthenticationInfo(token, principal, credentials, ctx); + } finally { + LdapUtils.closeContext(ctx); + } + } + + /** + * Returns the {@link AuthenticationInfo} resulting from a Subject's successful LDAP authentication attempt. + *

    + * This implementation ignores the {@code ldapPrincipal}, {@code ldapCredentials}, and the opened + * {@code ldapContext} arguments and merely returns an {@code AuthenticationInfo} instance mirroring the + * submitted token's principal and credentials. This is acceptable because this method is only ever invoked after + * a successful authentication attempt, which means the provided principal and credentials were correct, and can + * be used directly to populate the (now verified) {@code AuthenticationInfo}. + *

    + * Subclasses however are free to override this method for more advanced construction logic. + * + * @param token the submitted {@code AuthenticationToken} that resulted in a successful authentication + * @param ldapPrincipal the LDAP principal used when creating the LDAP connection. Unlike the token's + * {@link AuthenticationToken#getPrincipal() principal}, this value is usually a constructed + * User DN and not a simple username or uid. The exact value is depending on the + * configured + * + * LDAP authentication mechanism in use. + * @param ldapCredentials the LDAP credentials used when creating the LDAP connection. + * @param ldapContext the LdapContext created that resulted in a successful authentication. It can be used + * further by subclasses for more complex operations. It does not need to be closed - + * it will be closed automatically after this method returns. + * @return the {@link AuthenticationInfo} resulting from a Subject's successful LDAP authentication attempt. + * @throws NamingException if there was any problem using the {@code LdapContext} + */ + @SuppressWarnings({"UnusedDeclaration"}) + protected AuthenticationInfo createAuthenticationInfo(AuthenticationToken token, Object ldapPrincipal, + Object ldapCredentials, LdapContext ldapContext) + throws NamingException { + return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName()); + } + + + /** + * Method that should be implemented by subclasses to build an + * {@link AuthorizationInfo} object by querying the LDAP context for the + * specified principal.

    + * + * @param principals the principals of the Subject whose AuthenticationInfo should be queried from the LDAP server. + * @param ldapContextFactory factory used to retrieve LDAP connections. + * @return an {@link AuthorizationInfo} instance containing information retrieved from the LDAP server. + * @throws NamingException if any LDAP errors occur during the search. + */ + protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals, + LdapContextFactory ldapContextFactory) throws NamingException { + return null; + } +} diff --git a/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java b/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java index db667931d3..fcfd313e9a 100644 --- a/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java +++ b/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java @@ -1,533 +1,533 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.realm.ldap; - -import org.apache.shiro.lang.util.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.naming.AuthenticationException; -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.ldap.InitialLdapContext; -import javax.naming.ldap.LdapContext; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; - -/** - * {@link LdapContextFactory} implementation using the default Sun/Oracle JNDI Ldap API, utilizing JNDI - * environment properties and an {@link javax.naming.InitialContext}. - *

    Configuration

    - * This class basically wraps a default template JNDI environment properties Map. This properties map is the base - * configuration template used to acquire JNDI {@link LdapContext} connections at runtime. The - * {@link #getLdapContext(Object, Object)} method implementation merges this default template with other properties - * accessible at runtime only (for example per-method principals and credentials). The constructed runtime map is the - * one used to acquire the {@link LdapContext}. - *

    - * The template can be configured directly via the {@link #getEnvironment()}/{@link #setEnvironment(java.util.Map)} - * properties directly if necessary, but it is usually more convenient to use the supporting wrapper get/set methods - * for various environment properties. These wrapper methods interact with the environment - * template on your behalf, leaving your configuration cleaner and easier to understand. - *

    - * For example, consider the following two identical configurations: - *

    - * [main]
    - * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
    - * ldapRealm.contextFactory.url = ldap://localhost:389
    - * ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
    - * 
    - * and - *
    - * [main]
    - * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
    - * ldapRealm.contextFactory.environment[java.naming.provider.url] = ldap://localhost:389
    - * ldapRealm.contextFactory.environment[java.naming.security.authentication] = DIGEST-MD5
    - * 
    - * As you can see, the 2nd configuration block is a little more difficult to read and also requires knowledge - * of the underlying JNDI Context property keys. The first is easier to read and understand. - *

    - * Note that occasionally it will be necessary to use the latter configuration style to set environment properties - * where no corresponding wrapper method exists. In this case, the hybrid approach is still a little easier to read. - * For example: - *

    - * [main]
    - * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
    - * ldapRealm.contextFactory.url = ldap://localhost:389
    - * ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
    - * ldapRealm.contextFactory.environment[some.other.obscure.jndi.key] = some value
    - * 
    - * - * @since 1.1 - */ -public class JndiLdapContextFactory implements LdapContextFactory { - - /*------------------------------------------- - | C O N S T A N T S | - ===========================================*/ - /** - * The Sun LDAP property used to enable connection pooling. This is used in the default implementation - * to enable LDAP connection pooling. - */ - protected static final String SUN_CONNECTION_POOLING_PROPERTY = "com.sun.jndi.ldap.connect.pool"; - protected static final String DEFAULT_CONTEXT_FACTORY_CLASS_NAME = "com.sun.jndi.ldap.LdapCtxFactory"; - protected static final String SIMPLE_AUTHENTICATION_MECHANISM_NAME = "simple"; - protected static final String DEFAULT_REFERRAL = "follow"; - - private static final Logger log = LoggerFactory.getLogger(JndiLdapContextFactory.class); - - /*------------------------------------------- - | I N S T A N C E V A R I A B L E S | - ============================================*/ - private Map environment; - private boolean poolingEnabled; - private String systemPassword; - private String systemUsername; - - /*------------------------------------------- - | C O N S T R U C T O R S | - ===========================================*/ - - /** - * Default no-argument constructor that initializes the backing {@link #getEnvironment() environment template} with - * the {@link #setContextFactoryClassName(String) contextFactoryClassName} equal to - * {@code com.sun.jndi.ldap.LdapCtxFactory} (the Sun/Oracle default) and the default - * {@link #setReferral(String) referral} behavior to {@code follow}. - */ - public JndiLdapContextFactory() { - this.environment = new HashMap(); - setContextFactoryClassName(DEFAULT_CONTEXT_FACTORY_CLASS_NAME); - setReferral(DEFAULT_REFERRAL); - poolingEnabled = true; - } - - /*------------------------------------------- - | A C C E S S O R S / M O D I F I E R S | - ===========================================*/ - - /** - * Sets the type of LDAP authentication mechanism to use when connecting to the LDAP server. - * This is a wrapper method for setting the JNDI {@link #getEnvironment() environment template}'s - * {@link Context#SECURITY_AUTHENTICATION} property. - *

    - * "none" (i.e. anonymous) and "simple" authentications are supported automatically and don't need to be configured - * via this property. However, if you require a different mechanism, such as a SASL or External mechanism, you - * must configure that explicitly via this property. See the - * JNDI LDAP - * Authentication Mechanisms for more information. - * - * @param authenticationMechanism the type of LDAP authentication to perform. - * @see - * http://download-llnw.oracle.com/javase/tutorial/jndi/ldap/auth_mechs.html - */ - public void setAuthenticationMechanism(String authenticationMechanism) { - setEnvironmentProperty(Context.SECURITY_AUTHENTICATION, authenticationMechanism); - } - - /** - * Returns the type of LDAP authentication mechanism to use when connecting to the LDAP server. - * This is a wrapper method for getting the JNDI {@link #getEnvironment() environment template}'s - * {@link Context#SECURITY_AUTHENTICATION} property. - *

    - * If this property remains un-configured (i.e. {@code null} indicating the - * {@link #setAuthenticationMechanism(String)} method wasn't used), this indicates that the default JNDI - * "none" (anonymous) and "simple" authentications are supported automatically. Any non-null value returned - * represents an explicitly configured mechanism (e.g. a SASL or external mechanism). See the - * JNDI LDAP - * Authentication Mechanisms for more information. - * - * @return the type of LDAP authentication mechanism to use when connecting to the LDAP server. - * @see - * http://download-llnw.oracle.com/javase/tutorial/jndi/ldap/auth_mechs.html - */ - public String getAuthenticationMechanism() { - return (String) getEnvironmentProperty(Context.SECURITY_AUTHENTICATION); - } - - /** - * The name of the ContextFactory class to use. This defaults to the SUN LDAP JNDI implementation - * but can be overridden to use custom LDAP factories. - *

    - * This is a wrapper method for setting the JNDI environment's {@link Context#INITIAL_CONTEXT_FACTORY} property. - * - * @param contextFactoryClassName the context factory that should be used. - */ - public void setContextFactoryClassName(String contextFactoryClassName) { - setEnvironmentProperty(Context.INITIAL_CONTEXT_FACTORY, contextFactoryClassName); - } - - /** - * Sets the name of the ContextFactory class to use. This defaults to the SUN LDAP JNDI implementation - * but can be overridden to use custom LDAP factories. - *

    - * This is a wrapper method for getting the JNDI environment's {@link Context#INITIAL_CONTEXT_FACTORY} property. - * - * @return the name of the ContextFactory class to use. - */ - public String getContextFactoryClassName() { - return (String) getEnvironmentProperty(Context.INITIAL_CONTEXT_FACTORY); - } - - /** - * Returns the base JNDI environment template to use when acquiring an LDAP connection (an {@link LdapContext}). - * This property is the base configuration template to use for all connections. This template is then - * merged with appropriate runtime values as necessary in the - * {@link #getLdapContext(Object, Object)} implementation. The merged environment instance is what is used to - * acquire the {@link LdapContext} at runtime. - *

    - * Most other get/set methods in this class act as thin proxy wrappers that interact with this property. The - * benefit of using them is you have an easier-to-use configuration mechanism compared to setting map properties - * based on JNDI context keys. - * - * @return the base JNDI environment template to use when acquiring an LDAP connection (an {@link LdapContext}) - */ - public Map getEnvironment() { - return this.environment; - } - - /** - * Sets the base JNDI environment template to use when acquiring LDAP connections. It is typically more common - * to use the other get/set methods in this class to set individual environment settings rather than use - * this method, but it is available for advanced users that want full control over the base JNDI environment - * settings. - *

    - * Note that this template only represents the base/default environment settings. It is then merged with - * appropriate runtime values as necessary in the {@link #getLdapContext(Object, Object)} implementation. - * The merged environment instance is what is used to acquire the connection ({@link LdapContext}) at runtime. - * - * @param env the base JNDI environment template to use when acquiring LDAP connections. - */ - @SuppressWarnings({"unchecked"}) - public void setEnvironment(Map env) { - this.environment = env; - } - - /** - * Returns the environment property value bound under the specified key. - * - * @param name the name of the environment property - * @return the property value or {@code null} if the value has not been set. - */ - private Object getEnvironmentProperty(String name) { - return this.environment.get(name); - } - - /** - * Will apply the value to the environment attribute if and only if the value is not null or empty. If it is - * null or empty, the corresponding environment attribute will be removed. - * - * @param name the environment property key - * @param value the environment property value. A null/empty value will trigger removal. - */ - private void setEnvironmentProperty(String name, String value) { - if (StringUtils.hasText(value)) { - this.environment.put(name, value); - } else { - this.environment.remove(name); - } - } - - /** - * Returns whether or not connection pooling should be used when possible and appropriate. This property is NOT - * backed by the {@link #getEnvironment() environment template} like most other properties in this class. It - * is a flag to indicate that pooling is preferred. The default value is {@code true}. - *

    - * However, pooling will only actually be enabled if this property is {@code true} and the connection - * being created is for the {@link #getSystemUsername() systemUsername} user. Connection pooling is not used for - * general authentication attempts by application end-users because the probability of re-use for that same - * user-specific connection after an authentication attempt is extremely low. - *

    - * If this attribute is {@code true} and it has been determined that the connection is being made with the - * {@link #getSystemUsername() systemUsername}, the - * {@link #getLdapContext(Object, Object)} implementation will set the Sun/Oracle-specific - * {@code com.sun.jndi.ldap.connect.pool} environment property to "{@code true}". This means setting - * this property is only likely to work if using the Sun/Oracle default context factory class (i.e. not using - * a custom {@link #getContextFactoryClassName() contextFactoryClassName}). - * - * @return whether or not connection pooling should be used when possible and appropriate - */ - public boolean isPoolingEnabled() { - return poolingEnabled; - } - - /** - * Sets whether or not connection pooling should be used when possible and appropriate. This property is NOT - * a wrapper to the {@link #getEnvironment() environment template} like most other properties in this class. It - * is a flag to indicate that pooling is preferred. The default value is {@code true}. - *

    - * However, pooling will only actually be enabled if this property is {@code true} and the connection - * being created is for the {@link #getSystemUsername() systemUsername} user. Connection pooling is not used for - * general authentication attempts by application end-users because the probability of re-use for that same - * user-specific connection after an authentication attempt is extremely low. - *

    - * If this attribute is {@code true} and it has been determined that the connection is being made with the - * {@link #getSystemUsername() systemUsername}, the - * {@link #getLdapContext(Object, Object)} implementation will set the Sun/Oracle-specific - * {@code com.sun.jndi.ldap.connect.pool} environment property to "{@code true}". This means setting - * this property is only likely to work if using the Sun/Oracle default context factory class (i.e. not using - * a custom {@link #getContextFactoryClassName() contextFactoryClassName}). - * - * @param poolingEnabled whether or not connection pooling should be used when possible and appropriate - */ - public void setPoolingEnabled(boolean poolingEnabled) { - this.poolingEnabled = poolingEnabled; - } - - /** - * Sets the LDAP referral behavior when creating a connection. Defaults to {@code follow}. See the Sun/Oracle LDAP - * referral documentation for more. - * - * @param referral the referral property. - * @see Referrals in JNDI - */ - public void setReferral(String referral) { - setEnvironmentProperty(Context.REFERRAL, referral); - } - - /** - * Returns the LDAP referral behavior when creating a connection. Defaults to {@code follow}. - * See the Sun/Oracle LDAP - * referral documentation for more. - * - * @return the LDAP referral behavior when creating a connection. - * @see Referrals in JNDI - */ - public String getReferral() { - return (String) getEnvironmentProperty(Context.REFERRAL); - } - - /** - * The LDAP url to connect to. (e.g. ldap://<ldapDirectoryHostname>:<port>). This must be configured. - * - * @param url the LDAP url to connect to. (e.g. ldap://<ldapDirectoryHostname>:<port>) - */ - public void setUrl(String url) { - setEnvironmentProperty(Context.PROVIDER_URL, url); - } - - /** - * Returns the LDAP url to connect to. (e.g. ldap://<ldapDirectoryHostname>:<port>). - * This must be configured. - * - * @return the LDAP url to connect to. (e.g. ldap://<ldapDirectoryHostname>:<port>) - */ - public String getUrl() { - return (String) getEnvironmentProperty(Context.PROVIDER_URL); - } - - /** - * Sets the password of the {@link #setSystemUsername(String) systemUsername} that will be used when creating an - * LDAP connection used for authorization queries. - *

    - * Note that setting this property is not required if the calling LDAP Realm does not perform authorization - * checks. - * - * @param systemPassword the password of the {@link #setSystemUsername(String) systemUsername} that will be used - * when creating an LDAP connection used for authorization queries. - */ - public void setSystemPassword(String systemPassword) { - this.systemPassword = systemPassword; - } - - /** - * Returns the password of the {@link #setSystemUsername(String) systemUsername} that will be used when creating an - * LDAP connection used for authorization queries. - *

    - * Note that setting this property is not required if the calling LDAP Realm does not perform authorization - * checks. - * - * @return the password of the {@link #setSystemUsername(String) systemUsername} that will be used when creating an - * LDAP connection used for authorization queries. - */ - public String getSystemPassword() { - return this.systemPassword; - } - - /** - * Sets the system username that will be used when creating an LDAP connection used for authorization queries. - * The user must have the ability to query for authorization data for any application user. - *

    - * Note that setting this property is not required if the calling LDAP Realm does not perform authorization - * checks. - * - * @param systemUsername the system username that will be used when creating an LDAP connection used for - * authorization queries. - */ - public void setSystemUsername(String systemUsername) { - this.systemUsername = systemUsername; - } - - /** - * Returns the system username that will be used when creating an LDAP connection used for authorization queries. - * The user must have the ability to query for authorization data for any application user. - *

    - * Note that setting this property is not required if the calling LDAP Realm does not perform authorization - * checks. - * - * @return the system username that will be used when creating an LDAP connection used for authorization queries. - */ - public String getSystemUsername() { - return systemUsername; - } - - /*-------------------------------------------- - | M E T H O D S | - ============================================*/ - - /** - * This implementation delegates to {@link #getLdapContext(Object, Object)} using the - * {@link #getSystemUsername() systemUsername} and {@link #getSystemPassword() systemPassword} properties as - * arguments. - * - * @return the system LdapContext - * @throws NamingException if there is a problem connecting to the LDAP directory - */ - public LdapContext getSystemLdapContext() throws NamingException { - return getLdapContext(getSystemUsername(), getSystemPassword()); - } - - /** - * Returns {@code true} if LDAP connection pooling should be used when acquiring a connection based on the specified - * account principal, {@code false} otherwise. - *

    - * This implementation returns {@code true} only if {@link #isPoolingEnabled()} and the principal equals the - * {@link #getSystemUsername()}. The reasoning behind this is that connection pooling is not desirable for - * general authentication attempts by application end-users because the probability of re-use for that same - * user-specific connection after an authentication attempt is extremely low. - * - * @param principal the principal under which the connection will be made - * @return {@code true} if LDAP connection pooling should be used when acquiring a connection based on the specified - * account principal, {@code false} otherwise. - */ - protected boolean isPoolingConnections(Object principal) { - return isPoolingEnabled() && principal != null && principal.equals(getSystemUsername()); - } - - /** - * This implementation returns an LdapContext based on the configured JNDI/LDAP environment configuration. - * The environment (Map) used at runtime is created by merging the default/configured - * {@link #getEnvironment() environment template} with some runtime values as necessary (e.g. a principal and - * credential available at runtime only). - *

    - * After the merged Map instance is created, the LdapContext connection is - * {@link #createLdapContext(java.util.Hashtable) created} and returned. - * - * @param principal the principal to use when acquiring a connection to the LDAP directory - * @param credentials the credentials (password, X.509 certificate, etc.) to use when acquiring a connection to the - * LDAP directory - * @return the acquired {@code LdapContext} connection bound using the specified principal and credentials. - * @throws NamingException - * @throws IllegalStateException - */ - public LdapContext getLdapContext(Object principal, Object credentials) throws NamingException, - IllegalStateException { - - String url = getUrl(); - if (url == null) { - throw new IllegalStateException("An LDAP URL must be specified of the form ldap://:"); - } - - //copy the environment template into the runtime instance that will be further edited based on - //the method arguments and other class attributes. - Hashtable env = new Hashtable(this.environment); - - Object authcMech = getAuthenticationMechanism(); - if (authcMech == null && (principal != null || credentials != null)) { - //authenticationMechanism has not been set, but either a principal and/or credentials were - //supplied, indicating that at least a 'simple' authentication attempt is indeed occurring - the Shiro - //end-user just didn't configure it explicitly. So we set it to be 'simple' here as a convenience; - //the Sun provider implementation already does this same logic, but by repeating that logic here, we ensure - //this convenience exists regardless of provider implementation): - env.put(Context.SECURITY_AUTHENTICATION, SIMPLE_AUTHENTICATION_MECHANISM_NAME); - } - if (principal != null) { - env.put(Context.SECURITY_PRINCIPAL, principal); - } - if (credentials != null) { - env.put(Context.SECURITY_CREDENTIALS, credentials); - } - - boolean pooling = isPoolingConnections(principal); - if (pooling) { - env.put(SUN_CONNECTION_POOLING_PROPERTY, "true"); - } - - if (log.isDebugEnabled()) { - log.debug("Initializing LDAP context using URL [{}] and principal [{}] with pooling {}", - new Object[]{url, principal, (pooling ? "enabled" : "disabled")}); - } - - // validate the config before creating the context - validateAuthenticationInfo(env); - - return createLdapContext(env); - } - - /** - * Creates and returns a new {@link javax.naming.ldap.InitialLdapContext} instance. This method exists primarily - * to support testing where a mock LdapContext can be returned instead of actually creating a connection, but - * subclasses are free to provide a different implementation if necessary. - * - * @param env the JNDI environment settings used to create the LDAP connection - * @return an LdapConnection - * @throws NamingException if a problem occurs creating the connection - */ - protected LdapContext createLdapContext(Hashtable env) throws NamingException { - return new InitialLdapContext(env, null); - } - - - /** - * Validates the configuration in the JNDI environment settings and throws an exception if a problem - * exists. - *

    - * This implementation will throw a {@link AuthenticationException} if the authentication mechanism is set to - * 'simple', the principal is non-empty, and the credentials are empty (as per - * rfc4513 section-5.1.2). - * - * @param environment the JNDI environment settings to be validated - * @throws AuthenticationException if a configuration problem is detected - */ - protected void validateAuthenticationInfo(Hashtable environment) - throws AuthenticationException - { - // validate when using Simple auth both principal and credentials are set - if(SIMPLE_AUTHENTICATION_MECHANISM_NAME.equals(environment.get(Context.SECURITY_AUTHENTICATION))) { - - // only validate credentials if we have a non-empty principal - if( environment.get(Context.SECURITY_PRINCIPAL) != null && - StringUtils.hasText( String.valueOf( environment.get(Context.SECURITY_PRINCIPAL) ))) { - - Object credentials = environment.get(Context.SECURITY_CREDENTIALS); - - // from the FAQ, we need to check for empty credentials: - // http://docs.oracle.com/javase/tutorial/jndi/ldap/faq.html - if( credentials == null || - (credentials instanceof byte[] && ((byte[])credentials).length <= 0) || // empty byte[] - (credentials instanceof char[] && ((char[])credentials).length <= 0) || // empty char[] - (String.class.isInstance(credentials) && !StringUtils.hasText(String.valueOf(credentials)))) { - - throw new javax.naming.AuthenticationException("LDAP Simple authentication requires both a " - + "principal and credentials."); - } - } - } - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.realm.ldap; + +import org.apache.shiro.lang.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.naming.AuthenticationException; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.ldap.InitialLdapContext; +import javax.naming.ldap.LdapContext; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +/** + * {@link LdapContextFactory} implementation using the default Sun/Oracle JNDI Ldap API, utilizing JNDI + * environment properties and an {@link javax.naming.InitialContext}. + *

    Configuration

    + * This class basically wraps a default template JNDI environment properties Map. This properties map is the base + * configuration template used to acquire JNDI {@link LdapContext} connections at runtime. The + * {@link #getLdapContext(Object, Object)} method implementation merges this default template with other properties + * accessible at runtime only (for example per-method principals and credentials). The constructed runtime map is the + * one used to acquire the {@link LdapContext}. + *

    + * The template can be configured directly via the {@link #getEnvironment()}/{@link #setEnvironment(java.util.Map)} + * properties directly if necessary, but it is usually more convenient to use the supporting wrapper get/set methods + * for various environment properties. These wrapper methods interact with the environment + * template on your behalf, leaving your configuration cleaner and easier to understand. + *

    + * For example, consider the following two identical configurations: + *

    + * [main]
    + * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
    + * ldapRealm.contextFactory.url = ldap://localhost:389
    + * ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
    + * 
    + * and + *
    + * [main]
    + * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
    + * ldapRealm.contextFactory.environment[java.naming.provider.url] = ldap://localhost:389
    + * ldapRealm.contextFactory.environment[java.naming.security.authentication] = DIGEST-MD5
    + * 
    + * As you can see, the 2nd configuration block is a little more difficult to read and also requires knowledge + * of the underlying JNDI Context property keys. The first is easier to read and understand. + *

    + * Note that occasionally it will be necessary to use the latter configuration style to set environment properties + * where no corresponding wrapper method exists. In this case, the hybrid approach is still a little easier to read. + * For example: + *

    + * [main]
    + * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
    + * ldapRealm.contextFactory.url = ldap://localhost:389
    + * ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
    + * ldapRealm.contextFactory.environment[some.other.obscure.jndi.key] = some value
    + * 
    + * + * @since 1.1 + */ +public class JndiLdapContextFactory implements LdapContextFactory { + + /*------------------------------------------- + | C O N S T A N T S | + ===========================================*/ + /** + * The Sun LDAP property used to enable connection pooling. This is used in the default implementation + * to enable LDAP connection pooling. + */ + protected static final String SUN_CONNECTION_POOLING_PROPERTY = "com.sun.jndi.ldap.connect.pool"; + protected static final String DEFAULT_CONTEXT_FACTORY_CLASS_NAME = "com.sun.jndi.ldap.LdapCtxFactory"; + protected static final String SIMPLE_AUTHENTICATION_MECHANISM_NAME = "simple"; + protected static final String DEFAULT_REFERRAL = "follow"; + + private static final Logger LOGGER = LoggerFactory.getLogger(JndiLdapContextFactory.class); + + /*------------------------------------------- + | I N S T A N C E V A R I A B L E S | + ============================================*/ + private Map environment; + private boolean poolingEnabled; + private String systemPassword; + private String systemUsername; + + /*------------------------------------------- + | C O N S T R U C T O R S | + ===========================================*/ + + /** + * Default no-argument constructor that initializes the backing {@link #getEnvironment() environment template} with + * the {@link #setContextFactoryClassName(String) contextFactoryClassName} equal to + * {@code com.sun.jndi.ldap.LdapCtxFactory} (the Sun/Oracle default) and the default + * {@link #setReferral(String) referral} behavior to {@code follow}. + */ + public JndiLdapContextFactory() { + this.environment = new HashMap(); + setContextFactoryClassName(DEFAULT_CONTEXT_FACTORY_CLASS_NAME); + setReferral(DEFAULT_REFERRAL); + poolingEnabled = true; + } + + /*------------------------------------------- + | A C C E S S O R S / M O D I F I E R S | + ===========================================*/ + + /** + * Sets the type of LDAP authentication mechanism to use when connecting to the LDAP server. + * This is a wrapper method for setting the JNDI {@link #getEnvironment() environment template}'s + * {@link Context#SECURITY_AUTHENTICATION} property. + *

    + * "none" (i.e. anonymous) and "simple" authentications are supported automatically and don't need to be configured + * via this property. However, if you require a different mechanism, such as a SASL or External mechanism, you + * must configure that explicitly via this property. See the + * JNDI LDAP + * Authentication Mechanisms for more information. + * + * @param authenticationMechanism the type of LDAP authentication to perform. + * @see + * http://download-llnw.oracle.com/javase/tutorial/jndi/ldap/auth_mechs.html + */ + public void setAuthenticationMechanism(String authenticationMechanism) { + setEnvironmentProperty(Context.SECURITY_AUTHENTICATION, authenticationMechanism); + } + + /** + * Returns the type of LDAP authentication mechanism to use when connecting to the LDAP server. + * This is a wrapper method for getting the JNDI {@link #getEnvironment() environment template}'s + * {@link Context#SECURITY_AUTHENTICATION} property. + *

    + * If this property remains un-configured (i.e. {@code null} indicating the + * {@link #setAuthenticationMechanism(String)} method wasn't used), this indicates that the default JNDI + * "none" (anonymous) and "simple" authentications are supported automatically. Any non-null value returned + * represents an explicitly configured mechanism (e.g. a SASL or external mechanism). See the + * JNDI LDAP + * Authentication Mechanisms for more information. + * + * @return the type of LDAP authentication mechanism to use when connecting to the LDAP server. + * @see + * http://download-llnw.oracle.com/javase/tutorial/jndi/ldap/auth_mechs.html + */ + public String getAuthenticationMechanism() { + return (String) getEnvironmentProperty(Context.SECURITY_AUTHENTICATION); + } + + /** + * The name of the ContextFactory class to use. This defaults to the SUN LDAP JNDI implementation + * but can be overridden to use custom LDAP factories. + *

    + * This is a wrapper method for setting the JNDI environment's {@link Context#INITIAL_CONTEXT_FACTORY} property. + * + * @param contextFactoryClassName the context factory that should be used. + */ + public void setContextFactoryClassName(String contextFactoryClassName) { + setEnvironmentProperty(Context.INITIAL_CONTEXT_FACTORY, contextFactoryClassName); + } + + /** + * Sets the name of the ContextFactory class to use. This defaults to the SUN LDAP JNDI implementation + * but can be overridden to use custom LDAP factories. + *

    + * This is a wrapper method for getting the JNDI environment's {@link Context#INITIAL_CONTEXT_FACTORY} property. + * + * @return the name of the ContextFactory class to use. + */ + public String getContextFactoryClassName() { + return (String) getEnvironmentProperty(Context.INITIAL_CONTEXT_FACTORY); + } + + /** + * Returns the base JNDI environment template to use when acquiring an LDAP connection (an {@link LdapContext}). + * This property is the base configuration template to use for all connections. This template is then + * merged with appropriate runtime values as necessary in the + * {@link #getLdapContext(Object, Object)} implementation. The merged environment instance is what is used to + * acquire the {@link LdapContext} at runtime. + *

    + * Most other get/set methods in this class act as thin proxy wrappers that interact with this property. The + * benefit of using them is you have an easier-to-use configuration mechanism compared to setting map properties + * based on JNDI context keys. + * + * @return the base JNDI environment template to use when acquiring an LDAP connection (an {@link LdapContext}) + */ + public Map getEnvironment() { + return this.environment; + } + + /** + * Sets the base JNDI environment template to use when acquiring LDAP connections. It is typically more common + * to use the other get/set methods in this class to set individual environment settings rather than use + * this method, but it is available for advanced users that want full control over the base JNDI environment + * settings. + *

    + * Note that this template only represents the base/default environment settings. It is then merged with + * appropriate runtime values as necessary in the {@link #getLdapContext(Object, Object)} implementation. + * The merged environment instance is what is used to acquire the connection ({@link LdapContext}) at runtime. + * + * @param env the base JNDI environment template to use when acquiring LDAP connections. + */ + @SuppressWarnings({"unchecked"}) + public void setEnvironment(Map env) { + this.environment = env; + } + + /** + * Returns the environment property value bound under the specified key. + * + * @param name the name of the environment property + * @return the property value or {@code null} if the value has not been set. + */ + private Object getEnvironmentProperty(String name) { + return this.environment.get(name); + } + + /** + * Will apply the value to the environment attribute if and only if the value is not null or empty. If it is + * null or empty, the corresponding environment attribute will be removed. + * + * @param name the environment property key + * @param value the environment property value. A null/empty value will trigger removal. + */ + private void setEnvironmentProperty(String name, String value) { + if (StringUtils.hasText(value)) { + this.environment.put(name, value); + } else { + this.environment.remove(name); + } + } + + /** + * Returns whether or not connection pooling should be used when possible and appropriate. This property is NOT + * backed by the {@link #getEnvironment() environment template} like most other properties in this class. It + * is a flag to indicate that pooling is preferred. The default value is {@code true}. + *

    + * However, pooling will only actually be enabled if this property is {@code true} and the connection + * being created is for the {@link #getSystemUsername() systemUsername} user. Connection pooling is not used for + * general authentication attempts by application end-users because the probability of re-use for that same + * user-specific connection after an authentication attempt is extremely low. + *

    + * If this attribute is {@code true} and it has been determined that the connection is being made with the + * {@link #getSystemUsername() systemUsername}, the + * {@link #getLdapContext(Object, Object)} implementation will set the Sun/Oracle-specific + * {@code com.sun.jndi.ldap.connect.pool} environment property to "{@code true}". This means setting + * this property is only likely to work if using the Sun/Oracle default context factory class (i.e. not using + * a custom {@link #getContextFactoryClassName() contextFactoryClassName}). + * + * @return whether or not connection pooling should be used when possible and appropriate + */ + public boolean isPoolingEnabled() { + return poolingEnabled; + } + + /** + * Sets whether or not connection pooling should be used when possible and appropriate. This property is NOT + * a wrapper to the {@link #getEnvironment() environment template} like most other properties in this class. It + * is a flag to indicate that pooling is preferred. The default value is {@code true}. + *

    + * However, pooling will only actually be enabled if this property is {@code true} and the connection + * being created is for the {@link #getSystemUsername() systemUsername} user. Connection pooling is not used for + * general authentication attempts by application end-users because the probability of re-use for that same + * user-specific connection after an authentication attempt is extremely low. + *

    + * If this attribute is {@code true} and it has been determined that the connection is being made with the + * {@link #getSystemUsername() systemUsername}, the + * {@link #getLdapContext(Object, Object)} implementation will set the Sun/Oracle-specific + * {@code com.sun.jndi.ldap.connect.pool} environment property to "{@code true}". This means setting + * this property is only likely to work if using the Sun/Oracle default context factory class (i.e. not using + * a custom {@link #getContextFactoryClassName() contextFactoryClassName}). + * + * @param poolingEnabled whether or not connection pooling should be used when possible and appropriate + */ + public void setPoolingEnabled(boolean poolingEnabled) { + this.poolingEnabled = poolingEnabled; + } + + /** + * Sets the LDAP referral behavior when creating a connection. Defaults to {@code follow}. See the Sun/Oracle LDAP + * referral documentation for more. + * + * @param referral the referral property. + * @see Referrals in JNDI + */ + public void setReferral(String referral) { + setEnvironmentProperty(Context.REFERRAL, referral); + } + + /** + * Returns the LDAP referral behavior when creating a connection. Defaults to {@code follow}. + * See the Sun/Oracle LDAP + * referral documentation for more. + * + * @return the LDAP referral behavior when creating a connection. + * @see Referrals in JNDI + */ + public String getReferral() { + return (String) getEnvironmentProperty(Context.REFERRAL); + } + + /** + * The LDAP url to connect to. (e.g. ldap://<ldapDirectoryHostname>:<port>). This must be configured. + * + * @param url the LDAP url to connect to. (e.g. ldap://<ldapDirectoryHostname>:<port>) + */ + public void setUrl(String url) { + setEnvironmentProperty(Context.PROVIDER_URL, url); + } + + /** + * Returns the LDAP url to connect to. (e.g. ldap://<ldapDirectoryHostname>:<port>). + * This must be configured. + * + * @return the LDAP url to connect to. (e.g. ldap://<ldapDirectoryHostname>:<port>) + */ + public String getUrl() { + return (String) getEnvironmentProperty(Context.PROVIDER_URL); + } + + /** + * Sets the password of the {@link #setSystemUsername(String) systemUsername} that will be used when creating an + * LDAP connection used for authorization queries. + *

    + * Note that setting this property is not required if the calling LDAP Realm does not perform authorization + * checks. + * + * @param systemPassword the password of the {@link #setSystemUsername(String) systemUsername} that will be used + * when creating an LDAP connection used for authorization queries. + */ + public void setSystemPassword(String systemPassword) { + this.systemPassword = systemPassword; + } + + /** + * Returns the password of the {@link #setSystemUsername(String) systemUsername} that will be used when creating an + * LDAP connection used for authorization queries. + *

    + * Note that setting this property is not required if the calling LDAP Realm does not perform authorization + * checks. + * + * @return the password of the {@link #setSystemUsername(String) systemUsername} that will be used when creating an + * LDAP connection used for authorization queries. + */ + public String getSystemPassword() { + return this.systemPassword; + } + + /** + * Sets the system username that will be used when creating an LDAP connection used for authorization queries. + * The user must have the ability to query for authorization data for any application user. + *

    + * Note that setting this property is not required if the calling LDAP Realm does not perform authorization + * checks. + * + * @param systemUsername the system username that will be used when creating an LDAP connection used for + * authorization queries. + */ + public void setSystemUsername(String systemUsername) { + this.systemUsername = systemUsername; + } + + /** + * Returns the system username that will be used when creating an LDAP connection used for authorization queries. + * The user must have the ability to query for authorization data for any application user. + *

    + * Note that setting this property is not required if the calling LDAP Realm does not perform authorization + * checks. + * + * @return the system username that will be used when creating an LDAP connection used for authorization queries. + */ + public String getSystemUsername() { + return systemUsername; + } + + /*-------------------------------------------- + | M E T H O D S | + ============================================*/ + + /** + * This implementation delegates to {@link #getLdapContext(Object, Object)} using the + * {@link #getSystemUsername() systemUsername} and {@link #getSystemPassword() systemPassword} properties as + * arguments. + * + * @return the system LdapContext + * @throws NamingException if there is a problem connecting to the LDAP directory + */ + public LdapContext getSystemLdapContext() throws NamingException { + return getLdapContext(getSystemUsername(), getSystemPassword()); + } + + /** + * Returns {@code true} if LDAP connection pooling should be used when acquiring a connection based on the specified + * account principal, {@code false} otherwise. + *

    + * This implementation returns {@code true} only if {@link #isPoolingEnabled()} and the principal equals the + * {@link #getSystemUsername()}. The reasoning behind this is that connection pooling is not desirable for + * general authentication attempts by application end-users because the probability of re-use for that same + * user-specific connection after an authentication attempt is extremely low. + * + * @param principal the principal under which the connection will be made + * @return {@code true} if LDAP connection pooling should be used when acquiring a connection based on the specified + * account principal, {@code false} otherwise. + */ + protected boolean isPoolingConnections(Object principal) { + return isPoolingEnabled() && principal != null && principal.equals(getSystemUsername()); + } + + /** + * This implementation returns an LdapContext based on the configured JNDI/LDAP environment configuration. + * The environment (Map) used at runtime is created by merging the default/configured + * {@link #getEnvironment() environment template} with some runtime values as necessary (e.g. a principal and + * credential available at runtime only). + *

    + * After the merged Map instance is created, the LdapContext connection is + * {@link #createLdapContext(java.util.Hashtable) created} and returned. + * + * @param principal the principal to use when acquiring a connection to the LDAP directory + * @param credentials the credentials (password, X.509 certificate, etc.) to use when acquiring a connection to the + * LDAP directory + * @return the acquired {@code LdapContext} connection bound using the specified principal and credentials. + * @throws NamingException + * @throws IllegalStateException + */ + public LdapContext getLdapContext(Object principal, Object credentials) throws NamingException, + IllegalStateException { + + String url = getUrl(); + if (url == null) { + throw new IllegalStateException("An LDAP URL must be specified of the form ldap://:"); + } + + //copy the environment template into the runtime instance that will be further edited based on + //the method arguments and other class attributes. + Hashtable env = new Hashtable(this.environment); + + Object authcMech = getAuthenticationMechanism(); + if (authcMech == null && (principal != null || credentials != null)) { + //authenticationMechanism has not been set, but either a principal and/or credentials were + //supplied, indicating that at least a 'simple' authentication attempt is indeed occurring - the Shiro + //end-user just didn't configure it explicitly. So we set it to be 'simple' here as a convenience; + //the Sun provider implementation already does this same logic, but by repeating that logic here, we ensure + //this convenience exists regardless of provider implementation): + env.put(Context.SECURITY_AUTHENTICATION, SIMPLE_AUTHENTICATION_MECHANISM_NAME); + } + if (principal != null) { + env.put(Context.SECURITY_PRINCIPAL, principal); + } + if (credentials != null) { + env.put(Context.SECURITY_CREDENTIALS, credentials); + } + + boolean pooling = isPoolingConnections(principal); + if (pooling) { + env.put(SUN_CONNECTION_POOLING_PROPERTY, "true"); + } + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Initializing LDAP context using URL [{}] and principal [{}] with pooling {}", + new Object[] {url, principal, (pooling ? "enabled" : "disabled")}); + } + + // validate the config before creating the context + validateAuthenticationInfo(env); + + return createLdapContext(env); + } + + /** + * Creates and returns a new {@link javax.naming.ldap.InitialLdapContext} instance. This method exists primarily + * to support testing where a mock LdapContext can be returned instead of actually creating a connection, but + * subclasses are free to provide a different implementation if necessary. + * + * @param env the JNDI environment settings used to create the LDAP connection + * @return an LdapConnection + * @throws NamingException if a problem occurs creating the connection + */ + protected LdapContext createLdapContext(Hashtable env) throws NamingException { + return new InitialLdapContext(env, null); + } + + + /** + * Validates the configuration in the JNDI environment settings and throws an exception if a problem + * exists. + *

    + * This implementation will throw a {@link AuthenticationException} if the authentication mechanism is set to + * 'simple', the principal is non-empty, and the credentials are empty (as per + * rfc4513 section-5.1.2). + * + * @param environment the JNDI environment settings to be validated + * @throws AuthenticationException if a configuration problem is detected + */ + @SuppressWarnings({"checkstyle:LineLength", "checkstyle:BooleanExpressionComplexity"}) + protected void validateAuthenticationInfo(Hashtable environment) + throws AuthenticationException { + // validate when using Simple auth both principal and credentials are set + if (SIMPLE_AUTHENTICATION_MECHANISM_NAME.equals(environment.get(Context.SECURITY_AUTHENTICATION))) { + + // only validate credentials if we have a non-empty principal + if (environment.get(Context.SECURITY_PRINCIPAL) != null + && StringUtils.hasText(String.valueOf(environment.get(Context.SECURITY_PRINCIPAL)))) { + + Object credentials = environment.get(Context.SECURITY_CREDENTIALS); + + // from the FAQ, we need to check for empty credentials: + // http://docs.oracle.com/javase/tutorial/jndi/ldap/faq.html + if (credentials == null + || (credentials instanceof byte[] && ((byte[]) credentials).length <= 0) + || (credentials instanceof char[] && ((char[]) credentials).length <= 0) + || (String.class.isInstance(credentials) && !StringUtils.hasText(String.valueOf(credentials)))) { + + throw new javax.naming.AuthenticationException("LDAP Simple authentication requires both a " + + "principal and credentials."); + } + } + } + } + +} diff --git a/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapRealm.java b/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapRealm.java index 1c7fd54626..415c97fbe4 100644 --- a/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapRealm.java @@ -20,10 +20,10 @@ /** * This class has been replaced with DefaultLdapRealm. + * * @see DefaultLdapRealm - * @deprecated Renamed to {@link DefaultLdapRealm}, this class will be removed prior to 2.0 * @since 1.1 - * + * @deprecated Renamed to {@link DefaultLdapRealm}, this class will be removed prior to 2.0 */ public class JndiLdapRealm extends DefaultLdapRealm { diff --git a/core/src/main/java/org/apache/shiro/realm/ldap/LdapContextFactory.java b/core/src/main/java/org/apache/shiro/realm/ldap/LdapContextFactory.java index 632d3b6b51..6b69c025ff 100644 --- a/core/src/main/java/org/apache/shiro/realm/ldap/LdapContextFactory.java +++ b/core/src/main/java/org/apache/shiro/realm/ldap/LdapContextFactory.java @@ -34,7 +34,7 @@ public interface LdapContextFactory { * anonymously if no system account is configured. * * @return a {@code LdapContext} bound by the system account, or bound anonymously if no system account - * is configured. + * is configured. * @throws javax.naming.NamingException if there is an error creating the context. */ LdapContext getSystemLdapContext() throws NamingException; @@ -59,5 +59,5 @@ public interface LdapContextFactory { * @since 1.1 */ LdapContext getLdapContext(Object principal, Object credentials) throws NamingException; - + } diff --git a/core/src/main/java/org/apache/shiro/realm/ldap/LdapUtils.java b/core/src/main/java/org/apache/shiro/realm/ldap/LdapUtils.java index 750b017f75..ba12a2a28f 100644 --- a/core/src/main/java/org/apache/shiro/realm/ldap/LdapUtils.java +++ b/core/src/main/java/org/apache/shiro/realm/ldap/LdapUtils.java @@ -40,7 +40,10 @@ public final class LdapUtils { /** * Private internal log instance. */ - private static final Logger log = LoggerFactory.getLogger(LdapUtils.class); + private static final Logger LOGGER = LoggerFactory.getLogger(LdapUtils.class); + + private LdapUtils() { + } /** * Closes an LDAP context, logging any errors, but not throwing @@ -54,7 +57,7 @@ public static void closeContext(LdapContext ctx) { ctx.close(); } } catch (NamingException e) { - log.error("Exception while closing LDAP context. ", e); + LOGGER.error("Exception while closing LDAP context. ", e); } } @@ -81,16 +84,18 @@ public static Collection getAllAttributeValues(Attribute attr) throws Na return values; } - //added based on SHIRO-127, per Emmanuel's comment [1] - // [1] https://issues.apache.org/jira/browse/SHIRO-127?focusedCommentId=12891380&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#action_12891380 - + /** + * added based on SHIRO-127, per Emmanuel's comment [1] + * [1] + */ public static void closeEnumeration(NamingEnumeration ne) { try { if (ne != null) { ne.close(); } } catch (NamingException e) { - log.error("Exception while closing NamingEnumeration: ", e); + LOGGER.error("Exception while closing NamingEnumeration: ", e); } } diff --git a/core/src/main/java/org/apache/shiro/realm/text/IniRealm.java b/core/src/main/java/org/apache/shiro/realm/text/IniRealm.java index 10b993ba9b..96b9ba32b8 100644 --- a/core/src/main/java/org/apache/shiro/realm/text/IniRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/text/IniRealm.java @@ -44,13 +44,23 @@ */ public class IniRealm extends TextConfigurationRealm { + /** + * users section name + */ public static final String USERS_SECTION_NAME = "users"; + /** + * roles section name + */ public static final String ROLES_SECTION_NAME = "roles"; - private static transient final Logger log = LoggerFactory.getLogger(IniRealm.class); + private static final Logger LOGGER = LoggerFactory.getLogger(IniRealm.class); private String resourcePath; - private Ini ini; //reference added in 1.2 for SHIRO-322 + + /** + * reference added in 1.2 for SHIRO-322 + */ + private Ini ini; public IniRealm() { super(); @@ -107,7 +117,7 @@ public void setResourcePath(String resourcePath) { /** * Returns the Ini instance used to configure this realm. Provided for JavaBeans-style configuration of this * realm, particularly useful in Dependency Injection environments. - * + * * @return the Ini instance which will be inspected to create accounts, groups and permissions for this realm. */ public Ini getIni() { @@ -117,7 +127,7 @@ public Ini getIni() { /** * Sets the Ini instance used to configure this realm. Provided for JavaBeans-style configuration of this * realm, particularly useful in Dependency Injection environments. - * + * * @param ini the Ini instance which will be inspected to create accounts, groups and permissions for this realm. */ public void setIni(Ini ini) { @@ -130,38 +140,38 @@ protected void onInit() { // This is an in-memory realm only - no need for an additional cache when we're already // as memory-efficient as we can be. - + Ini ini = getIni(); String resourcePath = getResourcePath(); - + if (!CollectionUtils.isEmpty(this.users) || !CollectionUtils.isEmpty(this.roles)) { if (!CollectionUtils.isEmpty(ini)) { - log.warn("Users or Roles are already populated. Configured Ini instance will be ignored."); + LOGGER.warn("Users or Roles are already populated. Configured Ini instance will be ignored."); } if (StringUtils.hasText(resourcePath)) { - log.warn("Users or Roles are already populated. resourcePath '{}' will be ignored.", resourcePath); + LOGGER.warn("Users or Roles are already populated. resourcePath '{}' will be ignored.", resourcePath); } - - log.debug("Instance is already populated with users or roles. No additional user/role population " + - "will be performed."); + + LOGGER.debug("Instance is already populated with users or roles. No additional user/role population " + + "will be performed."); return; } - + if (CollectionUtils.isEmpty(ini)) { - log.debug("No INI instance configuration present. Checking resourcePath..."); - + LOGGER.debug("No INI instance configuration present. Checking resourcePath..."); + if (StringUtils.hasText(resourcePath)) { - log.debug("Resource path {} defined. Creating INI instance.", resourcePath); + LOGGER.debug("Resource path {} defined. Creating INI instance.", resourcePath); ini = Ini.fromResourcePath(resourcePath); if (!CollectionUtils.isEmpty(ini)) { setIni(ini); } } } - + if (CollectionUtils.isEmpty(ini)) { - String msg = "Ini instance and/or resourcePath resulted in null or empty Ini configuration. Cannot " + - "load account data."; + String msg = "Ini instance and/or resourcePath resulted in null or empty Ini configuration. Cannot " + + "load account data."; throw new IllegalStateException(msg); } @@ -170,24 +180,24 @@ protected void onInit() { private void processDefinitions(Ini ini) { if (CollectionUtils.isEmpty(ini)) { - log.warn("{} defined, but the ini instance is null or empty.", getClass().getSimpleName()); + LOGGER.warn("{} defined, but the ini instance is null or empty.", getClass().getSimpleName()); return; } Ini.Section rolesSection = ini.getSection(ROLES_SECTION_NAME); if (!CollectionUtils.isEmpty(rolesSection)) { - log.debug("Discovered the [{}] section. Processing...", ROLES_SECTION_NAME); + LOGGER.debug("Discovered the [{}] section. Processing...", ROLES_SECTION_NAME); processRoleDefinitions(rolesSection); } Ini.Section usersSection = ini.getSection(USERS_SECTION_NAME); if (!CollectionUtils.isEmpty(usersSection)) { - log.debug("Discovered the [{}] section. Processing...", USERS_SECTION_NAME); + LOGGER.debug("Discovered the [{}] section. Processing...", USERS_SECTION_NAME); processUserDefinitions(usersSection); } else { - log.info("{} defined, but there is no [{}] section defined. This realm will not be populated with any " + - "users and it is assumed that they will be populated programatically. Users must be defined " + - "for this Realm instance to be useful.", getClass().getSimpleName(), USERS_SECTION_NAME); + LOGGER.info("{} defined, but there is no [{}] section defined. This realm will not be populated with any " + + "users and it is assumed that they will be populated programatically. Users must be defined " + + "for this Realm instance to be useful.", getClass().getSimpleName(), USERS_SECTION_NAME); } } } diff --git a/core/src/main/java/org/apache/shiro/realm/text/PropertiesRealm.java b/core/src/main/java/org/apache/shiro/realm/text/PropertiesRealm.java index 3ced80f8ce..d60ba48c03 100644 --- a/core/src/main/java/org/apache/shiro/realm/text/PropertiesRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/text/PropertiesRealm.java @@ -96,10 +96,10 @@ public class PropertiesRealm extends TextConfigurationRealm implements Destroyab /*------------------------------------------- | I N S T A N C E V A R I A B L E S | ============================================*/ - private static final Logger log = LoggerFactory.getLogger(PropertiesRealm.class); + private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesRealm.class); - protected ExecutorService scheduler = null; - protected boolean useXmlFormat = false; + protected ExecutorService scheduler; + protected boolean useXmlFormat; protected String resourcePath = DEFAULT_RESOURCE_PATH; protected long fileLastModified; protected int reloadIntervalSeconds = DEFAULT_RELOAD_INTERVAL_SECONDS; @@ -176,14 +176,15 @@ public void destroy() { scheduler.shutdown(); } } catch (Exception e) { - if (log.isInfoEnabled()) { - log.info("Unable to cleanly shutdown Scheduler. Ignoring (shutting down)...", e); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Unable to cleanly shutdown Scheduler. Ignoring (shutting down)...", e); } } finally { scheduler = null; } } + @SuppressWarnings("checkstyle:LineLength") protected void startReloadThread() { if (this.reloadIntervalSeconds > 0) { this.scheduler = Executors.newSingleThreadScheduledExecutor(); @@ -195,20 +196,20 @@ public void run() { try { reloadPropertiesIfNecessary(); } catch (Exception e) { - if (log.isErrorEnabled()) { - log.error("Error while reloading property files for realm.", e); + if (LOGGER.isErrorEnabled()) { + LOGGER.error("Error while reloading property files for realm.", e); } } } private void loadProperties() { if (resourcePath == null || resourcePath.length() == 0) { - throw new IllegalStateException("The resourcePath property is not set. " + - "It must be set prior to this realm being initialized."); + throw new IllegalStateException("The resourcePath property is not set. " + + "It must be set prior to this realm being initialized."); } - if (log.isDebugEnabled()) { - log.debug("Loading user security information from file [" + resourcePath + "]..."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Loading user security information from file [" + resourcePath + "]..."); } Properties properties = loadProperties(resourcePath); @@ -221,30 +222,30 @@ private Properties loadProperties(String resourcePath) { InputStream is = null; try { - if (log.isDebugEnabled()) { - log.debug("Opening input stream for path [" + resourcePath + "]..."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Opening input stream for path [" + resourcePath + "]..."); } is = ResourceUtils.getInputStreamForPath(resourcePath); if (useXmlFormat) { - if (log.isDebugEnabled()) { - log.debug("Loading properties from path [" + resourcePath + "] in XML format..."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Loading properties from path [" + resourcePath + "] in XML format..."); } props.loadFromXML(is); } else { - if (log.isDebugEnabled()) { - log.debug("Loading properties from path [" + resourcePath + "]..."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Loading properties from path [" + resourcePath + "]..."); } props.load(is); } } catch (IOException e) { - throw new ShiroException("Error reading properties path [" + resourcePath + "]. " + - "Initializing of the realm from this file failed.", e); + throw new ShiroException("Error reading properties path [" + resourcePath + "]. " + + "Initializing of the realm from this file failed.", e); } finally { ResourceUtils.close(is); } @@ -280,12 +281,12 @@ private boolean isFileModified() { @SuppressWarnings("unchecked") private void restart() { if (resourcePath == null || resourcePath.length() == 0) { - throw new IllegalStateException("The resourcePath property is not set. " + - "It must be set prior to this realm being initialized."); + throw new IllegalStateException("The resourcePath property is not set. " + + "It must be set prior to this realm being initialized."); } - if (log.isDebugEnabled()) { - log.debug("Loading user security information from file [" + resourcePath + "]..."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Loading user security information from file [" + resourcePath + "]..."); } try { @@ -308,8 +309,8 @@ private void createRealmEntitiesFromProperties(Properties properties) { String key = propNames.nextElement().trim(); String value = properties.getProperty(key).trim(); - if (log.isTraceEnabled()) { - log.trace("Processing properties line - key: [" + key + "], value: [" + value + "]."); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Processing properties line - key: [" + key + "], value: [" + value + "]."); } if (isUsername(key)) { @@ -319,8 +320,8 @@ private void createRealmEntitiesFromProperties(Properties properties) { String rolename = getRolename(key); roleDefs.append(rolename).append(" = ").append(value).append("\n"); } else { - String msg = "Encountered unexpected key/value pair. All keys must be prefixed with either '" + - USERNAME_PREFIX + "' or '" + ROLENAME_PREFIX + "'."; + String msg = "Encountered unexpected key/value pair. All keys must be prefixed with either '" + + USERNAME_PREFIX + "' or '" + ROLENAME_PREFIX + "'."; throw new IllegalStateException(msg); } } diff --git a/core/src/main/java/org/apache/shiro/realm/text/TextConfigurationRealm.java b/core/src/main/java/org/apache/shiro/realm/text/TextConfigurationRealm.java index 8feb159929..c3880a4922 100644 --- a/core/src/main/java/org/apache/shiro/realm/text/TextConfigurationRealm.java +++ b/core/src/main/java/org/apache/shiro/realm/text/TextConfigurationRealm.java @@ -62,8 +62,8 @@ public TextConfigurationRealm() { /** * Will call 'processDefinitions' on startup. * - * @since 1.2 * @see SHIRO-223 + * @since 1.2 */ @Override protected void onInit() { @@ -148,7 +148,7 @@ protected void processRoleDefinitions(Map roleDefs) { if (roleDefs == null || roleDefs.isEmpty()) { return; } - for (Map.Entry entry : roleDefs.entrySet()) { + for (Map.Entry entry : roleDefs.entrySet()) { String rolename = entry.getKey(); String value = entry.getValue(); @@ -178,7 +178,7 @@ protected void processUserDefinitions(Map userDefs) { if (userDefs == null || userDefs.isEmpty()) { return; } - for (Map.Entry entry : userDefs.entrySet()) { + for (Map.Entry entry : userDefs.entrySet()) { String username = entry.getKey(); String value = entry.getValue(); diff --git a/core/src/main/java/org/apache/shiro/session/Session.java b/core/src/main/java/org/apache/shiro/session/Session.java index f91d224003..109dc69129 100644 --- a/core/src/main/java/org/apache/shiro/session/Session.java +++ b/core/src/main/java/org/apache/shiro/session/Session.java @@ -104,7 +104,7 @@ public interface Session { * if the host is unknown. * * @return the host name or IP string of the host that originated this session, or {@code null} - * if the host address is unknown. + * if the host address is unknown. */ String getHost(); @@ -149,7 +149,8 @@ public interface Session { * It is common for a {@code Subject} implementation to retain authentication state in the * {@code Session}. If the session * is explicitly stopped by application code by calling this method directly, it could clear out any - * authentication state that might exist, thereby effectively removing the "authenticated" state of the {@code Subject}. + * authentication state that might exist, thereby effectively removing + * the "authenticated" state of the {@code Subject}. *

    * As such, you might consider {@link org.apache.shiro.subject.Subject#logout logging-out} the 'owning' * {@code Subject} instead of manually calling this method, as a log out is expected to stop the @@ -164,7 +165,7 @@ public interface Session { * attributes, this returns an empty collection. * * @return the keys of all attributes stored under this session, or an empty collection if - * there are no session attributes. + * there are no session attributes. * @throws InvalidSessionException if this session has stopped or expired prior to calling this method. * @since 0.2 */ @@ -176,7 +177,7 @@ public interface Session { * * @param key the unique name of the object bound to this session * @return the object bound under the specified {@code key} name or {@code null} if there is - * no object bound under that name. + * no object bound under that name. * @throws InvalidSessionException if this session has stopped or expired prior to calling * this method. */ @@ -202,7 +203,7 @@ public interface Session { * * @param key the name uniquely identifying the object to remove * @return the object removed or {@code null} if there was no object bound under the name - * {@code key}. + * {@code key}. * @throws InvalidSessionException if this session has stopped or expired prior to calling * this method. */ diff --git a/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java b/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java index 86353bd63f..f6a701e0dd 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/AbstractNativeSessionManager.java @@ -42,9 +42,10 @@ * * @since 1.0 */ +@SuppressWarnings({"checkstyle:MethodCount", "checkstyle:LineLength"}) public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager, EventBusAware { - private static final Logger log = LoggerFactory.getLogger(AbstractSessionManager.class); + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractSessionManager.class); private EventBus eventBus; @@ -112,11 +113,10 @@ public Session start(SessionContext context) { * @param context the initialization data that can be used by the implementation or underlying * {@link SessionFactory} when instantiating the internal {@code Session} instance. * @return the new {@code Session} instance. - * @throws org.apache.shiro.authz.HostUnauthorizedException - * if the system access control policy restricts access based - * on client location/IP and the specified hostAddress hasn't been enabled. - * @throws AuthorizationException if the system access control policy does not allow the currently executing - * caller to start sessions. + * @throws org.apache.shiro.authz.HostUnauthorizedException if the system access control policy restricts access based + * on client location/IP and the specified hostAddress hasn't been enabled. + * @throws AuthorizationException if the system access control policy does not allow the currently executing + * caller to start sessions. */ protected abstract Session createSession(SessionContext context) throws AuthorizationException; @@ -280,8 +280,8 @@ public boolean isValid(SessionKey key) { public void stop(SessionKey key) throws InvalidSessionException { Session session = lookupRequiredSession(key); try { - if (log.isDebugEnabled()) { - log.debug("Stopping session with id [" + session.getId() + "]"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Stopping session with id [" + session.getId() + "]"); } session.stop(); onStop(session, key); diff --git a/core/src/main/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManager.java b/core/src/main/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManager.java index 8991bbae31..45e57f3bac 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManager.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManager.java @@ -39,16 +39,14 @@ public abstract class AbstractValidatingSessionManager extends AbstractNativeSessionManager implements ValidatingSessionManager, Destroyable { - //TODO - complete JavaDoc - - private static final Logger log = LoggerFactory.getLogger(AbstractValidatingSessionManager.class); - /** * The default interval at which sessions will be validated (1 hour); * This can be overridden by calling {@link #setSessionValidationInterval(long)} */ public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = MILLIS_PER_HOUR; + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractValidatingSessionManager.class); + protected boolean sessionValidationSchedulerEnabled; /** @@ -113,7 +111,7 @@ public long getSessionValidationInterval() { protected final Session doGetSession(final SessionKey key) throws InvalidSessionException { enableSessionValidationIfNecessary(); - log.trace("Attempting to retrieve session with key {}", key); + LOGGER.trace("Attempting to retrieve session with key {}", key); Session s = retrieveSession(key); if (s != null) { @@ -151,7 +149,7 @@ protected void validate(Session session, SessionKey key) throws InvalidSessionEx } protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) { - log.trace("Session with id [{}] has expired.", s.getId()); + LOGGER.trace("Session with id [{}] has expired.", s.getId()); try { onExpiration(s); notifyExpiration(s); @@ -172,7 +170,7 @@ protected void onInvalidation(Session s, InvalidSessionException ise, SessionKey onExpiration(s, (ExpiredSessionException) ise, key); return; } - log.trace("Session with id [{}] is invalid.", s.getId()); + LOGGER.trace("Session with id [{}] is invalid.", s.getId()); try { onStop(s); notifyStop(s); @@ -185,10 +183,10 @@ protected void doValidate(Session session) throws InvalidSessionException { if (session instanceof ValidatingSession) { ((ValidatingSession) session).validate(); } else { - String msg = "The " + getClass().getName() + " implementation only supports validating " + - "Session implementations of the " + ValidatingSession.class.getName() + " interface. " + - "Please either implement this interface in your session implementation or override the " + - AbstractValidatingSessionManager.class.getName() + ".doValidate(Session) method to perform validation."; + String msg = "The " + getClass().getName() + " implementation only supports validating " + + "Session implementations of the " + ValidatingSession.class.getName() + " interface. " + + "Please either implement this interface in your session implementation or override the " + + AbstractValidatingSessionManager.class.getName() + ".doValidate(Session) method to perform validation."; throw new IllegalStateException(msg); } } @@ -209,13 +207,13 @@ protected long getTimeout(Session session) { protected SessionValidationScheduler createSessionValidationScheduler() { ExecutorServiceSessionValidationScheduler scheduler; - if (log.isDebugEnabled()) { - log.debug("No sessionValidationScheduler set. Attempting to create default instance."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("No sessionValidationScheduler set. Attempting to create default instance."); } scheduler = new ExecutorServiceSessionValidationScheduler(this); scheduler.setSessionValidationInterval(getSessionValidationInterval()); - if (log.isTraceEnabled()) { - log.trace("Created default SessionValidationScheduler instance of type [" + scheduler.getClass().getName() + "]."); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Created default SessionValidationScheduler instance of type [" + scheduler.getClass().getName() + "]."); } return scheduler; } @@ -229,8 +227,8 @@ protected synchronized void enableSessionValidation() { // it is possible that that a scheduler was already created and set via 'setSessionValidationScheduler()' // but would not have been enabled/started yet if (!scheduler.isEnabled()) { - if (log.isInfoEnabled()) { - log.info("Enabling session validation scheduler..."); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Enabling session validation scheduler..."); } scheduler.enableSessionValidation(); afterSessionValidationEnabled(); @@ -246,13 +244,13 @@ protected synchronized void disableSessionValidation() { if (scheduler != null) { try { scheduler.disableSessionValidation(); - if (log.isInfoEnabled()) { - log.info("Disabled session validation scheduler."); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Disabled session validation scheduler."); } } catch (Exception e) { - if (log.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { String msg = "Unable to disable SessionValidationScheduler. Ignoring (shutting down)..."; - log.debug(msg, e); + LOGGER.debug(msg, e); } } LifecycleUtils.destroy(scheduler); @@ -271,8 +269,8 @@ public void destroy() { * @see ValidatingSessionManager#validateSessions() */ public void validateSessions() { - if (log.isInfoEnabled()) { - log.info("Validating all active sessions..."); + if (LOGGER.isInfoEnabled()) { + LOGGER.info("Validating all active sessions..."); } int invalidCount = 0; @@ -287,25 +285,25 @@ public void validateSessions() { SessionKey key = new DefaultSessionKey(s.getId()); validate(s, key); } catch (InvalidSessionException e) { - if (log.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { boolean expired = (e instanceof ExpiredSessionException); - String msg = "Invalidated session with id [" + s.getId() + "]" + - (expired ? " (expired)" : " (stopped)"); - log.debug(msg); + String msg = "Invalidated session with id [" + s.getId() + "]" + + (expired ? " (expired)" : " (stopped)"); + LOGGER.debug(msg); } invalidCount++; } } } - if (log.isInfoEnabled()) { + if (LOGGER.isInfoEnabled()) { String msg = "Finished session validation."; if (invalidCount > 0) { msg += " [" + invalidCount + "] sessions were stopped."; } else { msg += " No sessions were stopped."; } - log.info(msg); + LOGGER.info(msg); } } diff --git a/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionManager.java b/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionManager.java index c7e40cfcd1..604a9de425 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionManager.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/DefaultSessionManager.java @@ -40,13 +40,13 @@ */ public class DefaultSessionManager extends AbstractValidatingSessionManager implements CacheManagerAware { - //TODO - complete JavaDoc + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSessionManager.class); - private static final Logger log = LoggerFactory.getLogger(DefaultSessionManager.class); + //todo - move SessionDAO up to AbstractValidatingSessionManager? + protected SessionDAO sessionDAO; - private SessionFactory sessionFactory; - protected SessionDAO sessionDAO; //todo - move SessionDAO up to AbstractValidatingSessionManager? + private SessionFactory sessionFactory; private CacheManager cacheManager; @@ -104,7 +104,7 @@ public void setSessionFactory(SessionFactory sessionFactory) { * some other means (cron, quartz, etc.). * * @return {@code true} if sessions should be automatically deleted after they are discovered to be invalid, - * {@code false} if invalid sessions will be manually deleted by some process external to Shiro's control. + * {@code false} if invalid sessions will be manually deleted by some process external to Shiro's control. * @since 1.0 */ public boolean isDeleteInvalidSessions() { @@ -152,8 +152,8 @@ private void applyCacheManagerToSessionDAO() { protected Session doCreateSession(SessionContext context) { Session s = newSessionInstance(context); - if (log.isTraceEnabled()) { - log.trace("Creating session for host {}", s.getHost()); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Creating session for host {}", s.getHost()); } create(s); return s; @@ -171,8 +171,8 @@ protected Session newSessionInstance(SessionContext context) { * @param session the Session instance to persist to the underlying EIS. */ protected void create(Session session) { - if (log.isDebugEnabled()) { - log.debug("Creating new EIS record for new session instance [" + session + "]"); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Creating new EIS record for new session instance [" + session + "]"); } sessionDAO.create(session); } @@ -215,8 +215,8 @@ protected void onChange(Session session) { protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException { Serializable sessionId = getSessionId(sessionKey); if (sessionId == null) { - log.debug("Unable to resolve session ID from SessionKey [{}]. Returning null to indicate a " + - "session could not be found.", sessionKey); + LOGGER.debug("Unable to resolve session ID from SessionKey [{}]. Returning null to indicate a " + + "session could not be found.", sessionKey); return null; } Session s = retrieveSessionFromDataSource(sessionId); diff --git a/core/src/main/java/org/apache/shiro/session/mgt/DelegatingSession.java b/core/src/main/java/org/apache/shiro/session/mgt/DelegatingSession.java index 115b008df0..1e38fdc511 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/DelegatingSession.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/DelegatingSession.java @@ -47,8 +47,8 @@ public class DelegatingSession implements Session, Serializable { private final SessionKey key; //cached fields to avoid a server-side method call if out-of-process: - private Date startTimestamp = null; - private String host = null; + private Date startTimestamp; + private String host; /** * Handle to the target NativeSessionManager that will support the delegate calls. @@ -64,9 +64,9 @@ public DelegatingSession(NativeSessionManager sessionManager, SessionKey key) { throw new IllegalArgumentException("sessionKey argument cannot be null."); } if (key.getSessionId() == null) { - String msg = "The " + DelegatingSession.class.getName() + " implementation requires that the " + - "SessionKey argument returns a non-null sessionId to support the " + - "Session.getId() invocations."; + String msg = "The " + DelegatingSession.class.getName() + " implementation requires that the " + + "SessionKey argument returns a non-null sessionId to support the " + + "Session.getId() invocations."; throw new IllegalArgumentException(msg); } this.sessionManager = sessionManager; diff --git a/core/src/main/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationScheduler.java b/core/src/main/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationScheduler.java index fc10de7a96..72b16174a0 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationScheduler.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationScheduler.java @@ -39,13 +39,15 @@ public class ExecutorServiceSessionValidationScheduler implements SessionValidat //TODO - complete JavaDoc - /** Private internal log instance. */ - private static final Logger log = LoggerFactory.getLogger(ExecutorServiceSessionValidationScheduler.class); + /** + * Private internal log instance. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(ExecutorServiceSessionValidationScheduler.class); ValidatingSessionManager sessionManager; private ScheduledExecutorService service; private long sessionValidationInterval = DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL; - private boolean enabled = false; + private boolean enabled; private String threadNamePrefix = "SessionValidationThread-"; public ExecutorServiceSessionValidationScheduler() { @@ -85,47 +87,47 @@ public String getThreadNamePrefix() { } /** - * Creates a single thread {@link ScheduledExecutorService} to validate sessions at fixed intervals + * Creates a single thread {@link ScheduledExecutorService} to validate sessions at fixed intervals * and enables this scheduler. The executor is created as a daemon thread to allow JVM to shut down */ //TODO Implement an integration test to test for jvm exit as part of the standalone example // (so we don't have to change the unit test execution model for the core module) public void enableSessionValidation() { - if (this.sessionValidationInterval > 0l) { - this.service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { - private final AtomicInteger count = new AtomicInteger(1); - - public Thread newThread(Runnable r) { - Thread thread = new Thread(r); - thread.setDaemon(true); - thread.setName(threadNamePrefix + count.getAndIncrement()); - return thread; - } - }); + if (this.sessionValidationInterval > 0L) { + this.service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { + private final AtomicInteger count = new AtomicInteger(1); + + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName(threadNamePrefix + count.getAndIncrement()); + return thread; + } + }); this.service.scheduleAtFixedRate(this, sessionValidationInterval, - sessionValidationInterval, TimeUnit.MILLISECONDS); + sessionValidationInterval, TimeUnit.MILLISECONDS); } this.enabled = true; } public void run() { - if (log.isDebugEnabled()) { - log.debug("Executing session validation..."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Executing session validation..."); } Thread.currentThread().setUncaughtExceptionHandler((t, e) -> { - log.error("Error while validating the session, the thread will be stopped and session validation disabled", e); + LOGGER.error("Error while validating the session, the thread will be stopped and session validation disabled", e); this.disableSessionValidation(); }); long startTime = System.currentTimeMillis(); try { this.sessionManager.validateSessions(); } catch (RuntimeException e) { - log.error("Error while validating the session", e); + LOGGER.error("Error while validating the session", e); //we don't stop the thread } long stopTime = System.currentTimeMillis(); - if (log.isDebugEnabled()) { - log.debug("Session validation completed successfully in " + (stopTime - startTime) + " milliseconds."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Session validation completed successfully in " + (stopTime - startTime) + " milliseconds."); } } diff --git a/core/src/main/java/org/apache/shiro/session/mgt/ImmutableProxiedSession.java b/core/src/main/java/org/apache/shiro/session/mgt/ImmutableProxiedSession.java index 8b8c74a6bf..5f99f76a85 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/ImmutableProxiedSession.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/ImmutableProxiedSession.java @@ -58,8 +58,8 @@ public ImmutableProxiedSession(Session target) { * @throws InvalidSessionException in all cases - used by the Session 'write' method implementations. */ protected void throwImmutableException() throws InvalidSessionException { - String msg = "This session is immutable and read-only - it cannot be altered. This is usually because " + - "the session has been stopped or expired already."; + String msg = "This session is immutable and read-only - it cannot be altered. This is usually because " + + "the session has been stopped or expired already."; throw new InvalidSessionException(msg); } diff --git a/core/src/main/java/org/apache/shiro/session/mgt/NativeSessionManager.java b/core/src/main/java/org/apache/shiro/session/mgt/NativeSessionManager.java index 8c1259e48f..3e24bec6bb 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/NativeSessionManager.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/NativeSessionManager.java @@ -64,8 +64,8 @@ public interface NativeSessionManager extends SessionManager { * the reason for invalidation. * * @param key the session key to use to look up the target session. - * @throws org.apache.shiro.session.InvalidSessionException - * if the session id is invalid (it does not exist or it is stopped or expired). + * @throws org.apache.shiro.session.InvalidSessionException if the session id is invalid + * (it does not exist, or it is stopped or expired). */ void checkValid(SessionKey key) throws InvalidSessionException; @@ -79,8 +79,8 @@ public interface NativeSessionManager extends SessionManager { * * @param key the session key to use to look up the target session. * @return the time in milliseconds that the associated session may remain idle before expiring. - * @throws org.apache.shiro.session.InvalidSessionException - * if the session has been stopped or expired prior to calling this method. + * @throws org.apache.shiro.session.InvalidSessionException if the session has been stopped or + * expired prior to calling this method. */ long getTimeout(SessionKey key) throws InvalidSessionException; @@ -94,8 +94,8 @@ public interface NativeSessionManager extends SessionManager { * * @param key the session key to use to look up the target session. * @param maxIdleTimeInMillis the time in milliseconds that the associated session may remain idle before expiring. - * @throws org.apache.shiro.session.InvalidSessionException - * if the session has been stopped or expired prior to calling this method. + * @throws org.apache.shiro.session.InvalidSessionException if the session has been stopped + * or expired prior to calling this method. */ void setTimeout(SessionKey key, long maxIdleTimeInMillis) throws InvalidSessionException; @@ -104,8 +104,8 @@ public interface NativeSessionManager extends SessionManager { * can be used to explicitly ensure that a session does not time out. * * @param key the session key to use to look up the target session. - * @throws org.apache.shiro.session.InvalidSessionException - * if the session has been stopped or expired prior to calling this method. + * @throws org.apache.shiro.session.InvalidSessionException if the session has been stopped + * or expired prior to calling this method. * @see org.apache.shiro.session.Session#touch */ void touch(SessionKey key) throws InvalidSessionException; @@ -116,7 +116,7 @@ public interface NativeSessionManager extends SessionManager { * * @param key the session key to use to look up the target session. * @return the host name or ip address of the host where the session originated, if known. If unknown, - * this method returns {@code null}. + * this method returns {@code null}. */ String getHost(SessionKey key); diff --git a/core/src/main/java/org/apache/shiro/session/mgt/SessionContext.java b/core/src/main/java/org/apache/shiro/session/mgt/SessionContext.java index b5506df9bd..36471ed57f 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/SessionContext.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/SessionContext.java @@ -79,7 +79,7 @@ public interface SessionContext extends Map { * {@code Session} host. * * @return the originating host name or IP address (as a String) from where the {@code Subject} is initiating the - * {@code Session}. + * {@code Session}. * @see #setHost(String) setHost(String) */ String getHost(); diff --git a/core/src/main/java/org/apache/shiro/session/mgt/SessionManager.java b/core/src/main/java/org/apache/shiro/session/mgt/SessionManager.java index b23af5718d..593e158af7 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/SessionManager.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/SessionManager.java @@ -53,7 +53,7 @@ public interface SessionManager { * * @param key the Session key to use to look-up the Session * @return the {@code Session} instance corresponding to the given lookup key or {@code null} if no session - * could be acquired. + * could be acquired. * @throws SessionException if a session was found but it was invalid (stopped/expired). * @since 1.0 */ diff --git a/core/src/main/java/org/apache/shiro/session/mgt/SessionValidationScheduler.java b/core/src/main/java/org/apache/shiro/session/mgt/SessionValidationScheduler.java index 633239d68a..5fad2a7eda 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/SessionValidationScheduler.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/SessionValidationScheduler.java @@ -18,6 +18,7 @@ */ package org.apache.shiro.session.mgt; +@SuppressWarnings("checkstyle:LineLength") /** * Interface that should be implemented by classes that can control validating sessions on a regular * basis. This interface is used as a delegate for session validation by the {@link org.apache.shiro.session.mgt.DefaultSessionManager} @@ -49,4 +50,4 @@ public interface SessionValidationScheduler { */ void disableSessionValidation(); -} \ No newline at end of file +} diff --git a/core/src/main/java/org/apache/shiro/session/mgt/SimpleSession.java b/core/src/main/java/org/apache/shiro/session/mgt/SimpleSession.java index e7101b4f1b..0a0c0932d1 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/SimpleSession.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/SimpleSession.java @@ -30,7 +30,11 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.DateFormat; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; /** @@ -39,8 +43,16 @@ * * @since 0.1 */ +@SuppressWarnings("checkstyle:MethodCount") public class SimpleSession implements ValidatingSession, Serializable { + protected static final long MILLIS_PER_SECOND = 1000; + protected static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; + protected static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; + + //serialization bitmask fields. DO NOT CHANGE THE ORDER THEY ARE DECLARED! + static int bitIndexCounter; + // Serialization reminder: // You _MUST_ change this number if you introduce a change to this class // that is NOT serialization backwards compatible. Serialization-compatible @@ -48,15 +60,7 @@ public class SimpleSession implements ValidatingSession, Serializable { // a new number in this case, use the JDK's 'serialver' program to generate it. private static final long serialVersionUID = -7125642695178165650L; - //TODO - complete JavaDoc - private transient static final Logger log = LoggerFactory.getLogger(SimpleSession.class); - - protected static final long MILLIS_PER_SECOND = 1000; - protected static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; - protected static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; - - //serialization bitmask fields. DO NOT CHANGE THE ORDER THEY ARE DECLARED! - static int bitIndexCounter = 0; + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleSession.class); private static final int ID_BIT_MASK = 1 << bitIndexCounter++; private static final int START_TIMESTAMP_BIT_MASK = 1 << bitIndexCounter++; private static final int STOP_TIMESTAMP_BIT_MASK = 1 << bitIndexCounter++; @@ -93,7 +97,8 @@ public class SimpleSession implements ValidatingSession, Serializable { private transient Map attributes; public SimpleSession() { - this.timeout = DefaultSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT; //TODO - remove concrete reference to DefaultSessionManager + //TODO - remove concrete reference to DefaultSessionManager + this.timeout = DefaultSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT; this.startTimestamp = new Date(); this.lastAccessTime = this.startTimestamp; } @@ -134,7 +139,7 @@ public void setStartTimestamp(Date startTimestamp) { * Once stopped, a session may no longer be used. It is locked from all further activity. * * @return The time the session was stopped, or null if the session is still - * active. + * active. */ public Date getStopTimestamp() { return stopTimestamp; @@ -229,16 +234,16 @@ protected boolean isTimedOut() { long timeout = getTimeout(); - if (timeout >= 0l) { + if (timeout >= 0L) { Date lastAccessTime = getLastAccessTime(); if (lastAccessTime == null) { - String msg = "session.lastAccessTime for session with id [" + - getId() + "] is null. This value must be set at " + - "least once, preferably at least upon instantiation. Please check the " + - getClass().getName() + " implementation and ensure " + - "this value will be set (perhaps in the constructor?)"; + String msg = "session.lastAccessTime for session with id [" + + getId() + "] is null. This value must be set at " + + "least once, preferably at least upon instantiation. Please check the " + + getClass().getName() + " implementation and ensure " + + "this value will be set (perhaps in the constructor?)"; throw new IllegalStateException(msg); } @@ -251,9 +256,9 @@ protected boolean isTimedOut() { Date expireTime = new Date(expireTimeMillis); return lastAccessTime.before(expireTime); } else { - if (log.isTraceEnabled()) { - log.trace("No timeout for session with id [" + getId() + - "]. Session is not considered expired."); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("No timeout for session with id [" + getId() + + "]. Session is not considered expired."); } } @@ -264,9 +269,9 @@ public void validate() throws InvalidSessionException { //check for stopped: if (isStopped()) { //timestamp is set, so the session is considered stopped: - String msg = "Session with id [" + getId() + "] has been " + - "explicitly stopped. No further interaction under this session is " + - "allowed."; + String msg = "Session with id [" + getId() + "] has been " + + "explicitly stopped. No further interaction under this session is " + + "allowed."; throw new StoppedSessionException(msg); } @@ -281,13 +286,13 @@ public void validate() throws InvalidSessionException { Serializable sessionId = getId(); DateFormat df = DateFormat.getInstance(); - String msg = "Session with id [" + sessionId + "] has expired. " + - "Last access time: " + df.format(lastAccessTime) + - ". Current time: " + df.format(new Date()) + - ". Session timeout is set to " + timeout / MILLIS_PER_SECOND + " seconds (" + - timeout / MILLIS_PER_MINUTE + " minutes)"; - if (log.isTraceEnabled()) { - log.trace(msg); + String msg = "Session with id [" + sessionId + "] has expired. " + + "Last access time: " + df.format(lastAccessTime) + + ". Current time: " + df.format(new Date()) + + ". Session timeout is set to " + timeout / MILLIS_PER_SECOND + " seconds (" + + timeout / MILLIS_PER_MINUTE + " minutes)"; + if (LOGGER.isTraceEnabled()) { + LOGGER.trace(msg); } throw new ExpiredSessionException(msg); } @@ -374,14 +379,15 @@ public boolean equals(Object obj) { * @return true if all the attributes, except the id, are equal to this object's attributes. * @since 1.0 */ + @SuppressWarnings({"checkstyle:BooleanExpressionComplexity", "checkstyle:MethodCount", "checkstyle:LineLength"}) protected boolean onEquals(SimpleSession ss) { - return (getStartTimestamp() != null ? getStartTimestamp().equals(ss.getStartTimestamp()) : ss.getStartTimestamp() == null) && - (getStopTimestamp() != null ? getStopTimestamp().equals(ss.getStopTimestamp()) : ss.getStopTimestamp() == null) && - (getLastAccessTime() != null ? getLastAccessTime().equals(ss.getLastAccessTime()) : ss.getLastAccessTime() == null) && - (getTimeout() == ss.getTimeout()) && - (isExpired() == ss.isExpired()) && - (getHost() != null ? getHost().equals(ss.getHost()) : ss.getHost() == null) && - (getAttributes() != null ? getAttributes().equals(ss.getAttributes()) : ss.getAttributes() == null); + return (getStartTimestamp() != null ? getStartTimestamp().equals(ss.getStartTimestamp()) : ss.getStartTimestamp() == null) + && (getStopTimestamp() != null ? getStopTimestamp().equals(ss.getStopTimestamp()) : ss.getStopTimestamp() == null) + && (getLastAccessTime() != null ? getLastAccessTime().equals(ss.getLastAccessTime()) : ss.getLastAccessTime() == null) + && (getTimeout() == ss.getTimeout()) + && (isExpired() == ss.isExpired()) + && (getHost() != null ? getHost().equals(ss.getHost()) : ss.getHost() == null) + && (getAttributes() != null ? getAttributes().equals(ss.getAttributes()) : ss.getAttributes() == null); } /** @@ -415,7 +421,7 @@ public int hashCode() { * getClass().getName() + ",id=" + getId(). * * @return the string representation of this SimpleSession, equal to - * getClass().getName() + ",id=" + getId(). + * getClass().getName() + ",id=" + getId(). * @since 1.0 */ @Override @@ -432,6 +438,7 @@ public String toString() { * @throws IOException if any of this object's fields cannot be written to the stream. * @since 1.0 */ + @SuppressWarnings("checkstyle:NPathComplexity") private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); short alteredFieldsBitMask = getAlteredFieldsBitMask(); @@ -448,7 +455,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { if (lastAccessTime != null) { out.writeObject(lastAccessTime); } - if (timeout != 0l) { + if (timeout != 0L) { out.writeLong(timeout); } if (expired) { @@ -470,7 +477,7 @@ private void writeObject(ObjectOutputStream out) throws IOException { * @throws ClassNotFoundException if a required class needed for instantiation is not available in the present JVM * @since 1.0 */ - @SuppressWarnings({"unchecked"}) + @SuppressWarnings({"unchecked", "checkstyle:NPathComplexity"}) private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); short bitMask = in.readShort(); @@ -509,13 +516,14 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE * @return a bit mask used during serialization indicating which fields have been serialized. * @since 1.0 */ + @SuppressWarnings("checkstyle:NPathComplexity") private short getAlteredFieldsBitMask() { int bitMask = 0; bitMask = id != null ? bitMask | ID_BIT_MASK : bitMask; bitMask = startTimestamp != null ? bitMask | START_TIMESTAMP_BIT_MASK : bitMask; bitMask = stopTimestamp != null ? bitMask | STOP_TIMESTAMP_BIT_MASK : bitMask; bitMask = lastAccessTime != null ? bitMask | LAST_ACCESS_TIME_BIT_MASK : bitMask; - bitMask = timeout != 0l ? bitMask | TIMEOUT_BIT_MASK : bitMask; + bitMask = timeout != 0L ? bitMask | TIMEOUT_BIT_MASK : bitMask; bitMask = expired ? bitMask | EXPIRED_BIT_MASK : bitMask; bitMask = host != null ? bitMask | HOST_BIT_MASK : bitMask; bitMask = !CollectionUtils.isEmpty(attributes) ? bitMask | ATTRIBUTES_BIT_MASK : bitMask; @@ -531,7 +539,7 @@ private short getAlteredFieldsBitMask() { * been serialized, 0 means it hasn't been serialized. * @param fieldBitMask the field bit mask constant identifying which bit to inspect (corresponds to a class attribute). * @return {@code true} if the given {@code bitMask} argument indicates that the specified field has been - * serialized and therefore should be read during deserialization, {@code false} otherwise. + * serialized and therefore should be read during deserialization, {@code false} otherwise. * @since 1.0 */ private static boolean isFieldPresent(short bitMask, int fieldBitMask) { diff --git a/core/src/main/java/org/apache/shiro/session/mgt/ValidatingSession.java b/core/src/main/java/org/apache/shiro/session/mgt/ValidatingSession.java index 8b88228261..6d68a17a42 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/ValidatingSession.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/ValidatingSession.java @@ -28,7 +28,7 @@ *

    * Validation is usually an exercise of determining when the session was last accessed or modified and determining if * that time is longer than a specified allowed duration. - * + * * @since 0.9 */ public interface ValidatingSession extends Session { diff --git a/core/src/main/java/org/apache/shiro/session/mgt/eis/AbstractSessionDAO.java b/core/src/main/java/org/apache/shiro/session/mgt/eis/AbstractSessionDAO.java index b8055e0074..4a472ed5f1 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/eis/AbstractSessionDAO.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/eis/AbstractSessionDAO.java @@ -65,7 +65,7 @@ public AbstractSessionDAO() { * is a {@link JavaUuidSessionIdGenerator}. * * @return the {@code SessionIdGenerator} used by the {@link #generateSessionId(org.apache.shiro.session.Session)} - * method. + * method. */ public SessionIdGenerator getSessionIdGenerator() { return sessionIdGenerator; @@ -151,7 +151,7 @@ protected void assignSessionId(Session session, Serializable sessionId) { * * @param session the Session instance to persist to the EIS. * @return the id of the session created in the EIS (i.e. this is almost always a primary key and should be the - * value returned from {@link org.apache.shiro.session.Session#getId() Session.getId()}. + * value returned from {@link org.apache.shiro.session.Session#getId() Session.getId()}. */ protected abstract Serializable doCreate(Session session); @@ -178,7 +178,7 @@ public Session readSession(Serializable sessionId) throws UnknownSessionExceptio * * @param sessionId the id of the Session to retrieve. * @return the Session in the EIS identified by sessionId or {@code null} if a - * session with that ID could not be found. + * session with that ID could not be found. */ protected abstract Session doReadSession(Serializable sessionId); diff --git a/core/src/main/java/org/apache/shiro/session/mgt/eis/CachingSessionDAO.java b/core/src/main/java/org/apache/shiro/session/mgt/eis/CachingSessionDAO.java index f48dc75159..6ed409c187 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/eis/CachingSessionDAO.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/eis/CachingSessionDAO.java @@ -120,7 +120,7 @@ public void setActiveSessionsCacheName(String activeSessionsCacheName) { * {@code CacheManager} using the {@link #getActiveSessionsCacheName() activeSessionsCacheName}. * * @return the cache instance to use for storing active sessions or {@code null} if the {@code Cache} instance - * should be retrieved from the + * should be retrieved from the */ public Cache getActiveSessionsCache() { return this.activeSessions; @@ -162,7 +162,7 @@ private Cache getActiveSessionsCacheLazy() { * cacheManager.getCache(name);

* * @return a cache instance used to store active sessions, or {@code null} if the {@code CacheManager} has - * not been set. + * not been set. */ protected Cache createActiveSessionsCache() { Cache cache = null; @@ -192,7 +192,7 @@ public Serializable create(Session session) { * * @param sessionId the id of the cached session to acquire. * @return the cached session with the corresponding {@code sessionId}, or {@code null} if the session - * does not exist or is not cached. + * does not exist or is not cached. */ protected Session getCachedSession(Serializable sessionId) { Session cached = null; diff --git a/core/src/main/java/org/apache/shiro/session/mgt/eis/EnterpriseCacheSessionDAO.java b/core/src/main/java/org/apache/shiro/session/mgt/eis/EnterpriseCacheSessionDAO.java index 0896fba51d..c131dfa156 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/eis/EnterpriseCacheSessionDAO.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/eis/EnterpriseCacheSessionDAO.java @@ -67,9 +67,10 @@ protected Serializable doCreate(Session session) { } protected Session doReadSession(Serializable sessionId) { - return null; //should never execute because this implementation relies on parent class to access cache, which + //should never execute because this implementation relies on parent class to access cache, which //is where all sessions reside - it is the cache implementation that determines if the //cache is memory only or disk-persistent, etc. + return null; } protected void doUpdate(Session session) { diff --git a/core/src/main/java/org/apache/shiro/session/mgt/eis/MemorySessionDAO.java b/core/src/main/java/org/apache/shiro/session/mgt/eis/MemorySessionDAO.java index 0095a26800..aa07f99767 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/eis/MemorySessionDAO.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/eis/MemorySessionDAO.java @@ -55,9 +55,9 @@ */ public class MemorySessionDAO extends AbstractSessionDAO { - private static final Logger log = LoggerFactory.getLogger(MemorySessionDAO.class); + private static final Logger LOGGER = LoggerFactory.getLogger(MemorySessionDAO.class); - private ConcurrentMap sessions; + private final ConcurrentMap sessions; public MemorySessionDAO() { this.sessions = new ConcurrentHashMap(); diff --git a/core/src/main/java/org/apache/shiro/session/mgt/eis/RandomSessionIdGenerator.java b/core/src/main/java/org/apache/shiro/session/mgt/eis/RandomSessionIdGenerator.java index 09cf0ccf74..d4249cd701 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/eis/RandomSessionIdGenerator.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/eis/RandomSessionIdGenerator.java @@ -33,7 +33,7 @@ */ public class RandomSessionIdGenerator implements SessionIdGenerator { - private static final Logger log = LoggerFactory.getLogger(RandomSessionIdGenerator.class); + private static final Logger LOGGER = LoggerFactory.getLogger(RandomSessionIdGenerator.class); private static final String RANDOM_NUM_GENERATOR_ALGORITHM_NAME = "SHA1PRNG"; private Random random; @@ -42,8 +42,8 @@ public RandomSessionIdGenerator() { try { this.random = java.security.SecureRandom.getInstance(RANDOM_NUM_GENERATOR_ALGORITHM_NAME); } catch (java.security.NoSuchAlgorithmException e) { - log.debug("The SecureRandom SHA1PRNG algorithm is not available on the current platform. Using the " + - "platform's default SecureRandom algorithm.", e); + LOGGER.debug("The SecureRandom SHA1PRNG algorithm is not available on the current platform. Using the " + + "platform's default SecureRandom algorithm.", e); this.random = new java.security.SecureRandom(); } } diff --git a/core/src/main/java/org/apache/shiro/session/mgt/eis/SessionDAO.java b/core/src/main/java/org/apache/shiro/session/mgt/eis/SessionDAO.java index 04722eba6e..ce831e4738 100644 --- a/core/src/main/java/org/apache/shiro/session/mgt/eis/SessionDAO.java +++ b/core/src/main/java/org/apache/shiro/session/mgt/eis/SessionDAO.java @@ -82,9 +82,8 @@ public interface SessionDAO { * errors. * * @param session the Session to update - * @throws org.apache.shiro.session.UnknownSessionException - * if no existing EIS session record exists with the - * identifier of {@link Session#getId() session.getSessionId()} + * @throws org.apache.shiro.session.UnknownSessionException if no existing EIS session record exists with the + * identifier of {@link Session#getId() session.getSessionId()} */ void update(Session session) throws UnknownSessionException; @@ -124,7 +123,7 @@ public interface SessionDAO { * (e.g. now - 30 minutes). * * @return a Collection of {@code Session}s that are considered active, or an - * empty collection or {@code null} if there are no active sessions. + * empty collection or {@code null} if there are no active sessions. */ Collection getActiveSessions(); } diff --git a/core/src/main/java/org/apache/shiro/subject/PrincipalCollection.java b/core/src/main/java/org/apache/shiro/subject/PrincipalCollection.java index 75da30c286..1de2cc3ad1 100644 --- a/core/src/main/java/org/apache/shiro/subject/PrincipalCollection.java +++ b/core/src/main/java/org/apache/shiro/subject/PrincipalCollection.java @@ -18,6 +18,10 @@ */ package org.apache.shiro.subject; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.pam.AuthenticationStrategy; + import java.io.Serializable; import java.util.Collection; import java.util.List; @@ -68,7 +72,8 @@ public interface PrincipalCollection extends Iterable, Serializable { * If this heuristic is not sufficient, most Shiro end-users will need to implement a custom * {@link org.apache.shiro.authc.pam.AuthenticationStrategy}. An {@code AuthenticationStrategy} has exact control * over the {@link PrincipalCollection} returned at the end of an authentication attempt via the - * AuthenticationStrategy#{@link org.apache.shiro.authc.pam.AuthenticationStrategy#afterAllAttempts(org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.authc.AuthenticationInfo) afterAllAttempts} + * AuthenticationStrategy# + * {@link AuthenticationStrategy#afterAllAttempts(AuthenticationToken, AuthenticationInfo) afterAllAttempts} * implementation. * * @return the primary principal used to uniquely identify the owning account/Subject @@ -95,7 +100,7 @@ public interface PrincipalCollection extends Iterable, Serializable { * * @param type the type of the principals that should be returned. * @return a Collection of principals that are assignable from the specified type, or - * an empty Collection if no principals of this type are associated. + * an empty Collection if no principals of this type are associated. */ Collection byType(Class type); @@ -127,7 +132,7 @@ public interface PrincipalCollection extends Iterable, Serializable { * * @param realmName the name of the Realm from which the principals were retrieved. * @return the Subject's principals from the specified Realm only as a Collection or an empty Collection if there - * are not any principals from that realm. + * are not any principals from that realm. */ Collection fromRealm(String realmName); diff --git a/core/src/main/java/org/apache/shiro/subject/PrincipalMap.java b/core/src/main/java/org/apache/shiro/subject/PrincipalMap.java index 3c5841718d..d08a73c430 100644 --- a/core/src/main/java/org/apache/shiro/subject/PrincipalMap.java +++ b/core/src/main/java/org/apache/shiro/subject/PrincipalMap.java @@ -47,11 +47,11 @@ * * @since 1.2 */ -public interface PrincipalMap extends PrincipalCollection, Map { +public interface PrincipalMap extends PrincipalCollection, Map { - Map getRealmPrincipals(String realmName); + Map getRealmPrincipals(String realmName); - Map setRealmPrincipals(String realmName, Map principals); + Map setRealmPrincipals(String realmName, Map principals); Object setRealmPrincipal(String realmName, String principalName, Object principal); diff --git a/core/src/main/java/org/apache/shiro/subject/SimplePrincipalCollection.java b/core/src/main/java/org/apache/shiro/subject/SimplePrincipalCollection.java index 080c831b19..daf47e81ad 100644 --- a/core/src/main/java/org/apache/shiro/subject/SimplePrincipalCollection.java +++ b/core/src/main/java/org/apache/shiro/subject/SimplePrincipalCollection.java @@ -24,8 +24,16 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.util.*; - +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; /** * A simple implementation of the {@link MutablePrincipalCollection} interface that tracks principals internally @@ -44,10 +52,10 @@ public class SimplePrincipalCollection implements MutablePrincipalCollection { private static final long serialVersionUID = -6305224034025797558L; //TODO - complete JavaDoc - private Map realmPrincipals; - private transient String cachedToString; //cached toString() result, as this can be printed many times in logging + //cached toString() result, as this can be printed many times in logging + private transient String cachedToString; public SimplePrincipalCollection() { } @@ -229,7 +237,7 @@ public boolean equals(Object o) { } if (o instanceof SimplePrincipalCollection) { SimplePrincipalCollection other = (SimplePrincipalCollection) o; - return this.realmPrincipals != null ? this.realmPrincipals.equals(other.realmPrincipals) : other.realmPrincipals == null; + return Objects.equals(this.realmPrincipals, other.realmPrincipals); } return false; } diff --git a/core/src/main/java/org/apache/shiro/subject/SimplePrincipalMap.java b/core/src/main/java/org/apache/shiro/subject/SimplePrincipalMap.java index 258ac88a1f..e2b3e69b5e 100644 --- a/core/src/main/java/org/apache/shiro/subject/SimplePrincipalMap.java +++ b/core/src/main/java/org/apache/shiro/subject/SimplePrincipalMap.java @@ -20,11 +20,19 @@ import org.apache.shiro.util.CollectionUtils; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Default implementation of the {@link PrincipalMap} interface. - * + *

* *EXPERIMENTAL for Shiro 1.2 - DO NOT USE YET* * * @since 1.2 @@ -48,7 +56,7 @@ public SimplePrincipalMap(Map> backingMap) { if (!CollectionUtils.isEmpty(backingMap)) { this.realmPrincipals = backingMap; for (Map principals : this.realmPrincipals.values()) { - if (!CollectionUtils.isEmpty(principals) ) { + if (!CollectionUtils.isEmpty(principals)) { ensureCombinedPrincipals().putAll(principals); } } @@ -93,21 +101,18 @@ public void putAll(Map map) { } public Set keySet() { - return CollectionUtils.isEmpty(this.combinedPrincipals) ? - Collections.emptySet() : - Collections.unmodifiableSet(this.combinedPrincipals.keySet()); + return CollectionUtils.isEmpty(this.combinedPrincipals) ? Collections.emptySet() + : Collections.unmodifiableSet(this.combinedPrincipals.keySet()); } public Collection values() { - return CollectionUtils.isEmpty(this.combinedPrincipals) ? - Collections.emptySet() : - Collections.unmodifiableCollection(this.combinedPrincipals.values()); + return CollectionUtils.isEmpty(this.combinedPrincipals) ? Collections.emptySet() + : Collections.unmodifiableCollection(this.combinedPrincipals.values()); } public Set> entrySet() { - return CollectionUtils.isEmpty(this.combinedPrincipals) ? - Collections.>emptySet() : - Collections.unmodifiableSet(this.combinedPrincipals.entrySet()); + return CollectionUtils.isEmpty(this.combinedPrincipals) + ? Collections.>emptySet() : Collections.unmodifiableSet(this.combinedPrincipals.entrySet()); } public void clear() { @@ -117,17 +122,15 @@ public void clear() { public Object getPrimaryPrincipal() { //heuristic - just use the first one we come across: - return !CollectionUtils.isEmpty(this.combinedPrincipals) ? - this.combinedPrincipals.values().iterator().next() : - null; + return !CollectionUtils.isEmpty(this.combinedPrincipals) ? this.combinedPrincipals.values().iterator().next() : null; } public T oneByType(Class type) { if (CollectionUtils.isEmpty(this.combinedPrincipals)) { return null; } - for( Object value : this.combinedPrincipals.values()) { - if (type.isInstance(value) ) { + for (Object value : this.combinedPrincipals.values()) { + if (type.isInstance(value)) { return type.cast(value); } } @@ -139,8 +142,8 @@ public Collection byType(Class type) { return Collections.emptySet(); } Collection instances = null; - for( Object value : this.combinedPrincipals.values()) { - if (type.isInstance(value) ) { + for (Object value : this.combinedPrincipals.values()) { + if (type.isInstance(value)) { if (instances == null) { instances = new ArrayList(); } @@ -172,7 +175,7 @@ public Collection fromRealm(String realmName) { if (CollectionUtils.isEmpty(this.realmPrincipals)) { return Collections.emptySet(); } - Map principals = this.realmPrincipals.get(realmName); + Map principals = this.realmPrincipals.get(realmName); if (CollectionUtils.isEmpty(principals)) { return Collections.emptySet(); } @@ -198,28 +201,28 @@ public Map getRealmPrincipals(String name) { if (this.realmPrincipals == null) { return null; } - Map principals = this.realmPrincipals.get(name); + Map principals = this.realmPrincipals.get(name); if (principals == null) { return null; } return Collections.unmodifiableMap(principals); } - public Map setRealmPrincipals(String realmName, Map principals) { + public Map setRealmPrincipals(String realmName, Map principals) { if (realmName == null) { throw new NullPointerException("realmName argument cannot be null."); } if (this.realmPrincipals == null) { if (!CollectionUtils.isEmpty(principals)) { - this.realmPrincipals = new HashMap>(); - return this.realmPrincipals.put(realmName, new HashMap(principals)); + this.realmPrincipals = new HashMap>(); + return this.realmPrincipals.put(realmName, new HashMap(principals)); } else { return null; } } else { - Map existingPrincipals = this.realmPrincipals.remove(realmName); + Map existingPrincipals = this.realmPrincipals.remove(realmName); if (!CollectionUtils.isEmpty(principals)) { - this.realmPrincipals.put(realmName, new HashMap(principals)); + this.realmPrincipals.put(realmName, new HashMap(principals)); } return existingPrincipals; } @@ -236,11 +239,11 @@ public Object setRealmPrincipal(String realmName, String principalName, Object p return removeRealmPrincipal(realmName, principalName); } if (this.realmPrincipals == null) { - this.realmPrincipals = new HashMap>(); + this.realmPrincipals = new HashMap>(); } - Map principals = this.realmPrincipals.get(realmName); + Map principals = this.realmPrincipals.get(realmName); if (principals == null) { - principals = new HashMap(); + principals = new HashMap(); this.realmPrincipals.put(realmName, principals); } return principals.put(principalName, principal); @@ -256,7 +259,7 @@ public Object getRealmPrincipal(String realmName, String principalName) { if (this.realmPrincipals == null) { return null; } - Map principals = this.realmPrincipals.get(realmName); + Map principals = this.realmPrincipals.get(realmName); if (principals != null) { return principals.get(principalName); } @@ -273,7 +276,7 @@ public Object removeRealmPrincipal(String realmName, String principalName) { if (this.realmPrincipals == null) { return null; } - Map principals = this.realmPrincipals.get(realmName); + Map principals = this.realmPrincipals.get(realmName); if (principals != null) { return principals.remove(principalName); } diff --git a/core/src/main/java/org/apache/shiro/subject/Subject.java b/core/src/main/java/org/apache/shiro/subject/Subject.java index 8599c7d548..2da9716d53 100644 --- a/core/src/main/java/org/apache/shiro/subject/Subject.java +++ b/core/src/main/java/org/apache/shiro/subject/Subject.java @@ -57,6 +57,7 @@ * * @since 0.1 */ +@SuppressWarnings("checkstyle:MethodCount") public interface Subject { /** @@ -83,7 +84,8 @@ public interface Subject { * or any other similar suitable unique mechanism valuable to your application. *

* Most implementations will simply return - * {@link #getPrincipals()}.{@link org.apache.shiro.subject.PrincipalCollection#getPrimaryPrincipal() getPrimaryPrincipal()} + * {@link #getPrincipals()}. + * {@link org.apache.shiro.subject.PrincipalCollection#getPrimaryPrincipal() getPrimaryPrincipal()} * * @return this Subject's application-specific unique identity. * @see org.apache.shiro.subject.PrincipalCollection#getPrimaryPrincipal() @@ -140,9 +142,9 @@ public interface Subject { * * @param permissions the String representations of the Permissions that are being checked. * @return a boolean array where indices correspond to the index of the - * permissions in the given list. A true value at an index indicates this Subject is permitted for - * for the associated {@code Permission} string in the list. A false value at an index - * indicates otherwise. + * permissions in the given list. A true value at an index indicates this Subject is permitted for + * for the associated {@code Permission} string in the list. A false value at an index + * indicates otherwise. * @since 0.9 */ boolean[] isPermitted(String... permissions); @@ -160,9 +162,9 @@ public interface Subject { * * @param permissions the permissions that are being checked. * @return a boolean array where indices correspond to the index of the - * permissions in the given list. A true value at an index indicates this Subject is permitted for - * for the associated {@code Permission} object in the list. A false value at an index - * indicates otherwise. + * permissions in the given list. A true value at an index indicates this Subject is permitted for + * for the associated {@code Permission} object in the list. A false value at an index + * indicates otherwise. */ boolean[] isPermitted(List permissions); @@ -200,8 +202,7 @@ public interface Subject { * Please see the class-level JavaDoc for more information on these String-based permission methods. * * @param permission the String representation of the Permission to check. - * @throws org.apache.shiro.authz.AuthorizationException - * if the user does not have the permission. + * @throws org.apache.shiro.authz.AuthorizationException if the user does not have the permission. * @since 0.9 */ void checkPermission(String permission) throws AuthorizationException; @@ -213,8 +214,7 @@ public interface Subject { * the given permission, an {@link org.apache.shiro.authz.AuthorizationException} will be thrown. * * @param permission the Permission to check. - * @throws org.apache.shiro.authz.AuthorizationException - * if this Subject does not have the permission. + * @throws org.apache.shiro.authz.AuthorizationException if this Subject does not have the permission. */ void checkPermission(Permission permission) throws AuthorizationException; @@ -267,8 +267,8 @@ public interface Subject { * * @param roleIdentifiers the application-specific role identifiers to check (usually role ids or role names). * @return a boolean array where indices correspond to the index of the - * roles in the given identifiers. A true value indicates this Subject has the - * role at that index. False indicates this Subject does not have the role at that index. + * roles in the given identifiers. A true value indicates this Subject has the + * role at that index. False indicates this Subject does not have the role at that index. */ boolean[] hasRoles(List roleIdentifiers); @@ -285,8 +285,7 @@ public interface Subject { * {@link org.apache.shiro.authz.AuthorizationException} if they do not. * * @param roleIdentifier the application-specific role identifier (usually a role id or role name ). - * @throws org.apache.shiro.authz.AuthorizationException - * if this Subject does not have the role. + * @throws org.apache.shiro.authz.AuthorizationException if this Subject does not have the role. */ void checkRole(String roleIdentifier) throws AuthorizationException; @@ -295,8 +294,7 @@ public interface Subject { * {@link org.apache.shiro.authz.AuthorizationException} if they do not. * * @param roleIdentifiers the application-specific role identifiers to check (usually role ids or role names). - * @throws org.apache.shiro.authz.AuthorizationException - * if this Subject does not have all of the specified roles. + * @throws org.apache.shiro.authz.AuthorizationException if this Subject does not have all of the specified roles. */ void checkRoles(Collection roleIdentifiers) throws AuthorizationException; @@ -308,7 +306,7 @@ public interface Subject { * * @param roleIdentifiers roleIdentifiers the application-specific role identifiers to check (usually role ids or role names). * @throws AuthorizationException org.apache.shiro.authz.AuthorizationException - * if this Subject does not have all of the specified roles. + * if this Subject does not have all of the specified roles. * @since 1.1.0 */ void checkRoles(String... roleIdentifiers) throws AuthorizationException; @@ -325,8 +323,7 @@ public interface Subject { * * @param token the token encapsulating the subject's principals and credentials to be passed to the * Authentication subsystem for verification. - * @throws org.apache.shiro.authc.AuthenticationException - * if the authentication attempt fails. + * @throws org.apache.shiro.authc.AuthenticationException if the authentication attempt fails. * @since 0.9 */ void login(AuthenticationToken token) throws AuthenticationException; @@ -340,7 +337,7 @@ public interface Subject { * current session. See the {@link #isRemembered() isRemembered()} method JavaDoc for more. * * @return {@code true} if this Subject proved their identity during their current session - * by providing valid credentials matching those known to the system, {@code false} otherwise. + * by providing valid credentials matching those known to the system, {@code false} otherwise. * @since 0.9 */ boolean isAuthenticated(); @@ -392,7 +389,7 @@ public interface Subject { * check this guarantee via the {@link #isAuthenticated() isAuthenticated()} method and not via this method. * * @return {@code true} if this {@code Subject}'s identity (aka {@link #getPrincipals() principals}) is - * remembered from a successful authentication during a previous session, {@code false} otherwise. + * remembered from a successful authentication during a previous session, {@code false} otherwise. * @since 1.0 */ boolean isRemembered(); @@ -420,7 +417,7 @@ public interface Subject { * * @param create boolean argument determining if a new session should be created or not if there is no existing session. * @return the application {@code Session} associated with this {@code Subject} or {@code null} based - * on the above described logic. + * on the above described logic. * @since 0.2 */ Session getSession(boolean create); @@ -534,7 +531,7 @@ public interface Subject { * information. * * @return {@code true} if this {@code Subject} is 'running as' another identity other than its original one or - * {@code false} otherwise (normal {@code Subject} state). + * {@code false} otherwise (normal {@code Subject} state). * @see #runAs * @since 1.0 */ @@ -546,8 +543,8 @@ public interface Subject { * identity (normal state). See the {@link #runAs runAs} method for more information. * * @return the previous 'pre run as' identity of this {@code Subject} before assuming the current - * {@link #runAs runAs} identity, or {@code null} if this {@code Subject} is not operating under an assumed - * identity (normal state). + * {@link #runAs runAs} identity, or {@code null} if this {@code Subject} is not operating under an assumed + * identity (normal state). * @see #runAs * @since 1.0 */ @@ -561,7 +558,7 @@ public interface Subject { * operating under an assumed identity. * * @return the 'run as' (assumed) identity being released or {@code null} if this {@code Subject} is not operating - * under an assumed identity. + * under an assumed identity. * @see #runAs * @since 1.0 */ @@ -604,7 +601,7 @@ public interface Subject { * * @since 1.0 */ - public static class Builder { + class Builder { /** * Hold all contextual data via the Builder instance's method invocations to be sent to the @@ -639,8 +636,8 @@ public Builder(SecurityManager securityManager) { this.securityManager = securityManager; this.subjectContext = newSubjectContextInstance(); if (this.subjectContext == null) { - throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " + - "cannot be null."); + throw new IllegalStateException("Subject instance returned from 'newSubjectContextInstance' " + + "cannot be null."); } this.subjectContext.setSecurityManager(securityManager); } @@ -728,6 +725,7 @@ public Builder session(Session session) { return this; } + @SuppressWarnings("checkstyle:LineLength") /** * Ensures the {@code Subject} being built will reflect the specified principals (aka identity). *

@@ -737,7 +735,8 @@ public Builder session(Session session) { * was named "{@code myRealm}", you might create the '{@code jsmith} {@code Subject} instance this * way: *

-         * PrincipalCollection identity = new {@link org.apache.shiro.subject.SimplePrincipalCollection#SimplePrincipalCollection(Object, String) SimplePrincipalCollection}("jsmith", "myRealm");
+         * PrincipalCollection identity = new {@link org.apache.shiro.subject.SimplePrincipalCollection#SimplePrincipalCollection(Object, String)
+         *                                  SimplePrincipalCollection}("jsmith", "myRealm");
          * Subject jsmith = new Subject.Builder().principals(identity).buildSubject();
*

* Similarly, if your application's unique identifier for users is a {@code long} value (such as might be used @@ -746,7 +745,8 @@ public Builder session(Session session) { * instance this way: *

          * long userId = //get user ID from somewhere
-         * PrincipalCollection userIdentity = new {@link org.apache.shiro.subject.SimplePrincipalCollection#SimplePrincipalCollection(Object, String) SimplePrincipalCollection}(userId, "jdbcRealm");
+         * PrincipalCollection userIdentity = new {@link org.apache.shiro.subject.SimplePrincipalCollection#SimplePrincipalCollection(Object, String)
+         *                                  SimplePrincipalCollection}(userId, "jdbcRealm");
          * Subject user = new Subject.Builder().principals(identity).buildSubject();
* * @param principals the principals to use as the {@code Subject}'s identity. @@ -839,7 +839,7 @@ public Builder contextAttribute(String attributeKey, Object attributeValue) { * framework developer to bind the returned {@code Subject} for continued use if desired. * * @return a new {@code Subject} instance reflecting the cumulative state acquired by the - * other methods in this class. + * other methods in this class. */ public Subject buildSubject() { return this.securityManager.createSubject(this.subjectContext); diff --git a/core/src/main/java/org/apache/shiro/subject/SubjectContext.java b/core/src/main/java/org/apache/shiro/subject/SubjectContext.java index 3cb7a88099..d9e29a6392 100644 --- a/core/src/main/java/org/apache/shiro/subject/SubjectContext.java +++ b/core/src/main/java/org/apache/shiro/subject/SubjectContext.java @@ -63,7 +63,7 @@ public interface SubjectContext extends Map { * {@code null} if one has not yet been provided to this context. * * @return the SecurityManager instance that should be used to back the constructed {@link Subject} instance or - * {@code null} if one has not yet been provided to this context. + * {@code null} if one has not yet been provided to this context. */ SecurityManager getSecurityManager(); @@ -78,10 +78,11 @@ public interface SubjectContext extends Map { /** * Resolves the {@code SecurityManager} instance that should be used to back the constructed {@link Subject} - * instance (typically used to support {@link org.apache.shiro.subject.support.DelegatingSubject DelegatingSubject} implementations). + * instance (typically used to support + * {@link org.apache.shiro.subject.support.DelegatingSubject DelegatingSubject} implementations). * * @return the {@code SecurityManager} instance that should be used to back the constructed {@link Subject} - * instance + * instance */ SecurityManager resolveSecurityManager(); @@ -116,7 +117,7 @@ public interface SubjectContext extends Map { * newly authenticated instance. * * @return any existing {@code Subject} that may be in use at the time the new {@code Subject} instance is - * being created. + * being created. */ Subject getSubject(); @@ -176,7 +177,7 @@ public interface SubjectContext extends Map { * reason for ignoring Shiro's default authentication state mechanisms. * * @return {@code true} if the constructed {@code Subject} should be considered authenticated, {@code false} - * otherwise. + * otherwise. */ boolean isAuthenticated(); @@ -204,7 +205,7 @@ public interface SubjectContext extends Map { * {@code false} otherwise. * * @param enabled whether or not the constructed {@code Subject} instance should be allowed to create a session, - * {@code false} otherwise. + * {@code false} otherwise. * @since 1.2 */ void setSessionCreationEnabled(boolean enabled); diff --git a/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java b/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java index 5e888258bd..b2d9d99986 100644 --- a/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java +++ b/core/src/main/java/org/apache/shiro/subject/support/DefaultSubjectContext.java @@ -46,6 +46,21 @@ */ public class DefaultSubjectContext extends MapContext implements SubjectContext { + /** + * session creation enabled key. + */ + public static final String SESSION_CREATION_ENABLED = DefaultSubjectContext.class.getName() + ".SESSION_CREATION_ENABLED"; + + /** + * The session key that is used to store subject principals. + */ + public static final String PRINCIPALS_SESSION_KEY = DefaultSubjectContext.class.getName() + "_PRINCIPALS_SESSION_KEY"; + + /** + * The session key that is used to store whether or not the user is authenticated. + */ + public static final String AUTHENTICATED_SESSION_KEY = DefaultSubjectContext.class.getName() + "_AUTHENTICATED_SESSION_KEY"; + private static final String SECURITY_MANAGER = DefaultSubjectContext.class.getName() + ".SECURITY_MANAGER"; private static final String SESSION_ID = DefaultSubjectContext.class.getName() + ".SESSION_ID"; @@ -64,19 +79,7 @@ public class DefaultSubjectContext extends MapContext implements SubjectContext private static final String HOST = DefaultSubjectContext.class.getName() + ".HOST"; - public static final String SESSION_CREATION_ENABLED = DefaultSubjectContext.class.getName() + ".SESSION_CREATION_ENABLED"; - - /** - * The session key that is used to store subject principals. - */ - public static final String PRINCIPALS_SESSION_KEY = DefaultSubjectContext.class.getName() + "_PRINCIPALS_SESSION_KEY"; - - /** - * The session key that is used to store whether or not the user is authenticated. - */ - public static final String AUTHENTICATED_SESSION_KEY = DefaultSubjectContext.class.getName() + "_AUTHENTICATED_SESSION_KEY"; - - private static final transient Logger log = LoggerFactory.getLogger(DefaultSubjectContext.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultSubjectContext.class); public DefaultSubjectContext() { super(); @@ -97,15 +100,15 @@ public void setSecurityManager(SecurityManager securityManager) { public SecurityManager resolveSecurityManager() { SecurityManager securityManager = getSecurityManager(); if (securityManager == null) { - if (log.isDebugEnabled()) { - log.debug("No SecurityManager available in subject context map. " + - "Falling back to SecurityUtils.getSecurityManager() lookup."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("No SecurityManager available in subject context map. " + + "Falling back to SecurityUtils.getSecurityManager() lookup."); } try { securityManager = SecurityUtils.getSecurityManager(); } catch (UnavailableSecurityManagerException e) { - if (log.isDebugEnabled()) { - log.debug("No SecurityManager available via SecurityUtils. Heuristics exhausted.", e); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("No SecurityManager available via SecurityUtils. Heuristics exhausted.", e); } } } diff --git a/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java b/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java index 5605da2d46..d5c19c440d 100644 --- a/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java +++ b/core/src/main/java/org/apache/shiro/subject/support/DelegatingSubject.java @@ -69,9 +69,10 @@ * * @since 0.1 */ +@SuppressWarnings("checkstyle:MethodCount") public class DelegatingSubject implements Subject { - private static final Logger log = LoggerFactory.getLogger(DelegatingSubject.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DelegatingSubject.class); private static final String RUN_AS_PRINCIPALS_SESSION_KEY = DelegatingSubject.class.getName() + ".RUN_AS_PRINCIPALS_SESSION_KEY"; @@ -193,14 +194,14 @@ public boolean isPermittedAll(Collection permissions) { protected void assertAuthzCheckPossible() throws AuthorizationException { if (!hasPrincipals()) { - String msg = "This subject is anonymous - it does not have any identifying principals and " + - "authorization operations require an identity to check against. A Subject instance will " + - "acquire these identifying principals automatically after a successful login is performed " + - "be executing " + Subject.class.getName() + ".login(AuthenticationToken) or when 'Remember Me' " + - "functionality is enabled by the SecurityManager. This exception can also occur when a " + - "previously logged-in Subject has logged out which " + - "makes it anonymous again. Because an identity is currently not known due to any of these " + - "conditions, authorization is denied."; + String msg = "This subject is anonymous - it does not have any identifying principals and " + + "authorization operations require an identity to check against. A Subject instance will " + + "acquire these identifying principals automatically after a successful login is performed " + + "be executing " + Subject.class.getName() + ".login(AuthenticationToken) or when 'Remember Me' " + + "functionality is enabled by the SecurityManager. This exception can also occur when a " + + "previously logged-in Subject has logged out which " + + "makes it anonymous again. Because an identity is currently not known due to any of these " + + "conditions, authorization is denied."; throw new UnauthenticatedException(msg); } } @@ -274,8 +275,8 @@ public void login(AuthenticationToken token) throws AuthenticationException { } if (principals == null || principals.isEmpty()) { - String msg = "Principals returned from securityManager.login( token ) returned a null or " + - "empty value. This value must be non null and populated with one or more elements."; + String msg = "Principals returned from securityManager.login( token ) returned a null or " + + "empty value. This value must be non null and populated with one or more elements."; throw new IllegalStateException(msg); } this.principals = principals; @@ -318,25 +319,25 @@ public Session getSession() { } public Session getSession(boolean create) { - if (log.isTraceEnabled()) { - log.trace("attempting to get session; create = " + create + - "; session is null = " + (this.session == null) + - "; session has id = " + (this.session != null && session.getId() != null)); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("attempting to get session; create = " + create + + "; session is null = " + (this.session == null) + + "; session has id = " + (this.session != null && session.getId() != null)); } if (this.session == null && create) { //added in 1.2: if (!isSessionCreationEnabled()) { - String msg = "Session creation has been disabled for the current subject. This exception indicates " + - "that there is either a programming error (using a session when it should never be " + - "used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " + - "for the current Subject. See the " + DisabledSessionException.class.getName() + " JavaDoc " + - "for more."; + String msg = "Session creation has been disabled for the current subject. This exception indicates " + + "that there is either a programming error (using a session when it should never be " + + "used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " + + "for the current Subject. See the " + DisabledSessionException.class.getName() + " JavaDoc " + + "for more."; throw new DisabledSessionException(msg); } - log.trace("Starting session for host {}", getHost()); + LOGGER.trace("Starting session for host {}", getHost()); SessionContext sessionContext = createSessionContext(); Session session = this.securityManager.start(sessionContext); this.session = decorate(session); @@ -357,8 +358,8 @@ private void clearRunAsIdentitiesInternal() { try { clearRunAsIdentities(); } catch (SessionException se) { - log.debug("Encountered session exception trying to clear 'runAs' identities during logout. This " + - "can generally safely be ignored.", se); + LOGGER.debug("Encountered session exception trying to clear 'runAs' identities during logout. This " + + "can generally safely be ignored.", se); } } @@ -402,16 +403,16 @@ public Callable associateWith(Callable callable) { public Runnable associateWith(Runnable runnable) { if (runnable instanceof Thread) { - String msg = "This implementation does not support Thread arguments because of JDK ThreadLocal " + - "inheritance mechanisms required by Shiro. Instead, the method argument should be a non-Thread " + - "Runnable and the return value from this method can then be given to an ExecutorService or " + - "another Thread."; + String msg = "This implementation does not support Thread arguments because of JDK ThreadLocal " + + "inheritance mechanisms required by Shiro. Instead, the method argument should be a non-Thread " + + "Runnable and the return value from this method can then be given to an ExecutorService or " + + "another Thread."; throw new UnsupportedOperationException(msg); } return new SubjectRunnable(this, runnable); } - private class StoppingAwareProxiedSession extends ProxiedSession { + private static final class StoppingAwareProxiedSession extends ProxiedSession { private final DelegatingSubject owner; @@ -433,10 +434,10 @@ public void stop() throws InvalidSessionException { public void runAs(PrincipalCollection principals) { if (!hasPrincipals()) { - String msg = "This subject does not yet have an identity. Assuming the identity of another " + - "Subject is only allowed for Subjects with an existing identity. Try logging this subject in " + - "first, or using the " + Subject.Builder.class.getName() + " to build ad hoc Subject instances " + - "with identities as necessary."; + String msg = "This subject does not yet have an identity. Assuming the identity of another " + + "Subject is only allowed for Subjects with an existing identity. Try logging this subject in " + + "first, or using the " + Subject.Builder.class.getName() + " to build ad hoc Subject instances " + + "with identities as necessary."; throw new IllegalStateException(msg); } pushIdentity(principals); @@ -478,7 +479,7 @@ private List getRunAsPrincipalsStack() { // this thread could throw this exception, so we catch it // similar issue as in clearRunAsIdentitiesInternal() // See https://issues.apache.org/jira/browse/SHIRO-512 - log.debug("Encountered session exception trying to get 'runAs' principal stack. This " + LOGGER.debug("Encountered session exception trying to get 'runAs' principal stack. This " + "can generally safely be ignored.", se); } } @@ -529,12 +530,12 @@ private PrincipalCollection popIdentity() { @Override public String toString() { return new StringJoiner(", ", "DelegatingSubject{", "}") - .add("principals=" + principals) - .add("authenticated=" + authenticated) - .add("host='******") - .add("session='******'") - .add("sessionCreationEnabled=" + sessionCreationEnabled) - .add("securityManager=" + securityManager) - .toString(); + .add("principals=" + principals) + .add("authenticated=" + authenticated) + .add("host='******") + .add("session='******'") + .add("sessionCreationEnabled=" + sessionCreationEnabled) + .add("securityManager=" + securityManager) + .toString(); } } diff --git a/core/src/main/java/org/apache/shiro/subject/support/SubjectCallable.java b/core/src/main/java/org/apache/shiro/subject/support/SubjectCallable.java index d23971b7b4..d6d1ecfcbd 100644 --- a/core/src/main/java/org/apache/shiro/subject/support/SubjectCallable.java +++ b/core/src/main/java/org/apache/shiro/subject/support/SubjectCallable.java @@ -44,7 +44,7 @@ * invocations. It also guarantees that the running thread remains 'clean' in any thread-pooled environments. * *

Usage

- * + *

* This is typically considered a support class and is not often directly referenced. Most people prefer to use * the {@code Subject.}{@link Subject#associateWith(Callable) associateWith} method, which will automatically return * an instance of this class. @@ -53,6 +53,7 @@ * {@link org.apache.shiro.concurrent.SubjectAwareExecutorService SubjectAwareExecutorService}, which * transparently uses instances of this class. * + * @param V * @see Subject#associateWith(Callable) * @see org.apache.shiro.concurrent.SubjectAwareExecutorService SubjectAwareExecutorService * @since 1.0 diff --git a/core/src/main/java/org/apache/shiro/subject/support/SubjectRunnable.java b/core/src/main/java/org/apache/shiro/subject/support/SubjectRunnable.java index 9e633dd411..42eeb61bdb 100644 --- a/core/src/main/java/org/apache/shiro/subject/support/SubjectRunnable.java +++ b/core/src/main/java/org/apache/shiro/subject/support/SubjectRunnable.java @@ -40,7 +40,7 @@ *

* *

Usage

- * + *

* This is typically considered a support class and is not often directly referenced. Most people prefer to use * the {@code Subject.}{@link Subject#execute(Runnable) execute} or * {@code Subject.}{@link Subject#associateWith(Runnable) associateWith} methods, which transparently perform the diff --git a/core/src/main/java/org/apache/shiro/subject/support/SubjectThreadState.java b/core/src/main/java/org/apache/shiro/subject/support/SubjectThreadState.java index 2e439b1425..50a9c043d0 100644 --- a/core/src/main/java/org/apache/shiro/subject/support/SubjectThreadState.java +++ b/core/src/main/java/org/apache/shiro/subject/support/SubjectThreadState.java @@ -59,10 +59,10 @@ public SubjectThreadState(Subject subject) { this.subject = subject; SecurityManager securityManager = null; - if ( subject instanceof DelegatingSubject) { - securityManager = ((DelegatingSubject)subject).getSecurityManager(); + if (subject instanceof DelegatingSubject) { + securityManager = ((DelegatingSubject) subject).getSecurityManager(); } - if ( securityManager == null) { + if (securityManager == null) { securityManager = ThreadContext.getSecurityManager(); } this.securityManager = securityManager; @@ -88,7 +88,7 @@ protected Subject getSubject() { */ public void bind() { SecurityManager securityManager = this.securityManager; - if ( securityManager == null ) { + if (securityManager == null) { //try just in case the constructor didn't find one at the time: securityManager = ThreadContext.getSecurityManager(); } diff --git a/core/src/main/java/org/apache/shiro/subject/support/package-info.java b/core/src/main/java/org/apache/shiro/subject/support/package-info.java index f3441f93f5..b339001837 100644 --- a/core/src/main/java/org/apache/shiro/subject/support/package-info.java +++ b/core/src/main/java/org/apache/shiro/subject/support/package-info.java @@ -19,4 +19,4 @@ /** * Concrete support implementations of most of the {@code org.apache.shiro.subject} interfaces. */ -package org.apache.shiro.subject.support; \ No newline at end of file +package org.apache.shiro.subject.support; diff --git a/core/src/main/java/org/apache/shiro/util/AbstractFactory.java b/core/src/main/java/org/apache/shiro/util/AbstractFactory.java index 09e7e48cd1..97fce29f0e 100644 --- a/core/src/main/java/org/apache/shiro/util/AbstractFactory.java +++ b/core/src/main/java/org/apache/shiro/util/AbstractFactory.java @@ -21,8 +21,9 @@ import org.apache.shiro.lang.util.Factory; /** - * TODO - Class JavaDoc + * AbstractFactory. * + * @param T * @since 1.0 */ public abstract class AbstractFactory implements Factory { diff --git a/core/src/main/java/org/apache/shiro/util/AntPathMatcher.java b/core/src/main/java/org/apache/shiro/util/AntPathMatcher.java index 29f1511806..0c31e306e4 100644 --- a/core/src/main/java/org/apache/shiro/util/AntPathMatcher.java +++ b/core/src/main/java/org/apache/shiro/util/AntPathMatcher.java @@ -80,7 +80,9 @@ public void setPathSeparator(String pathSeparator) { } /** - * Checks if {@code path} is a pattern (i.e. contains a '*', or '?'). For example the {@code /foo/**} would return {@code true}, while {@code /bar/} would return {@code false}. + * Checks if {@code path} is a pattern (i.e. contains a '*', or '?'). + * For example the {@code /foo/**} would return {@code true}, while {@code /bar/} would return {@code false}. + * * @param path the string to check * @return this method returns {@code true} if {@code path} contains a '*' or '?', otherwise, {@code false} */ @@ -103,7 +105,6 @@ public boolean matchStart(String pattern, String path) { return doMatch(pattern, path, false); } - /** * Actually match the given path against the given pattern. * @@ -112,8 +113,10 @@ public boolean matchStart(String pattern, String path) { * @param fullMatch whether a full pattern match is required * (else a pattern match as far as the given base path goes is sufficient) * @return true if the supplied path matched, - * false if it didn't + * false if it didn't */ + @SuppressWarnings({"checkstyle:ReturnCount", "checkstyle:CyclomaticComplexity", + "checkstyle:NPathComplexity", "checkstyle:MethodLength"}) protected boolean doMatch(String pattern, String path, boolean fullMatch) { if (path == null || path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) { return false; @@ -143,18 +146,18 @@ protected boolean doMatch(String pattern, String path, boolean fullMatch) { if (pathIdxStart > pathIdxEnd) { // Path is exhausted, only match if rest of pattern is * or **'s if (pattIdxStart > pattIdxEnd) { - return (pattern.endsWith(this.pathSeparator) ? - path.endsWith(this.pathSeparator) : !path.endsWith(this.pathSeparator)); + return (pattern.endsWith(this.pathSeparator) + ? path.endsWith(this.pathSeparator) : !path.endsWith(this.pathSeparator)); } if (!fullMatch) { return true; } - if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && - path.endsWith(this.pathSeparator)) { + if (pattIdxStart == pattIdxEnd && "*".equals(pattDirs[pattIdxStart]) + && path.endsWith(this.pathSeparator)) { return true; } for (int i = pattIdxStart; i <= pattIdxEnd; i++) { - if (!pattDirs[i].equals("**")) { + if (!"**".equals(pattDirs[i])) { return false; } } @@ -249,8 +252,10 @@ protected boolean doMatch(String pattern, String path, boolean fullMatch) { * @param str string which must be matched against the pattern. * Must not be null. * @return true if the string matches against the - * pattern, or false otherwise. + * pattern, or false otherwise. */ + @SuppressWarnings({"checkstyle:ReturnCount", "checkstyle:CyclomaticComplexity", + "checkstyle:NPathComplexity", "checkstyle:MethodLength"}) private boolean matchStrings(String pattern, String str) { char[] patArr = pattern.toCharArray(); char[] strArr = str.toCharArray(); @@ -271,29 +276,34 @@ private boolean matchStrings(String pattern, String str) { if (!containsStar) { // No '*'s, so we make a shortcut if (patIdxEnd != strIdxEnd) { - return false; // Pattern and string do not have the same size + // Pattern and string do not have the same size + return false; } for (int i = 0; i <= patIdxEnd; i++) { ch = patArr[i]; if (ch != '?') { if (ch != strArr[i]) { - return false;// Character mismatch + // Character mismatch + return false; } } } - return true; // String matches against pattern + // String matches against pattern + return true; } if (patIdxEnd == 0) { - return true; // Pattern contains only '*', which matches anything + // Pattern contains only '*', which matches anything + return true; } // Process characters before first star while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) { if (ch != '?') { if (ch != strArr[strIdxStart]) { - return false;// Character mismatch + // Character mismatch + return false; } } patIdxStart++; @@ -314,7 +324,8 @@ private boolean matchStrings(String pattern, String str) { while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) { if (ch != '?') { if (ch != strArr[strIdxEnd]) { - return false;// Character mismatch + // Character mismatch + return false; } } patIdxEnd--; diff --git a/core/src/main/java/org/apache/shiro/util/CollectionUtils.java b/core/src/main/java/org/apache/shiro/util/CollectionUtils.java index 67fc5c94a4..61cd05b799 100644 --- a/core/src/main/java/org/apache/shiro/util/CollectionUtils.java +++ b/core/src/main/java/org/apache/shiro/util/CollectionUtils.java @@ -33,9 +33,10 @@ * * @since 0.9 */ -public class CollectionUtils { +public final class CollectionUtils { - //TODO - complete JavaDoc + private CollectionUtils() { + } public static Set asSet(E... elements) { if (elements == null || elements.length == 0) { @@ -57,7 +58,7 @@ public static Set asSet(E... elements) { * * @param c the collection to check * @return {@code true} if the specified {@code Collection} is {@code null} or {@link Collection#isEmpty empty}, - * {@code false} otherwise. + * {@code false} otherwise. * @since 1.0 */ public static boolean isEmpty(Collection c) { @@ -70,7 +71,7 @@ public static boolean isEmpty(Collection c) { * * @param m the {@code Map} to check * @return {@code true} if the specified {@code Map} is {@code null} or {@link Map#isEmpty empty}, - * {@code false} otherwise. + * {@code false} otherwise. * @since 1.0 */ public static boolean isEmpty(Map m) { @@ -106,7 +107,7 @@ public static int size(Map m) { * * @param principals the principals to check. * @return {@code true} if the specified {@code PrincipalCollection} is {@code null} or - * {@link PrincipalCollection#isEmpty empty}, {@code false} otherwise. + * {@link PrincipalCollection#isEmpty empty}, {@code false} otherwise. * @since 1.0 * @deprecated Use PrincipalCollection.isEmpty() directly. */ @@ -134,7 +135,7 @@ public static List asList(E... elements) { Collections.addAll(deque, elements); return deque; }*/ - + @SuppressWarnings("checkstyle:MagicNumber") static int computeListCapacity(int arraySize) { return (int) Math.min(5L + arraySize + (arraySize / 10), Integer.MAX_VALUE); } diff --git a/core/src/main/java/org/apache/shiro/util/JavaEnvironment.java b/core/src/main/java/org/apache/shiro/util/JavaEnvironment.java index 3a9e1bbef7..5d752d2789 100644 --- a/core/src/main/java/org/apache/shiro/util/JavaEnvironment.java +++ b/core/src/main/java/org/apache/shiro/util/JavaEnvironment.java @@ -66,14 +66,16 @@ public abstract class JavaEnvironment { */ public static final int JAVA_18 = 5; - /** The virtual machine version, i.e. System.getProperty("java.version");. */ - private static final String version; + /** + * The virtual machine version, i.e. System.getProperty("java.version");. + */ + private static final String VERSION; /** * The virtual machine major version. For example, with a version of * 1.5.6_10, this would be 1.5 */ - private static final int majorVersion; + private static final int MAJOR_VERSION; /** * Static code initialization block that sets the @@ -81,25 +83,25 @@ public abstract class JavaEnvironment { * upon initialization. */ static { - version = System.getProperty("java.version"); + VERSION = System.getProperty("java.version"); // version String should look like "1.4.2_10" // NOTE: JDK 1.9 will be versioned differently '9' and/or 9.x.x // https://blogs.oracle.com/java-platform-group/entry/a_new_jdk_9_version - if (version.contains("1.8.")) { - majorVersion = JAVA_18; - } else if (version.contains("1.7.")) { - majorVersion = JAVA_17; - } else if (version.contains("1.6.")) { - majorVersion = JAVA_16; - } else if (version.contains("1.5.")) { - majorVersion = JAVA_15; - } else if (version.contains("1.4.")) { - majorVersion = JAVA_14; + if (VERSION.contains("1.8.")) { + MAJOR_VERSION = JAVA_18; + } else if (VERSION.contains("1.7.")) { + MAJOR_VERSION = JAVA_17; + } else if (VERSION.contains("1.6.")) { + MAJOR_VERSION = JAVA_16; + } else if (VERSION.contains("1.5.")) { + MAJOR_VERSION = JAVA_15; + } else if (VERSION.contains("1.4.")) { + MAJOR_VERSION = JAVA_14; } else { // else leave 1.3 as default (it's either 1.3 or unknown) - majorVersion = JAVA_13; + MAJOR_VERSION = JAVA_13; } } @@ -112,7 +114,7 @@ public abstract class JavaEnvironment { * @see System#getProperty(String) */ public static String getVersion() { - return version; + return VERSION; } /** @@ -128,7 +130,7 @@ public static String getVersion() { * @see #JAVA_18 */ public static int getMajorVersion() { - return majorVersion; + return MAJOR_VERSION; } /** @@ -171,7 +173,6 @@ public static boolean isAtLeastVersion15() { * @see #JAVA_16 * @see #JAVA_17 * @see #JAVA_18 - * * @since 1.2 */ public static boolean isAtLeastVersion16() { diff --git a/core/src/main/java/org/apache/shiro/util/JdbcUtils.java b/core/src/main/java/org/apache/shiro/util/JdbcUtils.java index 0c5f3aeda2..5c02fdb71b 100644 --- a/core/src/main/java/org/apache/shiro/util/JdbcUtils.java +++ b/core/src/main/java/org/apache/shiro/util/JdbcUtils.java @@ -35,10 +35,12 @@ * * @since 0.2 */ -public class JdbcUtils { +public final class JdbcUtils { - /** Private internal log instance. */ - private static final Logger log = LoggerFactory.getLogger(JdbcUtils.class); + /** + * Private internal log instance. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(JdbcUtils.class); /** * Private constructor to prevent instantiation. @@ -57,12 +59,12 @@ public static void closeConnection(Connection connection) { try { connection.close(); } catch (SQLException ex) { - if (log.isDebugEnabled()) { - log.debug("Could not close JDBC Connection", ex); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Could not close JDBC Connection", ex); } } catch (Throwable ex) { - if (log.isDebugEnabled()) { - log.debug("Unexpected exception on closing JDBC Connection", ex); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Unexpected exception on closing JDBC Connection", ex); } } } @@ -79,12 +81,12 @@ public static void closeStatement(Statement statement) { try { statement.close(); } catch (SQLException ex) { - if (log.isDebugEnabled()) { - log.debug("Could not close JDBC Statement", ex); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Could not close JDBC Statement", ex); } } catch (Throwable ex) { - if (log.isDebugEnabled()) { - log.debug("Unexpected exception on closing JDBC Statement", ex); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Unexpected exception on closing JDBC Statement", ex); } } } @@ -101,12 +103,12 @@ public static void closeResultSet(ResultSet rs) { try { rs.close(); } catch (SQLException ex) { - if (log.isDebugEnabled()) { - log.debug("Could not close JDBC ResultSet", ex); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Could not close JDBC ResultSet", ex); } } catch (Throwable ex) { - if (log.isDebugEnabled()) { - log.debug("Unexpected exception on closing JDBC ResultSet", ex); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Unexpected exception on closing JDBC ResultSet", ex); } } } diff --git a/core/src/main/java/org/apache/shiro/util/MapContext.java b/core/src/main/java/org/apache/shiro/util/MapContext.java index 1ea2437bda..9adbc4d91a 100644 --- a/core/src/main/java/org/apache/shiro/util/MapContext.java +++ b/core/src/main/java/org/apache/shiro/util/MapContext.java @@ -19,7 +19,11 @@ package org.apache.shiro.util; import java.io.Serializable; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; /** * A {@code MapContext} provides a common base for context-based data storage in a {@link Map}. Type-safe attribute @@ -61,9 +65,9 @@ protected E getTypedValue(String key, Class type) { Object o = backingMap.get(key); if (o != null) { if (!type.isAssignableFrom(o.getClass())) { - String msg = "Invalid object found in SubjectContext Map under key [" + key + "]. Expected type " + - "was [" + type.getName() + "], but the object under that key is of type " + - "[" + o.getClass().getName() + "]."; + String msg = "Invalid object found in SubjectContext Map under key [" + key + "]. Expected type " + + "was [" + type.getName() + "], but the object under that key is of type " + + "[" + o.getClass().getName() + "]."; throw new IllegalArgumentException(msg); } found = (E) o; diff --git a/core/src/main/java/org/apache/shiro/util/PatternMatcher.java b/core/src/main/java/org/apache/shiro/util/PatternMatcher.java index 436f693534..ac2d7a7649 100644 --- a/core/src/main/java/org/apache/shiro/util/PatternMatcher.java +++ b/core/src/main/java/org/apache/shiro/util/PatternMatcher.java @@ -24,7 +24,7 @@ * Different implementations can support different pattern types, for example, Ant style path expressions, or * regular expressions, or other types of text based patterns. * - * @see org.apache.shiro.lang.util.AntPathMatcher AntPathMatcher + * @see AntPathMatcher AntPathMatcher * @since 0.9 RC2 */ public interface PatternMatcher { @@ -36,7 +36,7 @@ public interface PatternMatcher { * @param pattern the pattern to match against * @param source the source to match * @return true if the given source matches the specified pattern, - * false otherwise. + * false otherwise. */ boolean matches(String pattern, String source); } diff --git a/core/src/main/java/org/apache/shiro/util/PermissionUtils.java b/core/src/main/java/org/apache/shiro/util/PermissionUtils.java index 8d46eb5d72..f1e0ba1097 100644 --- a/core/src/main/java/org/apache/shiro/util/PermissionUtils.java +++ b/core/src/main/java/org/apache/shiro/util/PermissionUtils.java @@ -33,7 +33,10 @@ * * @since 0.1 */ -public class PermissionUtils { +public final class PermissionUtils { + + private PermissionUtils() { + } public static Set resolveDelimitedPermissions(String s, PermissionResolver permissionResolver) { Set permStrings = toPermissionStrings(s); @@ -48,6 +51,7 @@ public static Set toPermissionStrings(String permissionsString) { return null; } + @SuppressWarnings("checkstyle:LineLength") public static Set resolvePermissions(Collection permissionStrings, PermissionResolver permissionResolver) { Set permissions = new LinkedHashSet<>(permissionStrings.size()); for (String permissionString : permissionStrings) { diff --git a/core/src/main/java/org/apache/shiro/util/RegExPatternMatcher.java b/core/src/main/java/org/apache/shiro/util/RegExPatternMatcher.java index b07ce3d03a..b03d96b3da 100644 --- a/core/src/main/java/org/apache/shiro/util/RegExPatternMatcher.java +++ b/core/src/main/java/org/apache/shiro/util/RegExPatternMatcher.java @@ -33,7 +33,7 @@ public class RegExPatternMatcher implements PatternMatcher { private static final int CASE_INSENSITIVE = DEFAULT | Pattern.CASE_INSENSITIVE; - private boolean caseInsensitive = false; + private boolean caseInsensitive; /** * Simple implementation that merely uses the default pattern comparison logic provided by the @@ -43,6 +43,7 @@ public class RegExPatternMatcher implements PatternMatcher { * Pattern p = Pattern.compile(pattern, Pattern.DOTALL); * Matcher m = p.matcher(source); * return m.matches(); + * * @param pattern the pattern to match against * @param source the source to match * @return {@code true} if the source matches the required pattern, {@code false} otherwise. @@ -58,6 +59,7 @@ public boolean matches(String pattern, String source) { /** * Returns true if regex match should be case-insensitive. + * * @return true if regex match should be case-insensitive. */ public boolean isCaseInsensitive() { @@ -66,6 +68,7 @@ public boolean isCaseInsensitive() { /** * Adds the Pattern.CASE_INSENSITIVE flag when compiling patterns. + * * @param caseInsensitive true if patterns should match case-insensitive. */ public void setCaseInsensitive(boolean caseInsensitive) { diff --git a/core/src/main/java/org/apache/shiro/util/ThreadContext.java b/core/src/main/java/org/apache/shiro/util/ThreadContext.java index 28488a3e26..6fe7b3f71c 100644 --- a/core/src/main/java/org/apache/shiro/util/ThreadContext.java +++ b/core/src/main/java/org/apache/shiro/util/ThreadContext.java @@ -46,14 +46,21 @@ public abstract class ThreadContext { /** - * Private internal log instance. + * security manager key. */ - private static final Logger log = LoggerFactory.getLogger(ThreadContext.class); - public static final String SECURITY_MANAGER_KEY = ThreadContext.class.getName() + "_SECURITY_MANAGER_KEY"; + + /** + * subject key. + */ public static final String SUBJECT_KEY = ThreadContext.class.getName() + "_SUBJECT_KEY"; - private static final ThreadLocal> resources = new InheritableThreadLocalMap>(); + /** + * Private internal log instance. + */ + private static final Logger LOGGER = LoggerFactory.getLogger(ThreadContext.class); + + private static final ThreadLocal> RESOURCES = new InheritableThreadLocalMap>(); /** * Default no-argument constructor. @@ -68,10 +75,10 @@ protected ThreadContext() { * @return the map of bound resources */ public static Map getResources() { - if (resources.get() == null){ + if (RESOURCES.get() == null) { return Collections.emptyMap(); } else { - return new HashMap(resources.get()); + return new HashMap(RESOURCES.get()); } } @@ -88,8 +95,8 @@ public static void setResources(Map newResources) { return; } ensureResourcesInitialized(); - resources.get().clear(); - resources.get().putAll(newResources); + RESOURCES.get().clear(); + RESOURCES.get().putAll(newResources); } /** @@ -98,17 +105,17 @@ public static void setResources(Map newResources) { * * @param key the map key to use to lookup the value * @return the value bound in the {@code ThreadContext} under the specified {@code key}, or {@code null} if there - * is no value for that {@code key}. + * is no value for that {@code key}. * @since 1.0 */ private static Object getValue(Object key) { - Map perThreadResources = resources.get(); + Map perThreadResources = RESOURCES.get(); return perThreadResources != null ? perThreadResources.get(key) : null; } - private static void ensureResourcesInitialized(){ - if (resources.get() == null){ - resources.set(new HashMap()); + private static void ensureResourcesInitialized() { + if (RESOURCES.get() == null) { + RESOURCES.set(new HashMap()); } } @@ -118,19 +125,19 @@ private static void ensureResourcesInitialized(){ * * @param key the key that identifies the value to return * @return the object keyed by key or null if - * no value exists for the specified key + * no value exists for the specified key */ public static Object get(Object key) { - if (log.isTraceEnabled()) { + if (LOGGER.isTraceEnabled()) { String msg = "get() - in thread [" + Thread.currentThread().getName() + "]"; - log.trace(msg); + LOGGER.trace(msg); } Object value = getValue(key); - if ((value != null) && log.isTraceEnabled()) { - String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" + - key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]"; - log.trace(msg); + if ((value != null) && LOGGER.isTraceEnabled()) { + String msg = "Retrieved value of type [" + value.getClass().getName() + "] for key [" + + key + "] " + "bound to thread [" + Thread.currentThread().getName() + "]"; + LOGGER.trace(msg); } return value; } @@ -161,12 +168,12 @@ public static void put(Object key, Object value) { } ensureResourcesInitialized(); - resources.get().put(key, value); + RESOURCES.get().put(key, value); - if (log.isTraceEnabled()) { - String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" + - key + "] to thread " + "[" + Thread.currentThread().getName() + "]"; - log.trace(msg); + if (LOGGER.isTraceEnabled()) { + String msg = "Bound value of type [" + value.getClass().getName() + "] for key [" + + key + "] to thread " + "[" + Thread.currentThread().getName() + "]"; + LOGGER.trace(msg); } } @@ -176,16 +183,16 @@ public static void put(Object key, Object value) { * * @param key The key identifying the value bound to the current thread. * @return the object unbound or null if there was nothing bound - * under the specified key name. + * under the specified key name. */ public static Object remove(Object key) { - Map perThreadResources = resources.get(); + Map perThreadResources = RESOURCES.get(); Object value = perThreadResources != null ? perThreadResources.remove(key) : null; - if ((value != null) && log.isTraceEnabled()) { - String msg = "Removed value of type [" + value.getClass().getName() + "] for key [" + - key + "]" + "from thread [" + Thread.currentThread().getName() + "]"; - log.trace(msg); + if ((value != null) && LOGGER.isTraceEnabled()) { + String msg = "Removed value of type [" + value.getClass().getName() + "] for key [" + + key + "]" + "from thread [" + Thread.currentThread().getName() + "]"; + LOGGER.trace(msg); } return value; @@ -200,7 +207,7 @@ public static Object remove(Object key) { * @since 1.0 */ public static void remove() { - resources.remove(); + RESOURCES.remove(); } /** @@ -257,7 +264,7 @@ public static void bind(SecurityManager securityManager) { * during thread execution), use the {@link #getSecurityManager() getSecurityManager()} method instead. * * @return the application's SecurityManager instance previously bound to the thread, or null if there - * was none bound. + * was none bound. * @since 0.9 */ public static SecurityManager unbindSecurityManager() { @@ -320,13 +327,15 @@ public static void bind(Subject subject) { public static Subject unbindSubject() { return (Subject) remove(SUBJECT_KEY); } - + + @SuppressWarnings("checkstyle:LineLength") private static final class InheritableThreadLocalMap> extends InheritableThreadLocal> { /** * This implementation was added to address a * * user-reported issue. + * * @param parentValue the parent value, a HashMap as defined in the {@link #initialValue()} method. * @return the HashMap to be used by any parent-spawned child threads (a clone of the parent HashMap). */ diff --git a/core/src/test/groovy/org/apache/shiro/config/ogdl/event/LoggingBeanEventListenerTest.groovy b/core/src/test/groovy/org/apache/shiro/config/ogdl/event/LoggingBeanEventListenerTest.groovy index 3a5e354a94..13aa46256f 100644 --- a/core/src/test/groovy/org/apache/shiro/config/ogdl/event/LoggingBeanEventListenerTest.groovy +++ b/core/src/test/groovy/org/apache/shiro/config/ogdl/event/LoggingBeanEventListenerTest.groovy @@ -32,7 +32,7 @@ class LoggingBeanEventListenerTest { @Test void testMisnamedBeanEventClass() { - def m = [foo: 'bar'] as Map + def m = [foo: 'bar'] as Map Object o = new Object() BeanEvent evt = new MisnamedBean('baz', o, m) diff --git a/core/src/test/groovy/org/apache/shiro/lang/codec/H64Test.groovy b/core/src/test/groovy/org/apache/shiro/lang/codec/H64Test.groovy index 6db9bae606..257b6b7a30 100644 --- a/core/src/test/groovy/org/apache/shiro/lang/codec/H64Test.groovy +++ b/core/src/test/groovy/org/apache/shiro/lang/codec/H64Test.groovy @@ -36,7 +36,7 @@ class H64Test { public void testDefault() { byte[] orig = new SecureRandomNumberGenerator().nextBytes(6).bytes - LOG.trace("bytes: $orig");; + LOG.trace("bytes: $orig"); ; String encoded = H64.encodeToString(orig) LOG.trace("encoded: $encoded"); diff --git a/core/src/test/groovy/org/apache/shiro/mgt/DefaultSubjectDAOTest.groovy b/core/src/test/groovy/org/apache/shiro/mgt/DefaultSubjectDAOTest.groovy index 785dac22cf..3820b420bd 100644 --- a/core/src/test/groovy/org/apache/shiro/mgt/DefaultSubjectDAOTest.groovy +++ b/core/src/test/groovy/org/apache/shiro/mgt/DefaultSubjectDAOTest.groovy @@ -115,7 +115,7 @@ class DefaultSubjectDAOTest { expect(subject.getSession(false)).andReturn null //turn off session storage: - ((DefaultSessionStorageEvaluator)dao.sessionStorageEvaluator).sessionStorageEnabled = false + ((DefaultSessionStorageEvaluator) dao.sessionStorageEvaluator).sessionStorageEnabled = false replay subject diff --git a/core/src/test/java/org/apache/shiro/ExceptionTest.java b/core/src/test/java/org/apache/shiro/ExceptionTest.java index 112f4089cd..c9fb1ebaf4 100644 --- a/core/src/test/java/org/apache/shiro/ExceptionTest.java +++ b/core/src/test/java/org/apache/shiro/ExceptionTest.java @@ -23,6 +23,7 @@ /** + * */ @SuppressWarnings({"ThrowableInstanceNeverThrown"}) public abstract class ExceptionTest { diff --git a/core/src/test/java/org/apache/shiro/aop/AnnotationResolverTest.java b/core/src/test/java/org/apache/shiro/aop/AnnotationResolverTest.java index 495d505617..a60f6f3259 100644 --- a/core/src/test/java/org/apache/shiro/aop/AnnotationResolverTest.java +++ b/core/src/test/java/org/apache/shiro/aop/AnnotationResolverTest.java @@ -1,75 +1,78 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.aop; - -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; - -import java.lang.reflect.Method; - -import org.apache.shiro.authz.annotation.RequiresRoles; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.aop; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.lang.reflect.Method; + +import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresUser; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +public class AnnotationResolverTest { + @SuppressWarnings("unused") + @RequiresRoles("root") + private static final class MyFixture { + public void operateThis() { + } + + @RequiresUser() + public void operateThat() { + } + } -public class AnnotationResolverTest { - @SuppressWarnings("unused") - @RequiresRoles("root") - private class MyFixture { - public void operateThis() {} - @RequiresUser() - public void operateThat() {} - } - DefaultAnnotationResolver annotationResolver = new DefaultAnnotationResolver(); @Test - void testAnnotationFoundFromClass() throws SecurityException, NoSuchMethodException { - MyFixture myFixture = new MyFixture(); - MethodInvocation methodInvocation = createMock(MethodInvocation.class); - Method method = MyFixture.class.getDeclaredMethod("operateThis"); - expect(methodInvocation.getMethod()).andReturn(method); - expect(methodInvocation.getThis()).andReturn(myFixture); - replay(methodInvocation); - assertNotNull(annotationResolver.getAnnotation(methodInvocation, RequiresRoles.class)); + void testAnnotationFoundFromClass() throws SecurityException, NoSuchMethodException { + MyFixture myFixture = new MyFixture(); + MethodInvocation methodInvocation = createMock(MethodInvocation.class); + Method method = MyFixture.class.getDeclaredMethod("operateThis"); + expect(methodInvocation.getMethod()).andReturn(method); + expect(methodInvocation.getThis()).andReturn(myFixture); + replay(methodInvocation); + assertNotNull(annotationResolver.getAnnotation(methodInvocation, RequiresRoles.class)); } @Test - void testAnnotationFoundFromMethod() throws SecurityException, NoSuchMethodException { - MethodInvocation methodInvocation = createMock(MethodInvocation.class); - Method method = MyFixture.class.getDeclaredMethod("operateThat"); - expect(methodInvocation.getMethod()).andReturn(method); - replay(methodInvocation); - assertNotNull(annotationResolver.getAnnotation(methodInvocation, RequiresUser.class)); + void testAnnotationFoundFromMethod() throws SecurityException, NoSuchMethodException { + MethodInvocation methodInvocation = createMock(MethodInvocation.class); + Method method = MyFixture.class.getDeclaredMethod("operateThat"); + expect(methodInvocation.getMethod()).andReturn(method); + replay(methodInvocation); + assertNotNull(annotationResolver.getAnnotation(methodInvocation, RequiresUser.class)); } @Test - void testNullMethodInvocation() throws SecurityException, NoSuchMethodException { - MethodInvocation methodInvocation = createMock(MethodInvocation.class); - Method method = MyFixture.class.getDeclaredMethod("operateThis"); - expect(methodInvocation.getMethod()).andReturn(method); - expect(methodInvocation.getThis()).andReturn(null); - replay(methodInvocation); - assertNull(annotationResolver.getAnnotation(methodInvocation, RequiresUser.class)); - } -} - + void testNullMethodInvocation() throws SecurityException, NoSuchMethodException { + MethodInvocation methodInvocation = createMock(MethodInvocation.class); + Method method = MyFixture.class.getDeclaredMethod("operateThis"); + expect(methodInvocation.getMethod()).andReturn(method); + expect(methodInvocation.getThis()).andReturn(null); + replay(methodInvocation); + assertNull(annotationResolver.getAnnotation(methodInvocation, RequiresUser.class)); + } +} + diff --git a/core/src/test/java/org/apache/shiro/authc/AbstractAuthenticatorTest.java b/core/src/test/java/org/apache/shiro/authc/AbstractAuthenticatorTest.java index daadfbfdf9..4811c1cb5d 100644 --- a/core/src/test/java/org/apache/shiro/authc/AbstractAuthenticatorTest.java +++ b/core/src/test/java/org/apache/shiro/authc/AbstractAuthenticatorTest.java @@ -31,8 +31,11 @@ import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; -import static org.junit.jupiter.api.Assertions.*; - +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @since 0.1 @@ -44,9 +47,11 @@ public class AbstractAuthenticatorTest { AbstractAuthenticator abstractAuthenticator; private final SimpleAuthenticationInfo info = new SimpleAuthenticationInfo("user1", "secret", "realmName"); + @SuppressWarnings("checkstyle:LineLength") @BeforeAll static void setUpLogger() { - LoggerContext loggerContext = (LoggerContext) LogManager.getContext(AbstractAuthenticatorTest.class.getClassLoader(), false, URI.create("log4j2-list.xml")); + LoggerContext loggerContext = + (LoggerContext) LogManager.getContext(AbstractAuthenticatorTest.class.getClassLoader(), false, URI.create("log4j2-list.xml")); Configuration configuration = loggerContext.getConfiguration(); listAppender = configuration.getAppender("List"); } @@ -176,6 +181,7 @@ protected AuthenticationInfo doAuthenticate(AuthenticationToken token) throws Au }); } + @SuppressWarnings("checkstyle:LineLength") @Test void logExceptionAfterDoAuthenticateThrowsNonAuthenticationException() { // NOTE: log4j is a test dependency @@ -188,15 +194,15 @@ protected AuthenticationInfo doAuthenticate(AuthenticationToken token) throws Au }; AuthenticationToken token = newToken(); - try{ + try { abstractAuthenticator.authenticate(token); fail("the expected AuthenticationException was not thrown"); - }catch(AuthenticationException expectedException){ + } catch (AuthenticationException ignored) { } String logMsg = String.join("\n", listAppender.getMessages()); assertTrue(logMsg.contains("WARN")); - assertTrue(logMsg.contains("java.lang.IllegalArgumentException: "+ expectedExceptionMessage)); + assertTrue(logMsg.contains("java.lang.IllegalArgumentException: " + expectedExceptionMessage)); } } diff --git a/core/src/test/java/org/apache/shiro/authc/SimpleAuthenticationInfoTest.java b/core/src/test/java/org/apache/shiro/authc/SimpleAuthenticationInfoTest.java index 22c29b402b..fefbdc42da 100644 --- a/core/src/test/java/org/apache/shiro/authc/SimpleAuthenticationInfoTest.java +++ b/core/src/test/java/org/apache/shiro/authc/SimpleAuthenticationInfoTest.java @@ -18,7 +18,6 @@ */ package org.apache.shiro.authc; -import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -53,39 +52,61 @@ void testMergeWithAggregateNullCredentials() { aggregate.merge(local); } - @SuppressWarnings("serial") + @SuppressWarnings({"serial", "checkstyle:AnonInnerLength"}) @Test void testMergeWithImmutablePrincipalCollection() { SimpleAuthenticationInfo aggregate = new SimpleAuthenticationInfo(); - // Make a quick test fixture that does *not* implement MutablePrincipalCollection + // Make a quick test fixture that does *not* implement MutablePrincipalCollection PrincipalCollection principalCollection = new PrincipalCollection() { - @SuppressWarnings("unchecked") - public List asList() { return null;} - @SuppressWarnings("unchecked") - public Set asSet() {return null;} - public Collection byType(Class type) {return null;} - @SuppressWarnings("unchecked") - public Collection fromRealm(String realmName) { - Collection principals = new HashSet(); - principals.add("testprincipal"); - return principals; - } - public Object getPrimaryPrincipal() {return null;} - public Set getRealmNames() { - Set realms = new HashSet(); - realms.add("testrealm"); - return realms; - } - public boolean isEmpty() {return false;} - public T oneByType(Class type) {return null;} - @SuppressWarnings("unchecked") - public Iterator iterator() {return null;} - + @SuppressWarnings("unchecked") + public List asList() { + return null; + } + + @SuppressWarnings("unchecked") + public Set asSet() { + return null; + } + + public Collection byType(Class type) { + return null; + } + + @SuppressWarnings("unchecked") + public Collection fromRealm(String realmName) { + Collection principals = new HashSet(); + principals.add("testprincipal"); + return principals; + } + + public Object getPrimaryPrincipal() { + return null; + } + + public Set getRealmNames() { + Set realms = new HashSet(); + realms.add("testrealm"); + return realms; + } + + public boolean isEmpty() { + return false; + } + + public T oneByType(Class type) { + return null; + } + + @SuppressWarnings("unchecked") + public Iterator iterator() { + return null; + } + }; aggregate.setPrincipals(principalCollection); SimpleAuthenticationInfo local = new SimpleAuthenticationInfo("username", "password", "testRealm"); aggregate.merge(local); assertEquals(2, aggregate.getPrincipals().asList().size()); } - + } diff --git a/core/src/test/java/org/apache/shiro/authc/credential/AbstractHashedCredentialsMatcherTest.java b/core/src/test/java/org/apache/shiro/authc/credential/AbstractHashedCredentialsMatcherTest.java index 38f5596057..50fdb3a58c 100644 --- a/core/src/test/java/org/apache/shiro/authc/credential/AbstractHashedCredentialsMatcherTest.java +++ b/core/src/test/java/org/apache/shiro/authc/credential/AbstractHashedCredentialsMatcherTest.java @@ -21,6 +21,7 @@ import org.apache.shiro.authc.AuthenticationInfo; import static org.junit.jupiter.api.Assertions.assertTrue; + import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UsernamePasswordToken; diff --git a/core/src/test/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategyTest.java b/core/src/test/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategyTest.java index aac94febb3..76785ecb46 100644 --- a/core/src/test/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategyTest.java +++ b/core/src/test/java/org/apache/shiro/authc/pam/FirstSuccessfulStrategyTest.java @@ -24,7 +24,9 @@ import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.SimplePrincipalCollection; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/core/src/test/java/org/apache/shiro/authz/ModularRealmAuthorizerTest.java b/core/src/test/java/org/apache/shiro/authz/ModularRealmAuthorizerTest.java index 15e3ee23c7..3b86168ae0 100644 --- a/core/src/test/java/org/apache/shiro/authz/ModularRealmAuthorizerTest.java +++ b/core/src/test/java/org/apache/shiro/authz/ModularRealmAuthorizerTest.java @@ -33,82 +33,72 @@ import org.apache.shiro.subject.PrincipalCollection; import org.junit.jupiter.api.Test; -public class ModularRealmAuthorizerTest -{ +public class ModularRealmAuthorizerTest { @Test - void testSettingOfRolePermissionResolver() - { + void testSettingOfRolePermissionResolver() { Collection realms = new ArrayList(); - - realms.add( new MockAuthorizingRealm() ); - realms.add( new MockAuthorizingRealm() ); - + + realms.add(new MockAuthorizingRealm()); + realms.add(new MockAuthorizingRealm()); + // its null to start with - for ( Realm realm : realms ) - { - assertNull( ((AuthorizingRealm)realm).getRolePermissionResolver() ); + for (Realm realm : realms) { + assertNull(((AuthorizingRealm) realm).getRolePermissionResolver()); } - + ModularRealmAuthorizer modRealmAuthz = new ModularRealmAuthorizer(); - modRealmAuthz.setRealms( realms ); - + modRealmAuthz.setRealms(realms); + // make sure they are still null - for ( Realm realm : realms ) - { - assertNull( ((AuthorizingRealm)realm).getRolePermissionResolver() ); + for (Realm realm : realms) { + assertNull(((AuthorizingRealm) realm).getRolePermissionResolver()); } - + // now set the RolePermissionResolver - RolePermissionResolver rolePermissionResolver = new RolePermissionResolver() - { - public Collection resolvePermissionsInRole( String roleString ) - { + RolePermissionResolver rolePermissionResolver = new RolePermissionResolver() { + public Collection resolvePermissionsInRole(String roleString) { return null; } }; - modRealmAuthz.setRolePermissionResolver( rolePermissionResolver ); - + modRealmAuthz.setRolePermissionResolver(rolePermissionResolver); + // make sure they are set - for ( Realm realm : realms ) - { + for (Realm realm : realms) { // check for same instance - assertTrue( ((AuthorizingRealm)realm).getRolePermissionResolver() == rolePermissionResolver ); + assertTrue(((AuthorizingRealm) realm).getRolePermissionResolver() == rolePermissionResolver); } - + // add a new realm and make sure the RolePermissionResolver is set MockAuthorizingRealm mockRealm = new MockAuthorizingRealm(); - realms.add( mockRealm ); - modRealmAuthz.setRealms( realms ); - assertTrue( ((AuthorizingRealm) mockRealm).getRolePermissionResolver() == rolePermissionResolver ); - - - // TODO: no way to unset them, not sure if that is a valid use case, but this is consistent with the PermissionResolver logic + realms.add(mockRealm); + modRealmAuthz.setRealms(realms); + assertTrue(((AuthorizingRealm) mockRealm).getRolePermissionResolver() == rolePermissionResolver); + + +// TODO: no way to unset them, not sure if that is a valid use case, but this is consistent with the PermissionResolver logic // // now just to be sure, unset them // modRealmAuthz.setRolePermissionResolver( null ); // for ( Realm realm : realms ) // { // assertNull( ((AuthorizingRealm)realm).getRolePermissionResolver() ); // } - - + + } - - class MockAuthorizingRealm extends AuthorizingRealm - { + + class MockAuthorizingRealm extends AuthorizingRealm { @Override - protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals ) - { + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; } @Override - protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token ) - throws AuthenticationException - { + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) + throws AuthenticationException { return null; } - + } } diff --git a/core/src/test/java/org/apache/shiro/authz/aop/PermissionAnnotationHandlerTest.java b/core/src/test/java/org/apache/shiro/authz/aop/PermissionAnnotationHandlerTest.java index 50294b7a3f..9bad76553e 100644 --- a/core/src/test/java/org/apache/shiro/authz/aop/PermissionAnnotationHandlerTest.java +++ b/core/src/test/java/org/apache/shiro/authz/aop/PermissionAnnotationHandlerTest.java @@ -42,7 +42,7 @@ public void testGuestSinglePermissionAssertion() { Annotation requiresPermissionAnnotation = new RequiresPermissions() { @Override public String[] value() { - return new String[]{"test:test"}; + return new String[] {"test:test"}; } @Override @@ -50,10 +50,10 @@ public Class annotationType() { return RequiresPermissions.class; } - @Override - public Logical logical() { - return Logical.AND; - } + @Override + public Logical logical() { + return Logical.AND; + } }; runWithSubject(subject -> { @@ -70,18 +70,18 @@ public void testGuestMultiplePermissionAssertion() { Annotation requiresPermissionAnnotation = new RequiresPermissions() { @Override public String[] value() { - return new String[]{"test:test", "test2:test2"}; + return new String[] {"test:test", "test2:test2"}; } @Override public Class annotationType() { return RequiresPermissions.class; } - - @Override - public Logical logical() { - return Logical.AND; - } + + @Override + public Logical logical() { + return Logical.AND; + } }; runWithSubject(subject -> { diff --git a/core/src/test/java/org/apache/shiro/authz/aop/PermitAllAnnotationHandlerTest.java b/core/src/test/java/org/apache/shiro/authz/aop/PermitAllAnnotationHandlerTest.java index 5363df346b..cf28a64559 100644 --- a/core/src/test/java/org/apache/shiro/authz/aop/PermitAllAnnotationHandlerTest.java +++ b/core/src/test/java/org/apache/shiro/authz/aop/PermitAllAnnotationHandlerTest.java @@ -18,7 +18,6 @@ */ package org.apache.shiro.authz.aop; -import org.apache.shiro.subject.Subject; import org.apache.shiro.test.SecurityManagerTestSupport; import org.junit.jupiter.api.Test; diff --git a/core/src/test/java/org/apache/shiro/authz/aop/RoleAnnotationHandlerTest.java b/core/src/test/java/org/apache/shiro/authz/aop/RoleAnnotationHandlerTest.java index 0f34001a4c..6c3fdcbcc1 100644 --- a/core/src/test/java/org/apache/shiro/authz/aop/RoleAnnotationHandlerTest.java +++ b/core/src/test/java/org/apache/shiro/authz/aop/RoleAnnotationHandlerTest.java @@ -47,7 +47,7 @@ public void testGuestSingleRoleAssertion() throws Throwable { Annotation requiresRolesAnnotation = new RequiresRoles() { @Override public String[] value() { - return new String[]{"blah"}; + return new String[] {"blah"}; } @Override @@ -75,7 +75,7 @@ public void testGuestMultipleRolesAssertion() throws Throwable { Annotation requiresRolesAnnotation = new RequiresRoles() { @Override public String[] value() { - return new String[]{"blah", "blah2"}; + return new String[] {"blah", "blah2"}; } @Override @@ -110,7 +110,7 @@ protected Subject getSubject() { Annotation requiresRolesAnnotation = new RequiresRoles() { @Override public String[] value() { - return new String[]{"blah", "blah2"}; + return new String[] {"blah", "blah2"}; } @Override diff --git a/core/src/test/java/org/apache/shiro/authz/aop/RolesAllowedAnnotationHandlerTest.java b/core/src/test/java/org/apache/shiro/authz/aop/RolesAllowedAnnotationHandlerTest.java index f642ba2051..9dec9db459 100644 --- a/core/src/test/java/org/apache/shiro/authz/aop/RolesAllowedAnnotationHandlerTest.java +++ b/core/src/test/java/org/apache/shiro/authz/aop/RolesAllowedAnnotationHandlerTest.java @@ -53,7 +53,7 @@ protected Subject getSubject() { Annotation rolesAllowedAnnotation = new RolesAllowed() { @Override public String[] value() { - return new String[]{"blah"}; + return new String[] {"blah"}; } @Override @@ -85,7 +85,7 @@ protected Subject getSubject() { Annotation rolesAllowedAnnotation = new RolesAllowed() { @Override public String[] value() { - return new String[]{"blah", "blah2"}; + return new String[] {"blah", "blah2"}; } @Override @@ -114,7 +114,7 @@ protected Subject getSubject() { Annotation rolesAllowedAnnotation = new RolesAllowed() { @Override public String[] value() { - return new String[]{"blah", "blah2"}; + return new String[] {"blah", "blah2"}; } @Override diff --git a/core/src/test/java/org/apache/shiro/authz/permission/AllPermissionTest.java b/core/src/test/java/org/apache/shiro/authz/permission/AllPermissionTest.java index c4aab8127a..f9c9685dd3 100644 --- a/core/src/test/java/org/apache/shiro/authz/permission/AllPermissionTest.java +++ b/core/src/test/java/org/apache/shiro/authz/permission/AllPermissionTest.java @@ -24,6 +24,7 @@ /** + * */ public class AllPermissionTest { diff --git a/core/src/test/java/org/apache/shiro/authz/permission/DomainPermissionTest.java b/core/src/test/java/org/apache/shiro/authz/permission/DomainPermissionTest.java index e253594299..b5faee8205 100644 --- a/core/src/test/java/org/apache/shiro/authz/permission/DomainPermissionTest.java +++ b/core/src/test/java/org/apache/shiro/authz/permission/DomainPermissionTest.java @@ -24,7 +24,10 @@ import java.util.List; import java.util.Set; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * @since 1.3 diff --git a/core/src/test/java/org/apache/shiro/authz/permission/WildcardPermissionResolverTest.java b/core/src/test/java/org/apache/shiro/authz/permission/WildcardPermissionResolverTest.java index 66a5b17c47..4e4b151e47 100644 --- a/core/src/test/java/org/apache/shiro/authz/permission/WildcardPermissionResolverTest.java +++ b/core/src/test/java/org/apache/shiro/authz/permission/WildcardPermissionResolverTest.java @@ -20,52 +20,50 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class WildcardPermissionResolverTest { @Test - void testDefaultIsNonCaseSensitive() - { + void testDefaultIsNonCaseSensitive() { WildcardPermissionResolver resolver = new WildcardPermissionResolver(); - assertFalse( resolver.isCaseSensitive(), "Default sensitivity should be false"); - /* this is a round-about test as permissions don't store case sensitivity just lower case + assertFalse(resolver.isCaseSensitive(), "Default sensitivity should be false"); + /* this is a round-about test as permissions don't store case sensitivity just lower case the string. */ - WildcardPermission permission = (WildcardPermission) resolver.resolvePermission( "Foo:*" ); - assertEquals( "foo:*", permission.toString(), "string should be lowercase"); + WildcardPermission permission = (WildcardPermission) resolver.resolvePermission("Foo:*"); + assertEquals("foo:*", permission.toString(), "string should be lowercase"); } @Test - void testCaseSensitive() - { + void testCaseSensitive() { WildcardPermissionResolver resolver = new WildcardPermissionResolver(true); - assertTrue( resolver.isCaseSensitive(), "Sensitivity should be true"); - /* this is a round-about test as permissions don't store case sensitivity just lower case + assertTrue(resolver.isCaseSensitive(), "Sensitivity should be true"); + /* this is a round-about test as permissions don't store case sensitivity just lower case the string. */ - WildcardPermission permission = (WildcardPermission) resolver.resolvePermission( "Foo:*" ); - assertEquals( "Foo:*", permission.toString(), "string should be mixed case"); + WildcardPermission permission = (WildcardPermission) resolver.resolvePermission("Foo:*"); + assertEquals("Foo:*", permission.toString(), "string should be mixed case"); } @Test - void testCaseInsensitive() - { + void testCaseInsensitive() { WildcardPermissionResolver resolver = new WildcardPermissionResolver(false); - assertFalse( resolver.isCaseSensitive(), "Sensitivity should be false"); - /* this is a round-about test as permissions don't store case sensitivity just lower case + assertFalse(resolver.isCaseSensitive(), "Sensitivity should be false"); + /* this is a round-about test as permissions don't store case sensitivity just lower case the string. */ - WildcardPermission permission = (WildcardPermission) resolver.resolvePermission( "Foo:*" ); - assertEquals( "foo:*", permission.toString(), "string should be lowercase"); + WildcardPermission permission = (WildcardPermission) resolver.resolvePermission("Foo:*"); + assertEquals("foo:*", permission.toString(), "string should be lowercase"); } @Test - void testCaseSensitiveToggle() - { + void testCaseSensitiveToggle() { WildcardPermissionResolver resolver = new WildcardPermissionResolver(); - assertFalse( resolver.isCaseSensitive(), "Default sensitivity should be false"); - resolver.setCaseSensitive( true ); - assertTrue( resolver.isCaseSensitive(), "Sensitivity should be true"); - resolver.setCaseSensitive( false ); - assertFalse( resolver.isCaseSensitive(), "Sensitivity should be false"); + assertFalse(resolver.isCaseSensitive(), "Default sensitivity should be false"); + resolver.setCaseSensitive(true); + assertTrue(resolver.isCaseSensitive(), "Sensitivity should be true"); + resolver.setCaseSensitive(false); + assertFalse(resolver.isCaseSensitive(), "Sensitivity should be false"); } } diff --git a/core/src/test/java/org/apache/shiro/authz/permission/WildcardPermissionTest.java b/core/src/test/java/org/apache/shiro/authz/permission/WildcardPermissionTest.java index b619351d7b..b7914b9c2c 100644 --- a/core/src/test/java/org/apache/shiro/authz/permission/WildcardPermissionTest.java +++ b/core/src/test/java/org/apache/shiro/authz/permission/WildcardPermissionTest.java @@ -20,8 +20,10 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * @since 0.9 @@ -56,6 +58,7 @@ void testOnlyDelimiters() { }); } + @SuppressWarnings("checkstyle:MultipleVariableDeclarations") @Test void testNamed() { WildcardPermission p1, p2; @@ -98,6 +101,7 @@ void testNamed() { } + @SuppressWarnings("checkstyle:MultipleVariableDeclarations") @Test void testLists() { WildcardPermission p1, p2, p3; @@ -144,9 +148,10 @@ void testListDifferentOrder() { WildcardPermission p6 = new WildcardPermission("one,two:three,four"); WildcardPermission p6DiffOrder = new WildcardPermission("two,one:four,three"); - assertTrue(p6.equals(p6DiffOrder)); + assertEquals(p6, p6DiffOrder); } + @SuppressWarnings({"checkstyle:MultipleVariableDeclarations", "checkstyle:MethodLength"}) @Test void testWildcards() { WildcardPermission p1, p2, p3, p4, p5, p6, p7, p8, p9; @@ -236,18 +241,19 @@ void testToString() { WildcardPermission p4 = new WildcardPermission("one,two:three,four"); WildcardPermission p5 = new WildcardPermission("one,two:three,four,five:six:seven,eight"); - assertTrue("*".equals(p1.toString())); - assertTrue(p1.equals(new WildcardPermission(p1.toString()))); - assertTrue("one".equals(p2.toString())); - assertTrue(p2.equals(new WildcardPermission(p2.toString()))); - assertTrue("one:two".equals(p3.toString())); - assertTrue(p3.equals(new WildcardPermission(p3.toString()))); - assertTrue("one,two:three,four".equals(p4.toString())); - assertTrue(p4.equals(new WildcardPermission(p4.toString()))); - assertTrue("one,two:three,four,five:six:seven,eight".equals(p5.toString())); - assertTrue(p5.equals(new WildcardPermission(p5.toString()))); + assertEquals("*", p1.toString()); + assertEquals(p1, new WildcardPermission(p1.toString())); + assertEquals("one", p2.toString()); + assertEquals(p2, new WildcardPermission(p2.toString())); + assertEquals("one:two", p3.toString()); + assertEquals(p3, new WildcardPermission(p3.toString())); + assertEquals("one,two:three,four", p4.toString()); + assertEquals(p4, new WildcardPermission(p4.toString())); + assertEquals("one,two:three,four,five:six:seven,eight", p5.toString()); + assertEquals(p5, new WildcardPermission(p5.toString())); } + @SuppressWarnings("checkstyle:MultipleVariableDeclarations") @Test void testWildcardLeftTermination() { WildcardPermission p1, p2, p3, p4; @@ -273,4 +279,4 @@ void testWildcardLeftTermination() { assertFalse(p4.implies(p2)); assertFalse(p4.implies(p3)); } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/apache/shiro/concurrent/SubjectAwareExecutorServiceTest.java b/core/src/test/java/org/apache/shiro/concurrent/SubjectAwareExecutorServiceTest.java index 3b8e5ee66a..0f31f35f86 100644 --- a/core/src/test/java/org/apache/shiro/concurrent/SubjectAwareExecutorServiceTest.java +++ b/core/src/test/java/org/apache/shiro/concurrent/SubjectAwareExecutorServiceTest.java @@ -56,7 +56,7 @@ public void testSubmitRunnable() { }); } - private static class DummyFuture implements Future { + private static final class DummyFuture implements Future { @Override public boolean cancel(boolean b) { diff --git a/core/src/test/java/org/apache/shiro/config/HashMapCacheManager.java b/core/src/test/java/org/apache/shiro/config/HashMapCacheManager.java index c1c0895a83..853d1e7220 100644 --- a/core/src/test/java/org/apache/shiro/config/HashMapCacheManager.java +++ b/core/src/test/java/org/apache/shiro/config/HashMapCacheManager.java @@ -31,6 +31,7 @@ * * @since 1.0 */ +@SuppressWarnings("checkstyle:JavadocType") public class HashMapCacheManager extends AbstractCacheManager { @Override diff --git a/core/src/test/java/org/apache/shiro/lang/io/SerializationExceptionTest.java b/core/src/test/java/org/apache/shiro/lang/io/SerializationExceptionTest.java index 29340fe804..29fe20d1fc 100644 --- a/core/src/test/java/org/apache/shiro/lang/io/SerializationExceptionTest.java +++ b/core/src/test/java/org/apache/shiro/lang/io/SerializationExceptionTest.java @@ -22,6 +22,7 @@ /** + * */ public class SerializationExceptionTest extends ExceptionTest { diff --git a/core/src/test/java/org/apache/shiro/mgt/AbstractRememberMeManagerTest.java b/core/src/test/java/org/apache/shiro/mgt/AbstractRememberMeManagerTest.java index 236fe3a4b8..ec14a82984 100644 --- a/core/src/test/java/org/apache/shiro/mgt/AbstractRememberMeManagerTest.java +++ b/core/src/test/java/org/apache/shiro/mgt/AbstractRememberMeManagerTest.java @@ -56,6 +56,7 @@ protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) assertNull(principals); } + @SuppressWarnings("checkstyle:FinalClass") private static class DummyRememberMeManager extends AbstractRememberMeManager { public void forgetIdentity(SubjectContext subjectContext) { //do nothing diff --git a/core/src/test/java/org/apache/shiro/mgt/DefaultSecurityManagerTest.java b/core/src/test/java/org/apache/shiro/mgt/DefaultSecurityManagerTest.java index 064d9ce968..efa7153674 100644 --- a/core/src/test/java/org/apache/shiro/mgt/DefaultSecurityManagerTest.java +++ b/core/src/test/java/org/apache/shiro/mgt/DefaultSecurityManagerTest.java @@ -36,14 +36,19 @@ import java.io.Serializable; -import static org.junit.jupiter.api.Assertions.*; - +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; /** * @since 0.2 */ public class DefaultSecurityManagerTest extends AbstractSecurityManagerTest { + @SuppressWarnings("checkstyle:ExplicitInitialization") DefaultSecurityManager sm = null; @BeforeEach @@ -71,7 +76,7 @@ void testDefaultConfig() { AuthenticationToken token = new UsernamePasswordToken("guest", "guest"); subject.login(token); assertTrue(subject.isAuthenticated()); - assertTrue("guest".equals(subject.getPrincipal())); + assertEquals("guest", subject.getPrincipal()); assertTrue(subject.hasRole("guest")); Session session = subject.getSession(); @@ -89,11 +94,11 @@ void testDefaultConfig() { * Test that validates functionality for issue * JSEC-46 */ + @SuppressWarnings("checkstyle:MagicNumber") @Test void testAutoCreateSessionAfterInvalidation() { Subject subject = SecurityUtils.getSubject(); Session session = subject.getSession(); - Serializable origSessionId = session.getId(); String key = "foo"; String value1 = "bar"; @@ -126,7 +131,7 @@ void testSubjectReuseAfterLogout() { AuthenticationToken token = new UsernamePasswordToken("guest", "guest"); subject.login(token); assertTrue(subject.isAuthenticated()); - assertTrue("guest".equals(subject.getPrincipal())); + assertEquals("guest", subject.getPrincipal()); assertTrue(subject.hasRole("guest")); Session session = subject.getSession(); @@ -143,11 +148,11 @@ void testSubjectReuseAfterLogout() { subject.login(new UsernamePasswordToken("lonestarr", "vespa")); assertTrue(subject.isAuthenticated()); - assertTrue("lonestarr".equals(subject.getPrincipal())); + assertEquals("lonestarr", subject.getPrincipal()); assertTrue(subject.hasRole("goodguy")); assertNotNull(subject.getSession()); - assertFalse(firstSessionId.equals(subject.getSession().getId())); + assertNotEquals(firstSessionId, subject.getSession().getId()); subject.logout(); @@ -176,7 +181,7 @@ void testNewSubjectWithoutThreadSecurityManager() { // Specify sm to use and build subject with DelegatingSubject subject = - (DelegatingSubject)(new Subject.Builder(sm)).buildSubject(); + (DelegatingSubject) (new Subject.Builder(sm)).buildSubject(); // Login and verify specified sm is used and no error thrown AuthenticationToken token = new UsernamePasswordToken("guest", "guest"); diff --git a/core/src/test/java/org/apache/shiro/realm/AuthorizingRealmTest.java b/core/src/test/java/org/apache/shiro/realm/AuthorizingRealmTest.java index d53c9f36af..3f6d4f9cfb 100644 --- a/core/src/test/java/org/apache/shiro/realm/AuthorizingRealmTest.java +++ b/core/src/test/java/org/apache/shiro/realm/AuthorizingRealmTest.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; + import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; @@ -44,7 +45,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.fail; /** @@ -54,13 +59,13 @@ */ public class AuthorizingRealmTest { - AuthorizingRealm realm; - private static final String USERNAME = "testuser"; private static final String PASSWORD = "password"; private static final int USER_ID = 12345; private static final String ROLE = "admin"; - private String localhost = "localhost"; + private final String localhost = "localhost"; + + private AuthorizingRealm realm; @BeforeEach public void setup() { @@ -84,13 +89,13 @@ void testDefaultConfig() { assertTrue(principal instanceof UserIdPrincipal); UsernamePrincipal usernamePrincipal = info.getPrincipals().oneByType(UsernamePrincipal.class); - assertTrue(usernamePrincipal.getUsername().equals(USERNAME)); + assertEquals(USERNAME, usernamePrincipal.getUsername()); UserIdPrincipal userIdPrincipal = info.getPrincipals().oneByType(UserIdPrincipal.class); - assertTrue(userIdPrincipal.getUserId() == USER_ID); + assertEquals(USER_ID, userIdPrincipal.getUserId()); String stringPrincipal = info.getPrincipals().oneByType(String.class); - assertTrue(stringPrincipal.equals(USER_ID + USERNAME)); + assertEquals(USER_ID + USERNAME, stringPrincipal); } @Test @@ -115,9 +120,10 @@ protected AuthenticationInfo buildAuthenticationInfo(Object principal, Object cr } + @SuppressWarnings("checkstyle:MethodLength") @Test void testNullAuthzInfo() { - AuthorizingRealm realm = new AuthorizingRealm() { + AuthorizingRealm realm = new AuthorizingRealm() { protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; } @@ -186,42 +192,38 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) assertFalse(realm.hasAllRoles(pCollection, roleList)); assertFalse(realm.hasRole(pCollection, "role1")); - assertArrayEquals(new boolean[]{false, false}, realm.hasRoles(pCollection, roleList)); + assertArrayEquals(new boolean[] {false, false}, realm.hasRoles(pCollection, roleList)); assertFalse(realm.isPermitted(pCollection, "perm1")); assertFalse(realm.isPermitted(pCollection, new WildcardPermission("perm1"))); - assertArrayEquals(new boolean[]{false, false}, realm.isPermitted(pCollection, "perm1", "perm2")); - assertArrayEquals(new boolean[]{false, false}, realm.isPermitted(pCollection, permList)); + assertArrayEquals(new boolean[] {false, false}, realm.isPermitted(pCollection, "perm1", "perm2")); + assertArrayEquals(new boolean[] {false, false}, realm.isPermitted(pCollection, permList)); assertFalse(realm.isPermittedAll(pCollection, "perm1", "perm2")); assertFalse(realm.isPermittedAll(pCollection, permList)); } @Test - void testRealmWithRolePermissionResolver() - { + void testRealmWithRolePermissionResolver() { Principal principal = new UsernamePrincipal("rolePermResolver"); PrincipalCollection pCollection = new SimplePrincipalCollection(principal, "testRealmWithRolePermissionResolver"); - + AuthorizingRealm realm = new AllowAllRealm(); - realm.setRolePermissionResolver( new RolePermissionResolver() - { - public Collection resolvePermissionsInRole( String roleString ) - { + realm.setRolePermissionResolver(new RolePermissionResolver() { + public Collection resolvePermissionsInRole(String roleString) { Collection permissions = new HashSet(); - if( roleString.equals( ROLE )) - { - permissions.add( new WildcardPermission( ROLE + ":perm1" ) ); - permissions.add( new WildcardPermission( ROLE + ":perm2" ) ); - permissions.add( new WildcardPermission( "other:*:foo" ) ); + if (roleString.equals(ROLE)) { + permissions.add(new WildcardPermission(ROLE + ":perm1")); + permissions.add(new WildcardPermission(ROLE + ":perm2")); + permissions.add(new WildcardPermission("other:*:foo")); } return permissions; } }); - - assertTrue( realm.hasRole( pCollection, ROLE ) ); - assertTrue( realm.isPermitted( pCollection, ROLE + ":perm1" ) ); - assertTrue( realm.isPermitted( pCollection, ROLE + ":perm2" ) ); - assertFalse( realm.isPermitted( pCollection, ROLE + ":perm3" ) ); - assertTrue( realm.isPermitted( pCollection, "other:bar:foo" ) ); + + assertTrue(realm.hasRole(pCollection, ROLE)); + assertTrue(realm.isPermitted(pCollection, ROLE + ":perm1")); + assertTrue(realm.isPermitted(pCollection, ROLE + ":perm2")); + assertFalse(realm.isPermitted(pCollection, ROLE + ":perm3")); + assertTrue(realm.isPermitted(pCollection, "other:bar:foo")); } @Test @@ -230,18 +232,15 @@ void testRealmWithEmptyOrNullPermissions() { PrincipalCollection pCollection = new SimplePrincipalCollection(principal, "testRealmWithRolePermissionResolver"); AuthorizingRealm realm = new AllowAllRealm(); - realm.setRolePermissionResolver( new RolePermissionResolver() - { - public Collection resolvePermissionsInRole( String roleString ) - { + realm.setRolePermissionResolver(new RolePermissionResolver() { + public Collection resolvePermissionsInRole(String roleString) { Collection permissions = new HashSet(); - if( roleString.equals( ROLE )) - { - permissions.add( new WildcardPermission( ROLE + ":perm1" ) ); - permissions.add( new WildcardPermission( ROLE + ":perm2" ) ); - permissions.add( new WildcardPermission( ROLE + ": " ) ); - permissions.add( new WildcardPermission( ROLE + ":\t" ) ); - permissions.add( new WildcardPermission( "other:*:foo" ) ); + if (roleString.equals(ROLE)) { + permissions.add(new WildcardPermission(ROLE + ":perm1")); + permissions.add(new WildcardPermission(ROLE + ":perm2")); + permissions.add(new WildcardPermission(ROLE + ": ")); + permissions.add(new WildcardPermission(ROLE + ":\t")); + permissions.add(new WildcardPermission("other:*:foo")); } return permissions; } @@ -269,7 +268,7 @@ private void assertArrayEquals(boolean[] expected, boolean[] actual) { } } - public class AllowAllRealm extends AuthorizingRealm { + public static class AllowAllRealm extends AuthorizingRealm { public AllowAllRealm() { super(); diff --git a/core/src/test/java/org/apache/shiro/realm/UserIdPrincipal.java b/core/src/test/java/org/apache/shiro/realm/UserIdPrincipal.java index 09d2f49670..1ddc709317 100644 --- a/core/src/test/java/org/apache/shiro/realm/UserIdPrincipal.java +++ b/core/src/test/java/org/apache/shiro/realm/UserIdPrincipal.java @@ -36,4 +36,4 @@ public int getUserId() { public String getName() { return String.valueOf(userId); } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/apache/shiro/realm/activedirectory/ActiveDirectoryRealmTest.java b/core/src/test/java/org/apache/shiro/realm/activedirectory/ActiveDirectoryRealmTest.java index 4fe9e415e5..2180f1f8c1 100644 --- a/core/src/test/java/org/apache/shiro/realm/activedirectory/ActiveDirectoryRealmTest.java +++ b/core/src/test/java/org/apache/shiro/realm/activedirectory/ActiveDirectoryRealmTest.java @@ -19,7 +19,12 @@ package org.apache.shiro.realm.activedirectory; import org.apache.shiro.SecurityUtils; -import org.apache.shiro.authc.*; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAccount; +import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; @@ -55,7 +60,9 @@ import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; + +import static org.hamcrest.Matchers.arrayWithSize; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -66,18 +73,18 @@ * heart of ActiveDirectoryRealm (no meaningful implementation of queryForLdapAccount, etc.) it obviously should. * This version was intended to mimic my current usage scenario in an effort to debug upgrade issues which were not related * to LDAP connectivity. - * */ public class ActiveDirectoryRealmTest { - DefaultSecurityManager securityManager = null; - AuthorizingRealm realm; - private static final String USERNAME = "testuser"; private static final String PASSWORD = "password"; private static final int USER_ID = 12345; private static final String ROLE = "admin"; + @SuppressWarnings("checkstyle:ExplicitInitialization") + DefaultSecurityManager securityManager = null; + AuthorizingRealm realm; + @BeforeEach public void setup() { ThreadContext.remove(); @@ -115,13 +122,15 @@ void testDefaultConfig() { @Test void testExistingUserSuffix() throws Exception { - assertExistingUserSuffix(USERNAME, "testuser@ExAmple.COM"); // suffix case matches configure suffix + // suffix case matches configure suffix + assertExistingUserSuffix(USERNAME, "testuser@ExAmple.COM"); // suffix matches user entry assertExistingUserSuffix(USERNAME + "@example.com", "testuser@example.com"); assertExistingUserSuffix(USERNAME + "@EXAMPLE.com", "testuser@EXAMPLE.com"); } + @SuppressWarnings("checkstyle:LineLength") public void assertExistingUserSuffix(String username, String expectedPrincipalName) throws Exception { LdapContext ldapContext = createMock(LdapContext.class); @@ -150,7 +159,7 @@ public void assertExistingUserSuffix(String username, String expectedPrincipalNa assertThat(args[0], is(expectedPrincipalName)); } - public class TestActiveDirectoryRealm extends ActiveDirectoryRealm { + public static class TestActiveDirectoryRealm extends ActiveDirectoryRealm { /*-------------------------------------------- | C O N S T R U C T O R S | @@ -195,10 +204,11 @@ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal } // override ldap query because i don't care about testing that piece in this case - protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, LdapContextFactory ldapContextFactory) throws NamingException { + protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token, LdapContextFactory ldapContextFactory) + throws NamingException { return new SimpleAccount(token.getPrincipal(), token.getCredentials(), getName()); } } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/apache/shiro/realm/jdbc/JDBCRealmTest.java b/core/src/test/java/org/apache/shiro/realm/jdbc/JDBCRealmTest.java index 04f9110835..6516b98611 100644 --- a/core/src/test/java/org/apache/shiro/realm/jdbc/JDBCRealmTest.java +++ b/core/src/test/java/org/apache/shiro/realm/jdbc/JDBCRealmTest.java @@ -36,9 +36,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInfo; import javax.sql.DataSource; @@ -56,21 +54,23 @@ */ public class JDBCRealmTest { + @SuppressWarnings("checkstyle:ExplicitInitialization") protected DefaultSecurityManager securityManager = null; protected AuthorizingRealm realm; protected final String username = "testUser"; protected final String plainTextPassword = "testPassword"; - protected final String salt = username; //Default impl of getSaltForUser returns username + //Default impl of getSaltForUser returns username + protected final String salt = username; protected final String testRole = "testRole"; protected final String testPermissionString = "testDomain:testTarget:testAction"; - + // Maps keyed on test method name so setup/teardown can manage per test resources protected HashMap realmMap = new HashMap(); protected HashMap dsMap = new HashMap(); - - public String name; + private String name; + @SuppressWarnings("checkstyle:LineLength") @BeforeEach public void setup(TestInfo testInfo) { Optional testMethod = testInfo.getTestMethod(); @@ -89,7 +89,7 @@ public void setup(TestInfo testInfo) { SecurityUtils.setSecurityManager(securityManager); // Create a database and realm for the test - createRealm( name); + createRealm(name); } @AfterEach @@ -107,7 +107,7 @@ void testUnSaltedSuccess() throws Exception { JdbcRealm realm = realmMap.get(testMethodName); createDefaultSchema(testMethodName, false); realm.setSaltStyle(JdbcRealm.SaltStyle.NO_SALT); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, plainTextPassword); @@ -121,7 +121,7 @@ void testUnSaltedWrongPassword() throws Exception { JdbcRealm realm = realmMap.get(testMethodName); createDefaultSchema(testMethodName, false); realm.setSaltStyle(JdbcRealm.SaltStyle.NO_SALT); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, "passwrd"); @@ -141,7 +141,7 @@ void testUnSaltedMultipleRows() throws Exception { Connection conn = dsMap.get(testMethodName).getConnection(); Statement sql = conn.createStatement(); sql.executeUpdate("insert into users values ('" + username + "', 'dupe')"); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, "passwrd"); @@ -159,7 +159,7 @@ void testSaltColumnSuccess() throws Exception { createSaltColumnSchema(testMethodName, false); realm.setSaltStyle(JdbcRealm.SaltStyle.COLUMN); realm.setSaltIsBase64Encoded(false); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, plainTextPassword); @@ -188,7 +188,7 @@ void testSaltColumnWrongPassword() throws Exception { createSaltColumnSchema(testMethodName, false); realm.setSaltStyle(JdbcRealm.SaltStyle.COLUMN); realm.setSaltIsBase64Encoded(false); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, "passwrd"); @@ -223,7 +223,7 @@ void testExternalSuccess() throws Exception { JdbcRealm realm = realmMap.get(testMethodName); createDefaultSchema(testMethodName, true); realm.setSaltStyle(JdbcRealm.SaltStyle.EXTERNAL); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, plainTextPassword); @@ -237,7 +237,7 @@ void testExternalWrongPassword() throws Exception { JdbcRealm realm = realmMap.get(testMethodName); createDefaultSchema(testMethodName, true); realm.setSaltStyle(JdbcRealm.SaltStyle.EXTERNAL); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, "passwrd"); @@ -254,7 +254,7 @@ void testRolePresent() throws Exception { JdbcRealm realm = realmMap.get(testMethodName); createDefaultSchema(testMethodName, false); realm.setSaltStyle(JdbcRealm.SaltStyle.NO_SALT); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, plainTextPassword); @@ -268,7 +268,7 @@ void testRoleNotPresent() throws Exception { JdbcRealm realm = realmMap.get(testMethodName); createDefaultSchema(testMethodName, false); realm.setSaltStyle(JdbcRealm.SaltStyle.NO_SALT); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, plainTextPassword); @@ -283,7 +283,7 @@ void testPermissionPresent() throws Exception { createDefaultSchema(testMethodName, false); realm.setSaltStyle(JdbcRealm.SaltStyle.NO_SALT); realm.setPermissionsLookupEnabled(true); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, plainTextPassword); @@ -298,14 +298,14 @@ void testPermissionNotPresent() throws Exception { createDefaultSchema(testMethodName, false); realm.setSaltStyle(JdbcRealm.SaltStyle.NO_SALT); realm.setPermissionsLookupEnabled(true); - + Subject.Builder builder = new Subject.Builder(securityManager); Subject currentUser = builder.buildSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, plainTextPassword); currentUser.login(token); Assertions.assertFalse(currentUser.isPermitted("testDomain:testTarget:specialAction")); } - + /** * Creates a realm for a test method and puts it in the realMap. */ @@ -313,7 +313,7 @@ protected void createRealm(String testMethodName) { JdbcRealm realm = (JdbcRealm) securityManager.getRealms().iterator().next(); realmMap.put(testMethodName, realm); } - + /** * Shuts down the database and removes the realm from the realm map. */ @@ -334,7 +334,7 @@ protected void shutDown(String testName) { realmMap.remove(testName); } } - + /** * Creates a test database with the default (no separate salt column) schema, salting with * username if salted is true. Sets the DataSource of the realm associated with the test @@ -352,8 +352,8 @@ protected void createDefaultSchema(String testName, boolean salted) { conn = ds.getConnection(); sql = conn.createStatement(); sql.executeUpdate("create table users (username varchar(20), password varchar(100))"); - Sha256Hash sha256Hash = salted ? new Sha256Hash(plainTextPassword, salt) : - new Sha256Hash(plainTextPassword); + Sha256Hash sha256Hash = salted ? new Sha256Hash(plainTextPassword, salt) + : new Sha256Hash(plainTextPassword); String password = sha256Hash.toHex(); sql.executeUpdate("insert into users values ('" + username + "', '" + password + "')"); } catch (SQLException ex) { @@ -366,12 +366,13 @@ protected void createDefaultSchema(String testName, boolean salted) { realmMap.get(testName).setDataSource(ds); dsMap.put(testName, ds); } - + /** * Creates a test database with a separate salt column in the users table. Sets the * DataSource of the realm associated with the test to a DataSource connected to the database. - * @param The name of the test which is used as the key when saving the created realm in the realmMap - * @param base64EncodeSalt if true, the salt will be base64 encoded before it's stored in the database + * + * @param testName name of the test which is used as the key when saving the created realm in the realmMap + * @param base64EncodeSalt if true, the salt will be base64 encoded before it's stored in the database */ protected void createSaltColumnSchema(String testName, boolean base64EncodeSalt) { JDBCDataSource ds = new JDBCDataSource(); @@ -388,7 +389,9 @@ protected void createSaltColumnSchema(String testName, boolean base64EncodeSalt) Sha256Hash sha256Hash = new Sha256Hash(plainTextPassword, salt); String password = sha256Hash.toHex(); String maybeBase64EncodedSalt = base64EncodeSalt ? Base64.encodeToString(CodecSupport.toBytes(salt)) : salt; - sql.executeUpdate("insert into users values ('" + username + "', '" + password + "', '" + maybeBase64EncodedSalt + "')"); + sql.executeUpdate( + "insert into users values ('" + username + "', '" + password + "', '" + + maybeBase64EncodedSalt + "')"); } catch (SQLException ex) { Assertions.fail("Exception creating test database"); } finally { @@ -399,12 +402,12 @@ protected void createSaltColumnSchema(String testName, boolean base64EncodeSalt) realmMap.get(testName).setDataSource(ds); dsMap.put(testName, ds); } - + /** * Creates and adds test data to user_role and roles_permissions tables. */ protected void createRolesAndPermissions(DataSource ds) { - Connection conn = null;; + Connection conn = null; Statement sql = null; try { conn = ds.getConnection(); diff --git a/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapRealmTest.java b/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapRealmTest.java index 9974a2ca27..6e4d5146fd 100644 --- a/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapRealmTest.java +++ b/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapRealmTest.java @@ -1,63 +1,70 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.realm.ldap; - -import org.apache.shiro.authc.AuthenticationException; -import org.apache.shiro.authc.AuthenticationToken; -import org.apache.shiro.authc.UsernamePasswordToken; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.realm.ldap; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import javax.naming.NamingException; -import javax.naming.ldap.LdapContext; - -import java.util.UUID; - -import static org.easymock.EasyMock.*; -import static org.junit.jupiter.api.Assertions.*; - -/** - * Tests for the {@link DefaultLdapRealm} class. - * - * @since 1.3 - */ -@SuppressWarnings({"ThrowableInstanceNeverThrown"}) -public class DefaultLdapRealmTest { - - private DefaultLdapRealm realm; - - // this method can be collapsed back into setUp once the JndiLdapRealm has been removed in 2.0 - protected DefaultLdapRealm getNewRealmUnderTest() { - return new DefaultLdapRealm(); - } - - @BeforeEach - public void setUp() { - realm = getNewRealmUnderTest(); +import org.junit.jupiter.api.Test; + +import javax.naming.NamingException; +import javax.naming.ldap.LdapContext; +import java.util.UUID; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.eq; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.isA; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests for the {@link DefaultLdapRealm} class. + * + * @since 1.3 + */ +@SuppressWarnings({"ThrowableInstanceNeverThrown"}) +public class DefaultLdapRealmTest { + + private DefaultLdapRealm realm; + + // this method can be collapsed back into setUp once the JndiLdapRealm has been removed in 2.0 + protected DefaultLdapRealm getNewRealmUnderTest() { + return new DefaultLdapRealm(); + } + + @BeforeEach + public void setUp() { + realm = getNewRealmUnderTest(); } @Test - void testDefaultInstance() { - assertTrue(realm.getCredentialsMatcher() instanceof AllowAllCredentialsMatcher); - assertEquals(AuthenticationToken.class, realm.getAuthenticationTokenClass()); - assertTrue(realm.getContextFactory() instanceof JndiLdapContextFactory); + void testDefaultInstance() { + assertTrue(realm.getCredentialsMatcher() instanceof AllowAllCredentialsMatcher); + assertEquals(AuthenticationToken.class, realm.getAuthenticationTokenClass()); + assertTrue(realm.getContextFactory() instanceof JndiLdapContextFactory); } @Test @@ -82,25 +89,26 @@ void testSetUserDnTemplateWithoutToken() { } @Test - void testUserDnTemplate() { - String template = "uid={0},ou=users,dc=mycompany,dc=com"; - realm.setUserDnTemplate(template); - assertEquals(template, realm.getUserDnTemplate()); + void testUserDnTemplate() { + String template = "uid={0},ou=users,dc=mycompany,dc=com"; + realm.setUserDnTemplate(template); + assertEquals(template, realm.getUserDnTemplate()); } @Test - void testUserDnTemplateSubstitution() throws NamingException { - realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com"); - LdapContextFactory factory = createMock(LdapContextFactory.class); - realm.setContextFactory(factory); - - Object expectedPrincipal = "uid=jsmith,ou=users,dc=mycompany,dc=com"; - - expect(factory.getLdapContext(eq(expectedPrincipal), isA(Object.class))).andReturn(createNiceMock(LdapContext.class)); - replay(factory); - - realm.getAuthenticationInfo(new UsernamePasswordToken("jsmith", "secret") ); - verify(factory); + void testUserDnTemplateSubstitution() throws NamingException { + realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com"); + LdapContextFactory factory = createMock(LdapContextFactory.class); + realm.setContextFactory(factory); + + Object expectedPrincipal = "uid=jsmith,ou=users,dc=mycompany,dc=com"; + + expect(factory.getLdapContext(eq(expectedPrincipal), isA(Object.class))) + .andReturn(createNiceMock(LdapContext.class)); + replay(factory); + + realm.getAuthenticationInfo(new UsernamePasswordToken("jsmith", "secret")); + verify(factory); } @Test @@ -141,27 +149,27 @@ void testGetAuthenticationInfoNamingException() throws NamingException { * @throws NamingException not thrown */ @Test - void testGetAuthenticationInfoNonSimpleToken() throws NamingException { - realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com"); - LdapContextFactory factory = createMock(LdapContextFactory.class); - realm.setContextFactory(factory); - - final UUID userId = UUID.randomUUID(); - - //ensure the userId is passed as-is: - expect(factory.getLdapContext(eq(userId), isA(Object.class))).andReturn(createNiceMock(LdapContext.class)); - replay(factory); - - realm.getAuthenticationInfo(new AuthenticationToken() { - public Object getPrincipal() { - return userId; - } - - public Object getCredentials() { - return "secret"; - } - }); - verify(factory); + void testGetAuthenticationInfoNonSimpleToken() throws NamingException { + realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com"); + LdapContextFactory factory = createMock(LdapContextFactory.class); + realm.setContextFactory(factory); + + final UUID userId = UUID.randomUUID(); + + //ensure the userId is passed as-is: + expect(factory.getLdapContext(eq(userId), isA(Object.class))).andReturn(createNiceMock(LdapContext.class)); + replay(factory); + + realm.getAuthenticationInfo(new AuthenticationToken() { + public Object getPrincipal() { + return userId; + } + + public Object getCredentials() { + return "secret"; + } + }); + verify(factory); } @Test @@ -172,20 +180,20 @@ void testGetUserDnNullArgument() { } @Test - void testGetUserDnWithOutPrefixAndSuffix() { - realm = new DefaultLdapRealm() { - @Override - protected String getUserDnPrefix() { - return null; - } - - @Override - protected String getUserDnSuffix() { - return null; - } - }; - String principal = "foo"; - String userDn = realm.getUserDn(principal); - assertEquals(principal, userDn); - } -} + void testGetUserDnWithOutPrefixAndSuffix() { + realm = new DefaultLdapRealm() { + @Override + protected String getUserDnPrefix() { + return null; + } + + @Override + protected String getUserDnSuffix() { + return null; + } + }; + String principal = "foo"; + String userDn = realm.getUserDn(principal); + assertEquals(principal, userDn); + } +} diff --git a/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapContextFactoryTest.java b/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapContextFactoryTest.java index fa3955696b..98a86bc7ee 100644 --- a/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapContextFactoryTest.java +++ b/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapContextFactoryTest.java @@ -1,56 +1,60 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.realm.ldap; - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.realm.ldap; + import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import javax.naming.AuthenticationException; -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.ldap.LdapContext; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; -import java.util.UUID; - -import static org.easymock.EasyMock.*; -import static org.junit.jupiter.api.Assertions.*; - -/** - * Tests for the {@link JndiLdapContextFactory} class. - * - * @since 1.1 - */ -public class JndiLdapContextFactoryTest { - - private JndiLdapContextFactory factory; - - @BeforeEach - public void setUp() { - factory = new JndiLdapContextFactory() { - //Fake a JNDI environment for the tests: - @Override - protected LdapContext createLdapContext(Hashtable env) throws NamingException { - return createNiceMock(LdapContext.class); - } - }; +import org.junit.jupiter.api.Test; + +import javax.naming.AuthenticationException; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.ldap.LdapContext; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.UUID; + +import static org.easymock.EasyMock.createNiceMock; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + + +/** + * Tests for the {@link JndiLdapContextFactory} class. + * + * @since 1.1 + */ +public class JndiLdapContextFactoryTest { + + private JndiLdapContextFactory factory; + + @BeforeEach + public void setUp() { + factory = new JndiLdapContextFactory() { + //Fake a JNDI environment for the tests: + @Override + protected LdapContext createLdapContext(Hashtable env) throws NamingException { + return createNiceMock(LdapContext.class); + } + }; } /** @@ -70,37 +74,37 @@ void testGetLdapContext() throws NamingException { } @Test - void testAuthenticationMechanism() { - String mech = "MD5-DIGEST"; - factory.setAuthenticationMechanism(mech); - assertEquals(mech, factory.getAuthenticationMechanism()); + void testAuthenticationMechanism() { + String mech = "MD5-DIGEST"; + factory.setAuthenticationMechanism(mech); + assertEquals(mech, factory.getAuthenticationMechanism()); } @Test - void testReferral() { - String referral = "throw"; - factory.setReferral(referral); - assertEquals(referral, factory.getReferral()); + void testReferral() { + String referral = "throw"; + factory.setReferral(referral); + assertEquals(referral, factory.getReferral()); } @Test - void testGetContextFactoryClassName() { - assertEquals(JndiLdapContextFactory.DEFAULT_CONTEXT_FACTORY_CLASS_NAME, factory.getContextFactoryClassName()); + void testGetContextFactoryClassName() { + assertEquals(JndiLdapContextFactory.DEFAULT_CONTEXT_FACTORY_CLASS_NAME, factory.getContextFactoryClassName()); } @Test - void testSetEnvironmentPropertyNull() { - factory.setAuthenticationMechanism("MD5-DIGEST"); - factory.setAuthenticationMechanism(null); - assertNull(factory.getAuthenticationMechanism()); + void testSetEnvironmentPropertyNull() { + factory.setAuthenticationMechanism("MD5-DIGEST"); + factory.setAuthenticationMechanism(null); + assertNull(factory.getAuthenticationMechanism()); } @Test - void testCustomEnvironment() { - Map map = new HashMap(); - map.put("foo", "bar"); - factory.setEnvironment(map); - assertEquals("bar", factory.getEnvironment().get("foo")); + void testCustomEnvironment() { + Map map = new HashMap(); + map.put("foo", "bar"); + factory.setEnvironment(map); + assertEquals("bar", factory.getEnvironment().get("foo")); } @Test @@ -111,81 +115,81 @@ void testGetLdapContextWithoutUrl() throws NamingException { } @Test - void testGetLdapContextDefault() throws NamingException { - factory = new JndiLdapContextFactory() { - @Override - protected LdapContext createLdapContext(Hashtable env) throws NamingException { - assertEquals("ldap://localhost:389", env.get(Context.PROVIDER_URL)); - assertEquals("foo", env.get(Context.SECURITY_PRINCIPAL)); - assertEquals("bar", env.get(Context.SECURITY_CREDENTIALS)); - assertEquals("simple", env.get(Context.SECURITY_AUTHENTICATION)); - assertNull(env.get(SUN_CONNECTION_POOLING_PROPERTY)); - return createNiceMock(LdapContext.class); - } - }; - - factory.setUrl("ldap://localhost:389"); - factory.getLdapContext("foo", "bar"); + void testGetLdapContextDefault() throws NamingException { + factory = new JndiLdapContextFactory() { + @Override + protected LdapContext createLdapContext(Hashtable env) throws NamingException { + assertEquals("ldap://localhost:389", env.get(Context.PROVIDER_URL)); + assertEquals("foo", env.get(Context.SECURITY_PRINCIPAL)); + assertEquals("bar", env.get(Context.SECURITY_CREDENTIALS)); + assertEquals("simple", env.get(Context.SECURITY_AUTHENTICATION)); + assertNull(env.get(SUN_CONNECTION_POOLING_PROPERTY)); + return createNiceMock(LdapContext.class); + } + }; + + factory.setUrl("ldap://localhost:389"); + factory.getLdapContext("foo", "bar"); } @SuppressWarnings({"deprecation"}) @Test - void testGetLdapContextStringArguments() throws NamingException { - factory = new JndiLdapContextFactory() { - @Override - protected LdapContext createLdapContext(Hashtable env) throws NamingException { - assertEquals("ldap://localhost:389", env.get(Context.PROVIDER_URL)); - assertEquals("foo", env.get(Context.SECURITY_PRINCIPAL)); - assertEquals("bar", env.get(Context.SECURITY_CREDENTIALS)); - assertEquals("simple", env.get(Context.SECURITY_AUTHENTICATION)); - assertNull(env.get(SUN_CONNECTION_POOLING_PROPERTY)); - return createNiceMock(LdapContext.class); - } - }; - - factory.setUrl("ldap://localhost:389"); - factory.getLdapContext("foo", "bar"); + void testGetLdapContextStringArguments() throws NamingException { + factory = new JndiLdapContextFactory() { + @Override + protected LdapContext createLdapContext(Hashtable env) throws NamingException { + assertEquals("ldap://localhost:389", env.get(Context.PROVIDER_URL)); + assertEquals("foo", env.get(Context.SECURITY_PRINCIPAL)); + assertEquals("bar", env.get(Context.SECURITY_CREDENTIALS)); + assertEquals("simple", env.get(Context.SECURITY_AUTHENTICATION)); + assertNull(env.get(SUN_CONNECTION_POOLING_PROPERTY)); + return createNiceMock(LdapContext.class); + } + }; + + factory.setUrl("ldap://localhost:389"); + factory.getLdapContext("foo", "bar"); } @Test - void testGetSystemLdapContext() throws NamingException { - factory = new JndiLdapContextFactory() { - @Override - protected LdapContext createLdapContext(Hashtable env) throws NamingException { - assertEquals("ldap://localhost:389", env.get(Context.PROVIDER_URL)); - assertEquals("foo", env.get(Context.SECURITY_PRINCIPAL)); - assertEquals("bar", env.get(Context.SECURITY_CREDENTIALS)); - assertEquals("simple", env.get(Context.SECURITY_AUTHENTICATION)); - assertNotNull(env.get(SUN_CONNECTION_POOLING_PROPERTY)); - return createNiceMock(LdapContext.class); - } - }; - - factory.setSystemUsername("foo"); - factory.setSystemPassword("bar"); - factory.setUrl("ldap://localhost:389"); - factory.getSystemLdapContext(); + void testGetSystemLdapContext() throws NamingException { + factory = new JndiLdapContextFactory() { + @Override + protected LdapContext createLdapContext(Hashtable env) throws NamingException { + assertEquals("ldap://localhost:389", env.get(Context.PROVIDER_URL)); + assertEquals("foo", env.get(Context.SECURITY_PRINCIPAL)); + assertEquals("bar", env.get(Context.SECURITY_CREDENTIALS)); + assertEquals("simple", env.get(Context.SECURITY_AUTHENTICATION)); + assertNotNull(env.get(SUN_CONNECTION_POOLING_PROPERTY)); + return createNiceMock(LdapContext.class); + } + }; + + factory.setSystemUsername("foo"); + factory.setSystemPassword("bar"); + factory.setUrl("ldap://localhost:389"); + factory.getSystemLdapContext(); } @Test - void testGetSystemLdapContextPoolingDisabled() throws NamingException { - factory = new JndiLdapContextFactory() { - @Override - protected LdapContext createLdapContext(Hashtable env) throws NamingException { - assertEquals("ldap://localhost:389", env.get(Context.PROVIDER_URL)); - assertEquals("foo", env.get(Context.SECURITY_PRINCIPAL)); - assertEquals("bar", env.get(Context.SECURITY_CREDENTIALS)); - assertEquals("simple", env.get(Context.SECURITY_AUTHENTICATION)); - assertNull(env.get(SUN_CONNECTION_POOLING_PROPERTY)); - return createNiceMock(LdapContext.class); - } - }; - - factory.setSystemUsername("foo"); - factory.setSystemPassword("bar"); - factory.setPoolingEnabled(false); - factory.setUrl("ldap://localhost:389"); - factory.getSystemLdapContext(); + void testGetSystemLdapContextPoolingDisabled() throws NamingException { + factory = new JndiLdapContextFactory() { + @Override + protected LdapContext createLdapContext(Hashtable env) throws NamingException { + assertEquals("ldap://localhost:389", env.get(Context.PROVIDER_URL)); + assertEquals("foo", env.get(Context.SECURITY_PRINCIPAL)); + assertEquals("bar", env.get(Context.SECURITY_CREDENTIALS)); + assertEquals("simple", env.get(Context.SECURITY_AUTHENTICATION)); + assertNull(env.get(SUN_CONNECTION_POOLING_PROPERTY)); + return createNiceMock(LdapContext.class); + } + }; + + factory.setSystemUsername("foo"); + factory.setSystemPassword("bar"); + factory.setPoolingEnabled(false); + factory.setUrl("ldap://localhost:389"); + factory.getSystemLdapContext(); } @Test @@ -218,8 +222,7 @@ void testEmptyNullCredentials() throws NamingException { factory.setUrl("ldap://localhost:389"); factory.getLdapContext("jcoder", null); }); - } - - - -} + } + + +} diff --git a/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapRealmTest.java b/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapRealmTest.java index 80bae44e76..03f3c6c9f2 100644 --- a/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapRealmTest.java +++ b/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapRealmTest.java @@ -1,55 +1,55 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.realm.ldap; - -import org.junit.jupiter.api.Test; - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.realm.ldap; + +import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertEquals; -/** - * Tests for the {@link JndiLdapRealm} class. - * - * @since 1.1 - * @deprecated Replaced by {@link DefaultLdapRealmTest} - */ -@SuppressWarnings({"ThrowableInstanceNeverThrown", "deprecation"}) -public class JndiLdapRealmTest extends DefaultLdapRealmTest { - - protected DefaultLdapRealm getNewRealmUnderTest() { - return new JndiLdapRealm(); - } - - @Test - public void testGetUserDnWithOutPrefixAndSuffix() { - JndiLdapRealm realm = new JndiLdapRealm() { - @Override - protected String getUserDnPrefix() { - return null; - } - - @Override - protected String getUserDnSuffix() { - return null; - } - }; - String principal = "foo"; - String userDn = realm.getUserDn(principal); - assertEquals(principal, userDn); - } -} +/** + * Tests for the {@link JndiLdapRealm} class. + * + * @since 1.1 + * @deprecated Replaced by {@link DefaultLdapRealmTest} + */ +@SuppressWarnings({"ThrowableInstanceNeverThrown", "deprecation"}) +public class JndiLdapRealmTest extends DefaultLdapRealmTest { + + protected DefaultLdapRealm getNewRealmUnderTest() { + return new JndiLdapRealm(); + } + + @Test + public void testGetUserDnWithOutPrefixAndSuffix() { + JndiLdapRealm realm = new JndiLdapRealm() { + @Override + protected String getUserDnPrefix() { + return null; + } + + @Override + protected String getUserDnSuffix() { + return null; + } + }; + String principal = "foo"; + String userDn = realm.getUserDn(principal); + assertEquals(principal, userDn); + } +} diff --git a/core/src/test/java/org/apache/shiro/realm/text/IniRealmTest.java b/core/src/test/java/org/apache/shiro/realm/text/IniRealmTest.java index 76430f172d..3ccb596ced 100644 --- a/core/src/test/java/org/apache/shiro/realm/text/IniRealmTest.java +++ b/core/src/test/java/org/apache/shiro/realm/text/IniRealmTest.java @@ -23,7 +23,9 @@ import org.apache.shiro.config.Ini; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Unit tests for the {@link IniRealm} class. diff --git a/core/src/test/java/org/apache/shiro/realm/text/TextConfigurationRealmTest.java b/core/src/test/java/org/apache/shiro/realm/text/TextConfigurationRealmTest.java index 86d1cb4b89..6a69bfc52a 100644 --- a/core/src/test/java/org/apache/shiro/realm/text/TextConfigurationRealmTest.java +++ b/core/src/test/java/org/apache/shiro/realm/text/TextConfigurationRealmTest.java @@ -26,8 +26,11 @@ import java.text.ParseException; import java.util.Arrays; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +@SuppressWarnings("checkstyle:MagicNumber") public class TextConfigurationRealmTest { private TestRealm realm; @@ -55,9 +58,9 @@ private void setUpForReadConfigurationTest() { */ public void test(Thread runnable) throws InterruptedException { // Obtain the realm's locks - USERS_LOCK.writeLock().lock(); + usersLock.writeLock().lock(); try { - ROLES_LOCK.writeLock().lock(); + rolesLock.writeLock().lock(); try { // Any read threads attempting to obtain the realms lock will block runnable.start(); @@ -66,10 +69,10 @@ public void test(Thread runnable) throws InterruptedException { realm.onInit(); } finally { - ROLES_LOCK.writeLock().unlock(); + rolesLock.writeLock().unlock(); } } finally { - USERS_LOCK.writeLock().unlock(); + usersLock.writeLock().unlock(); } } }; @@ -90,7 +93,7 @@ private void executeTest(Runnable runnable) throws InterruptedException { } /* - * Tests that roles and account can't be tested while the realm is being loaded. + * Tests that roles and account can't be tested while the realm is being loaded. */ @Test void testRoleAndUserAccount() throws InterruptedException { @@ -104,7 +107,7 @@ public void run() { } /* - * Tests that roles can't be read while the realm is being loaded. + * Tests that roles can't be read while the realm is being loaded. */ @Test void testHasRole() throws InterruptedException { @@ -114,14 +117,14 @@ public void run() { PrincipalCollection principalCollection = new SimplePrincipalCollection("user1", "realm1"); assertTrue(realm.hasRole(principalCollection, "role2"), "principal doesn't have role when it should"); - assertTrue(realm.hasAllRoles(principalCollection, Arrays.asList(new String[]{"role1", "role2"})), + assertTrue(realm.hasAllRoles(principalCollection, Arrays.asList(new String[] {"role1", "role2"})), "principal doesn't have all roles when it should"); } }); } /* - * Tests that roles can't be checked while the realm is being loaded. + * Tests that roles can't be checked while the realm is being loaded. */ @Test void testCheckRole() throws InterruptedException { @@ -130,7 +133,7 @@ void testCheckRole() throws InterruptedException { public void run() { PrincipalCollection principalCollection = new SimplePrincipalCollection("user1", "realm1"); try { - realm.checkRoles(principalCollection, new String[]{"role1", "role2"}); + realm.checkRoles(principalCollection, new String[] {"role1", "role2"}); } catch (AuthorizationException ae) { fail("principal doesn't have all roles when it should"); } @@ -139,7 +142,7 @@ public void run() { } /* - * Tests that a principal's permissions can't be checked while the realm is being loaded. + * Tests that a principal's permissions can't be checked while the realm is being loaded. */ @Test void testCheckPermission() throws InterruptedException { @@ -149,7 +152,7 @@ public void run() { PrincipalCollection principalCollection = new SimplePrincipalCollection("user1", "realm1"); try { realm.checkPermission(principalCollection, "role1_permission1"); - realm.checkPermissions(principalCollection, new String[]{"role1_permission1", "role2_permission2"}); + realm.checkPermissions(principalCollection, new String[] {"role1_permission1", "role2_permission2"}); } catch (AuthorizationException ae) { fail("principal doesn't have permission when it should"); } @@ -158,16 +161,17 @@ public void run() { } /* - * Tests that a principal's permissions can't be checked while the realm is being loaded. + * Tests that a principal's permissions can't be checked while the realm is being loaded. */ @Test void testIsPermitted() throws InterruptedException { setUpForReadConfigurationTest(); executeTest(new Runnable() { + @SuppressWarnings("checkstyle:LineLength") public void run() { PrincipalCollection principalCollection = new SimplePrincipalCollection("user1", "realm1"); assertTrue(realm.isPermitted(principalCollection, "role1_permission1"), "permission not permitted when it should be"); - assertTrue(realm.isPermittedAll(principalCollection, new String[]{"role1_permission1", "role2_permission2"}), + assertTrue(realm.isPermittedAll(principalCollection, new String[] {"role1_permission1", "role2_permission2"}), "permission not permitted when it should be"); } }); @@ -182,14 +186,14 @@ void testProcessRoleDefinitions() throws InterruptedException { public void test(Thread runnable) throws InterruptedException { // While the realm's lock is held by this thread role definitions cannot be processed // Obtain the realm's locks - ROLES_LOCK.writeLock().lock(); + rolesLock.writeLock().lock(); try { runnable.start(); Thread.sleep(500); // No role until lock is released and role definitions are processed assertFalse(realm.roleExists("role1"), "role exists when it shouldn't"); } finally { - ROLES_LOCK.writeLock().unlock(); + rolesLock.writeLock().unlock(); } } }; @@ -220,14 +224,14 @@ void testProcessUserDefinitions() throws InterruptedException { public void test(Thread runnable) throws InterruptedException { // While the realm's lock is held by this thread user definitions cannot be processed // Obtain the realm's locks - USERS_LOCK.writeLock().lock(); + usersLock.writeLock().lock(); try { runnable.start(); Thread.sleep(500); // No account until lock is released and user definitions are processed assertFalse(realm.accountExists("user1"), "account exists when it shouldn't"); } finally { - USERS_LOCK.writeLock().unlock(); + usersLock.writeLock().unlock(); } } }; @@ -255,6 +259,7 @@ private class TestThread implements Runnable { private Runnable test; private volatile AssertionError ae; + @SuppressWarnings("checkstyle:RedundantModifier") public TestThread(Runnable test) { this.test = test; } @@ -268,15 +273,17 @@ public void run() { } public void test() { - if (ae != null) + if (ae != null) { throw ae; + } } } /* * Provides an additional method that has access to the realm's lock for mutual exclusion. */ - private abstract class TestRealm extends TextConfigurationRealm { - abstract public void test(Thread runnable) throws InterruptedException; + private abstract static class TestRealm extends TextConfigurationRealm { + + public abstract void test(Thread runnable) throws InterruptedException; } } diff --git a/core/src/test/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManagerTest.java b/core/src/test/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManagerTest.java index 46662d80f2..10f928467a 100644 --- a/core/src/test/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManagerTest.java +++ b/core/src/test/java/org/apache/shiro/session/mgt/AbstractValidatingSessionManagerTest.java @@ -1,42 +1,42 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.session.mgt; - -import org.apache.shiro.authz.AuthorizationException; -import org.apache.shiro.session.Session; -import org.apache.shiro.session.SessionListener; -import org.apache.shiro.session.SessionListenerAdapter; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.session.mgt; + +import org.apache.shiro.authz.AuthorizationException; +import org.apache.shiro.session.Session; +import org.apache.shiro.session.SessionListener; +import org.apache.shiro.session.SessionListenerAdapter; import org.apache.shiro.session.UnknownSessionException; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Unit tests for the {@link org.apache.shiro.session.mgt.AbstractValidatingSessionManager} class. - */ +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Unit tests for the {@link org.apache.shiro.session.mgt.AbstractValidatingSessionManager} class. + */ public class AbstractValidatingSessionManagerTest { /** @@ -44,53 +44,53 @@ public class AbstractValidatingSessionManagerTest { * Verifies SHIRO-199. */ @Test - void testValidateSessions() { - - final SimpleSession validSession = new SimpleSession(); - validSession.setId(1); - final SimpleSession invalidSession = new SimpleSession(); - //set to a time in the past: - Calendar cal = Calendar.getInstance(); - Long expiredTimeout = AbstractSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT + 1; - cal.add(Calendar.MILLISECOND, -(expiredTimeout.intValue()) ); - Date past = cal.getTime(); - invalidSession.setStartTimestamp(past); - invalidSession.setLastAccessTime(past); - invalidSession.setId(2); - - final AtomicInteger expirationCount = new AtomicInteger(); - - SessionListener sessionListener = new SessionListenerAdapter() { - @Override - public void onExpiration(Session session) { - expirationCount.incrementAndGet(); - } - }; - - AbstractValidatingSessionManager sessionManager = new AbstractValidatingSessionManager() { - @Override - protected Session retrieveSession(SessionKey key) throws UnknownSessionException { - throw new UnsupportedOperationException("Should not be called in this test."); - } - - @Override - protected Session doCreateSession(SessionContext initData) throws AuthorizationException { - throw new UnsupportedOperationException("Should not be called in this test."); - } - - @Override - protected Collection getActiveSessions() { - Collection sessions = new ArrayList(2); - sessions.add(validSession); - sessions.add(invalidSession); - return sessions; - } - }; - - sessionManager.setSessionListeners(Arrays.asList(sessionListener)); - sessionManager.validateSessions(); - - assertEquals(1, expirationCount.intValue()); + void testValidateSessions() { + + final SimpleSession validSession = new SimpleSession(); + validSession.setId(1); + final SimpleSession invalidSession = new SimpleSession(); + //set to a time in the past: + Calendar cal = Calendar.getInstance(); + long expiredTimeout = AbstractSessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT + 1; + cal.add(Calendar.MILLISECOND, -((int) expiredTimeout)); + Date past = cal.getTime(); + invalidSession.setStartTimestamp(past); + invalidSession.setLastAccessTime(past); + invalidSession.setId(2); + + final AtomicInteger expirationCount = new AtomicInteger(); + + SessionListener sessionListener = new SessionListenerAdapter() { + @Override + public void onExpiration(Session session) { + expirationCount.incrementAndGet(); + } + }; + + AbstractValidatingSessionManager sessionManager = new AbstractValidatingSessionManager() { + @Override + protected Session retrieveSession(SessionKey key) throws UnknownSessionException { + throw new UnsupportedOperationException("Should not be called in this test."); + } + + @Override + protected Session doCreateSession(SessionContext initData) throws AuthorizationException { + throw new UnsupportedOperationException("Should not be called in this test."); + } + + @Override + protected Collection getActiveSessions() { + Collection sessions = new ArrayList(2); + sessions.add(validSession); + sessions.add(invalidSession); + return sessions; + } + }; + + sessionManager.setSessionListeners(Arrays.asList(sessionListener)); + sessionManager.validateSessions(); + + assertEquals(1, expirationCount.intValue()); } @@ -99,40 +99,40 @@ protected Collection getActiveSessions() { * Verifies SHIRO-399. */ @Test - void testNoMemoryLeakOnInvalidSessions() throws Exception { - SessionListener sessionListener = new SessionListener() { - public void onStart(Session session) { - session.setAttribute("I love", "Romania"); - } - - public void onStop(Session session) { - tryToCleanSession(session); - } - - public void onExpiration(Session session) { - tryToCleanSession(session); - } - - private void tryToCleanSession(Session session) { - Collection keys = session.getAttributeKeys(); - for (Object key : keys) { - session.removeAttribute(key); - } - } - }; - - DefaultSessionManager sessionManager = new DefaultSessionManager(); - sessionManager.setSessionListeners(Arrays.asList(sessionListener)); - - Session session = sessionManager.start(null); - assertEquals(1, sessionManager.getActiveSessions().size()); - - session.setTimeout(0L); - //last access timestamp needs to be older than the current timestamp when validating, so ensure a delay: - Thread.sleep(1); - - sessionManager.validateSessions(); - - assertEquals(0, sessionManager.getActiveSessions().size()); - } -} + void testNoMemoryLeakOnInvalidSessions() throws Exception { + SessionListener sessionListener = new SessionListener() { + public void onStart(Session session) { + session.setAttribute("I love", "Romania"); + } + + public void onStop(Session session) { + tryToCleanSession(session); + } + + public void onExpiration(Session session) { + tryToCleanSession(session); + } + + private void tryToCleanSession(Session session) { + Collection keys = session.getAttributeKeys(); + for (Object key : keys) { + session.removeAttribute(key); + } + } + }; + + DefaultSessionManager sessionManager = new DefaultSessionManager(); + sessionManager.setSessionListeners(Arrays.asList(sessionListener)); + + Session session = sessionManager.start(null); + assertEquals(1, sessionManager.getActiveSessions().size()); + + session.setTimeout(0L); + //last access timestamp needs to be older than the current timestamp when validating, so ensure a delay: + Thread.sleep(1); + + sessionManager.validateSessions(); + + assertEquals(0, sessionManager.getActiveSessions().size()); + } +} diff --git a/core/src/test/java/org/apache/shiro/session/mgt/DefaultSessionManagerTest.java b/core/src/test/java/org/apache/shiro/session/mgt/DefaultSessionManagerTest.java index 42554f2d9b..946bd3d105 100644 --- a/core/src/test/java/org/apache/shiro/session/mgt/DefaultSessionManagerTest.java +++ b/core/src/test/java/org/apache/shiro/session/mgt/DefaultSessionManagerTest.java @@ -33,14 +33,24 @@ import java.util.UUID; -import static org.easymock.EasyMock.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.eq; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.expectLastCall; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reset; +import static org.easymock.EasyMock.verify; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * Unit test for the {@link DefaultSessionManager DefaultSessionManager} implementation. */ public class DefaultSessionManagerTest { + @SuppressWarnings("checkstyle:ExplicitInitialization") DefaultSessionManager sm = null; @BeforeEach @@ -63,6 +73,7 @@ public void sleep(long millis) { } } + @SuppressWarnings("checkstyle:MagicNumber") @Test void testGlobalTimeout() { long timeout = 1000; @@ -110,7 +121,7 @@ void testSessionListenerStopNotificationWithReadAttribute() { SessionListener listener = new SessionListenerAdapter() { public void onStop(Session session) { stopped[0] = true; - value[0] = (String)session.getAttribute("foo"); + value[0] = (String) session.getAttribute("foo"); } }; sm.getSessionListeners().add(listener); @@ -123,6 +134,7 @@ public void onStop(Session session) { assertEquals("bar", value[0]); } + @SuppressWarnings("checkstyle:MagicNumber") @Test void testSessionListenerExpiredNotification() { final boolean[] expired = new boolean[1]; @@ -144,6 +156,7 @@ public void onExpiration(Session session) { assertTrue(expired[0]); } + @SuppressWarnings("checkstyle:MagicNumber") @Test void testSessionDeleteOnExpiration() { sm.setGlobalSessionTimeout(100); @@ -155,7 +168,7 @@ void testSessionDeleteOnExpiration() { final SimpleSession session1 = new SimpleSession(); session1.setId(sessionId1); - final Session[] activeSession = new SimpleSession[]{session1}; + final Session[] activeSession = new SimpleSession[] {session1}; sm.setSessionFactory(new SessionFactory() { public Session createSession(SessionContext initData) { return activeSession[0]; @@ -181,7 +194,8 @@ public Session createSession(SessionContext initData) { sleep(20); expect(sessionDAO.readSession(sessionId1)).andReturn(session1); - sessionDAO.update(eq(session1)); //update's the stop timestamp + //update's the stop timestamp + sessionDAO.update(eq(session1)); sessionDAO.delete(session1); replay(sessionDAO); @@ -193,7 +207,8 @@ public Session createSession(SessionContext initData) { //expected } - verify(sessionDAO); //verify that the delete call was actually made on the DAO + //verify that the delete call was actually made on the DAO + verify(sessionDAO); } /** @@ -214,8 +229,7 @@ void testEnablingOfCustomSessionValidationScheduler() { // now sessionValidationScheduler should be enabled assertTrue(sessionValidationScheduler.isEnabled(), "sessionValidationScheduler was not enabled"); - } - finally { + } finally { // cleanup after test sessionManager.destroy(); } @@ -230,7 +244,7 @@ private static class SessionTimeoutMatcher implements IArgumentMatcher { private final long timeout; - public SessionTimeoutMatcher(long timeout) { + SessionTimeoutMatcher(long timeout) { this.timeout = timeout; } diff --git a/core/src/test/java/org/apache/shiro/session/mgt/DelegatingSessionTest.java b/core/src/test/java/org/apache/shiro/session/mgt/DelegatingSessionTest.java index 8df9bb22e0..da3984c447 100644 --- a/core/src/test/java/org/apache/shiro/session/mgt/DelegatingSessionTest.java +++ b/core/src/test/java/org/apache/shiro/session/mgt/DelegatingSessionTest.java @@ -34,7 +34,9 @@ */ public class DelegatingSessionTest { + @SuppressWarnings("checkstyle:ExplicitInitialization") DelegatingSession session = null; + @SuppressWarnings("checkstyle:ExplicitInitialization") DefaultSessionManager sm = null; @BeforeEach @@ -58,6 +60,7 @@ public void sleep(long millis) { } } + @SuppressWarnings("checkstyle:MagicNumber") @Test void testTimeout() { Serializable origId = session.getId(); diff --git a/core/src/test/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationSchedulerTest.java b/core/src/test/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationSchedulerTest.java index 6694b18f5c..98c482a930 100644 --- a/core/src/test/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationSchedulerTest.java +++ b/core/src/test/java/org/apache/shiro/session/mgt/ExecutorServiceSessionValidationSchedulerTest.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +@SuppressWarnings("checkstyle:MagicNumber") public class ExecutorServiceSessionValidationSchedulerTest { ExecutorServiceSessionValidationScheduler executorServiceSessionValidationScheduler; @@ -96,7 +97,7 @@ public void tearDown() throws Exception { executorServiceSessionValidationScheduler.disableSessionValidation(); } - private class FakeDefaultSessionManager extends DefaultSessionManager { + private static final class FakeDefaultSessionManager extends DefaultSessionManager { public void validateSessions() throws RuntimeException { throw new RuntimeException("Session test exception"); } diff --git a/core/src/test/java/org/apache/shiro/session/mgt/SimpleSessionTest.java b/core/src/test/java/org/apache/shiro/session/mgt/SimpleSessionTest.java index 81c9675add..c231cde553 100644 --- a/core/src/test/java/org/apache/shiro/session/mgt/SimpleSessionTest.java +++ b/core/src/test/java/org/apache/shiro/session/mgt/SimpleSessionTest.java @@ -1,66 +1,70 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.session.mgt; - -import org.junit.jupiter.api.Test; - -import java.io.*; -import java.util.Date; - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.session.mgt; + +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Date; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class SimpleSessionTest { @Test - void testDefaultSerialization() throws Exception { - SimpleSession session = new SimpleSession(); - - long timeout = session.getTimeout(); - Date start = session.getStartTimestamp(); - Date lastAccess = session.getLastAccessTime(); - - SimpleSession deserialized = serializeAndDeserialize(session); - - assertEquals(timeout, deserialized.getTimeout()); - assertEquals(start, deserialized.getStartTimestamp()); - assertEquals(lastAccess, deserialized.getLastAccessTime()); + void testDefaultSerialization() throws Exception { + SimpleSession session = new SimpleSession(); + + long timeout = session.getTimeout(); + Date start = session.getStartTimestamp(); + Date lastAccess = session.getLastAccessTime(); + + SimpleSession deserialized = serializeAndDeserialize(session); + + assertEquals(timeout, deserialized.getTimeout()); + assertEquals(start, deserialized.getStartTimestamp()); + assertEquals(lastAccess, deserialized.getLastAccessTime()); } @Test - void serializeHost() throws IOException, ClassNotFoundException { - SimpleSession session = new SimpleSession("localhost"); - assertEquals("localhost", serializeAndDeserialize(session).getHost()); + void serializeHost() throws IOException, ClassNotFoundException { + SimpleSession session = new SimpleSession("localhost"); + assertEquals("localhost", serializeAndDeserialize(session).getHost()); } @Test - void serializeExpired() throws IOException, ClassNotFoundException { - SimpleSession session = new SimpleSession(); - session.setExpired(true); - assertTrue(serializeAndDeserialize(session).isExpired()); - } - - private SimpleSession serializeAndDeserialize(SimpleSession session) throws IOException, ClassNotFoundException { - ByteArrayOutputStream serialized = new ByteArrayOutputStream(); - ObjectOutputStream serializer = new ObjectOutputStream(serialized); - serializer.writeObject(session); - serializer.close(); - return (SimpleSession) new ObjectInputStream(new ByteArrayInputStream(serialized.toByteArray())).readObject(); - } -} + void serializeExpired() throws IOException, ClassNotFoundException { + SimpleSession session = new SimpleSession(); + session.setExpired(true); + assertTrue(serializeAndDeserialize(session).isExpired()); + } + + private SimpleSession serializeAndDeserialize(SimpleSession session) throws IOException, ClassNotFoundException { + ByteArrayOutputStream serialized = new ByteArrayOutputStream(); + ObjectOutputStream serializer = new ObjectOutputStream(serialized); + serializer.writeObject(session); + serializer.close(); + return (SimpleSession) new ObjectInputStream(new ByteArrayInputStream(serialized.toByteArray())).readObject(); + } +} diff --git a/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java b/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java index 748e0507b1..311b3591d8 100644 --- a/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java +++ b/core/src/test/java/org/apache/shiro/subject/DelegatingSubjectTest.java @@ -36,8 +36,11 @@ import java.util.concurrent.Callable; import static org.easymock.EasyMock.createNiceMock; -import static org.junit.jupiter.api.Assertions.*; - +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @since Aug 1, 2008 2:11:17 PM @@ -158,7 +161,8 @@ void testRunAs() { assertTrue(subject.hasRole("role1")); assertFalse(subject.hasRole("role2")); assertFalse(subject.hasRole("role3")); - assertNull(subject.getPreviousPrincipals()); //no previous principals since we haven't called runAs yet + //no previous principals since we haven't called runAs yet + assertNull(subject.getPreviousPrincipals()); //runAs user2: subject.runAs(new SimplePrincipalCollection("user2", IniSecurityManagerFactory.INI_REALM_NAME)); @@ -210,7 +214,8 @@ void testRunAs() { assertTrue(subject.hasRole("role1")); assertFalse(subject.hasRole("role2")); assertFalse(subject.hasRole("role3")); - assertNull(subject.getPreviousPrincipals()); //no previous principals in orig state + //no previous principals in orig state + assertNull(subject.getPreviousPrincipals()); subject.logout(); diff --git a/core/src/test/java/org/apache/shiro/subject/SimplePrincipalCollectionTest.java b/core/src/test/java/org/apache/shiro/subject/SimplePrincipalCollectionTest.java index 5aec0f635d..a38c32f5a1 100644 --- a/core/src/test/java/org/apache/shiro/subject/SimplePrincipalCollectionTest.java +++ b/core/src/test/java/org/apache/shiro/subject/SimplePrincipalCollectionTest.java @@ -30,8 +30,7 @@ public class SimplePrincipalCollectionTest { private static final Logger LOG = LoggerFactory.getLogger(SimplePrincipalCollectionTest.class); @Test - void multiplePrincipalsTest() - { + void multiplePrincipalsTest() { SimplePrincipalCollection principalCollection = new SimplePrincipalCollection(); principalCollection.add("frank", "realm1"); principalCollection.add("johnny", "realm1"); diff --git a/core/src/test/java/org/apache/shiro/test/AbstractShiroTest.java b/core/src/test/java/org/apache/shiro/test/AbstractShiroTest.java index 93944074bf..2e6e66ad3f 100644 --- a/core/src/test/java/org/apache/shiro/test/AbstractShiroTest.java +++ b/core/src/test/java/org/apache/shiro/test/AbstractShiroTest.java @@ -1,96 +1,96 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.test; - -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.UnavailableSecurityManagerException; -import org.apache.shiro.mgt.SecurityManager; -import org.apache.shiro.subject.Subject; -import org.apache.shiro.subject.support.SubjectThreadState; -import org.apache.shiro.lang.util.LifecycleUtils; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.test; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.UnavailableSecurityManagerException; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.subject.support.SubjectThreadState; +import org.apache.shiro.lang.util.LifecycleUtils; import org.apache.shiro.util.ThreadState; import org.junit.jupiter.api.AfterAll; -/** - * Abstract test case showing how to use Shiro in testing environments. - * - * @since 1.2 - */ -public abstract class AbstractShiroTest { - - private static ThreadState subjectThreadState; - - public AbstractShiroTest() { - } - - /** - * Allows subclasses to set the currently executing {@link Subject} instance. - * - * @param subject the Subject instance - */ - protected void setSubject(Subject subject) { - clearSubject(); - subjectThreadState = createThreadState(subject); - subjectThreadState.bind(); - } - - protected Subject getSubject() { - return SecurityUtils.getSubject(); - } - - protected ThreadState createThreadState(Subject subject) { - return new SubjectThreadState(subject); - } - - /** - * Clears Shiro's thread state, ensuring the thread remains clean for future test execution. - */ - protected void clearSubject() { - doClearSubject(); - } - - private static void doClearSubject() { - if (subjectThreadState != null) { - subjectThreadState.clear(); - subjectThreadState = null; - } - } - - protected static void setSecurityManager(SecurityManager securityManager) { - SecurityUtils.setSecurityManager(securityManager); - } - - protected static SecurityManager getSecurityManager() { - return SecurityUtils.getSecurityManager(); - } - - @AfterAll - public static void tearDownShiro() { - doClearSubject(); - try { - SecurityManager securityManager = getSecurityManager(); - LifecycleUtils.destroy(securityManager); - } catch (UnavailableSecurityManagerException e) { - //we don't care about this when cleaning up the test environment - //(for example, maybe the subclass is a unit test and it didn't - // need a SecurityManager instance because it was using only mock Subject instances) - } - setSecurityManager(null); - } -} +/** + * Abstract test case showing how to use Shiro in testing environments. + * + * @since 1.2 + */ +public abstract class AbstractShiroTest { + + private static ThreadState subjectThreadState; + + public AbstractShiroTest() { + } + + /** + * Allows subclasses to set the currently executing {@link Subject} instance. + * + * @param subject the Subject instance + */ + protected void setSubject(Subject subject) { + clearSubject(); + subjectThreadState = createThreadState(subject); + subjectThreadState.bind(); + } + + protected Subject getSubject() { + return SecurityUtils.getSubject(); + } + + protected ThreadState createThreadState(Subject subject) { + return new SubjectThreadState(subject); + } + + /** + * Clears Shiro's thread state, ensuring the thread remains clean for future test execution. + */ + protected void clearSubject() { + doClearSubject(); + } + + private static void doClearSubject() { + if (subjectThreadState != null) { + subjectThreadState.clear(); + subjectThreadState = null; + } + } + + protected static void setSecurityManager(SecurityManager securityManager) { + SecurityUtils.setSecurityManager(securityManager); + } + + protected static SecurityManager getSecurityManager() { + return SecurityUtils.getSecurityManager(); + } + + @AfterAll + public static void tearDownShiro() { + doClearSubject(); + try { + SecurityManager securityManager = getSecurityManager(); + LifecycleUtils.destroy(securityManager); + } catch (UnavailableSecurityManagerException e) { + //we don't care about this when cleaning up the test environment + //(for example, maybe the subclass is a unit test and it didn't + // need a SecurityManager instance because it was using only mock Subject instances) + } + setSecurityManager(null); + } +} diff --git a/core/src/test/java/org/apache/shiro/test/ExampleShiroIntegrationTest.java b/core/src/test/java/org/apache/shiro/test/ExampleShiroIntegrationTest.java index c01b9b80ab..202d1dc62d 100644 --- a/core/src/test/java/org/apache/shiro/test/ExampleShiroIntegrationTest.java +++ b/core/src/test/java/org/apache/shiro/test/ExampleShiroIntegrationTest.java @@ -1,63 +1,63 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.test; - -import org.apache.shiro.ini.IniSecurityManagerFactory; -import org.apache.shiro.mgt.SecurityManager; -import org.apache.shiro.subject.Subject; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.test; + +import org.apache.shiro.ini.IniSecurityManagerFactory; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.subject.Subject; import org.apache.shiro.lang.util.Factory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -/** - * Simple example test class to be used to show how one might write Shiro-compatible unit tests. - * - * @since 1.2 - */ -public class ExampleShiroIntegrationTest extends AbstractShiroTest { - - @BeforeAll - public static void beforeClass() { - //0. Build and set the SecurityManager used to build Subject instances used in your tests - // This typically only needs to be done once per class if your shiro.ini doesn't change, - // otherwise, you'll need to do this logic in each test that is different - Factory factory = new IniSecurityManagerFactory("classpath:test.shiro.ini"); - setSecurityManager(factory.getInstance()); +/** + * Simple example test class to be used to show how one might write Shiro-compatible unit tests. + * + * @since 1.2 + */ +public class ExampleShiroIntegrationTest extends AbstractShiroTest { + + @BeforeAll + public static void beforeClass() { + //0. Build and set the SecurityManager used to build Subject instances used in your tests + // This typically only needs to be done once per class if your shiro.ini doesn't change, + // otherwise, you'll need to do this logic in each test that is different + Factory factory = new IniSecurityManagerFactory("classpath:test.shiro.ini"); + setSecurityManager(factory.getInstance()); } @Test - void testSimple() { - //1. Build the Subject instance for the test to run: - Subject subjectUnderTest = new Subject.Builder(getSecurityManager()).buildSubject(); - - //2. Bind the subject to the current thread: - setSubject(subjectUnderTest); - - //perform test logic here. Any call to - //SecurityUtils.getSubject() directly (or nested in the - //call stack) will work properly. - } - - @AfterEach - public void tearDownSubject() { - //3. Unbind the subject from the current thread: - clearSubject(); - } -} + void testSimple() { + //1. Build the Subject instance for the test to run: + Subject subjectUnderTest = new Subject.Builder(getSecurityManager()).buildSubject(); + + //2. Bind the subject to the current thread: + setSubject(subjectUnderTest); + + //perform test logic here. Any call to + //SecurityUtils.getSubject() directly (or nested in the + //call stack) will work properly. + } + + @AfterEach + public void tearDownSubject() { + //3. Unbind the subject from the current thread: + clearSubject(); + } +} diff --git a/core/src/test/java/org/apache/shiro/test/SecurityManagerTestSupport.java b/core/src/test/java/org/apache/shiro/test/SecurityManagerTestSupport.java index 2bbbc788a8..a793337045 100644 --- a/core/src/test/java/org/apache/shiro/test/SecurityManagerTestSupport.java +++ b/core/src/test/java/org/apache/shiro/test/SecurityManagerTestSupport.java @@ -72,7 +72,8 @@ protected Subject createSubject() { /** * Associates the {@code consumer} with the {@code subject} and executes. If an exeception was thrown by the * consumer, it is re-thrown by this method. - * @param subject The subject to bind to the current thread. + * + * @param subject The subject to bind to the current thread. * @param consumer The block of code to run under the context of the subject. * @throws Exception propagates any exception thrown by the consumer. */ diff --git a/core/src/test/java/org/apache/shiro/util/AntPathMatcherTests.java b/core/src/test/java/org/apache/shiro/util/AntPathMatcherTests.java index 12046c30a4..682ccc89e9 100644 --- a/core/src/test/java/org/apache/shiro/util/AntPathMatcherTests.java +++ b/core/src/test/java/org/apache/shiro/util/AntPathMatcherTests.java @@ -21,17 +21,21 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +@SuppressWarnings("checkstyle:LineLength") /** * Unit tests for {@link AntPathMatcher}. - * + *

* Adapted from Spring Framework's similar AntPathMatcherTests */ public class AntPathMatcherTests { private final AntPathMatcher pathMatcher = new AntPathMatcher(); + @SuppressWarnings("checkstyle:MethodLength") @Test void match() { // test exact matching @@ -125,6 +129,7 @@ void matchWithNullPath() { assertFalse(pathMatcher.match(null, null)); } + @SuppressWarnings("checkstyle:MethodLength") @Test void matchStart() { // test exact matching @@ -274,6 +279,7 @@ void uniqueDelimiter() { assertFalse(pathMatcher.match(".*bla.test", "XXXbl.test")); } + @SuppressWarnings("checkstyle:LineLength") @Test void extractPathWithinPattern() throws Exception { assertEquals(pathMatcher.extractPathWithinPattern("/docs/commit.html", "/docs/commit.html"), ""); @@ -325,4 +331,4 @@ void matches() { void isPatternWithNullPath() { assertFalse(pathMatcher.isPattern(null)); } -} \ No newline at end of file +} diff --git a/core/src/test/java/org/apache/shiro/util/PermissionUtilsTest.java b/core/src/test/java/org/apache/shiro/util/PermissionUtilsTest.java index b33e0718f5..0a449eca9a 100644 --- a/core/src/test/java/org/apache/shiro/util/PermissionUtilsTest.java +++ b/core/src/test/java/org/apache/shiro/util/PermissionUtilsTest.java @@ -35,6 +35,7 @@ class PermissionUtilsTest { private static final PermissionResolver RESOLVER = new WildcardPermissionResolver(true); + @SuppressWarnings("checkstyle:MethodName") @Test void SHIRO_902_quoted_permissions() { // given diff --git a/core/src/test/java/org/apache/shiro/util/RegExPatternMatcherTest.java b/core/src/test/java/org/apache/shiro/util/RegExPatternMatcherTest.java index 9bf0315f3e..bace343840 100644 --- a/core/src/test/java/org/apache/shiro/util/RegExPatternMatcherTest.java +++ b/core/src/test/java/org/apache/shiro/util/RegExPatternMatcherTest.java @@ -20,7 +20,8 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Unit tests for the {@link RegExPatternMatcher}. @@ -61,7 +62,7 @@ private void assertPatternMatch(String pattern, String path) { } private void assertPatternMatch(String pattern, String path, PatternMatcher pm) { - assertTrue(pm.matches(pattern, path), "Expected path '" + path + "' to match pattern '" + pattern + "'" ); + assertTrue(pm.matches(pattern, path), "Expected path '" + path + "' to match pattern '" + pattern + "'"); } private void assertPatternNotMatch(String pattern, String path) { @@ -69,6 +70,6 @@ private void assertPatternNotMatch(String pattern, String path) { } private void assertPatternNotMatch(String pattern, String path, PatternMatcher pm) { - assertFalse(pm.matches(pattern, path), "Expected path '" + path + "' to NOT match pattern '" + pattern + "'" ); + assertFalse(pm.matches(pattern, path), "Expected path '" + path + "' to NOT match pattern '" + pattern + "'"); } } diff --git a/core/src/test/java/org/apache/shiro/util/StringUtilsTest.java b/core/src/test/java/org/apache/shiro/util/StringUtilsTest.java index 66bc894913..5dc352777c 100644 --- a/core/src/test/java/org/apache/shiro/util/StringUtilsTest.java +++ b/core/src/test/java/org/apache/shiro/util/StringUtilsTest.java @@ -18,11 +18,12 @@ */ package org.apache.shiro.util; -import static org.junit.jupiter.api.Assertions.*; - import org.apache.shiro.lang.util.StringUtils; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; /** * @since 0.9 @@ -41,7 +42,7 @@ void splitWithCommas() { String line = "shall,we,play,a,game?"; String[] split = StringUtils.split(line); assertNotNull(split); - assertTrue(split.length == 5); + assertEquals(5, split.length); assertEquals("shall", split[0]); assertEquals("we", split[1]); assertEquals("play", split[2]); @@ -54,7 +55,7 @@ void splitWithCommasAndSpaces() { String line = "shall,we , play, a,game?"; String[] split = StringUtils.split(line); assertNotNull(split); - assertTrue(split.length == 5); + assertEquals(5, split.length); assertEquals("shall", split[0]); assertEquals("we", split[1]); assertEquals("play", split[2]); @@ -67,7 +68,7 @@ void splitWithQuotedCommasAndSpaces() { String line = "shall, \"we, play\", a, game?"; String[] split = StringUtils.split(line); assertNotNull(split); - assertTrue(split.length == 4); + assertEquals(4, split.length); assertEquals("shall", split[0]); assertEquals("we, play", split[1]); assertEquals("a", split[2]); @@ -79,7 +80,7 @@ void splitWithQuotedCommasAndSpacesAndDifferentQuoteChars() { String line = "authc, test[blah], test[1,2,3], test[]"; String[] split = StringUtils.split(line, ',', '[', ']', false, true); assertNotNull(split); - assertTrue(split.length == 4); + assertEquals(4, split.length); assertEquals("authc", split[0]); assertEquals("testblah", split[1]); assertEquals("test1,2,3", split[2]); @@ -91,7 +92,7 @@ void splitWithQuotedCommasAndSpacesAndDifferentQuoteCharsWhileRetainingQuotes() String line = "authc, test[blah], test[1,2,3], test[]"; String[] split = StringUtils.split(line, ',', '[', ']', true, true); assertNotNull(split); - assertTrue(split.length == 4); + assertEquals(4, split.length); assertEquals("authc", split[0]); assertEquals("test[blah]", split[1]); assertEquals("test[1,2,3]", split[2]); @@ -103,7 +104,7 @@ void splitTestWithQuotedCommas() { String line = "authc, test[blah], test[\"1,2,3\"], test[]"; String[] split = StringUtils.split(line); assertNotNull(split); - assertTrue(split.length == 4); + assertEquals(4, split.length); assertEquals("authc", split[0]); assertEquals("test[blah]", split[1]); assertEquals("test[1,2,3]", split[2]); @@ -115,7 +116,7 @@ void splitWithQuotedCommasAndSpacesAndEscapedQuotes() { String line = "shall, \"\"\"we, play\", a, \"\"\"game?"; String[] split = StringUtils.split(line); assertNotNull(split); - assertTrue(split.length == 4); + assertEquals(4, split.length); assertEquals("shall", split[0]); assertEquals("\"we, play", split[1]); assertEquals("a", split[2]); diff --git a/core/src/test/resources/org/apache/shiro/config/IniSecurityManagerFactoryTest.propsRealm.properties b/core/src/test/resources/org/apache/shiro/config/IniSecurityManagerFactoryTest.propsRealm.properties index 9170a2711a..e3ed3561d4 100644 --- a/core/src/test/resources/org/apache/shiro/config/IniSecurityManagerFactoryTest.propsRealm.properties +++ b/core/src/test/resources/org/apache/shiro/config/IniSecurityManagerFactoryTest.propsRealm.properties @@ -16,15 +16,13 @@ # specific language governing permissions and limitations # under the License. # - # ------------------------------ # Users and their assigned roles # ------------------------------ # user 'root' with password 'secret' and the 'root' role -user.root = secret,root - +user.root=secret,root # ------------------------------- # Roles with assigned permissions # ------------------------------- # 'root' role has all permissions, indicated by the wildcard '*' -role.root = * +role.root=* diff --git a/core/src/test/resources/test.shiro.ini b/core/src/test/resources/test.shiro.ini index 40a3bba883..884752a9c7 100644 --- a/core/src/test/resources/test.shiro.ini +++ b/core/src/test/resources/test.shiro.ini @@ -1,21 +1,21 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -[users] +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +[users] jsmith = jsmith,role1 \ No newline at end of file diff --git a/crypto/cipher/pom.xml b/crypto/cipher/pom.xml index a190c73b55..7b11ce4e15 100644 --- a/crypto/cipher/pom.xml +++ b/crypto/cipher/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + org.apache.shiro diff --git a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/AesCipherService.java b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/AesCipherService.java index 3b1a0585b5..5d402336dd 100644 --- a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/AesCipherService.java +++ b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/AesCipherService.java @@ -27,10 +27,12 @@ * The AES algorithm can support key sizes of {@code 128}, {@code 192} and {@code 256} bits*. This implementation * defaults to 128 bits. *

- * Note that this class retains changes the parent class's default {@link OperationMode#CBC CBC} mode to {@link OperationMode#GCM GCM} of operation - * instead of the typical JDK default of {@link OperationMode#ECB ECB}. {@code ECB} should not be used in - * security-sensitive environments because {@code ECB} does not allow for initialization vectors, which are - * considered necessary for strong encryption. See the {@link DefaultBlockCipherService parent class}'s JavaDoc and the + * Note that this class retains changes the parent class's default + * {@link OperationMode#CBC CBC} modeto {@link OperationMode#GCM GCM} of operation + * instead of the typical JDK default of {@link OperationMode#ECB ECB}. + * {@code ECB} should not be used in security-sensitive environments because {@code ECB} + * does not allow for initialization vectors, which are considered necessary for strong encryption. + * See the {@link DefaultBlockCipherService parent class}'s JavaDoc and the * {@link JcaCipherService JcaCipherService} JavaDoc for more on why the JDK default should not be used and is not * used in this implementation. *

@@ -91,6 +93,7 @@ public class AesCipherService extends DefaultBlockCipherService { * {@code AES/GCM/NoPadding}. *

* NOTE: As of Java 14, setting a streaming padding for the above example will throw a NoSuchAlgorithmException + * * @see JDK-8180392 */ public AesCipherService() { @@ -105,7 +108,7 @@ public AesCipherService() { protected AlgorithmParameterSpec createParameterSpec(byte[] iv, boolean streaming) { if ((streaming && OperationMode.GCM.name().equals(getStreamingModeName())) - || (!streaming && OperationMode.GCM.name().equals(getModeName()))) { + || (!streaming && OperationMode.GCM.name().equals(getModeName()))) { return new GCMParameterSpec(getKeySize(), iv); } diff --git a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/BlowfishCipherService.java b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/BlowfishCipherService.java index 894193d37a..45e47e55cb 100644 --- a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/BlowfishCipherService.java +++ b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/BlowfishCipherService.java @@ -87,6 +87,7 @@ public class BlowfishCipherService extends DefaultBlockCipherService { */ public BlowfishCipherService() { super(ALGORITHM_NAME); - setInitializationVectorSize(BLOCK_SIZE); //like most block ciphers, the IV size is the same as the block size + //like most block ciphers, the IV size is the same as the block size + setInitializationVectorSize(BLOCK_SIZE); } } diff --git a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/DefaultBlockCipherService.java b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/DefaultBlockCipherService.java index d147938e75..7496159fdf 100644 --- a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/DefaultBlockCipherService.java +++ b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/DefaultBlockCipherService.java @@ -122,7 +122,8 @@ * *

* These attributes have the same meaning as the {@code mode}, {@code blockSize}, and {@code paddingScheme} attributes - * described above, but they are applied during streaming method invocations only ({@link #encrypt(java.io.InputStream, java.io.OutputStream, byte[])} + * described above, but they are applied during streaming method invocations only + * ({@link #encrypt(java.io.InputStream, java.io.OutputStream, byte[])} * and {@link #decrypt(java.io.InputStream, java.io.OutputStream, byte[])}). * * @see BlowfishCipherService @@ -135,18 +136,30 @@ public class DefaultBlockCipherService extends AbstractSymmetricCipherService { private static final int DEFAULT_BLOCK_SIZE = 0; private static final String TRANSFORMATION_STRING_DELIMITER = "/"; - private static final int DEFAULT_STREAMING_BLOCK_SIZE = 8; //8 bits (1 byte) + //8 bits (1 byte) + private static final int DEFAULT_STREAMING_BLOCK_SIZE = 8; private String modeName; - private int blockSize; //size in bits (not bytes) - i.e. a blockSize of 8 equals 1 byte. negative or zero value = use system default + + /** + * size in bits (not bytes) - i.e. a blockSize of 8 equals 1 byte. negative or zero value = use system default + */ + private int blockSize; private String paddingSchemeName; private String streamingModeName; private int streamingBlockSize; private String streamingPaddingSchemeName; - private String transformationString; //cached value - rebuilt whenever any of its constituent parts change - private String streamingTransformationString; //cached value - rebuilt whenever any of its constituent parts change + /** + * cached value - rebuilt whenever any of its constituent parts change. + */ + private String transformationString; + + /** + * cached value - rebuilt whenever any of its constituent parts change. + */ + private String streamingTransformationString; /** @@ -166,7 +179,8 @@ public DefaultBlockCipherService(String algorithmName) { this.modeName = OperationMode.CBC.name(); this.paddingSchemeName = PaddingScheme.PKCS5.getTransformationName(); - this.blockSize = DEFAULT_BLOCK_SIZE; //0 = use the JCA provider's default + //0 = use the JCA provider's default + this.blockSize = DEFAULT_BLOCK_SIZE; this.streamingModeName = OperationMode.CBC.name(); this.streamingPaddingSchemeName = PaddingScheme.PKCS5.getTransformationName(); @@ -186,8 +200,8 @@ public DefaultBlockCipherService(String algorithmName) { * The default value is {@code null} to retain the JCA Provider default. * * @return the cipher operation mode name (as a String) to be used when constructing the - * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA Provider default - * mode for the specified {@link #getAlgorithmName() algorithm} should be used. + * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA Provider default + * mode for the specified {@link #getAlgorithmName() algorithm} should be used. */ public String getModeName() { return modeName; @@ -256,8 +270,8 @@ public void setMode(OperationMode mode) { * The default value is {@code null} to retain the JCA Provider default. * * @return the padding scheme name (as a String) to be used when constructing the - * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA Provider default - * padding scheme for the specified {@link #getAlgorithmName() algorithm} should be used. + * {@link javax.crypto.Cipher Cipher} transformation string, or {@code null} if the JCA Provider default + * padding scheme for the specified {@link #getAlgorithmName() algorithm} should be used. */ public String getPaddingSchemeName() { return paddingSchemeName; @@ -328,8 +342,8 @@ public void setPaddingScheme(PaddingScheme paddingScheme) { * The default value is {@code 0} which retains the JCA Provider default. * * @return the block cipher block size to be used when constructing the - * {@link javax.crypto.Cipher Cipher} transformation string, or {@code 0} if the JCA Provider default - * block size for the specified {@link #getAlgorithmName() algorithm} should be used. + * {@link javax.crypto.Cipher Cipher} transformation string, or {@code 0} if the JCA Provider default + * block size for the specified {@link #getAlgorithmName() algorithm} should be used. */ public int getBlockSize() { return blockSize; @@ -376,9 +390,9 @@ public String getStreamingModeName() { } private boolean isModeStreamingCompatible(String modeName) { - return modeName != null && - !modeName.equalsIgnoreCase(OperationMode.ECB.name()) && - !modeName.equalsIgnoreCase(OperationMode.NONE.name()); + return modeName != null + && !modeName.equalsIgnoreCase(OperationMode.ECB.name()) + && !modeName.equalsIgnoreCase(OperationMode.NONE.name()); } /** @@ -483,12 +497,12 @@ private String buildTransformationString(String modeName, String paddingSchemeNa * * @param modeName the raw text name of the mode of operation * @return {@code true} if the specified cipher operation mode name supports initialization vectors, - * {@code false} otherwise. + * {@code false} otherwise. */ private boolean isModeInitializationVectorCompatible(String modeName) { - return modeName != null && - !modeName.equalsIgnoreCase(OperationMode.ECB.name()) && - !modeName.equalsIgnoreCase(OperationMode.NONE.name()); + return modeName != null + && !modeName.equalsIgnoreCase(OperationMode.ECB.name()) + && !modeName.equalsIgnoreCase(OperationMode.NONE.name()); } /** @@ -500,7 +514,7 @@ private boolean isModeInitializationVectorCompatible(String modeName) { * * @param streaming whether or not streaming is being performed * @return {@code true} if streaming or a value computed based on if the currently configured mode is compatible - * with initialization vectors. + * with initialization vectors. */ @Override protected boolean isGenerateInitializationVectors(boolean streaming) { @@ -512,17 +526,17 @@ protected byte[] generateInitializationVector(boolean streaming) { if (streaming) { String streamingModeName = getStreamingModeName(); if (!isModeInitializationVectorCompatible(streamingModeName)) { - String msg = "streamingMode attribute value [" + streamingModeName + "] does not support " + - "Initialization Vectors. Ensure the streamingMode value represents an operation mode " + - "that is compatible with initialization vectors."; + String msg = "streamingMode attribute value [" + streamingModeName + "] does not support " + + "Initialization Vectors. Ensure the streamingMode value represents an operation mode " + + "that is compatible with initialization vectors."; throw new IllegalStateException(msg); } } else { String modeName = getModeName(); if (!isModeInitializationVectorCompatible(modeName)) { - String msg = "mode attribute value [" + modeName + "] does not support " + - "Initialization Vectors. Ensure the mode value represents an operation mode " + - "that is compatible with initialization vectors."; + String msg = "mode attribute value [" + modeName + "] does not support " + + "Initialization Vectors. Ensure the mode value represents an operation mode " + + "that is compatible with initialization vectors."; throw new IllegalStateException(msg); } } diff --git a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/JcaCipherService.java b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/JcaCipherService.java index 49d738b0fa..fb832d201b 100644 --- a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/JcaCipherService.java +++ b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/JcaCipherService.java @@ -67,12 +67,13 @@ * * @since 1.0 */ +@SuppressWarnings("checkstyle:MethodCount") public abstract class JcaCipherService implements CipherService { /** * Internal private log instance. */ - private static final Logger log = LoggerFactory.getLogger(JcaCipherService.class); + private static final Logger LOGGER = LoggerFactory.getLogger(JcaCipherService.class); /** * Default key size (in bits) for generated keys. @@ -129,7 +130,8 @@ protected JcaCipherService(String algorithmName) { } this.algorithmName = algorithmName; this.keySize = DEFAULT_KEY_SIZE; - this.initializationVectorSize = DEFAULT_KEY_SIZE; //default to same size as the key size (a common algorithm practice) + //default to same size as the key size (a common algorithm practice) + this.initializationVectorSize = DEFAULT_KEY_SIZE; this.streamingBufferSize = DEFAULT_STREAMING_BUFFER_SIZE; this.generateInitializationVectors = true; } @@ -189,8 +191,8 @@ public int getInitializationVectorSize() { */ public void setInitializationVectorSize(int initializationVectorSize) throws IllegalArgumentException { if (initializationVectorSize % BITS_PER_BYTE != 0) { - String msg = "Initialization vector sizes are specified in bits, but must be a multiple of 8 so they " + - "can be easily represented as a byte array."; + String msg = "Initialization vector sizes are specified in bits, but must be a multiple of 8 so they " + + "can be easily represented as a byte array."; throw new IllegalArgumentException(msg); } this.initializationVectorSize = initializationVectorSize; @@ -208,7 +210,7 @@ protected boolean isGenerateInitializationVectors(boolean streaming) { * Default size is {@code 512} bytes. * * @return the size of the internal buffer used to transfer data from one stream to another during stream - * operations + * operations */ public int getStreamingBufferSize() { return streamingBufferSize; @@ -233,7 +235,7 @@ public void setStreamingBufferSize(int streamingBufferSize) { * algorithm needs one, the JDK {@code SHA1PRNG} instance will be used by default. * * @return a source of randomness for encryption operations. If one is not configured, and the underlying - * algorithm needs one, the JDK {@code SHA1PRNG} instance will be used by default. + * algorithm needs one, the JDK {@code SHA1PRNG} instance will be used by default. */ public SecureRandom getSecureRandom() { return secureRandom; @@ -254,8 +256,8 @@ protected static SecureRandom getDefaultSecureRandom() { try { return java.security.SecureRandom.getInstance(RANDOM_NUM_GENERATOR_ALGORITHM_NAME); } catch (java.security.NoSuchAlgorithmException e) { - log.debug("The SecureRandom SHA1PRNG algorithm is not available on the current platform. Using the " + - "platform's default SecureRandom algorithm.", e); + LOGGER.debug("The SecureRandom SHA1PRNG algorithm is not available on the current platform. Using the " + + "platform's default SecureRandom algorithm.", e); return new java.security.SecureRandom(); } } @@ -276,7 +278,7 @@ protected SecureRandom ensureSecureRandom() { * * @param streaming if the transformation string is going to be used for a Cipher for stream-based encryption or not. * @return the transformation string to use with the {@link javax.crypto.Cipher#getInstance} invocation when - * creating a new {@code Cipher} instance. + * creating a new {@code Cipher} instance. */ protected String getTransformationString(boolean streaming) { return getAlgorithmName(); @@ -285,9 +287,9 @@ protected String getTransformationString(boolean streaming) { protected byte[] generateInitializationVector(boolean streaming) { int size = getInitializationVectorSize(); if (size <= 0) { - String msg = "initializationVectorSize property must be greater than zero. This number is " + - "typically set in the " + CipherService.class.getSimpleName() + " subclass constructor. " + - "Also check your configuration to ensure that if you are setting a value, it is positive."; + String msg = "initializationVectorSize property must be greater than zero. This number is " + + "typically set in the " + CipherService.class.getSimpleName() + " subclass constructor. " + + "Also check your configuration to ensure that if you are setting a value, it is positive."; throw new IllegalStateException(msg); } if (size % BITS_PER_BYTE != 0) { @@ -307,8 +309,8 @@ public ByteSource encrypt(byte[] plaintext, byte[] key) { if (generate) { ivBytes = generateInitializationVector(false); if (ivBytes == null || ivBytes.length == 0) { - throw new IllegalStateException("Initialization vector generation is enabled - generated vector " + - "cannot be null or empty."); + throw new IllegalStateException("Initialization vector generation is enabled - generated vector " + + "cannot be null or empty."); } } return encrypt(plaintext, key, ivBytes, generate); @@ -316,13 +318,13 @@ public ByteSource encrypt(byte[] plaintext, byte[] key) { private ByteSource encrypt(byte[] plaintext, byte[] key, byte[] iv, boolean prependIv) throws CryptoException { - final int MODE = javax.crypto.Cipher.ENCRYPT_MODE; + final int mode = javax.crypto.Cipher.ENCRYPT_MODE; byte[] output; if (prependIv && iv != null && iv.length > 0) { - byte[] encrypted = crypt(plaintext, key, iv, MODE); + byte[] encrypted = crypt(plaintext, key, iv, mode); output = new byte[iv.length + encrypted.length]; @@ -334,12 +336,12 @@ private ByteSource encrypt(byte[] plaintext, byte[] key, byte[] iv, boolean prep // + encrypted bytes: System.arraycopy(encrypted, 0, output, iv.length, encrypted.length); } else { - output = crypt(plaintext, key, iv, MODE); + output = crypt(plaintext, key, iv, mode); } - if (log.isTraceEnabled()) { - log.trace("Incoming plaintext of size " + (plaintext != null ? plaintext.length : 0) + ". Ciphertext " + - "byte array is size " + (output != null ? output.length : 0)); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Incoming plaintext of size " + (plaintext != null ? plaintext.length : 0) + ". Ciphertext " + + "byte array is size " + (output != null ? output.length : 0)); } return ByteSource.Util.bytes(output); @@ -388,9 +390,9 @@ ByteSource decryptInternal(byte[] ciphertext, byte[] key) throws CryptoException } private ByteSource decryptInternal(byte[] ciphertext, byte[] key, byte[] iv) throws CryptoException { - if (log.isTraceEnabled()) { - log.trace("Attempting to decrypt incoming byte array of length " + - (ciphertext != null ? ciphertext.length : 0)); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Attempting to decrypt incoming byte array of length " + + (ciphertext != null ? ciphertext.length : 0)); } byte[] decrypted = crypt(ciphertext, key, iv, javax.crypto.Cipher.DECRYPT_MODE); return decrypted == null ? null : ByteSource.Util.bytes(decrypted); @@ -412,10 +414,10 @@ private javax.crypto.Cipher newCipherInstance(boolean streaming) throws CryptoEx try { return javax.crypto.Cipher.getInstance(transformationString); } catch (Exception e) { - String msg = "Unable to acquire a Java JCA Cipher instance using " + - javax.crypto.Cipher.class.getName() + ".getInstance( \"" + transformationString + "\" ). " + - getAlgorithmName() + " under this configuration is required for the " + - getClass().getName() + " instance to function."; + String msg = "Unable to acquire a Java JCA Cipher instance using " + + javax.crypto.Cipher.class.getName() + ".getInstance( \"" + transformationString + "\" ). " + + getAlgorithmName() + " under this configuration is required for the " + + getClass().getName() + " instance to function."; throw new CryptoException(msg, e); } } @@ -508,8 +510,8 @@ public void encrypt(InputStream in, OutputStream out, byte[] key) throws CryptoE if (generate) { iv = generateInitializationVector(true); if (iv == null || iv.length == 0) { - throw new IllegalStateException("Initialization vector generation is enabled - generated vector " + - "cannot be null or empty."); + throw new IllegalStateException("Initialization vector generation is enabled - generated vector " + + "cannot be null or empty."); } } encrypt(in, out, key, iv, generate); @@ -552,9 +554,8 @@ private void decrypt(InputStream in, OutputStream out, byte[] key, boolean ivPre } if (read != ivByteSize) { - throw new CryptoException("Unable to read initialization vector bytes from the InputStream. " + - "This is required when initialization vectors are autogenerated during an encryption " + - "operation."); + throw new CryptoException("Unable to read initialization vector bytes from the InputStream. " + + "This is required when initialization vectors are autogenerated during an encryption operation."); } } diff --git a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/PaddingScheme.java b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/PaddingScheme.java index 2d7264cdb6..5a3e16b401 100644 --- a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/PaddingScheme.java +++ b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/PaddingScheme.java @@ -147,7 +147,7 @@ public enum PaddingScheme { private final String transformationName; - private PaddingScheme(String transformationName) { + PaddingScheme(String transformationName) { this.transformationName = transformationName; } @@ -156,7 +156,7 @@ private PaddingScheme(String transformationName) { * {@code transformation string}. * * @return the actual string name to use when building the {@link javax.crypto.Cipher Cipher} - * {@code transformation string}. + * {@code transformation string}. * @see javax.crypto.Cipher#getInstance(String) */ public String getTransformationName() { diff --git a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/SimpleByteSourceBroker.java b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/SimpleByteSourceBroker.java index c5f7625b75..8e872f558c 100644 --- a/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/SimpleByteSourceBroker.java +++ b/crypto/cipher/src/main/java/org/apache/shiro/crypto/cipher/SimpleByteSourceBroker.java @@ -23,6 +23,7 @@ import org.apache.shiro.lang.util.Destroyable; import java.io.IOException; + import org.apache.shiro.lang.util.ByteSourceWrapper; import org.apache.shiro.lang.util.ByteUtils; @@ -34,7 +35,7 @@ public class SimpleByteSourceBroker implements ByteSourceBroker, Destroyable { private JcaCipherService cipherService; private byte[] ciphertext; private byte[] key; - private boolean destroyed = false; + private boolean destroyed; public SimpleByteSourceBroker(JcaCipherService cipherService, byte[] ciphertext, byte[] key) { this.cipherService = cipherService; @@ -58,7 +59,8 @@ public synchronized void useBytes(ByteSourceUser user) { public byte[] getClonedBytes() { ByteSource byteSource = cipherService.decryptInternal(ciphertext, key); - return byteSource.getBytes(); // this's a newly created byte array + // this's a newly created byte array + return byteSource.getBytes(); } public void destroy() throws Exception { diff --git a/crypto/cipher/src/test/groovy/org/apache/shiro/crypto/cipher/AesCipherServiceTest.groovy b/crypto/cipher/src/test/groovy/org/apache/shiro/crypto/cipher/AesCipherServiceTest.groovy index 6188804a12..7da9895fbe 100644 --- a/crypto/cipher/src/test/groovy/org/apache/shiro/crypto/cipher/AesCipherServiceTest.groovy +++ b/crypto/cipher/src/test/groovy/org/apache/shiro/crypto/cipher/AesCipherServiceTest.groovy @@ -38,8 +38,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue class AesCipherServiceTest { private static final String[] PLAINTEXTS = [ - "Hello, this is a test.", - "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + "Hello, this is a test.", + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." ] AesCipherServiceTest() { diff --git a/crypto/cipher/src/test/groovy/org/apache/shiro/crypto/cipher/BlowfishCipherServiceTest.groovy b/crypto/cipher/src/test/groovy/org/apache/shiro/crypto/cipher/BlowfishCipherServiceTest.groovy index edac0b6523..bbb0b8f58b 100644 --- a/crypto/cipher/src/test/groovy/org/apache/shiro/crypto/cipher/BlowfishCipherServiceTest.groovy +++ b/crypto/cipher/src/test/groovy/org/apache/shiro/crypto/cipher/BlowfishCipherServiceTest.groovy @@ -34,8 +34,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue public class BlowfishCipherServiceTest { private static final String[] PLAINTEXTS = [ - "Hello, this is a test.", - "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + "Hello, this is a test.", + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." ]; @Test diff --git a/crypto/core/pom.xml b/crypto/core/pom.xml index 6105d06b10..2e5e210e54 100644 --- a/crypto/core/pom.xml +++ b/crypto/core/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + org.apache.shiro diff --git a/crypto/core/src/main/java/org/apache/shiro/crypto/RandomNumberGenerator.java b/crypto/core/src/main/java/org/apache/shiro/crypto/RandomNumberGenerator.java index 080d2f7a47..7a5a85a1e5 100644 --- a/crypto/core/src/main/java/org/apache/shiro/crypto/RandomNumberGenerator.java +++ b/crypto/core/src/main/java/org/apache/shiro/crypto/RandomNumberGenerator.java @@ -1,72 +1,72 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.crypto; - -import org.apache.shiro.lang.util.ByteSource; - -/** - * A component that can generate random number/byte values as needed. Useful in cryptography or security scenarios - * where random byte arrays are needed, such as for password salts, nonces, initialization vectors and other seeds. - *

- * This is essentially the same as a {@link java.security.SecureRandom SecureRandom}, and indeed implementations - * of this interface will probably all use {@link java.security.SecureRandom SecureRandom} instances, but this - * interface provides a few additional benefits to end-users: - *

    - *
  • It is an interface rather than the JDK's {@code SecureRandom} concrete implementation. Implementation details - * can be customized as necessary based on the application's needs
  • - *
  • Default per-instance behavior can be customized on implementations, typically via JavaBeans mutators.
  • - *
  • Perhaps most important for Shiro end-users, tt can more easily be used as a source of cryptographic seed data, - * and the data returned is already in a more convenient {@link ByteSource ByteSource} format in case that data needs - * to be {@link org.apache.shiro.lang.util.ByteSource#toHex() hex} or - * {@link org.apache.shiro.lang.util.ByteSource#toBase64() base64}-encoded.
  • - *
- * For example, consider the following example generating password salts for new user accounts: - *
- * RandomNumberGenerator saltGenerator = new {@link org.apache.shiro.crypto.SecureRandomNumberGenerator SecureRandomNumberGenerator}();
- * User user = new User();
- * user.setPasswordSalt(saltGenerator.nextBytes().toBase64());
- * userDAO.save(user);
- * 
- * - * @since 1.1 - */ -public interface RandomNumberGenerator { - - /** - * Generates a byte array of fixed length filled with random data, often useful for generating salts, - * initialization vectors or other seed data. The length is specified as a configuration - * value on the underlying implementation. - *

- * If you'd like per-invocation control the number of bytes generated, use the - * {@link #nextBytes(int) nextBytes(int)} method instead. - * - * @return a byte array of fixed length filled with random data. - * @see #nextBytes(int) - */ - ByteSource nextBytes(); - - /** - * Generates a byte array of the specified length filled with random data. - * - * @param numBytes the number of bytes to be populated with random data. - * @return a byte array of the specified length filled with random data. - * @see #nextBytes() - */ - ByteSource nextBytes(int numBytes); -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.crypto; + +import org.apache.shiro.lang.util.ByteSource; + +/** + * A component that can generate random number/byte values as needed. Useful in cryptography or security scenarios + * where random byte arrays are needed, such as for password salts, nonces, initialization vectors and other seeds. + *

+ * This is essentially the same as a {@link java.security.SecureRandom SecureRandom}, and indeed implementations + * of this interface will probably all use {@link java.security.SecureRandom SecureRandom} instances, but this + * interface provides a few additional benefits to end-users: + *

    + *
  • It is an interface rather than the JDK's {@code SecureRandom} concrete implementation. Implementation details + * can be customized as necessary based on the application's needs
  • + *
  • Default per-instance behavior can be customized on implementations, typically via JavaBeans mutators.
  • + *
  • Perhaps most important for Shiro end-users, tt can more easily be used as a source of cryptographic seed data, + * and the data returned is already in a more convenient {@link ByteSource ByteSource} format in case that data needs + * to be {@link org.apache.shiro.lang.util.ByteSource#toHex() hex} or + * {@link org.apache.shiro.lang.util.ByteSource#toBase64() base64}-encoded.
  • + *
+ * For example, consider the following example generating password salts for new user accounts: + *
+ * RandomNumberGenerator saltGenerator = new {@link SecureRandomNumberGenerator SecureRandomNumberGenerator}();
+ * User user = new User();
+ * user.setPasswordSalt(saltGenerator.nextBytes().toBase64());
+ * userDAO.save(user);
+ * 
+ * + * @since 1.1 + */ +public interface RandomNumberGenerator { + + /** + * Generates a byte array of fixed length filled with random data, often useful for generating salts, + * initialization vectors or other seed data. The length is specified as a configuration + * value on the underlying implementation. + *

+ * If you'd like per-invocation control the number of bytes generated, use the + * {@link #nextBytes(int) nextBytes(int)} method instead. + * + * @return a byte array of fixed length filled with random data. + * @see #nextBytes(int) + */ + ByteSource nextBytes(); + + /** + * Generates a byte array of the specified length filled with random data. + * + * @param numBytes the number of bytes to be populated with random data. + * @return a byte array of the specified length filled with random data. + * @see #nextBytes() + */ + ByteSource nextBytes(int numBytes); +} diff --git a/crypto/core/src/main/java/org/apache/shiro/crypto/SecureRandomNumberGenerator.java b/crypto/core/src/main/java/org/apache/shiro/crypto/SecureRandomNumberGenerator.java index 8bcb0f5fb1..25a091eb93 100644 --- a/crypto/core/src/main/java/org/apache/shiro/crypto/SecureRandomNumberGenerator.java +++ b/crypto/core/src/main/java/org/apache/shiro/crypto/SecureRandomNumberGenerator.java @@ -1,120 +1,121 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.crypto; - -import org.apache.shiro.lang.util.ByteSource; - -import java.security.SecureRandom; - -/** - * Default implementation of the {@link RandomNumberGenerator RandomNumberGenerator} interface, backed by a - * {@link SecureRandom SecureRandom} instance. - *

- * This class is a little easier to use than using the JDK's {@code SecureRandom} class directly. It also - * allows for JavaBeans-style of customization, convenient for Shiro's INI configuration or other IoC configuration - * mechanism. - * - * @since 1.1 - */ -public class SecureRandomNumberGenerator implements RandomNumberGenerator { - - protected static final int DEFAULT_NEXT_BYTES_SIZE = 16; //16 bytes == 128 bits (a common number in crypto) - - private int defaultNextBytesSize; - private SecureRandom secureRandom; - - /** - * Creates a new instance with a default backing {@link SecureRandom SecureRandom} and a - * {@link #getDefaultNextBytesSize() defaultNextBytesSize} of {@code 16}, which equals 128 bits, a size commonly - * used in cryptographic algorithms. - */ - public SecureRandomNumberGenerator() { - this.defaultNextBytesSize = DEFAULT_NEXT_BYTES_SIZE; - this.secureRandom = new SecureRandom(); - } - - /** - * Seeds the backing {@link SecureRandom SecureRandom} instance with additional seed data. - * - * @param bytes the seed bytes - * @see SecureRandom#setSeed(byte[]) - */ - public void setSeed(byte[] bytes) { - this.secureRandom.setSeed(bytes); - } - - /** - * Returns the {@link SecureRandom SecureRandom} backing this instance. - * - * @return the {@link SecureRandom SecureRandom} backing this instance. - */ - public SecureRandom getSecureRandom() { - return secureRandom; - } - - /** - * Sets the {@link SecureRandom SecureRandom} to back this instance. - * - * @param random the {@link SecureRandom SecureRandom} to back this instance. - * @throws NullPointerException if the method argument is null - */ - public void setSecureRandom(SecureRandom random) throws NullPointerException { - if (random == null) { - throw new NullPointerException("SecureRandom argument cannot be null."); - } - this.secureRandom = random; - } - - /** - * Returns the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. Defaults to - * {@code 16}, which equals 128 bits, a size commonly used in cryptographic algorithms. - * - * @return the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. - */ - public int getDefaultNextBytesSize() { - return defaultNextBytesSize; - } - - /** - * Sets the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. Defaults to - * {@code 16}, which equals 128 bits, a size commonly used in cryptographic algorithms. - * - * @param defaultNextBytesSize the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. - * @throws IllegalArgumentException if the argument is 0 or negative - */ - public void setDefaultNextBytesSize(int defaultNextBytesSize) throws IllegalArgumentException { - if ( defaultNextBytesSize <= 0) { - throw new IllegalArgumentException("size value must be a positive integer (1 or larger)"); - } - this.defaultNextBytesSize = defaultNextBytesSize; - } - - public ByteSource nextBytes() { - return nextBytes(getDefaultNextBytesSize()); - } - - public ByteSource nextBytes(int numBytes) { - if (numBytes <= 0) { - throw new IllegalArgumentException("numBytes argument must be a positive integer (1 or larger)"); - } - byte[] bytes = new byte[numBytes]; - this.secureRandom.nextBytes(bytes); - return ByteSource.Util.bytes(bytes); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.crypto; + +import org.apache.shiro.lang.util.ByteSource; + +import java.security.SecureRandom; + +/** + * Default implementation of the {@link RandomNumberGenerator RandomNumberGenerator} interface, backed by a + * {@link SecureRandom SecureRandom} instance. + *

+ * This class is a little easier to use than using the JDK's {@code SecureRandom} class directly. It also + * allows for JavaBeans-style of customization, convenient for Shiro's INI configuration or other IoC configuration + * mechanism. + * + * @since 1.1 + */ +public class SecureRandomNumberGenerator implements RandomNumberGenerator { + + //16 bytes == 128 bits (a common number in crypto) + protected static final int DEFAULT_NEXT_BYTES_SIZE = 16; + + private int defaultNextBytesSize; + private SecureRandom secureRandom; + + /** + * Creates a new instance with a default backing {@link SecureRandom SecureRandom} and a + * {@link #getDefaultNextBytesSize() defaultNextBytesSize} of {@code 16}, which equals 128 bits, a size commonly + * used in cryptographic algorithms. + */ + public SecureRandomNumberGenerator() { + this.defaultNextBytesSize = DEFAULT_NEXT_BYTES_SIZE; + this.secureRandom = new SecureRandom(); + } + + /** + * Seeds the backing {@link SecureRandom SecureRandom} instance with additional seed data. + * + * @param bytes the seed bytes + * @see SecureRandom#setSeed(byte[]) + */ + public void setSeed(byte[] bytes) { + this.secureRandom.setSeed(bytes); + } + + /** + * Returns the {@link SecureRandom SecureRandom} backing this instance. + * + * @return the {@link SecureRandom SecureRandom} backing this instance. + */ + public SecureRandom getSecureRandom() { + return secureRandom; + } + + /** + * Sets the {@link SecureRandom SecureRandom} to back this instance. + * + * @param random the {@link SecureRandom SecureRandom} to back this instance. + * @throws NullPointerException if the method argument is null + */ + public void setSecureRandom(SecureRandom random) throws NullPointerException { + if (random == null) { + throw new NullPointerException("SecureRandom argument cannot be null."); + } + this.secureRandom = random; + } + + /** + * Returns the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. Defaults to + * {@code 16}, which equals 128 bits, a size commonly used in cryptographic algorithms. + * + * @return the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. + */ + public int getDefaultNextBytesSize() { + return defaultNextBytesSize; + } + + /** + * Sets the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. Defaults to + * {@code 16}, which equals 128 bits, a size commonly used in cryptographic algorithms. + * + * @param defaultNextBytesSize the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. + * @throws IllegalArgumentException if the argument is 0 or negative + */ + public void setDefaultNextBytesSize(int defaultNextBytesSize) throws IllegalArgumentException { + if (defaultNextBytesSize <= 0) { + throw new IllegalArgumentException("size value must be a positive integer (1 or larger)"); + } + this.defaultNextBytesSize = defaultNextBytesSize; + } + + public ByteSource nextBytes() { + return nextBytes(getDefaultNextBytesSize()); + } + + public ByteSource nextBytes(int numBytes) { + if (numBytes <= 0) { + throw new IllegalArgumentException("numBytes argument must be a positive integer (1 or larger)"); + } + byte[] bytes = new byte[numBytes]; + this.secureRandom.nextBytes(bytes); + return ByteSource.Util.bytes(bytes); + } +} diff --git a/crypto/core/src/main/java/org/apache/shiro/crypto/package-info.java b/crypto/core/src/main/java/org/apache/shiro/crypto/package-info.java new file mode 100644 index 0000000000..0fe457af3a --- /dev/null +++ b/crypto/core/src/main/java/org/apache/shiro/crypto/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * shiro crypto package. + */ +package org.apache.shiro.crypto; diff --git a/crypto/hash/pom.xml b/crypto/hash/pom.xml index 82acf151df..7f7d8001ac 100644 --- a/crypto/hash/pom.xml +++ b/crypto/hash/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + org.apache.shiro diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/AbstractCryptHash.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/AbstractCryptHash.java index f056c617ff..631505b8d9 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/AbstractCryptHash.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/AbstractCryptHash.java @@ -47,10 +47,10 @@ */ public abstract class AbstractCryptHash implements Hash, Serializable { - private static final long serialVersionUID = 2483214646921027859L; - protected static final Pattern DELIMITER = Pattern.compile("\\$"); + private static final long serialVersionUID = 2483214646921027859L; + private final String algorithmName; private final byte[] hashedData; private final ByteSource salt; @@ -70,8 +70,8 @@ public abstract class AbstractCryptHash implements Hash, Serializable { *

Other required parameters must be stored by the implementation.

* * @param algorithmName internal algorithm name, e.g. {@code 2y} for bcrypt and {@code argon2id} for argon2. - * @param hashedData the hashed data as a byte array. Does not include the salt or other parameters. - * @param salt the salt which was used when generating the hash. + * @param hashedData the hashed data as a byte array. Does not include the salt or other parameters. + * @param salt the salt which was used when generating the hash. * @throws IllegalArgumentException if the salt is not the same size as {@link #getSaltLength()}. */ public AbstractCryptHash(final String algorithmName, final byte[] hashedData, final ByteSource salt) { @@ -100,7 +100,7 @@ protected final void checkValid() { /** * Default check method for a valid salt. Can be overridden, because multiple salt lengths could be valid. - * + *

* By default, this method checks if the number of bytes in the salt * are equal to the int returned by {@link #getSaltLength()}. * @@ -227,6 +227,7 @@ public boolean equals(final Object other) { * *

Implementations should not override this method, as different algorithms produce different output formats * and require different parameters.

+ * * @return a hashcode from the {@link #formatToCryptString() formatted output}. */ @Override diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/AbstractHash.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/AbstractHash.java index 684647c7dc..5c84a8799c 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/AbstractHash.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/AbstractHash.java @@ -50,16 +50,16 @@ public abstract class AbstractHash extends CodecSupport implements Hash, Seriali /** * The hashed data */ - private byte[] bytes = null; + private byte[] bytes; /** * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead. */ - private transient String hexEncoded = null; + private transient String hexEncoded; /** * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead. */ - private transient String base64Encoded = null; + private transient String base64Encoded; /** * Creates an new instance without any of its properties set (no hashing is performed). @@ -138,7 +138,7 @@ public AbstractHash(Object source, Object salt, int hashIterations) throws Codec } /** - * Implemented by subclasses, this specifies the {@link MessageDigest MessageDigest} algorithm name + * Implemented by subclasses, this specifies the {@link MessageDigest MessageDigest} algorithm name * to use when performing the hash. * * @return the {@link MessageDigest MessageDigest} algorithm name to use when performing the hash. @@ -218,7 +218,8 @@ protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws Unkn digest.update(salt); } byte[] hashed = digest.digest(bytes); - int iterations = hashIterations - 1; //already hashed once above + //already hashed once above + int iterations = hashIterations - 1; //iterate remaining number: for (int i = 0; i < iterations; i++) { digest.reset(); @@ -278,7 +279,7 @@ public String toString() { * * @param o the object (Hash) to check for equality. * @return {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to - * this Hash's byte array, {@code false} otherwise. + * this Hash's byte array, {@code false} otherwise. */ @Override public boolean equals(Object o) { diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/DefaultHashService.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/DefaultHashService.java index ed2653f1c6..4879424c62 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/DefaultHashService.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/DefaultHashService.java @@ -78,7 +78,7 @@ public DefaultHashService() { * * @param request the request to process * @return the response containing the result of the hash computation, as well as any hash salt used that should be - * exposed to the caller. + * exposed to the caller. */ @Override public Hash computeHash(HashRequest request) { diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashRequest.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashRequest.java index 2f0232c1c1..d8bd344ca7 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashRequest.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashRequest.java @@ -54,7 +54,7 @@ public interface HashRequest { * strategy for a request, even if the request did not specify one. * * @return a salt to be used by the {@link HashService} during hash computation, or {@code null} if no salt is - * provided as part of the request. + * provided as part of the request. */ Optional getSalt(); @@ -94,11 +94,11 @@ public interface HashRequest { * @see SimpleHashRequest * @since 1.2 */ - public static class Builder { + class Builder { private ByteSource source; private ByteSource salt = SimpleByteSource.empty(); - private Map parameters = new ConcurrentHashMap<>(); + private final Map parameters = new ConcurrentHashMap<>(); private String algorithmName; /** diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashService.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashService.java index 126c2e982b..30e4e9807b 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashService.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashService.java @@ -56,7 +56,7 @@ public interface HashService { * Computes a hash based on the given request. * *

Salt Notice

- * + *

* If a salt accompanies the return value * (i.e. returnedHash.{@link org.apache.shiro.crypto.hash.Hash#getSalt() getSalt()} != null), this * same exact salt MUST be presented back to the {@code HashService} if hash diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashSpi.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashSpi.java index de4f2cf65a..879f74c792 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashSpi.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/HashSpi.java @@ -77,10 +77,11 @@ interface HashFactory { * should use default parameters where applicable.

*

If the hash requests’ salt is missing or empty, the implementation should create a salt * with a default size.

+ * * @param hashRequest the request to build a Hash from. * @return a generated Hash according to the specs. * @throws IllegalArgumentException if any of the parameters is outside of valid boundaries (algorithm-specific) - * or if the given algorithm is not applicable for this {@link HashFactory}. + * or if the given algorithm is not applicable for this {@link HashFactory}. */ Hash generate(HashRequest hashRequest); } diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha256Hash.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha256Hash.java index 79384695bd..176abb4096 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha256Hash.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha256Hash.java @@ -34,8 +34,9 @@ */ public class Sha256Hash extends SimpleHash { - //TODO - complete JavaDoc - + /** + * Sha256 algorithm name. + */ public static final String ALGORITHM_NAME = "SHA-256"; public Sha256Hash() { diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha384Hash.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha384Hash.java index 9345bc6c3a..8ec0cf90a5 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha384Hash.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha384Hash.java @@ -35,8 +35,9 @@ */ public class Sha384Hash extends SimpleHash { - //TODO - complete JavaDoc - + /** + * Sha384 algorithm name. + */ public static final String ALGORITHM_NAME = "SHA-384"; public Sha384Hash() { diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha512Hash.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha512Hash.java index 400f833483..211a2ea7c4 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha512Hash.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/Sha512Hash.java @@ -34,8 +34,9 @@ */ public class Sha512Hash extends SimpleHash { - //TODO - complete JavaDoc - + /** + * Sha512 algorithm name. + */ public static final String ALGORITHM_NAME = "SHA-512"; public Sha512Hash() { diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHash.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHash.java index 3028ceb45e..ed70ba2067 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHash.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHash.java @@ -1,482 +1,486 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.shiro.crypto.hash; - -import org.apache.shiro.crypto.UnknownAlgorithmException; -import org.apache.shiro.lang.codec.Base64; -import org.apache.shiro.lang.codec.CodecException; -import org.apache.shiro.lang.codec.Hex; -import org.apache.shiro.lang.util.ByteSource; -import org.apache.shiro.lang.util.SimpleByteSource; -import org.apache.shiro.lang.util.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import static java.util.Objects.requireNonNull; - -/** - * A {@code Hash} implementation that allows any {@link java.security.MessageDigest MessageDigest} algorithm name to - * be used. This class is a less type-safe variant than the other {@code AbstractHash} subclasses - * (e.g. {@link Sha512Hash}, etc.), but it does allow for any algorithm name to be specified in case the other subclass - * implementations do not represent an algorithm that you may want to use. - *

- * As of Shiro 1.1, this class effectively replaces the (now-deprecated) {@link AbstractHash} class. It subclasses - * {@code AbstractHash} only to retain backwards-compatibility. - * - * @since 1.1 - */ -public class SimpleHash extends AbstractHash { - - private static final int DEFAULT_ITERATIONS = 1; - private static final long serialVersionUID = -6689895264902387303L; - - private static final Logger LOG = LoggerFactory.getLogger(SimpleHash.class); - - /** - * The {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. - */ - private final String algorithmName; - - /** - * The hashed data - */ - private byte[] bytes; - - /** - * Supplied salt, if any. - */ - private ByteSource salt; - - /** - * Number of hash iterations to perform. Defaults to 1 in the constructor. - */ - private int iterations; - - /** - * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead. - */ - private transient String hexEncoded = null; - - /** - * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead. - */ - private transient String base64Encoded = null; - - /** - * Creates an new instance with only its {@code algorithmName} set - no hashing is performed. - *

- * Because all other constructors in this class hash the {@code source} constructor argument, this - * constructor is useful in scenarios when you have a byte array that you know is already hashed and - * just want to set the bytes in their raw form directly on an instance. After using this constructor, - * you can then immediately call {@link #setBytes setBytes} to have a fully-initialized instance. - *

- * N.B.The algorithm identified by the {@code algorithmName} parameter must be available on the JVM. If it - * is not, a {@link UnknownAlgorithmException} will be thrown when the hash is performed (not at instantiation). - * - * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when - * performing the hash. - * @see UnknownAlgorithmException - */ - public SimpleHash(String algorithmName) { - this.algorithmName = algorithmName; - this.iterations = DEFAULT_ITERATIONS; - } - - /** - * Creates an {@code algorithmName}-specific hash of the specified {@code source} with no {@code salt} using a - * single hash iteration. - *

- * This is a convenience constructor that merely executes this( algorithmName, source, null, 1);. - *

- * Please see the - * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) SimpleHashHash(algorithmName, Object,Object,int)} - * constructor for the types of Objects that may be passed into this constructor, as well as how to support further - * types. - * - * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when - * performing the hash. - * @param source the object to be hashed. - * @throws org.apache.shiro.lang.codec.CodecException - * if the specified {@code source} cannot be converted into a byte array (byte[]). - * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. - */ - public SimpleHash(String algorithmName, Object source) throws CodecException, UnknownAlgorithmException { - //noinspection NullableProblems - this(algorithmName, source, SimpleByteSource.empty(), DEFAULT_ITERATIONS); - } - - /** - * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given {@code salt} - * using a single hash iteration. - *

- * It is a convenience constructor that merely executes this( algorithmName, source, salt, 1);. - *

- * Please see the - * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) SimpleHashHash(algorithmName, Object,Object,int)} - * constructor for the types of Objects that may be passed into this constructor, as well as how to support further - * types. - * - * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when - * performing the hash. - * @param source the source object to be hashed. - * @param salt the salt to use for the hash - * @throws CodecException if either constructor argument cannot be converted into a byte array. - * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. - */ - public SimpleHash(String algorithmName, Object source, Object salt) throws CodecException, UnknownAlgorithmException { - this(algorithmName, source, salt, DEFAULT_ITERATIONS); - } - - /** - * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given {@code salt} - * using a single hash iteration. - *

- * It is a convenience constructor that merely executes this( algorithmName, source, salt, 1);. - *

- * Please see the - * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) SimpleHashHash(algorithmName, Object,Object,int)} - * constructor for the types of Objects that may be passed into this constructor, as well as how to support further - * types. - * - * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when - * performing the hash. - * @param source the source object to be hashed. - * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency. - * @throws CodecException if either constructor argument cannot be converted into a byte array. - * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. - */ - public SimpleHash(String algorithmName, Object source, int hashIterations) throws CodecException, UnknownAlgorithmException { - this(algorithmName, source, SimpleByteSource.empty(), hashIterations); - } - - /** - * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given - * {@code salt} a total of {@code hashIterations} times. - *

- * By default, this class only supports Object method arguments of - * type {@code byte[]}, {@code char[]}, {@link String}, {@link java.io.File File}, - * {@link java.io.InputStream InputStream} or {@link org.apache.shiro.lang.util.ByteSource ByteSource}. If either - * argument is anything other than these types a {@link org.apache.shiro.lang.codec.CodecException CodecException} - * will be thrown. - *

- * If you want to be able to hash other object types, or use other salt types, you need to override the - * {@link #toBytes(Object) toBytes(Object)} method to support those specific types. Your other option is to - * convert your arguments to one of the default supported types first before passing them in to this - * constructor}. - * - * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when - * performing the hash. - * @param source the source object to be hashed. - * @param salt the salt to use for the hash - * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency. - * @throws CodecException if either Object constructor argument cannot be converted into a byte array. - * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. - */ - public SimpleHash(String algorithmName, Object source, Object salt, int hashIterations) - throws CodecException, UnknownAlgorithmException { - if (!StringUtils.hasText(algorithmName)) { - throw new NullPointerException("algorithmName argument cannot be null or empty."); - } - this.algorithmName = algorithmName; - this.iterations = Math.max(DEFAULT_ITERATIONS, hashIterations); - ByteSource saltBytes = convertSaltToBytes(salt); - this.salt = saltBytes; - ByteSource sourceBytes = convertSourceToBytes(source); - hash(sourceBytes, saltBytes, hashIterations); - } - - /** - * Acquires the specified {@code source} argument's bytes and returns them in the form of a {@code ByteSource} instance. - *

- * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic - * conversion. Can be overridden by subclasses for source-specific conversion. - * - * @param source the source object to be hashed. - * @return the source's bytes in the form of a {@code ByteSource} instance. - * @since 1.2 - */ - protected ByteSource convertSourceToBytes(Object source) { - return toByteSource(source); - } - - /** - * Acquires the specified {@code salt} argument's bytes and returns them in the form of a {@code ByteSource} instance. - *

- * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic - * conversion. Can be overridden by subclasses for salt-specific conversion. - * - * @param salt the salt to be use for the hash. - * @return the salt's bytes in the form of a {@code ByteSource} instance. - * @since 1.2 - */ - protected ByteSource convertSaltToBytes(Object salt) { - return toByteSource(salt); - } - - /** - * Converts a given object into a {@code ByteSource} instance. Assumes the object can be converted to bytes. - * - * @param object the Object to convert into a {@code ByteSource} instance. - * @return the {@code ByteSource} representation of the specified object's bytes. - * @since 1.2 - */ - protected ByteSource toByteSource(Object object) { - if (object instanceof ByteSource) { - return (ByteSource) object; - } - byte[] bytes = toBytes(object); - return ByteSource.Util.bytes(bytes); - } - - private void hash(ByteSource source, ByteSource salt, int hashIterations) throws CodecException, UnknownAlgorithmException { - byte[] saltBytes = requireNonNull(salt).getBytes(); - byte[] hashedBytes = hash(source.getBytes(), saltBytes, hashIterations); - setBytes(hashedBytes); - } - - /** - * Returns the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. - * - * @return the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. - */ - @Override - public String getAlgorithmName() { - return this.algorithmName; - } - - @Override - public ByteSource getSalt() { - return this.salt; - } - - @Override - public int getIterations() { - return this.iterations; - } - - @Override - public boolean matchesPassword(ByteSource plaintextBytes) { - try { - SimpleHash otherHash = new SimpleHash(this.getAlgorithmName(), plaintextBytes, this.getSalt(), this.getIterations()); - return this.equals(otherHash); - } catch (IllegalArgumentException illegalArgumentException) { - // cannot recreate hash. Do not log password. - LOG.warn("Cannot recreate a hash using the same parameters.", illegalArgumentException); - return false; - } - } - - @Override - public byte[] getBytes() { - return this.bytes; - } - - /** - * Sets the raw bytes stored by this hash instance. - *

- * The bytes are kept in raw form - they will not be hashed/changed. This is primarily a utility method for - * constructing a Hash instance when the hashed value is already known. - * - * @param alreadyHashedBytes the raw already-hashed bytes to store in this instance. - */ - @Override - public void setBytes(byte[] alreadyHashedBytes) { - this.bytes = alreadyHashedBytes; - this.hexEncoded = null; - this.base64Encoded = null; - } - - /** - * Sets the iterations used to previously compute AN ALREADY GENERATED HASH. - *

- * This is provided ONLY to reconstitute an already-created Hash instance. It should ONLY ever be - * invoked when re-constructing a hash instance from an already-hashed value. - * - * @param iterations the number of hash iterations used to previously create the hash/digest. - * @since 1.2 - */ - public void setIterations(int iterations) { - this.iterations = Math.max(DEFAULT_ITERATIONS, iterations); - } - - /** - * Sets the salt used to previously compute AN ALREADY GENERATED HASH. - *

- * This is provided ONLY to reconstitute a Hash instance that has already been computed. It should ONLY - * ever be invoked when re-constructing a hash instance from an already-hashed value. - * - * @param salt the salt used to previously create the hash/digest. - * @since 1.2 - */ - public void setSalt(ByteSource salt) { - this.salt = salt; - } - - /** - * Returns the JDK MessageDigest instance to use for executing the hash. - * - * @param algorithmName the algorithm to use for the hash, provided by subclasses. - * @return the MessageDigest object for the specified {@code algorithm}. - * @throws UnknownAlgorithmException if the specified algorithm name is not available. - */ - @Override - protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException { - try { - return MessageDigest.getInstance(algorithmName); - } catch (NoSuchAlgorithmException e) { - String msg = "No native '" + algorithmName + "' MessageDigest instance available on the current JVM."; - throw new UnknownAlgorithmException(msg, e); - } - } - - /** - * Hashes the specified byte array without a salt for a single iteration. - * - * @param bytes the bytes to hash. - * @return the hashed bytes. - * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available. - */ - @Override - protected byte[] hash(byte[] bytes) throws UnknownAlgorithmException { - return hash(bytes, null, DEFAULT_ITERATIONS); - } - - /** - * Hashes the specified byte array using the given {@code salt} for a single iteration. - * - * @param bytes the bytes to hash - * @param salt the salt to use for the initial hash - * @return the hashed bytes - * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available. - */ - @Override - protected byte[] hash(byte[] bytes, byte[] salt) throws UnknownAlgorithmException { - return hash(bytes, salt, DEFAULT_ITERATIONS); - } - - /** - * Hashes the specified byte array using the given {@code salt} for the specified number of iterations. - * - * @param bytes the bytes to hash - * @param salt the salt to use for the initial hash - * @param hashIterations the number of times the the {@code bytes} will be hashed (for attack resiliency). - * @return the hashed bytes. - * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() algorithmName} is not available. - */ - @Override - protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException { - MessageDigest digest = getDigest(getAlgorithmName()); - if (salt.length != 0) { - digest.reset(); - digest.update(salt); - } - byte[] hashed = digest.digest(bytes); - int iterations = hashIterations - 1; //already hashed once above - //iterate remaining number: - for (int i = 0; i < iterations; i++) { - digest.reset(); - hashed = digest.digest(hashed); - } - return hashed; - } - - @Override - public boolean isEmpty() { - return this.bytes == null || this.bytes.length == 0; - } - - /** - * Returns a hex-encoded string of the underlying {@link #getBytes byte array}. - *

- * This implementation caches the resulting hex string so multiple calls to this method remain efficient. - * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the - * next time this method is called. - * - * @return a hex-encoded string of the underlying {@link #getBytes byte array}. - */ - @Override - public String toHex() { - if (this.hexEncoded == null) { - this.hexEncoded = Hex.encodeToString(getBytes()); - } - return this.hexEncoded; - } - - /** - * Returns a Base64-encoded string of the underlying {@link #getBytes byte array}. - *

- * This implementation caches the resulting Base64 string so multiple calls to this method remain efficient. - * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the - * next time this method is called. - * - * @return a Base64-encoded string of the underlying {@link #getBytes byte array}. - */ - @Override - public String toBase64() { - if (this.base64Encoded == null) { - //cache result in case this method is called multiple times. - this.base64Encoded = Base64.encodeToString(getBytes()); - } - return this.base64Encoded; - } - - /** - * Simple implementation that merely returns {@link #toHex() toHex()}. - * - * @return the {@link #toHex() toHex()} value. - */ - @Override - public String toString() { - return toHex(); - } - - /** - * Returns {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to - * this Hash's byte array, {@code false} otherwise. - * - * @param o the object (Hash) to check for equality. - * @return {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to - * this Hash's byte array, {@code false} otherwise. - */ - @Override - public boolean equals(Object o) { - if (o instanceof Hash) { - Hash other = (Hash) o; - return MessageDigest.isEqual(getBytes(), other.getBytes()); - } - return false; - } - - /** - * Simply returns toHex().hashCode(); - * - * @return toHex().hashCode() - */ - @Override - public int hashCode() { - if (this.bytes == null || this.bytes.length == 0) { - return 0; - } - return Arrays.hashCode(this.bytes); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.shiro.crypto.hash; + +import org.apache.shiro.crypto.UnknownAlgorithmException; +import org.apache.shiro.lang.codec.Base64; +import org.apache.shiro.lang.codec.CodecException; +import org.apache.shiro.lang.codec.Hex; +import org.apache.shiro.lang.util.ByteSource; +import org.apache.shiro.lang.util.SimpleByteSource; +import org.apache.shiro.lang.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import static java.util.Objects.requireNonNull; + +/** + * A {@code Hash} implementation that allows any {@link java.security.MessageDigest MessageDigest} algorithm name to + * be used. This class is a less type-safe variant than the other {@code AbstractHash} subclasses + * (e.g. {@link Sha512Hash}, etc.), but it does allow for any algorithm name to be specified in case the other subclass + * implementations do not represent an algorithm that you may want to use. + *

+ * As of Shiro 1.1, this class effectively replaces the (now-deprecated) {@link AbstractHash} class. It subclasses + * {@code AbstractHash} only to retain backwards-compatibility. + * + * @since 1.1 + */ +public class SimpleHash extends AbstractHash { + + private static final int DEFAULT_ITERATIONS = 1; + private static final long serialVersionUID = -6689895264902387303L; + + private static final Logger LOG = LoggerFactory.getLogger(SimpleHash.class); + + /** + * The {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. + */ + private final String algorithmName; + + /** + * The hashed data + */ + private byte[] bytes; + + /** + * Supplied salt, if any. + */ + private ByteSource salt; + + /** + * Number of hash iterations to perform. Defaults to 1 in the constructor. + */ + private int iterations; + + /** + * Cached value of the {@link #toHex() toHex()} call so multiple calls won't incur repeated overhead. + */ + private transient String hexEncoded; + + /** + * Cached value of the {@link #toBase64() toBase64()} call so multiple calls won't incur repeated overhead. + */ + private transient String base64Encoded; + + /** + * Creates an new instance with only its {@code algorithmName} set - no hashing is performed. + *

+ * Because all other constructors in this class hash the {@code source} constructor argument, this + * constructor is useful in scenarios when you have a byte array that you know is already hashed and + * just want to set the bytes in their raw form directly on an instance. After using this constructor, + * you can then immediately call {@link #setBytes setBytes} to have a fully-initialized instance. + *

+ * N.B.The algorithm identified by the {@code algorithmName} parameter must be available on the JVM. If it + * is not, a {@link UnknownAlgorithmException} will be thrown when the hash is performed (not at instantiation). + * + * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when + * performing the hash. + * @see UnknownAlgorithmException + */ + public SimpleHash(String algorithmName) { + this.algorithmName = algorithmName; + this.iterations = DEFAULT_ITERATIONS; + } + + /** + * Creates an {@code algorithmName}-specific hash of the specified {@code source} with no {@code salt} using a + * single hash iteration. + *

+ * This is a convenience constructor that merely executes this( algorithmName, source, null, 1);. + *

+ * Please see the + * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) + * SimpleHashHash(algorithmName, Object,Object,int)} + * constructor for the types of Objects that may be passed into this constructor, as well as how to support further + * types. + * + * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when + * performing the hash. + * @param source the object to be hashed. + * @throws org.apache.shiro.lang.codec.CodecException if the specified {@code source} cannot be converted + * into a byte array (byte[]). + * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. + */ + public SimpleHash(String algorithmName, Object source) throws CodecException, UnknownAlgorithmException { + //noinspection NullableProblems + this(algorithmName, source, SimpleByteSource.empty(), DEFAULT_ITERATIONS); + } + + /** + * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given {@code salt} + * using a single hash iteration. + *

+ * It is a convenience constructor that merely executes this( algorithmName, source, salt, 1);. + *

+ * Please see the + * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) + * SimpleHashHash(algorithmName, Object,Object,int)} + * constructor for the types of Objects that may be passed into this constructor, as well as how to support further + * types. + * + * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when + * performing the hash. + * @param source the source object to be hashed. + * @param salt the salt to use for the hash + * @throws CodecException if either constructor argument cannot be converted into a byte array. + * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. + */ + public SimpleHash(String algorithmName, Object source, Object salt) throws CodecException, UnknownAlgorithmException { + this(algorithmName, source, salt, DEFAULT_ITERATIONS); + } + + /** + * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given {@code salt} + * using a single hash iteration. + *

+ * It is a convenience constructor that merely executes this( algorithmName, source, salt, 1);. + *

+ * Please see the + * {@link #SimpleHash(String algorithmName, Object source, Object salt, int numIterations) + * SimpleHashHash(algorithmName, Object,Object,int)} + * constructor for the types of Objects that may be passed into this constructor, as well as how to support further + * types. + * + * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when + * performing the hash. + * @param source the source object to be hashed. + * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency. + * @throws CodecException if either constructor argument cannot be converted into a byte array. + * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. + */ + public SimpleHash(String algorithmName, Object source, int hashIterations) throws CodecException, UnknownAlgorithmException { + this(algorithmName, source, SimpleByteSource.empty(), hashIterations); + } + + /** + * Creates an {@code algorithmName}-specific hash of the specified {@code source} using the given + * {@code salt} a total of {@code hashIterations} times. + *

+ * By default, this class only supports Object method arguments of + * type {@code byte[]}, {@code char[]}, {@link String}, {@link java.io.File File}, + * {@link java.io.InputStream InputStream} or {@link org.apache.shiro.lang.util.ByteSource ByteSource}. If either + * argument is anything other than these types a {@link org.apache.shiro.lang.codec.CodecException CodecException} + * will be thrown. + *

+ * If you want to be able to hash other object types, or use other salt types, you need to override the + * {@link #toBytes(Object) toBytes(Object)} method to support those specific types. Your other option is to + * convert your arguments to one of the default supported types first before passing them in to this + * constructor}. + * + * @param algorithmName the {@link java.security.MessageDigest MessageDigest} algorithm name to use when + * performing the hash. + * @param source the source object to be hashed. + * @param salt the salt to use for the hash + * @param hashIterations the number of times the {@code source} argument hashed for attack resiliency. + * @throws CodecException if either Object constructor argument cannot be converted into a byte array. + * @throws UnknownAlgorithmException if the {@code algorithmName} is not available. + */ + public SimpleHash(String algorithmName, Object source, Object salt, int hashIterations) + throws CodecException, UnknownAlgorithmException { + if (!StringUtils.hasText(algorithmName)) { + throw new NullPointerException("algorithmName argument cannot be null or empty."); + } + this.algorithmName = algorithmName; + this.iterations = Math.max(DEFAULT_ITERATIONS, hashIterations); + ByteSource saltBytes = convertSaltToBytes(salt); + this.salt = saltBytes; + ByteSource sourceBytes = convertSourceToBytes(source); + hash(sourceBytes, saltBytes, hashIterations); + } + + /** + * Acquires the specified {@code source} argument's bytes and returns them in the form of a {@code ByteSource} instance. + *

+ * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic + * conversion. Can be overridden by subclasses for source-specific conversion. + * + * @param source the source object to be hashed. + * @return the source's bytes in the form of a {@code ByteSource} instance. + * @since 1.2 + */ + protected ByteSource convertSourceToBytes(Object source) { + return toByteSource(source); + } + + /** + * Acquires the specified {@code salt} argument's bytes and returns them in the form of a {@code ByteSource} instance. + *

+ * This implementation merely delegates to the convenience {@link #toByteSource(Object)} method for generic + * conversion. Can be overridden by subclasses for salt-specific conversion. + * + * @param salt the salt to be use for the hash. + * @return the salt's bytes in the form of a {@code ByteSource} instance. + * @since 1.2 + */ + protected ByteSource convertSaltToBytes(Object salt) { + return toByteSource(salt); + } + + /** + * Converts a given object into a {@code ByteSource} instance. Assumes the object can be converted to bytes. + * + * @param object the Object to convert into a {@code ByteSource} instance. + * @return the {@code ByteSource} representation of the specified object's bytes. + * @since 1.2 + */ + protected ByteSource toByteSource(Object object) { + if (object instanceof ByteSource) { + return (ByteSource) object; + } + byte[] bytes = toBytes(object); + return ByteSource.Util.bytes(bytes); + } + + private void hash(ByteSource source, ByteSource salt, int hashIterations) throws CodecException, UnknownAlgorithmException { + byte[] saltBytes = requireNonNull(salt).getBytes(); + byte[] hashedBytes = hash(source.getBytes(), saltBytes, hashIterations); + setBytes(hashedBytes); + } + + /** + * Returns the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. + * + * @return the {@link java.security.MessageDigest MessageDigest} algorithm name to use when performing the hash. + */ + @Override + public String getAlgorithmName() { + return this.algorithmName; + } + + @Override + public ByteSource getSalt() { + return this.salt; + } + + @Override + public int getIterations() { + return this.iterations; + } + + @Override + public boolean matchesPassword(ByteSource plaintextBytes) { + try { + SimpleHash otherHash = new SimpleHash(this.getAlgorithmName(), plaintextBytes, this.getSalt(), this.getIterations()); + return this.equals(otherHash); + } catch (IllegalArgumentException illegalArgumentException) { + // cannot recreate hash. Do not log password. + LOG.warn("Cannot recreate a hash using the same parameters.", illegalArgumentException); + return false; + } + } + + @Override + public byte[] getBytes() { + return this.bytes; + } + + /** + * Sets the raw bytes stored by this hash instance. + *

+ * The bytes are kept in raw form - they will not be hashed/changed. This is primarily a utility method for + * constructing a Hash instance when the hashed value is already known. + * + * @param alreadyHashedBytes the raw already-hashed bytes to store in this instance. + */ + @Override + public void setBytes(byte[] alreadyHashedBytes) { + this.bytes = alreadyHashedBytes; + this.hexEncoded = null; + this.base64Encoded = null; + } + + /** + * Sets the iterations used to previously compute AN ALREADY GENERATED HASH. + *

+ * This is provided ONLY to reconstitute an already-created Hash instance. It should ONLY ever be + * invoked when re-constructing a hash instance from an already-hashed value. + * + * @param iterations the number of hash iterations used to previously create the hash/digest. + * @since 1.2 + */ + public void setIterations(int iterations) { + this.iterations = Math.max(DEFAULT_ITERATIONS, iterations); + } + + /** + * Sets the salt used to previously compute AN ALREADY GENERATED HASH. + *

+ * This is provided ONLY to reconstitute a Hash instance that has already been computed. It should ONLY + * ever be invoked when re-constructing a hash instance from an already-hashed value. + * + * @param salt the salt used to previously create the hash/digest. + * @since 1.2 + */ + public void setSalt(ByteSource salt) { + this.salt = salt; + } + + /** + * Returns the JDK MessageDigest instance to use for executing the hash. + * + * @param algorithmName the algorithm to use for the hash, provided by subclasses. + * @return the MessageDigest object for the specified {@code algorithm}. + * @throws UnknownAlgorithmException if the specified algorithm name is not available. + */ + @Override + protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException { + try { + return MessageDigest.getInstance(algorithmName); + } catch (NoSuchAlgorithmException e) { + String msg = "No native '" + algorithmName + "' MessageDigest instance available on the current JVM."; + throw new UnknownAlgorithmException(msg, e); + } + } + + /** + * Hashes the specified byte array without a salt for a single iteration. + * + * @param bytes the bytes to hash. + * @return the hashed bytes. + * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available. + */ + @Override + protected byte[] hash(byte[] bytes) throws UnknownAlgorithmException { + return hash(bytes, null, DEFAULT_ITERATIONS); + } + + /** + * Hashes the specified byte array using the given {@code salt} for a single iteration. + * + * @param bytes the bytes to hash + * @param salt the salt to use for the initial hash + * @return the hashed bytes + * @throws UnknownAlgorithmException if the configured {@link #getAlgorithmName() algorithmName} is not available. + */ + @Override + protected byte[] hash(byte[] bytes, byte[] salt) throws UnknownAlgorithmException { + return hash(bytes, salt, DEFAULT_ITERATIONS); + } + + /** + * Hashes the specified byte array using the given {@code salt} for the specified number of iterations. + * + * @param bytes the bytes to hash + * @param salt the salt to use for the initial hash + * @param hashIterations the number of times the the {@code bytes} will be hashed (for attack resiliency). + * @return the hashed bytes. + * @throws UnknownAlgorithmException if the {@link #getAlgorithmName() algorithmName} is not available. + */ + @Override + protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException { + MessageDigest digest = getDigest(getAlgorithmName()); + if (salt.length != 0) { + digest.reset(); + digest.update(salt); + } + byte[] hashed = digest.digest(bytes); + //already hashed once above + int iterations = hashIterations - 1; + //iterate remaining number: + for (int i = 0; i < iterations; i++) { + digest.reset(); + hashed = digest.digest(hashed); + } + return hashed; + } + + @Override + public boolean isEmpty() { + return this.bytes == null || this.bytes.length == 0; + } + + /** + * Returns a hex-encoded string of the underlying {@link #getBytes byte array}. + *

+ * This implementation caches the resulting hex string so multiple calls to this method remain efficient. + * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the + * next time this method is called. + * + * @return a hex-encoded string of the underlying {@link #getBytes byte array}. + */ + @Override + public String toHex() { + if (this.hexEncoded == null) { + this.hexEncoded = Hex.encodeToString(getBytes()); + } + return this.hexEncoded; + } + + /** + * Returns a Base64-encoded string of the underlying {@link #getBytes byte array}. + *

+ * This implementation caches the resulting Base64 string so multiple calls to this method remain efficient. + * However, calling {@link #setBytes setBytes} will null the cached value, forcing it to be recalculated the + * next time this method is called. + * + * @return a Base64-encoded string of the underlying {@link #getBytes byte array}. + */ + @Override + public String toBase64() { + if (this.base64Encoded == null) { + //cache result in case this method is called multiple times. + this.base64Encoded = Base64.encodeToString(getBytes()); + } + return this.base64Encoded; + } + + /** + * Simple implementation that merely returns {@link #toHex() toHex()}. + * + * @return the {@link #toHex() toHex()} value. + */ + @Override + public String toString() { + return toHex(); + } + + /** + * Returns {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to + * this Hash's byte array, {@code false} otherwise. + * + * @param o the object (Hash) to check for equality. + * @return {@code true} if the specified object is a Hash and its {@link #getBytes byte array} is identical to + * this Hash's byte array, {@code false} otherwise. + */ + @Override + public boolean equals(Object o) { + if (o instanceof Hash) { + Hash other = (Hash) o; + return MessageDigest.isEqual(getBytes(), other.getBytes()); + } + return false; + } + + /** + * Simply returns toHex().hashCode(); + * + * @return toHex().hashCode() + */ + @Override + public int hashCode() { + if (this.bytes == null || this.bytes.length == 0) { + return 0; + } + return Arrays.hashCode(this.bytes); + } +} diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHashProvider.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHashProvider.java index 5b4a44df2c..e0772b9491 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHashProvider.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHashProvider.java @@ -36,15 +36,16 @@ /** * Creates a hash provider for salt (+pepper) and Hash-based KDFs, i.e. where the algorithm name * is a SHA algorithm or similar. + * * @since 2.0 */ public class SimpleHashProvider implements HashSpi { - private static final Set IMPLEMENTED_ALGORITHMS = Arrays.stream(new String[]{ - Sha256Hash.ALGORITHM_NAME, - Sha384Hash.ALGORITHM_NAME, - Sha512Hash.ALGORITHM_NAME - }) + private static final Set IMPLEMENTED_ALGORITHMS = Arrays.stream(new String[] { + Sha256Hash.ALGORITHM_NAME, + Sha384Hash.ALGORITHM_NAME, + Sha512Hash.ALGORITHM_NAME + }) .collect(toSet()); @Override @@ -72,7 +73,7 @@ static class SimpleHashFactory implements HashSpi.HashFactory { private final Random random; - public SimpleHashFactory(Random random) { + SimpleHashFactory(Random random) { this.random = random; } @@ -113,6 +114,7 @@ protected ByteSource getPublicSalt(HashRequest request) { } // generate salt if absent from the request. + @SuppressWarnings("checkstyle:MagicNumber") byte[] ps = new byte[16]; random.nextBytes(ps); @@ -129,7 +131,8 @@ private ByteSource getSecretSalt(HashRequest request) { .orElse(null); } - private SimpleHash createSimpleHash(String algorithmName, ByteSource source, int iterations, ByteSource publicSalt, ByteSource salt) { + private SimpleHash createSimpleHash(String algorithmName, ByteSource source, + int iterations, ByteSource publicSalt, ByteSource salt) { Hash computed = new SimpleHash(algorithmName, source, salt, iterations); SimpleHash result = new SimpleHash(algorithmName); @@ -203,7 +206,8 @@ static final class Parameters { /** * A secret part added to the salt. Sometimes also referred to as {@literal "Pepper"}. * - *

For more information, see Pepper (cryptography) on Wikipedia.

+ *

For more information, see + * Pepper (cryptography) on Wikipedia.

*/ public static final String PARAMETER_SECRET_SALT = "SimpleHash.secretSalt"; diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHashRequest.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHashRequest.java index ffd2989d28..302b8b4eb1 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHashRequest.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/SimpleHashRequest.java @@ -34,18 +34,28 @@ */ public class SimpleHashRequest implements HashRequest { - private final ByteSource source; //cannot be null - this is the source to hash. - private final ByteSource salt; //null = no salt specified - private final String algorithmName; //null = let the HashService decide. + /** + * cannot be null - this is the source to hash. + */ + private final ByteSource source; + /** + * can be null = no salt specified + */ + private final ByteSource salt; + /** + * can be null = let the HashService decide. + */ + private final String algorithmName; private final Map parameters = new ConcurrentHashMap<>(); /** * Creates a new SimpleHashRequest instance. * * @param algorithmName the name of the hash algorithm to use. This is often null as the - * {@link HashService} implementation is usually configured with an appropriate algorithm name, but this - * can be non-null if the hash service's algorithm should be overridden with a specific one for the duration - * of the request. + * {@link HashService} implementation is usually configured with an + * appropriate algorithm name, but this can be non-null + * if the hash service's algorithm should be overridden with a + * specific one for the duration of the request. * @param source the source to be hashed * @param salt any public salt which should be used when computing the hash * @param parameters e.g. the number of hash iterations to execute or other parameters. diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/DefaultHashFormatFactory.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/DefaultHashFormatFactory.java index ae09b13bce..aa8b1d0a91 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/DefaultHashFormatFactory.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/DefaultHashFormatFactory.java @@ -18,6 +18,9 @@ */ package org.apache.shiro.crypto.hash.format; +import java.util.Iterator; +import java.util.stream.Stream; + import org.apache.shiro.lang.util.ClassUtils; import org.apache.shiro.lang.util.StringUtils; import org.apache.shiro.lang.util.UnknownClassException; @@ -36,9 +39,11 @@ */ public class DefaultHashFormatFactory implements HashFormatFactory { - private Map formatClassNames; //id - to - fully qualified class name + //id - to - fully qualified class name + private Map formatClassNames; - private Set searchPackages; //packages to search for HashFormat implementations + //packages to search for HashFormat implementations + private Set searchPackages; public DefaultHashFormatFactory() { this.searchPackages = new HashSet(); @@ -279,6 +284,7 @@ protected Class getHashFormatClass(String token) { * @param token the string token from which a class name will be heuristically determined. * @return the discovered HashFormat class implementation or {@code null} if no class could be heuristically determined. */ + @SuppressWarnings("unchecked") protected Class getHashFormatClass(String packageName, String token) { String test = token; Class clazz = null; @@ -287,43 +293,22 @@ protected Class getHashFormatClass(String packageName, String token) { //1. Assume the arg is a fully qualified class name in the classpath: clazz = lookupHashFormatClass(test); - if (clazz == null) { - test = pkg + "." + token; - clazz = lookupHashFormatClass(test); - } - - if (clazz == null) { - test = pkg + "." + StringUtils.uppercaseFirstChar(token) + "Format"; - clazz = lookupHashFormatClass(test); - } - - if (clazz == null) { - test = pkg + "." + token + "Format"; - clazz = lookupHashFormatClass(test); - } - - if (clazz == null) { - test = pkg + "." + StringUtils.uppercaseFirstChar(token) + "HashFormat"; - clazz = lookupHashFormatClass(test); + final Iterator iterator = Stream.of( + pkg + "." + token, + pkg + "." + StringUtils.uppercaseFirstChar(token) + "Format", + pkg + "." + token + "Format", + pkg + "." + StringUtils.uppercaseFirstChar(token) + "HashFormat", + pkg + "." + token + "HashFormat", + pkg + "." + StringUtils.uppercaseFirstChar(token) + "CryptFormat", + pkg + "." + token + "CryptFormat").iterator(); + + while (clazz == null && iterator.hasNext()) { + clazz = lookupHashFormatClass(iterator.next()); } if (clazz == null) { - test = pkg + "." + token + "HashFormat"; - clazz = lookupHashFormatClass(test); - } - - if (clazz == null) { - test = pkg + "." + StringUtils.uppercaseFirstChar(token) + "CryptFormat"; - clazz = lookupHashFormatClass(test); - } - - if (clazz == null) { - test = pkg + "." + token + "CryptFormat"; - clazz = lookupHashFormatClass(test); - } - - if (clazz == null) { - return null; //ran out of options + //ran out of options + return null; } assertHashFormatImpl(clazz); @@ -342,8 +327,8 @@ protected Class lookupHashFormatClass(String name) { protected final void assertHashFormatImpl(Class clazz) { if (!HashFormat.class.isAssignableFrom(clazz) || clazz.isInterface()) { - throw new IllegalArgumentException("Discovered class [" + clazz.getName() + "] is not a " + - HashFormat.class.getName() + " implementation."); + throw new IllegalArgumentException("Discovered class [" + clazz.getName() + "] is not a " + + HashFormat.class.getName() + " implementation."); } } diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/ModularCryptFormat.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/ModularCryptFormat.java index ce4917556e..78ba41eebd 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/ModularCryptFormat.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/ModularCryptFormat.java @@ -28,7 +28,10 @@ */ public interface ModularCryptFormat extends HashFormat { - public static final String TOKEN_DELIMITER = "$"; + /** + * token delimiter. + */ + String TOKEN_DELIMITER = "$"; /** * Returns the Modular Crypt Format identifier that indicates how the formatted String should be parsed. This id diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/ParsableHashFormat.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/ParsableHashFormat.java index 045756805b..cd4b901846 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/ParsableHashFormat.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/ParsableHashFormat.java @@ -28,7 +28,6 @@ * (like Unix Crypt(3)) only support one way conversion and therefore wouldn't implement this interface. * * @see Shiro1CryptFormat - * * @since 1.2 */ public interface ParsableHashFormat extends HashFormat { diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/Shiro1CryptFormat.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/Shiro1CryptFormat.java index 1428f3abd9..e706488e39 100644 --- a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/Shiro1CryptFormat.java +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/Shiro1CryptFormat.java @@ -83,12 +83,18 @@ * * @see ModularCryptFormat * @see ParsableHashFormat - * * @since 1.2 */ public class Shiro1CryptFormat implements ModularCryptFormat, ParsableHashFormat { + /** + * shiro1 crypt id. + */ public static final String ID = "shiro1"; + + /** + * shiro1 crypt format prefix + */ public static final String MCF_PREFIX = TOKEN_DELIMITER + ID + TOKEN_DELIMITER; public Shiro1CryptFormat() { @@ -108,7 +114,11 @@ public String format(final Hash hash) { String algorithmName = hash.getAlgorithmName(); ByteSource salt = hash.getSalt(); int iterations = hash.getIterations(); - StringBuilder sb = new StringBuilder(MCF_PREFIX).append(algorithmName).append(TOKEN_DELIMITER).append(iterations).append(TOKEN_DELIMITER); + StringBuilder sb = new StringBuilder(MCF_PREFIX) + .append(algorithmName) + .append(TOKEN_DELIMITER) + .append(iterations) + .append(TOKEN_DELIMITER); if (salt != null) { sb.append(salt.toBase64()); diff --git a/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/package-info.java b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/package-info.java new file mode 100644 index 0000000000..e3c5042348 --- /dev/null +++ b/crypto/hash/src/main/java/org/apache/shiro/crypto/hash/format/package-info.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/** + * Cryptographic Hashing components that greatly simplify one-way data hashing in an application. + *

+ * The {@link org.apache.shiro.crypto.hash.Hash Hash} interface and its implementations are significantly + * easier to understand and use compared to the JDK's MessageDigest mechanism. + */ +package org.apache.shiro.crypto.hash.format; diff --git a/crypto/hash/src/test/groovy/org/apache/shiro/crypto/hash/DefaultHashServiceTest.groovy b/crypto/hash/src/test/groovy/org/apache/shiro/crypto/hash/DefaultHashServiceTest.groovy index 93e4cca6ad..2d8ea5b94e 100644 --- a/crypto/hash/src/test/groovy/org/apache/shiro/crypto/hash/DefaultHashServiceTest.groovy +++ b/crypto/hash/src/test/groovy/org/apache/shiro/crypto/hash/DefaultHashServiceTest.groovy @@ -51,7 +51,7 @@ class DefaultHashServiceTest { @Test void testRequestWithEmptySource() { - def source = ByteSource.Util.bytes((byte[])null) + def source = ByteSource.Util.bytes((byte[]) null) def request = new HashRequest.Builder().setSource(source).build() def service = createSha256Service() assertNull service.computeHash(request) diff --git a/crypto/hash/src/test/groovy/org/apache/shiro/crypto/hash/format/Shiro1CryptFormatTest.groovy b/crypto/hash/src/test/groovy/org/apache/shiro/crypto/hash/format/Shiro1CryptFormatTest.groovy index 7ac441e3a6..7a82d3d6e0 100644 --- a/crypto/hash/src/test/groovy/org/apache/shiro/crypto/hash/format/Shiro1CryptFormatTest.groovy +++ b/crypto/hash/src/test/groovy/org/apache/shiro/crypto/hash/format/Shiro1CryptFormatTest.groovy @@ -51,7 +51,7 @@ class Shiro1CryptFormatTest { String formatted = format.format(hash); String expected = - Shiro1CryptFormat.MCF_PREFIX + alg + '$' + iterations + '$' + salt.toBase64() + '$' + hash.toBase64() + Shiro1CryptFormat.MCF_PREFIX + alg + '$' + iterations + '$' + salt.toBase64() + '$' + hash.toBase64() assertNotNull formatted assertEquals expected, formatted diff --git a/crypto/pom.xml b/crypto/pom.xml index 28cda20fb4..566402c660 100644 --- a/crypto/pom.xml +++ b/crypto/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + 4.0.0 diff --git a/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/Argon2Hash.java b/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/Argon2Hash.java index 86bcdcac6a..d9d00dc4fa 100644 --- a/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/Argon2Hash.java +++ b/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/Argon2Hash.java @@ -41,6 +41,7 @@ import static java.util.Collections.unmodifiableSet; import static java.util.Objects.requireNonNull; +@SuppressWarnings("checkstyle:LineLength") /** * The Argon2 key derivation function (KDF) is a modern algorithm to shade and hash passwords. * @@ -59,9 +60,16 @@ * @since 2.0 */ class Argon2Hash extends AbstractCryptHash { - private static final long serialVersionUID = 2647354947284558921L; - private static final Logger LOG = LoggerFactory.getLogger(Argon2Hash.class); + /** + * Number of default lanes, p=4 is the default recommendation, taken from draft-irtf-cfrg-argon2-13, 4.2. + */ + public static final int DEFAULT_PARALLELISM = 4; + + /** + * 256 bits tag size is the default recommendation, taken from draft-irtf-cfrg-argon2-13, 4.2. + */ + public static final int DEFAULT_OUTPUT_LENGTH_BITS = 256; public static final String DEFAULT_ALGORITHM_NAME = "argon2id"; @@ -77,20 +85,13 @@ class Argon2Hash extends AbstractCryptHash { */ public static final int DEFAULT_MEMORY_KIB = 64 * 1024; - private static final Set ALGORITHMS_ARGON2 = new HashSet<>(Arrays.asList("argon2id", "argon2i", "argon2d")); - - private static final Pattern DELIMITER_COMMA = Pattern.compile(","); + private static final long serialVersionUID = 2647354947284558921L; - /** - * Number of default lanes, p=4 is the default recommendation, taken from draft-irtf-cfrg-argon2-13, 4.2. - */ - public static final int DEFAULT_PARALLELISM = 4; + private static final Logger LOG = LoggerFactory.getLogger(Argon2Hash.class); - /** - * 256 bits tag size is the default recommendation, taken from draft-irtf-cfrg-argon2-13, 4.2. - */ - public static final int DEFAULT_OUTPUT_LENGTH_BITS = 256; + private static final Set ALGORITHMS_ARGON2 = new HashSet<>(Arrays.asList("argon2id", "argon2i", "argon2d")); + private static final Pattern DELIMITER_COMMA = Pattern.compile(","); /** * 128 bits of salt is the recommended salt length, taken from draft-irtf-cfrg-argon2-13, 4.2. @@ -105,7 +106,7 @@ class Argon2Hash extends AbstractCryptHash { private final int parallelism; - public Argon2Hash(String algorithmName, int argonVersion, byte[] hashedData, ByteSource salt, int iterations, int memoryAsKB, int parallelism) { + Argon2Hash(String algorithmName, int argonVersion, byte[] hashedData, ByteSource salt, int iterations, int memoryAsKB, int parallelism) { super(algorithmName, hashedData, salt); this.argonVersion = argonVersion; this.iterations = iterations; @@ -123,6 +124,7 @@ protected static ByteSource createSalt() { return createSalt(new SecureRandom()); } + @SuppressWarnings("checkstyle:MagicNumber") public static ByteSource createSalt(SecureRandom random) { return new SimpleByteSource(random.generateSeed(SALT_LENGTH_BITS / 8)); } @@ -195,7 +197,8 @@ public static Argon2Hash generate(final ByteSource source, final ByteSource salt } public static Argon2Hash generate(String algorithmName, ByteSource source, ByteSource salt, int iterations) { - return generate(algorithmName, DEFAULT_ALGORITHM_VERSION, source, salt, iterations, DEFAULT_MEMORY_KIB, DEFAULT_PARALLELISM, DEFAULT_OUTPUT_LENGTH_BITS); + return generate(algorithmName, DEFAULT_ALGORITHM_VERSION, source, salt, iterations, + DEFAULT_MEMORY_KIB, DEFAULT_PARALLELISM, DEFAULT_OUTPUT_LENGTH_BITS); } public static Argon2Hash generate( @@ -236,7 +239,7 @@ public static Argon2Hash generate( final Argon2BytesGenerator gen = new Argon2BytesGenerator(); gen.init(parameters); - final byte[] hash = new byte[outputLengthBits / 8]; + @SuppressWarnings("checkstyle:MagicNumber") final byte[] hash = new byte[outputLengthBits / 8]; gen.generateBytes(source.getBytes(), hash); return new Argon2Hash(algorithmName, argonVersion, hash, new SimpleByteSource(salt), iterations, memoryAsKB, parallelism); @@ -247,8 +250,8 @@ protected void checkValidAlgorithm() { if (!ALGORITHMS_ARGON2.contains(getAlgorithmName())) { final String message = String.format( Locale.ENGLISH, - "Given algorithm name [%s] not valid for argon2. " + - "Valid algorithms: [%s].", + "Given algorithm name [%s] not valid for argon2. " + + "Valid algorithms: [%s].", getAlgorithmName(), ALGORITHMS_ARGON2 ); @@ -276,6 +279,7 @@ public int getIterations() { @Override public boolean matchesPassword(ByteSource plaintextBytes) { try { + @SuppressWarnings("checkstyle:MagicNumber") Argon2Hash compare = generate( this.getAlgorithmName(), this.argonVersion, @@ -295,6 +299,7 @@ public boolean matchesPassword(ByteSource plaintextBytes) { } @Override + @SuppressWarnings("checkstyle:MagicNumber") public int getSaltLength() { return SALT_LENGTH_BITS / 8; } diff --git a/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/Argon2HashProvider.java b/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/Argon2HashProvider.java index 2a8fdee40b..5f1266036c 100644 --- a/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/Argon2HashProvider.java +++ b/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/Argon2HashProvider.java @@ -67,7 +67,7 @@ static class Argon2HashFactory implements HashSpi.HashFactory { private final SecureRandom random; - public Argon2HashFactory(Random random) { + Argon2HashFactory(Random random) { if (!(random instanceof SecureRandom)) { throw new IllegalArgumentException("Only SecureRandom instances are supported at the moment!"); } @@ -99,7 +99,8 @@ public Argon2Hash generate(HashRequest hashRequest) { .flatMap(algoV -> intOrEmpty(algoV, Parameters.PARAMETER_PARALLELISM)) .orElse(Parameters.DEFAULT_PARALLELISM); - final int outputLengthBits = Optional.ofNullable(hashRequest.getParameters().get(Parameters.PARAMETER_OUTPUT_LENGTH_BITS)) + final int outputLengthBits = Optional.ofNullable(hashRequest.getParameters() + .get(Parameters.PARAMETER_OUTPUT_LENGTH_BITS)) .flatMap(algoV -> intOrEmpty(algoV, Parameters.PARAMETER_OUTPUT_LENGTH_BITS)) .orElse(Parameters.DEFAULT_OUTPUT_LENGTH_BITS); @@ -123,6 +124,7 @@ private ByteSource parseSalt(HashRequest hashRequest) { .orElseGet(() -> Argon2Hash.createSalt(random)); } + @SuppressWarnings("checkstyle:MagicNumber") private Optional lengthValidOrEmpty(ByteSource bytes) { if (bytes.getBytes().length != 16) { return Optional.empty(); @@ -131,6 +133,7 @@ private Optional lengthValidOrEmpty(ByteSource bytes) { return Optional.of(bytes); } + @SuppressWarnings("checkstyle:MagicNumber") private Optional intOrEmpty(Object maybeInt, String parameterName) { try { return Optional.of(Integer.parseInt((String) maybeInt, 10)); @@ -157,11 +160,34 @@ private Optional intOrEmpty(Object maybeInt, String parameterName) { */ public static final class Parameters { + /** + * Default algorithm name. + */ public static final String DEFAULT_ALGORITHM_NAME = Argon2Hash.DEFAULT_ALGORITHM_NAME; + + /** + * Default algorithm version. + */ public static final int DEFAULT_ALGORITHM_VERSION = Argon2Hash.DEFAULT_ALGORITHM_VERSION; + + /** + * Default iterations. + */ public static final int DEFAULT_ITERATIONS = Argon2Hash.DEFAULT_ITERATIONS; + + /** + * Default memory kib. + */ public static final int DEFAULT_MEMORY_KIB = Argon2Hash.DEFAULT_MEMORY_KIB; + + /** + * Default parallelism number. + */ public static final int DEFAULT_PARALLELISM = Argon2Hash.DEFAULT_PARALLELISM; + + /** + * Default output length bits. + */ public static final int DEFAULT_OUTPUT_LENGTH_BITS = Argon2Hash.DEFAULT_OUTPUT_LENGTH_BITS; /** @@ -174,6 +200,10 @@ public static final class Parameters { *

The default value is {@value DEFAULT_ALGORITHM_NAME} when this parameter is not specified.

*/ public static final String PARAMETER_ALGORITHM_NAME = "Argon2.algorithmName"; + + /** + * Argon2 algorithm version. + */ public static final String PARAMETER_ALGORITHM_VERSION = "Argon2.version"; /** @@ -187,8 +217,19 @@ public static final class Parameters { */ public static final String PARAMETER_SALT = "Argon2.salt"; + /** + * Argon2 parameter iterations. + */ public static final String PARAMETER_ITERATIONS = "Argon2.iterations"; + + /** + * Argon2 parameter memory kib. + */ public static final String PARAMETER_MEMORY_KIB = "Argon2.memoryKib"; + + /** + * Argon2 parameter parallelism. + */ public static final String PARAMETER_PARALLELISM = "Argon2.parallelism"; /** diff --git a/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/package-info.java b/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/package-info.java new file mode 100644 index 0000000000..1cc5c85153 --- /dev/null +++ b/crypto/support/hashes/argon2/src/main/java/org/apache/shiro/crypto/support/hashes/argon2/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.shiro.crypto.support.hashes.argon2; diff --git a/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/BCryptHash.java b/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/BCryptHash.java index f73b40a560..c77c7c1175 100644 --- a/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/BCryptHash.java +++ b/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/BCryptHash.java @@ -41,23 +41,23 @@ */ class BCryptHash extends AbstractCryptHash { - private static final long serialVersionUID = 6957869292324606101L; - - private static final Logger LOG = LoggerFactory.getLogger(AbstractCryptHash.class); - public static final String DEFAULT_ALGORITHM_NAME = "2y"; public static final int DEFAULT_COST = 10; public static final int SALT_LENGTH = 16; + private static final long serialVersionUID = 6957869292324606101L; + + private static final Logger LOG = LoggerFactory.getLogger(AbstractCryptHash.class); + private static final Set ALGORITHMS_BCRYPT = new HashSet<>(Arrays.asList("2", "2a", "2b", "2y")); private final int cost; private final int iterations; - public BCryptHash(final String version, final byte[] hashedData, final ByteSource salt, final int cost) { + BCryptHash(final String version, final byte[] hashedData, final ByteSource salt, final int cost) { super(version, hashedData, salt); this.cost = cost; this.iterations = (int) Math.pow(2, cost); @@ -69,8 +69,8 @@ protected final void checkValidAlgorithm() { if (!ALGORITHMS_BCRYPT.contains(getAlgorithmName())) { final String message = String.format( Locale.ENGLISH, - "Given algorithm name [%s] not valid for bcrypt. " + - "Valid algorithms: [%s].", + "Given algorithm name [%s] not valid for bcrypt. " + + "Valid algorithms: [%s].", getAlgorithmName(), ALGORITHMS_BCRYPT ); @@ -82,6 +82,7 @@ protected final void checkValidCost() { checkValidCost(this.cost); } + @SuppressWarnings("checkstyle:MagicNumber") public static int checkValidCost(final int cost) { if (cost < 4 || cost > 31) { final String message = String.format( @@ -179,7 +180,10 @@ public int getIterations() { @Override public boolean matchesPassword(ByteSource plaintextBytes) { try { - final String cryptString = OpenBSDBCrypt.generate(this.getAlgorithmName(), plaintextBytes.getBytes(), this.getSalt().getBytes(), this.getCost()); + final String cryptString = OpenBSDBCrypt.generate(this.getAlgorithmName(), + plaintextBytes.getBytes(), + this.getSalt().getBytes(), + this.getCost()); BCryptHash other = fromString(cryptString); return this.equals(other); diff --git a/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/BCryptProvider.java b/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/BCryptProvider.java index 74961564b3..6a31c432d8 100644 --- a/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/BCryptProvider.java +++ b/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/BCryptProvider.java @@ -61,7 +61,7 @@ static class BCryptHashFactory implements HashSpi.HashFactory { private final SecureRandom random; - public BCryptHashFactory(Random random) { + BCryptHashFactory(Random random) { if (!(random instanceof SecureRandom)) { throw new IllegalArgumentException("Only SecureRandom instances are supported at the moment!"); } @@ -96,6 +96,7 @@ private int getCost(HashRequest hashRequest) { String costStr = optCostStr.orElseThrow(NoSuchElementException::new); try { + @SuppressWarnings("checkstyle:MagicNumber") int cost = Integer.parseInt(costStr, 10); BCryptHash.checkValidCost(cost); return cost; @@ -132,9 +133,20 @@ private ByteSource getSalt(HashRequest hashRequest) { } public static final class Parameters { + + /** + * Set BCryptHash algorithm name to default. + */ public static final String DEFAULT_ALGORITHM_NAME = BCryptHash.DEFAULT_ALGORITHM_NAME; + /** + * BCrypt salt param. + */ public static final String PARAMETER_SALT = "BCrypt.salt"; + + /** + * BCrypt cost param. + */ public static final String PARAMETER_COST = "BCrypt.cost"; private Parameters() { diff --git a/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/OpenBSDBase64.java b/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/OpenBSDBase64.java index ad05fe853b..27199e1679 100644 --- a/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/OpenBSDBase64.java +++ b/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/OpenBSDBase64.java @@ -28,12 +28,15 @@ * *
* Unix stores password hashes computed with crypt in the /etc/passwd file using radix-64 encoding called B64. It uses a - * mostly-alphanumeric set of characters, plus . and /. Its 64-character set is "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". + * mostly-alphanumeric set of characters, plus . and /. + * Its 64-character set is "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". * Padding is not used. *
* * @since 2.0 */ +@SuppressWarnings({"checkstyle:MagicNumber", "checkstyle:BooleanExpressionComplexity", + "checkstyle:NPathComplexity", "checkstyle:CyclomaticComplexity"}) interface OpenBSDBase64 { @@ -46,9 +49,11 @@ interface OpenBSDBase64 { byte[] encode(byte[] rawBytes); /** - * From a UTF-8 encoded string representing radix64 encoded data as byte array, decodes the raw bytes from it. + * From a UTF-8 encoded string representing radix64 encoded data as byte array, + * decodes the raw bytes from it. * - * @param utf8EncodedRadix64String from a string get it with "m0CrhHm10qJ3lXRY.5zDGO".getBytes(StandardCharsets.UTF8) + * @param utf8EncodedRadix64String from a string get it with + * "m0CrhHm10qJ3lXRY.5zDGO".getBytes(StandardCharsets.UTF8) * @return the raw bytes encoded by this utf-8 radix4 string */ byte[] decode(byte[] utf8EncodedRadix64String); @@ -70,7 +75,7 @@ class Default implements OpenBSDBase64 { 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53}; - private static final byte[] MAP = new byte[]{ + private static final byte[] MAP = new byte[] { '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', @@ -172,6 +177,7 @@ private static byte[] encode(final byte[] in, final byte[] map) { out[index++] = map[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)]; out[index] = map[((in[end + 1] & 0x0f) << 2)]; break; + default: } return out; } diff --git a/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/package-info.java b/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/package-info.java new file mode 100644 index 0000000000..b7ff699798 --- /dev/null +++ b/crypto/support/hashes/bcrypt/src/main/java/org/apache/shiro/crypto/support/hashes/bcrypt/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.shiro.crypto.support.hashes.bcrypt; diff --git a/event/pom.xml b/event/pom.xml index 090e6a3cf2..2652390688 100644 --- a/event/pom.xml +++ b/event/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + org.apache.shiro diff --git a/event/src/main/java/org/apache/shiro/event/Event.java b/event/src/main/java/org/apache/shiro/event/Event.java index 9fecf8a516..ac5e5e5e76 100644 --- a/event/src/main/java/org/apache/shiro/event/Event.java +++ b/event/src/main/java/org/apache/shiro/event/Event.java @@ -27,7 +27,10 @@ */ public abstract class Event extends EventObject { - private final long timestamp; //millis since Epoch (UTC time zone). + /** + * millis since Epoch (UTC time zone). + */ + private final long timestamp; public Event(Object source) { super(source); diff --git a/event/src/main/java/org/apache/shiro/event/package-info.java b/event/src/main/java/org/apache/shiro/event/package-info.java new file mode 100644 index 0000000000..177e091ddc --- /dev/null +++ b/event/src/main/java/org/apache/shiro/event/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.shiro.event; diff --git a/event/src/main/java/org/apache/shiro/event/support/AnnotationEventListenerResolver.java b/event/src/main/java/org/apache/shiro/event/support/AnnotationEventListenerResolver.java index 9a4fe1d7a1..3ab39f9cd2 100644 --- a/event/src/main/java/org/apache/shiro/event/support/AnnotationEventListenerResolver.java +++ b/event/src/main/java/org/apache/shiro/event/support/AnnotationEventListenerResolver.java @@ -51,7 +51,7 @@ public AnnotationEventListenerResolver() { * * @param instance the instance to inspect for annotated event handler methods. * @return a new collection of {@link EventListener} instances, each instance corresponding to an annotated - * method discovered on the specified {@code instance} argument. + * method discovered on the specified {@code instance} argument. */ public List getEventListeners(Object instance) { if (instance == null) { @@ -77,7 +77,7 @@ public List getEventListeners(Object instance) { * defaults to {@link Subscribe}. * * @return the type of annotation that indicates a method that should be represented as an {@link EventListener}, - * defaults to {@link Subscribe}. + * defaults to {@link Subscribe}. */ public Class getAnnotationClass() { return annotationClass; diff --git a/event/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java b/event/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java index d4d5e8c1e6..b4f62459bf 100644 --- a/event/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java +++ b/event/src/main/java/org/apache/shiro/event/support/DefaultEventBus.java @@ -23,7 +23,6 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -62,22 +61,22 @@ * * After registering the component, when when an event of a respective type is published, the component's * {@code Subscribe}-annotated method(s) will be invoked as expected. - * + *

* This design (and its constituent helper components) was largely influenced by * Guava's EventBus * concept, although no code was shared/imported (even though Guava is Apache 2.0 licensed and could have * been used). - * + *

* This implementation is thread-safe and may be used concurrently. * * @since 1.3 */ public class DefaultEventBus implements EventBus { - private static final Logger log = LoggerFactory.getLogger(DefaultEventBus.class); + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultEventBus.class); - private static final String EVENT_LISTENER_ERROR_MSG = "Event listener processing failed. Listeners should " + - "generally handle exceptions directly and not propagate to the event bus."; + private static final String EVENT_LISTENER_ERROR_MSG = "Event listener processing failed. Listeners should " + + "generally handle exceptions directly and not propagate to the event bus."; //this is stateless, we can retain a static final reference: private static final EventListenerComparator EVENT_LISTENER_COMPARATOR = new EventListenerComparator(); @@ -103,7 +102,8 @@ public class DefaultEventBus implements EventBus { private final Lock registryWriteLock; public DefaultEventBus() { - this.registry = new LinkedHashMap(); //not thread safe, so we need locks: + //not thread safe, so we need locks: + this.registry = new LinkedHashMap(); ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); this.registryReadLock = rwl.readLock(); this.registryWriteLock = rwl.writeLock(); @@ -120,7 +120,7 @@ public void setEventListenerResolver(EventListenerResolver eventListenerResolver public void publish(Object event) { if (event == null) { - log.info("Received null event for publishing. Ignoring and returning."); + LOGGER.info("Received null event for publishing. Ignoring and returning."); return; } @@ -149,7 +149,7 @@ public void publish(Object event) { public void register(Object instance) { if (instance == null) { - log.info("Received null instance for event listener registration. Ignoring registration request."); + LOGGER.info("Received null instance for event listener registration. Ignoring registration request."); return; } @@ -158,7 +158,7 @@ public void register(Object instance) { List listeners = getEventListenerResolver().getEventListeners(instance); if (listeners == null || listeners.isEmpty()) { - log.warn("Unable to resolve event listeners for subscriber instance [{}]. Ignoring registration request.", + LOGGER.warn("Unable to resolve event listeners for subscriber instance [{}]. Ignoring registration request.", instance); return; } @@ -185,13 +185,13 @@ public void unregister(Object instance) { } } - private class Subscription { + private static class Subscription { private final List listeners; - public Subscription(List listeners) { + Subscription(List listeners) { List toSort = new ArrayList(listeners); - Collections.sort(toSort, EVENT_LISTENER_COMPARATOR); + toSort.sort(EVENT_LISTENER_COMPARATOR); this.listeners = toSort; } @@ -209,7 +209,7 @@ public void onEvent(Object event) { try { listener.onEvent(event); } catch (Throwable t) { - log.warn(EVENT_LISTENER_ERROR_MSG, t); + LOGGER.warn(EVENT_LISTENER_ERROR_MSG, t); } delivered.add(target); } diff --git a/event/src/main/java/org/apache/shiro/event/support/EventClassComparator.java b/event/src/main/java/org/apache/shiro/event/support/EventClassComparator.java index 4a6ba71eb2..0fcbba3b4a 100644 --- a/event/src/main/java/org/apache/shiro/event/support/EventClassComparator.java +++ b/event/src/main/java/org/apache/shiro/event/support/EventClassComparator.java @@ -37,7 +37,7 @@ * @Subscribe * public void onEvent(B b) { ... } * - * + *

* The {@code onEvent(B b)} method will be invoked on the subscriber and the * {@code onEvent(A a)} method will not be invoked. This is to prevent multiple dispatching of a single event * to the same consumer. diff --git a/event/src/main/java/org/apache/shiro/event/support/EventListener.java b/event/src/main/java/org/apache/shiro/event/support/EventListener.java index 683c51d72e..53821688d4 100644 --- a/event/src/main/java/org/apache/shiro/event/support/EventListener.java +++ b/event/src/main/java/org/apache/shiro/event/support/EventListener.java @@ -34,13 +34,13 @@ * * @see SingleArgumentMethodEventListener * @see AnnotationEventListenerResolver - * * @since 1.3 */ public interface EventListener { /** * Returns {@code true} if the listener instance can process the specified event object, {@code false} otherwise. + * * @param event the event object to test * @return {@code true} if the listener instance can process the specified event object, {@code false} otherwise. */ diff --git a/event/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java b/event/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java index 92bb595cdf..06e15ddaee 100644 --- a/event/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java +++ b/event/src/main/java/org/apache/shiro/event/support/EventListenerComparator.java @@ -52,12 +52,13 @@ public int compare(EventListener a, EventListener b) { return 0; } else { if (a instanceof TypedEventListener) { - TypedEventListener ta = (TypedEventListener)a; + TypedEventListener ta = (TypedEventListener) a; if (b instanceof TypedEventListener) { - TypedEventListener tb = (TypedEventListener)b; + TypedEventListener tb = (TypedEventListener) b; return EVENT_CLASS_COMPARATOR.compare(ta.getEventType(), tb.getEventType()); } else { - return -1; //TypedEventListeners are 'less than' (higher priority) than non typed + //TypedEventListeners are 'less than' (higher priority) than non typed + return -1; } } else { if (b instanceof TypedEventListener) { diff --git a/event/src/main/java/org/apache/shiro/event/support/package-info.java b/event/src/main/java/org/apache/shiro/event/support/package-info.java new file mode 100644 index 0000000000..d67797c80d --- /dev/null +++ b/event/src/main/java/org/apache/shiro/event/support/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.shiro.event.support; diff --git a/event/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy b/event/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy index 44d3f251ec..e930941591 100644 --- a/event/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy +++ b/event/src/test/groovy/org/apache/shiro/event/support/SingleArgumentMethodEventListenerTest.groovy @@ -100,5 +100,4 @@ class SingleArgumentMethodEventListenerTest { } - } diff --git a/integration-tests/guice3/pom.xml b/integration-tests/guice3/pom.xml index 1bac8e1b49..584f05728c 100644 --- a/integration-tests/guice3/pom.xml +++ b/integration-tests/guice3/pom.xml @@ -17,88 +17,89 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - 4.0.0 + + 4.0.0 - - org.apache.shiro.integrationtests - shiro-integration-tests - 2.0.0-SNAPSHOT - + + org.apache.shiro.integrationtests + shiro-integration-tests + 2.0.0-SNAPSHOT + - shiro-its-guice3 - Apache Shiro :: ITs :: Guice 3 - war + shiro-its-guice3 + Apache Shiro :: ITs :: Guice 3 + war - - 3.0 - + + 3.0 + - - - - org.eclipse.jetty - jetty-maven-plugin - - - + + + + org.eclipse.jetty + jetty-maven-plugin + + + - - - org.apache.taglibs - taglibs-standard-spec - compile - - - org.apache.taglibs - taglibs-standard-impl - compile - - - javax.servlet - javax.servlet-api - provided - - - org.apache.logging.log4j - log4j-slf4j2-impl - runtime - - - org.apache.logging.log4j - log4j-core - runtime - - - org.apache.shiro - shiro-core - - - org.apache.shiro - shiro-web - - - org.apache.shiro - shiro-guice - - - com.google.inject.extensions - guice-servlet - - - org.slf4j - jcl-over-slf4j - runtime - + + + org.apache.taglibs + taglibs-standard-spec + compile + + + org.apache.taglibs + taglibs-standard-impl + compile + + + javax.servlet + javax.servlet-api + provided + + + org.apache.logging.log4j + log4j-slf4j2-impl + runtime + + + org.apache.logging.log4j + log4j-core + runtime + + + org.apache.shiro + shiro-core + + + org.apache.shiro + shiro-web + + + org.apache.shiro + shiro-guice + + + com.google.inject.extensions + guice-servlet + + + org.slf4j + jcl-over-slf4j + runtime + - - org.apache.shiro - shiro-guice - ${project.version} - tests - test-jar - test - + + org.apache.shiro + shiro-guice + ${project.version} + tests + test-jar + test + org.htmlunit htmlunit @@ -120,32 +121,32 @@ org.apache.shiro.integrationtests shiro-its-support - test + test - + - - - jdk16 - - [16,) - - - - --illegal-access=permit - --illegal-access=permit - - - - jdk17 - - [17,) - - - - --add-opens java.base/java.lang=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - + + + jdk16 + + [16,) + + + + --illegal-access=permit + --illegal-access=permit + + + + jdk17 + + [17,) + + + + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + + + diff --git a/integration-tests/guice3/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java b/integration-tests/guice3/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java index 9e6af33c12..6254a0d1db 100644 --- a/integration-tests/guice3/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java +++ b/integration-tests/guice3/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java @@ -68,23 +68,19 @@ Ini loadShiroIni() throws MalformedURLException { } @Override - protected void bindWebSecurityManager(AnnotatedBindingBuilder bind) - { - try - { - String cipherKey = loadShiroIni().getSectionProperty( "main", "securityManager.rememberMeManager.cipherKey" ); + protected void bindWebSecurityManager(AnnotatedBindingBuilder bind) { + try { + String cipherKey = loadShiroIni().getSectionProperty("main", "securityManager.rememberMeManager.cipherKey"); DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); - rememberMeManager.setCipherKey( Base64.decode( cipherKey ) ); + rememberMeManager.setCipherKey(Base64.decode(cipherKey)); securityManager.setRememberMeManager(rememberMeManager); bind.toInstance(securityManager); - } - catch ( MalformedURLException e ) - { + } catch (MalformedURLException e) { // for now just throw, you could just call // super.bindWebSecurityManager(bind) if you do not need rememberMe functionality - throw new ConfigurationException( "securityManager.rememberMeManager.cipherKey must be set in shiro.ini." ); + throw new ConfigurationException("securityManager.rememberMeManager.cipherKey must be set in shiro.ini."); } diff --git a/integration-tests/guice3/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java b/integration-tests/guice3/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java index fe9d7a1502..550b9e2298 100644 --- a/integration-tests/guice3/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java +++ b/integration-tests/guice3/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java @@ -40,8 +40,7 @@ public void logOut() throws IOException { final HtmlPage homePage = webClient.getPage(getBaseUri()); try { homePage.getAnchorByHref("/logout").click(); - } - catch (ElementNotFoundException e) { + } catch (ElementNotFoundException e) { //Ignore } } diff --git a/integration-tests/guice4/pom.xml b/integration-tests/guice4/pom.xml index 51e27429a1..e3dc2e706f 100644 --- a/integration-tests/guice4/pom.xml +++ b/integration-tests/guice4/pom.xml @@ -17,93 +17,94 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - 4.0.0 + + 4.0.0 - - org.apache.shiro.integrationtests - shiro-integration-tests - 2.0.0-SNAPSHOT - + + org.apache.shiro.integrationtests + shiro-integration-tests + 2.0.0-SNAPSHOT + - shiro-its-guice4 - Apache Shiro :: ITs :: Guice 4 - war + shiro-its-guice4 + Apache Shiro :: ITs :: Guice 4 + war - - - - org.eclipse.jetty - jetty-maven-plugin - - - org.apache.maven.plugins - maven-surefire-plugin - - - org.apache.shiro:shiro-guice - - - - - + + + + org.eclipse.jetty + jetty-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.shiro:shiro-guice + + + + + - - - org.apache.taglibs - taglibs-standard-spec - compile - - - org.apache.taglibs - taglibs-standard-impl - compile - - - javax.servlet - javax.servlet-api - provided - - - org.apache.logging.log4j - log4j-slf4j2-impl - runtime - - - org.apache.logging.log4j - log4j-core - runtime - - - org.apache.shiro - shiro-core - - - org.apache.shiro - shiro-web - - - org.apache.shiro - shiro-guice - - - com.google.inject.extensions - guice-servlet - - - org.slf4j - jcl-over-slf4j - runtime - + + + org.apache.taglibs + taglibs-standard-spec + compile + + + org.apache.taglibs + taglibs-standard-impl + compile + + + javax.servlet + javax.servlet-api + provided + + + org.apache.logging.log4j + log4j-slf4j2-impl + runtime + + + org.apache.logging.log4j + log4j-core + runtime + + + org.apache.shiro + shiro-core + + + org.apache.shiro + shiro-web + + + org.apache.shiro + shiro-guice + + + com.google.inject.extensions + guice-servlet + + + org.slf4j + jcl-over-slf4j + runtime + - - org.apache.shiro - shiro-guice - ${project.version} - tests - test-jar - test - + + org.apache.shiro + shiro-guice + ${project.version} + tests + test-jar + test + org.htmlunit htmlunit @@ -127,30 +128,30 @@ shiro-its-support test - + - - - jdk16 - - [16,) - - - - --illegal-access=permit - --illegal-access=permit - - - - jdk17 - - [17,) - - - - --add-opens java.base/java.lang=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - + + + jdk16 + + [16,) + + + + --illegal-access=permit + --illegal-access=permit + + + + jdk17 + + [17,) + + + + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + + + diff --git a/integration-tests/guice4/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java b/integration-tests/guice4/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java index 312a59326c..3826089072 100644 --- a/integration-tests/guice4/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java +++ b/integration-tests/guice4/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java @@ -44,6 +44,7 @@ public SampleShiroServletModule(ServletContext servletContext) { this.servletContext = servletContext; } + @SuppressWarnings("checkstyle:LineLength") @Override protected void configureShiroWeb() { bindConstant().annotatedWith(Names.named("shiro.loginUrl")).to("/login.jsp"); @@ -68,23 +69,19 @@ Ini loadShiroIni() throws MalformedURLException { } @Override - protected void bindWebSecurityManager(AnnotatedBindingBuilder bind) - { - try - { - String cipherKey = loadShiroIni().getSectionProperty( "main", "securityManager.rememberMeManager.cipherKey" ); + protected void bindWebSecurityManager(AnnotatedBindingBuilder bind) { + try { + String cipherKey = loadShiroIni().getSectionProperty("main", "securityManager.rememberMeManager.cipherKey"); DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); - rememberMeManager.setCipherKey( Base64.decode( cipherKey ) ); + rememberMeManager.setCipherKey(Base64.decode(cipherKey)); securityManager.setRememberMeManager(rememberMeManager); bind.toInstance(securityManager); - } - catch ( MalformedURLException e ) - { + } catch (MalformedURLException e) { // for now just throw, you could just call // super.bindWebSecurityManager(bind) if you do not need rememberMe functionality - throw new ConfigurationException( "securityManager.rememberMeManager.cipherKey must be set in shiro.ini." ); + throw new ConfigurationException("securityManager.rememberMeManager.cipherKey must be set in shiro.ini."); } diff --git a/integration-tests/guice4/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java b/integration-tests/guice4/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java index fe9d7a1502..550b9e2298 100644 --- a/integration-tests/guice4/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java +++ b/integration-tests/guice4/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java @@ -40,8 +40,7 @@ public void logOut() throws IOException { final HtmlPage homePage = webClient.getPage(getBaseUri()); try { homePage.getAnchorByHref("/logout").click(); - } - catch (ElementNotFoundException e) { + } catch (ElementNotFoundException e) { //Ignore } } diff --git a/integration-tests/jakarta-ee-support/pom.xml b/integration-tests/jakarta-ee-support/pom.xml index 228cc9a06e..c6c4f50b19 100644 --- a/integration-tests/jakarta-ee-support/pom.xml +++ b/integration-tests/jakarta-ee-support/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + 4.0.0 org.apache.shiro.integrationtests jakarta-ee-support diff --git a/integration-tests/jakarta-ee/pom.xml b/integration-tests/jakarta-ee/pom.xml index ed2fd6baf5..5f3b6d3c72 100644 --- a/integration-tests/jakarta-ee/pom.xml +++ b/integration-tests/jakarta-ee/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + 4.0.0 org.apache.shiro.integrationtests shiro-its-jakarta-ee @@ -385,7 +386,8 @@ pre-integration-test create-domain --nopassword --checkports=false - --portbase ${payara.portbase} ${domain.name} + --portbase ${payara.portbase} ${domain.name} + ${create-domain-skip} @@ -400,7 +402,8 @@ -i ${mac-backup-file} -e 's/jvm-options>\[17|/jvm-options>\[16|/g' -e 's/property name="HZ_LISTENER_PORT" value="5900/property name="HZ_LISTENER_PORT" value="5920/g' -e 's/hazelcast-runtime-configuration start-port="5900/hazelcast-runtime-configuration start-port="5920/g' - ${project.build.directory}/dependency/payara5/glassfish/domains/${domain.name}/config/domain.xml + ${project.build.directory}/dependency/payara5/glassfish/domains/${domain.name}/config/domain.xml + ${unix-sed-skip} @@ -436,7 +439,9 @@ payara-config-tmpdir pre-integration-test - -p ${payara.adminport} create-system-properties java.io.tmpdir=${project.build.directory}/payara_tmp + -p ${payara.adminport} create-system-properties + java.io.tmpdir=${project.build.directory}/payara_tmp + ${payara.restart.skip} @@ -469,10 +474,6 @@ - - org.apache.maven.plugins - maven-checkstyle-plugin - diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/cdi/CipherKeyGenerator.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/cdi/CipherKeyGenerator.java index 171810d3d0..a818d4744c 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/cdi/CipherKeyGenerator.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/cdi/CipherKeyGenerator.java @@ -14,6 +14,7 @@ package org.apache.shiro.testing.cdi; import javax.enterprise.context.ApplicationScoped; + import org.apache.shiro.cdi.annotations.CipherKeySupplier; @ApplicationScoped diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/cdi/ComponentInjectionBean.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/cdi/ComponentInjectionBean.java index 2add290c1b..b7d0144079 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/cdi/ComponentInjectionBean.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/cdi/ComponentInjectionBean.java @@ -15,6 +15,7 @@ import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; + import lombok.Getter; import org.apache.shiro.cdi.annotations.NoSessionCreation; import org.apache.shiro.cdi.annotations.Principal; @@ -22,6 +23,7 @@ import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.apache.shiro.testing.jakarta.ee.PropertyPrincipal; + import java.util.function.Supplier; @ApplicationScoped diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/FormBean.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/FormBean.java index b1d52aefc8..7af3ae0180 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/FormBean.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/FormBean.java @@ -14,6 +14,7 @@ package org.apache.shiro.testing.jakarta.ee; import javax.enterprise.inject.Model; + import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -25,7 +26,8 @@ * form's backing bean */ @Model -@Getter @Setter +@Getter +@Setter @Slf4j public class FormBean { private String firstName; diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/PropertyPrincipal.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/PropertyPrincipal.java index 9bbfc7543e..44563c4a34 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/PropertyPrincipal.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/PropertyPrincipal.java @@ -15,6 +15,7 @@ import java.io.Serializable; import javax.enterprise.inject.Vetoed; + import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/PropertyRealm.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/PropertyRealm.java index 32edd55b81..505f0480b5 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/PropertyRealm.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/PropertyRealm.java @@ -16,6 +16,7 @@ import java.util.Map; import javax.enterprise.context.ApplicationScoped; import javax.inject.Named; + import lombok.Getter; import lombok.Setter; import org.apache.shiro.authc.SimpleAccount; @@ -27,7 +28,8 @@ @Named @ApplicationScoped public class PropertyRealm extends IniRealm { - @Getter @Setter + @Getter + @Setter private IniRealm iniRealm; @Override diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedFacesViewScopedBean.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedFacesViewScopedBean.java index 543153c053..b424e38949 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedFacesViewScopedBean.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedFacesViewScopedBean.java @@ -14,6 +14,7 @@ package org.apache.shiro.testing.jakarta.ee; import static org.apache.shiro.testing.jakarta.ee.StatisticsResource.increment; + import java.io.Serializable; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.PostConstruct; @@ -21,6 +22,7 @@ import javax.faces.context.FacesContext; import javax.faces.view.ViewScoped; import javax.inject.Named; + import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authz.annotation.RequiresUser; @@ -49,6 +51,6 @@ void preDestroy() { public String hello() { return String.format("Hello from FacesViewScoped %s - %s", count, - FacesContext.class.getPackage().getImplementationVersion()); + FacesContext.class.getPackage().getImplementationVersion()); } } diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedOmniViewScopedBean.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedOmniViewScopedBean.java index c4721a351b..87daa8d1ad 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedOmniViewScopedBean.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedOmniViewScopedBean.java @@ -14,12 +14,14 @@ package org.apache.shiro.testing.jakarta.ee; import static org.apache.shiro.testing.jakarta.ee.StatisticsResource.increment; + import java.io.Serializable; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.faces.context.FacesContext; import javax.inject.Named; + import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authz.annotation.RequiresUser; import org.omnifaces.cdi.ViewScoped; @@ -49,6 +51,6 @@ void preDestroy() { public String hello() { return String.format("Hello from OmniViewScoped %s - %s", count, - FacesContext.class.getPackage().getImplementationVersion()); + FacesContext.class.getPackage().getImplementationVersion()); } } diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedOneMethod.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedOneMethod.java index 21006e15d4..d12624edc7 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedOneMethod.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedOneMethod.java @@ -14,6 +14,7 @@ package org.apache.shiro.testing.jakarta.ee; import javax.enterprise.context.RequestScoped; + import org.apache.shiro.authz.annotation.RequiresUser; /** diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedSessionScopedBean.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedSessionScopedBean.java index e07e2c187b..44715c6264 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedSessionScopedBean.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedSessionScopedBean.java @@ -14,12 +14,14 @@ package org.apache.shiro.testing.jakarta.ee; import static org.apache.shiro.testing.jakarta.ee.StatisticsResource.increment; + import java.io.Serializable; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.enterprise.context.SessionScoped; import javax.inject.Named; + import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authz.annotation.RequiresUser; diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedStatelessBean.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedStatelessBean.java index be2227d593..1359005088 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedStatelessBean.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/ProtectedStatelessBean.java @@ -14,6 +14,7 @@ package org.apache.shiro.testing.jakarta.ee; import javax.ejb.Stateless; + import org.apache.shiro.authz.annotation.RequiresUser; /** diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/StatisticsResource.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/StatisticsResource.java index b30b2980a2..bd25d688a2 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/StatisticsResource.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/StatisticsResource.java @@ -22,6 +22,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; + import lombok.AccessLevel; import lombok.Getter; diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/UnprotectedFacade.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/UnprotectedFacade.java index e7087a31c5..d9cfe3bcca 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/UnprotectedFacade.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/UnprotectedFacade.java @@ -14,15 +14,20 @@ package org.apache.shiro.testing.jakarta.ee; import static org.apache.shiro.ee.cdi.ShiroScopeContext.isWebContainerSessions; + import org.apache.shiro.ee.filters.Forms; + import java.util.Map; import javax.ejb.EJBException; import javax.enterprise.inject.Model; import javax.inject.Inject; + import lombok.extern.slf4j.Slf4j; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authz.UnauthenticatedException; + import static org.omnifaces.util.Exceptions.unwrap; + import org.omnifaces.util.Messages; /** diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/UseFallback.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/UseFallback.java index 305d45d0ef..5e438da4ea 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/UseFallback.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/UseFallback.java @@ -15,7 +15,9 @@ import javax.enterprise.context.ApplicationScoped; import javax.inject.Named; + import org.apache.shiro.ee.filters.Forms.FallbackPredicate; + import javax.servlet.http.HttpServletRequest; /** diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/servlets/ExceptionServlet.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/servlets/ExceptionServlet.java index 61ee78b1f6..6e391b7948 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/servlets/ExceptionServlet.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/servlets/ExceptionServlet.java @@ -14,6 +14,7 @@ package org.apache.shiro.testing.jakarta.ee.servlets; import org.apache.shiro.testing.logcapture.LogCapture; + import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; @@ -23,6 +24,7 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import static javax.ws.rs.core.MediaType.TEXT_PLAIN; /** diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/servlets/LogoutServlet.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/servlets/LogoutServlet.java index 3eb1ddcaf4..cdce25d705 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/servlets/LogoutServlet.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jakarta/ee/servlets/LogoutServlet.java @@ -19,6 +19,7 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import org.apache.shiro.SecurityUtils; /** diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/JsonPojo.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/JsonPojo.java index 519ff99d1f..d68df31408 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/JsonPojo.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/JsonPojo.java @@ -14,6 +14,7 @@ package org.apache.shiro.testing.jaxrs; import java.io.Serializable; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -21,7 +22,8 @@ import lombok.Setter; @Builder -@Getter @Setter +@Getter +@Setter @NoArgsConstructor @AllArgsConstructor public class JsonPojo implements Serializable { diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/TestApplication.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/TestApplication.java index 96e2b3544d..9b6cdb6b7d 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/TestApplication.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/TestApplication.java @@ -20,6 +20,7 @@ import javax.enterprise.event.Observes; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; + import lombok.Getter; import org.apache.shiro.authc.SimpleAccount; import org.apache.shiro.mgt.DefaultSecurityManager; diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiBean.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiBean.java index ccecad958d..3e934058e5 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiBean.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiBean.java @@ -15,6 +15,7 @@ import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; + import org.apache.shiro.authz.annotation.RequiresUser; import org.apache.shiro.subject.Subject; diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiResource.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiResource.java index 795d3c2dd7..995450b4bf 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiResource.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/jaxrs/WhoamiResource.java @@ -19,9 +19,12 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; + import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; + import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.lang.ShiroException; diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/logcapture/LogCapture.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/logcapture/LogCapture.java index e553b839e4..e082f27dc6 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/logcapture/LogCapture.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/logcapture/LogCapture.java @@ -20,6 +20,7 @@ import java.util.logging.Handler; import java.util.logging.LogRecord; import java.util.logging.Logger; + import lombok.AccessLevel; import lombok.NoArgsConstructor; diff --git a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/logcapture/LogCaptureSingleton.java b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/logcapture/LogCaptureSingleton.java index 01b175a870..bb2e4e7d3e 100644 --- a/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/logcapture/LogCaptureSingleton.java +++ b/integration-tests/jakarta-ee/src/main/java/org/apache/shiro/testing/logcapture/LogCaptureSingleton.java @@ -21,7 +21,8 @@ /** * Entry point for capturing logs and exceptions via servlet */ -@Singleton @Startup +@Singleton +@Startup @SuppressWarnings("MagicNumber") public class LogCaptureSingleton { @PostConstruct diff --git a/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/errorpages/invalidErrorPage.xhtml b/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/errorpages/invalidErrorPage.xhtml index a324ab4e00..3dc14a1ca8 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/errorpages/invalidErrorPage.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/errorpages/invalidErrorPage.xhtml @@ -18,28 +18,28 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - Exception Page - - -

Exception happened
-

-

    -
  • Date/time: #{of:formatDate(now, 'yyyy-MM-dd HH:mm:ss')}
  • -
  • User agent: #{header['user-agent']}
  • -
  • User IP: #{request.remoteAddr}
  • -
  • Request URI: #{requestScope['javax.servlet.error.request_uri']}
  • -
  • Ajax request: #{facesContext.partialViewContext.ajaxRequest ? 'Yes' : 'No'}
  • -
  • Status code: #{requestScope['javax.servlet.error.status_code']}
  • -
  • Exception type: #{requestScope['javax.servlet.error.exception_type']}
  • -
  • Exception message: #{requestScope['javax.servlet.error.message']}
  • -
  • Exception UUID: #{requestScope['org.omnifaces.exception_uuid']}
  • -
  • Stack trace: -
    #{of:printStackTrace(requestScope['javax.servlet.error.exception'])}
    -
  • -
- + + Exception Page + + +
Exception happened
+

+

    +
  • Date/time: #{of:formatDate(now, 'yyyy-MM-dd HH:mm:ss')}
  • +
  • User agent: #{header['user-agent']}
  • +
  • User IP: #{request.remoteAddr}
  • +
  • Request URI: #{requestScope['javax.servlet.error.request_uri']}
  • +
  • Ajax request: #{facesContext.partialViewContext.ajaxRequest ? 'Yes' : 'No'}
  • +
  • Status code: #{requestScope['javax.servlet.error.status_code']}
  • +
  • Exception type: #{requestScope['javax.servlet.error.exception_type']}
  • +
  • Exception message: #{requestScope['javax.servlet.error.message']}
  • +
  • Exception UUID: #{requestScope['org.omnifaces.exception_uuid']}
  • +
  • Stack trace: +
    #{of:printStackTrace(requestScope['javax.servlet.error.exception'])}
    +
  • +
+
diff --git a/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/faces-config.xml b/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/faces-config.xml index 95cd0a3d51..c7381f96ed 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/faces-config.xml +++ b/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/faces-config.xml @@ -24,9 +24,11 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd"> - org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory + org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory + - org.omnifaces.exceptionhandler.ViewExpiredExceptionHandlerFactory + org.omnifaces.exceptionhandler.ViewExpiredExceptionHandlerFactory + org.omnifaces.resourcehandler.VersionedResourceHandler diff --git a/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/payara-web.xml b/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/payara-web.xml index dffb4abb98..6bbebbecd1 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/payara-web.xml +++ b/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/payara-web.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + diff --git a/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/web.xml b/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/web.xml index f2d6b867d2..62cea200bd 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/web.xml +++ b/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/web.xml @@ -35,7 +35,9 @@ org.omnifaces.EXCEPTION_TYPES_TO_IGNORE_IN_LOGGING - javax.faces.application.ViewExpiredException, java.nio.channels.ClosedByInterruptException, java.sql.SQLException + javax.faces.application.ViewExpiredException, java.nio.channels.ClosedByInterruptException, + java.sql.SQLException + diff --git a/integration-tests/jakarta-ee/src/main/webapp/index.xhtml b/integration-tests/jakarta-ee/src/main/webapp/index.xhtml index 2db3a33cae..89c0b33cdd 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/index.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/index.xhtml @@ -17,12 +17,12 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - Index - - - Apache Shiro Examples - + + Index + + + Apache Shiro Examples + diff --git a/integration-tests/jakarta-ee/src/main/webapp/shiro/adminpage.xhtml b/integration-tests/jakarta-ee/src/main/webapp/shiro/adminpage.xhtml index fbc74a33c8..97ecfdf2dc 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/shiro/adminpage.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/shiro/adminpage.xhtml @@ -17,15 +17,15 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - Admin Page - - - Admin user required to access this page - Access Granted! - - - - + + Admin Page + + + Admin user required to access this page - Access Granted! + + + + diff --git a/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/loginform.xhtml b/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/loginform.xhtml index 3d39e29ff9..59b5d8f893 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/loginform.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/loginform.xhtml @@ -17,35 +17,38 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - Please Log In - - - - Please Log In + + Please Log In + + + + Please Log In +

+ + Username: +

- - Username: -

- Password: -

- Remember Me: -

- - - + Password: +

-

- Your Session Has Expired -
-
- Login Failed -
- -
+ Remember Me: + +

+ + + +

+

+ Your Session Has Expired +
+
+ Login Failed +
+ +
diff --git a/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/unauthenticated.xhtml b/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/unauthenticated.xhtml index 8d80e06d04..bc38bc52e3 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/unauthenticated.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/unauthenticated.xhtml @@ -17,13 +17,13 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - User Not Authenticated - - - User Not Authenticated - + + User Not Authenticated + + + User Not Authenticated + diff --git a/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/unauthorized.xhtml b/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/unauthorized.xhtml index 1bb4583fbb..8fa1961431 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/unauthorized.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/shiro/auth/unauthorized.xhtml @@ -17,15 +17,15 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - Unauthorized - - - Not enough permissions for this user - - - - + + Unauthorized + + + Not enough permissions for this user + + + + diff --git a/integration-tests/jakarta-ee/src/main/webapp/shiro/form.xhtml b/integration-tests/jakarta-ee/src/main/webapp/shiro/form.xhtml index 2aab6c82e4..aa04f36d1c 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/shiro/form.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/shiro/form.xhtml @@ -17,47 +17,48 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - Form Page - - - - First Name: - -

- Last Name: - -

- - - - Address: - -

- City: - -

- - - -

- - -

- -
- Logout (via filter) -
- + + Form Page + + + + First Name: + +

+ Last Name: + +

+ + + + Address: + +

+ City: + +

+ + + +

+ + +

+ +
+ Logout (via filter) + +
+ diff --git a/integration-tests/jakarta-ee/src/main/webapp/shiro/index.xhtml b/integration-tests/jakarta-ee/src/main/webapp/shiro/index.xhtml index 38f57ed656..3dd9b5071a 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/shiro/index.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/shiro/index.xhtml @@ -17,18 +17,18 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - Apache Shiro Example - - - This is the Apache Shiro home page -

- Administrator Page -

- Regular User Page -

- Fill out form - + + Apache Shiro Example + + + This is the Apache Shiro home page +

+ Administrator Page +

+ Regular User Page +

+ Fill out form + diff --git a/integration-tests/jakarta-ee/src/main/webapp/shiro/protected.xhtml b/integration-tests/jakarta-ee/src/main/webapp/shiro/protected.xhtml index 0f3348479d..8023c5b62a 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/shiro/protected.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/shiro/protected.xhtml @@ -17,18 +17,18 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - Protected Page - - - Protected Page - User login required - Authorized! - - -

- - - - + + Protected Page + + + Protected Page - User login required - Authorized! + + +

+ + + + diff --git a/integration-tests/jakarta-ee/src/main/webapp/shiro/unprotected/manybeans.xhtml b/integration-tests/jakarta-ee/src/main/webapp/shiro/unprotected/manybeans.xhtml index 11fd926243..84a5eb7a8b 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/shiro/unprotected/manybeans.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/shiro/unprotected/manybeans.xhtml @@ -17,53 +17,57 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - Many Beans Unprotected - - - Using Web Sessions: + + Many Beans Unprotected + + + Using Web Sessions: + +

+ + + + + + + + + + + + + + + + + + +

- - - - - - - - - - - - - - - - - - - -

- - - -

- Your Session Has Expired -
- - - -
- + + + +
+ Your Session Has Expired +
+ + + + + diff --git a/integration-tests/jakarta-ee/src/main/webapp/shiro/unprotected/tags.xhtml b/integration-tests/jakarta-ee/src/main/webapp/shiro/unprotected/tags.xhtml index 901fe15a09..6505c89333 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/shiro/unprotected/tags.xhtml +++ b/integration-tests/jakarta-ee/src/main/webapp/shiro/unprotected/tags.xhtml @@ -17,27 +17,29 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - - Tags Test Page - - - Guest Content - User Content - Authenticated Content - Not Authenticated Content - - - - Regular Role - Lacks Admin Role - Has Some Role - Has Some Permission - Has Permission - Lacks Permission - Remembered - + + Tags Test Page + + + Guest Content + User Content + Authenticated Content + Not Authenticated Content + + + + Regular Role + Lacks Admin Role + Has Some Role + Has Some Permission + Has Permission + Lacks Permission + Remembered + diff --git a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/cdi/ComponentInjectionIT.java b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/cdi/ComponentInjectionIT.java index 1488d07d0a..ee9281565f 100644 --- a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/cdi/ComponentInjectionIT.java +++ b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/cdi/ComponentInjectionIT.java @@ -14,10 +14,13 @@ package org.apache.shiro.testing.cdi; import javax.inject.Inject; + import lombok.extern.slf4j.Slf4j; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; + import static org.apache.shiro.ee.util.JakartaTransformer.jakartify; + import org.apache.shiro.testing.jakarta.ee.PropertyPrincipal; import org.apache.shiro.testing.jaxrs.NoIniJaxRsIT; import org.apache.shiro.testing.jaxrs.TestApplication; @@ -34,7 +37,9 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; + import java.util.Optional; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/Deployments.java b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/Deployments.java index df70916e02..913c905c04 100644 --- a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/Deployments.java +++ b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/Deployments.java @@ -15,12 +15,16 @@ import static com.flowlogix.util.ShrinkWrapManipulator.Action; import static com.flowlogix.util.ShrinkWrapManipulator.getContextParamValue; + import java.util.List; + import org.apache.shiro.testing.cdi.ComponentInjectionIT; + import static org.apache.shiro.ee.util.JakartaTransformer.jakartify; import static org.apache.shiro.testing.cdi.ComponentInjectionIT.TESTABLE_MODE; import static org.apache.shiro.testing.jakarta.ee.ShiroAuthFormsIT.DEPLOYMENT_DEV_MODE; import static org.apache.shiro.testing.jakarta.ee.ShiroAuthFormsIT.DEPLOYMENT_PROD_MODE; + import org.eu.ingwar.tools.arquillian.extension.suite.annotations.ArquillianSuiteDeployment; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.shrinkwrap.api.spec.WebArchive; @@ -72,7 +76,7 @@ private static List initializeStandardActions() { + ",classpath:META-INF/shiro-native-sessions.ini"))); case SHIRO_EE_DISABLED: return List.of(new Action(getContextParamValue("org.apache.shiro.ee.disabled"), - node -> node.setTextContent("true"), true)); + node -> node.setTextContent("true"), true)); default: return List.of(); } diff --git a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/FacesTagsIT.java b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/FacesTagsIT.java index aee6aa7fe2..bad6cc1587 100644 --- a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/FacesTagsIT.java +++ b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/FacesTagsIT.java @@ -14,16 +14,22 @@ package org.apache.shiro.testing.jakarta.ee; import java.net.URL; + import static org.apache.shiro.testing.jakarta.ee.ShiroAuthFormsIT.DEPLOYMENT_DEV_MODE; import static org.apache.shiro.testing.jakarta.ee.ShiroAuthFormsIT.createDeploymentDev; + import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.OperateOnDeployment; import org.jboss.arquillian.drone.api.annotation.Drone; + import static org.jboss.arquillian.graphene.Graphene.guardHttp; + import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.shrinkwrap.api.spec.WebArchive; + import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -200,7 +206,7 @@ void hasAnyPermission() { void hasPermission() { login(); webDriver.get(baseURL + "shiro/unprotected/tags"); - assertEquals("Has Permission" , hasPermission.getText()); + assertEquals("Has Permission", hasPermission.getText()); } @Test @@ -208,7 +214,7 @@ void hasPermission() { void lacksPermissio() { login(); webDriver.get(baseURL + "shiro/unprotected/tags"); - assertEquals("Lacks Permission" , lacksPermission.getText()); + assertEquals("Lacks Permission", lacksPermission.getText()); } @Test @@ -216,7 +222,7 @@ void lacksPermissio() { void remembered() { login(); webDriver.get(baseURL + "shiro/unprotected/tags"); - assertEquals("" , remembered.getText()); + assertEquals("", remembered.getText()); } private void login() { diff --git a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroAuthFormsIT.java b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroAuthFormsIT.java index f188e9d29a..85c3a58ba6 100644 --- a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroAuthFormsIT.java +++ b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroAuthFormsIT.java @@ -15,30 +15,38 @@ import com.flowlogix.util.ShrinkWrapManipulator; import com.flowlogix.util.ShrinkWrapManipulator.Action; + import static com.flowlogix.util.ShrinkWrapManipulator.getContextParamValue; import static org.apache.shiro.testing.jakarta.ee.Deployments.standardActions; import static org.apache.shiro.testing.jakarta.ee.Deployments.isClientStateSavingIntegrationTest; import static org.apache.shiro.testing.jakarta.ee.Deployments.isShiroNativeSessionsIntegrationTest; + import java.net.URL; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; + import static org.apache.shiro.ee.util.JakartaTransformer.jakartify; import static org.apache.shiro.web.servlet.ShiroHttpSession.DEFAULT_SESSION_ID_NAME; + import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.OperateOnDeployment; import org.jboss.arquillian.drone.api.annotation.Drone; + import static org.jboss.arquillian.graphene.Graphene.guardAjax; import static org.jboss.arquillian.graphene.Graphene.guardHttp; import static org.jboss.arquillian.graphene.Graphene.waitForHttp; import static org.jboss.arquillian.graphene.Graphene.waitGui; + import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.jboss.shrinkwrap.resolver.api.maven.archive.importer.MavenImporter; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; diff --git a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroBeansIT.java b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroBeansIT.java index 97625824f6..cd211377c7 100644 --- a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroBeansIT.java +++ b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroBeansIT.java @@ -14,20 +14,26 @@ package org.apache.shiro.testing.jakarta.ee; import java.net.URL; + import static org.apache.shiro.ee.util.JakartaTransformer.isJakarta; import static org.apache.shiro.ee.util.JakartaTransformer.jakartify; import static org.apache.shiro.testing.jakarta.ee.ShiroAuthFormsIT.DEPLOYMENT_DEV_MODE; + import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.OperateOnDeployment; import org.jboss.arquillian.drone.api.annotation.Drone; + import static org.jboss.arquillian.graphene.Graphene.guardAjax; import static org.jboss.arquillian.graphene.Graphene.guardHttp; import static org.jboss.arquillian.graphene.Graphene.waitGui; + import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.shrinkwrap.api.spec.WebArchive; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -117,7 +123,7 @@ void checkDontCallWhenNotAuth() { webDriver.get(baseURL + "lastException"); String exceptionText = webDriver.findElement(By.tagName("body")).getText(); assertTrue(exceptionText - .startsWith(jakartify("WARNING: javax.ejb.EJBException: Attempting to perform a user-only operation")), + .startsWith(jakartify("WARNING: javax.ejb.EJBException: Attempting to perform a user-only operation")), String.format("capturing correct warning from the server: %s", exceptionText)); } @@ -149,7 +155,7 @@ void beanDestroyCalled() { } private void exersizeViewAndSessionScoped(WebElement elem, String createStatistic, String destroyStatistic, - boolean isBrokenDestructor) { + boolean isBrokenDestructor) { webDriver.get(baseURL + "shiro/auth/loginform"); login(); diff --git a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroSSLFilterIT.java b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroSSLFilterIT.java index b0af647cfa..b1898ab53b 100644 --- a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroSSLFilterIT.java +++ b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jakarta/ee/ShiroSSLFilterIT.java @@ -14,16 +14,21 @@ package org.apache.shiro.testing.jakarta.ee; import static com.flowlogix.util.ShrinkWrapManipulator.toHttpsURL; + import java.net.URL; + import static org.apache.shiro.testing.jakarta.ee.ShiroAuthFormsIT.DEPLOYMENT_PROD_MODE; + import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.OperateOnDeployment; import org.jboss.arquillian.drone.api.annotation.Drone; import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.shrinkwrap.api.spec.WebArchive; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; + import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jaxrs/NoIniJaxRsIT.java b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jaxrs/NoIniJaxRsIT.java index 48537d2b13..df56c074d5 100644 --- a/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jaxrs/NoIniJaxRsIT.java +++ b/integration-tests/jakarta-ee/src/test/java/org/apache/shiro/testing/jaxrs/NoIniJaxRsIT.java @@ -21,17 +21,22 @@ import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; + import lombok.Builder; import lombok.SneakyThrows; import org.apache.shiro.testing.cdi.ComponentInjectionIT; + import static org.apache.shiro.testing.cdi.ComponentInjectionIT.TESTABLE_MODE; + import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.OperateOnDeployment; import org.jboss.arquillian.junit5.ArquillianExtension; import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.shrinkwrap.api.spec.WebArchive; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/integration-tests/jakarta-ee/src/test/resources/arquillian.xml b/integration-tests/jakarta-ee/src/test/resources/arquillian.xml index 83ec397d6e..182a23f888 100644 --- a/integration-tests/jakarta-ee/src/test/resources/arquillian.xml +++ b/integration-tests/jakarta-ee/src/test/resources/arquillian.xml @@ -13,9 +13,9 @@ limitations under the License. --> + xmlns="http://jboss.org/schema/arquillian" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd"> ${webdriver.browser} -headless diff --git a/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/JaxRsApplication.java b/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/JaxRsApplication.java index 6a74dccc1a..4319cfdc81 100644 --- a/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/JaxRsApplication.java +++ b/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/JaxRsApplication.java @@ -25,6 +25,7 @@ /** * Simple JAX-RS {@link Application} that is implementation agnostic. + * * @since 2.0.0 */ @Dependent diff --git a/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/config/ShiroServletFilter.java b/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/config/ShiroServletFilter.java index 39b023c313..173848c4fd 100644 --- a/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/config/ShiroServletFilter.java +++ b/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/config/ShiroServletFilter.java @@ -23,6 +23,7 @@ import javax.servlet.DispatcherType; import javax.servlet.annotation.WebFilter; +@SuppressWarnings("checkstyle:LineLength") @Dependent @WebFilter( asyncSupported = true, diff --git a/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/dao/InMemoryStormtrooperDao.java b/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/dao/InMemoryStormtrooperDao.java index 25ad4b4400..e24161e546 100644 --- a/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/dao/InMemoryStormtrooperDao.java +++ b/integration-tests/jaxrs/app/src/main/java/org/apache/shiro/testing/jaxrs/app/dao/InMemoryStormtrooperDao.java @@ -36,6 +36,7 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +@SuppressWarnings("checkstyle:MagicNumber") @Dependent @Default public class InMemoryStormtrooperDao implements StormtrooperDao { @@ -61,7 +62,7 @@ public Collection listTroopers() { @Override public Optional getStormtrooper(StormtrooperId id) { - return Optional.ofNullable(TROOPERS.get(id)); + return Optional.ofNullable(TROOPERS.get(id)); } @Override @@ -87,6 +88,7 @@ public Stormtrooper updateStormtrooper(StormtrooperId id, Stormtrooper stormtroo @Override public boolean deleteStormtrooper(StormtrooperId id) { // TODO: implement - throw new UnsupportedOperationException("not yet implemented: [org.apache.shiro.testing.meecrowave.jaxrs.dao.InMemoryStormtrooperDao::deleteStormtrooper]."); + throw new UnsupportedOperationException("not yet implemented: " + + "[org.apache.shiro.testing.meecrowave.jaxrs.dao.InMemoryStormtrooperDao::deleteStormtrooper]."); } } diff --git a/integration-tests/jaxrs/app/src/main/resources/META-INF/beans.xml b/integration-tests/jaxrs/app/src/main/resources/META-INF/beans.xml index e052c65014..0c5842171a 100644 --- a/integration-tests/jaxrs/app/src/main/resources/META-INF/beans.xml +++ b/integration-tests/jaxrs/app/src/main/resources/META-INF/beans.xml @@ -23,5 +23,5 @@ xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" bean-discovery-mode="all"> - + diff --git a/integration-tests/jaxrs/app/src/main/webapp/WEB-INF/beans.xml b/integration-tests/jaxrs/app/src/main/webapp/WEB-INF/beans.xml index e052c65014..0c5842171a 100644 --- a/integration-tests/jaxrs/app/src/main/webapp/WEB-INF/beans.xml +++ b/integration-tests/jaxrs/app/src/main/webapp/WEB-INF/beans.xml @@ -23,5 +23,5 @@ xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd" bean-discovery-mode="all"> - + diff --git a/integration-tests/jaxrs/meecrowave/src/test/java/org/apache/shiro/testing/jaxrs/meecrowave/MeecrowaveIT.java b/integration-tests/jaxrs/meecrowave/src/test/java/org/apache/shiro/testing/jaxrs/meecrowave/MeecrowaveIT.java index 21d7f51220..1c5a0e19f0 100644 --- a/integration-tests/jaxrs/meecrowave/src/test/java/org/apache/shiro/testing/jaxrs/meecrowave/MeecrowaveIT.java +++ b/integration-tests/jaxrs/meecrowave/src/test/java/org/apache/shiro/testing/jaxrs/meecrowave/MeecrowaveIT.java @@ -35,7 +35,7 @@ public class MeecrowaveIT extends AbstractShiroJaxRsIT { private Meecrowave.Builder config; @Override - protected URI getBaseUri() { + protected URI getBaseUri() { return URI.create("http://localhost:" + config.getHttpPort() + "/api"); } diff --git a/integration-tests/jaxrs/meecrowave/src/test/resources/log4j2.xml b/integration-tests/jaxrs/meecrowave/src/test/resources/log4j2.xml index f42f81616b..0e5ceed64b 100644 --- a/integration-tests/jaxrs/meecrowave/src/test/resources/log4j2.xml +++ b/integration-tests/jaxrs/meecrowave/src/test/resources/log4j2.xml @@ -18,53 +18,53 @@ ~ under the License. --> - - [%d][%highlight{%-5level}][%15.15t][%30.30logger] %msg%n - - - - - - - - - - - - - - + + [%d][%highlight{%-5level}][%15.15t][%30.30logger] %msg%n + + + + + + + + + + + + + + - + - - - - - - - + + + + + + + diff --git a/integration-tests/jaxrs/openliberty/pom.xml b/integration-tests/jaxrs/openliberty/pom.xml index 3aad63bb86..9ae48b4a96 100644 --- a/integration-tests/jaxrs/openliberty/pom.xml +++ b/integration-tests/jaxrs/openliberty/pom.xml @@ -138,8 +138,12 @@ [16,) - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED --add-opens + java.base/java.net=ALL-UNNAMED + + --add-opens java.base/java.lang=ALL-UNNAMED --add-opens + java.base/java.net=ALL-UNNAMED + diff --git a/integration-tests/jaxrs/openliberty/src/main/liberty/config/server.xml b/integration-tests/jaxrs/openliberty/src/main/liberty/config/server.xml index 7bf5ef9e49..27ceb77063 100644 --- a/integration-tests/jaxrs/openliberty/src/main/liberty/config/server.xml +++ b/integration-tests/jaxrs/openliberty/src/main/liberty/config/server.xml @@ -31,14 +31,14 @@ + httpsPort="${default.https.port}"/> - + - + diff --git a/integration-tests/jaxrs/openliberty/src/test/java/org/apache/shiro/testing/jaxrs/openliberty/OpenLibertyIT.java b/integration-tests/jaxrs/openliberty/src/test/java/org/apache/shiro/testing/jaxrs/openliberty/OpenLibertyIT.java index 6b1f227414..b760d2e018 100644 --- a/integration-tests/jaxrs/openliberty/src/test/java/org/apache/shiro/testing/jaxrs/openliberty/OpenLibertyIT.java +++ b/integration-tests/jaxrs/openliberty/src/test/java/org/apache/shiro/testing/jaxrs/openliberty/OpenLibertyIT.java @@ -26,6 +26,7 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class OpenLibertyIT extends AbstractShiroJaxRsIT { + @SuppressWarnings("checkstyle:LineLength") @Override protected URI getBaseUri() { return URI.create("http://localhost:" + System.getProperty("http.port") + "/" + System.getProperty("app.context.root") + "/api"); diff --git a/integration-tests/jaxrs/tests/src/main/java/org/apache/shiro/testing/jaxrs/tests/AbstractShiroJaxRsIT.java b/integration-tests/jaxrs/tests/src/main/java/org/apache/shiro/testing/jaxrs/tests/AbstractShiroJaxRsIT.java index ee0946aaf6..355491b5e1 100644 --- a/integration-tests/jaxrs/tests/src/main/java/org/apache/shiro/testing/jaxrs/tests/AbstractShiroJaxRsIT.java +++ b/integration-tests/jaxrs/tests/src/main/java/org/apache/shiro/testing/jaxrs/tests/AbstractShiroJaxRsIT.java @@ -53,7 +53,7 @@ public void logOut() { @Test - public void testGetUsersUnauthenticated() { + public void testGetUsersUnauthenticated() { final WebTarget usersTarget = client.target(getBaseUri()).path("troopers"); final Response usersResponse = usersTarget.request(MediaType.APPLICATION_JSON_TYPE) .buildGet() @@ -61,6 +61,7 @@ public void testGetUsersUnauthenticated() { assertEquals(Status.UNAUTHORIZED.getStatusCode(), usersResponse.getStatus()); } + @SuppressWarnings({"checkstyle:MagicNumber", "checkstyle:LineLength"}) @Test public void testGetUsersBasicAuthenticated() { final WebTarget usersTarget = client.target(getBaseUri()).path("troopers"); diff --git a/integration-tests/meecrowave-support/src/main/resources/META-INF/openwebbeans/openwebbeans.properties b/integration-tests/meecrowave-support/src/main/resources/META-INF/openwebbeans/openwebbeans.properties index 537f6c4bbf..59e58aec4a 100644 --- a/integration-tests/meecrowave-support/src/main/resources/META-INF/openwebbeans/openwebbeans.properties +++ b/integration-tests/meecrowave-support/src/main/resources/META-INF/openwebbeans/openwebbeans.properties @@ -16,23 +16,16 @@ # specific language governing permissions and limitations # under the License. # - # suppress inspection "UnusedProperty" for whole file - -configuration.ordinal = 1000 - +configuration.ordinal=1000 # avoid warns on java 11 - but prevents package scoped methods in proxies -org.apache.webbeans.spi.DefiningClassService = org.apache.webbeans.service.ClassLoaderProxyService - +org.apache.webbeans.spi.DefiningClassService=org.apache.webbeans.service.ClassLoaderProxyService # flat classpath so capture the startup classloader, # avoids surprises at runtime when classloader is not app one at startup (rare but happens in maven plugins) -org.apache.webbeans.spi.ApplicationBoundaryService = org.apache.webbeans.corespi.se.SimpleApplicationBoundaryService - +org.apache.webbeans.spi.ApplicationBoundaryService=org.apache.webbeans.corespi.se.SimpleApplicationBoundaryService # faster and more controlled - requires META-INF/beans.xml for scanning -org.apache.webbeans.scanBeansXmlOnly = true - +org.apache.webbeans.scanBeansXmlOnly=true # don't use loadClass(package-info) to check @Vetoed, this slows down boot for nothing -org.apache.webbeans.spi.deployer.skipVetoedOnPackages = true - +org.apache.webbeans.spi.deployer.skipVetoedOnPackages=true # speed up startup if no classpath issue -org.apache.webbeans.spi.deployer.skipNoClassDefFoundTriggers = true +org.apache.webbeans.spi.deployer.skipNoClassDefFoundTriggers=true diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index df8bce4ced..63447d91ba 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + 4.0.0 diff --git a/integration-tests/support/pom.xml b/integration-tests/support/pom.xml index d9085c6812..f843c4557b 100644 --- a/integration-tests/support/pom.xml +++ b/integration-tests/support/pom.xml @@ -17,37 +17,38 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - 4.0.0 + + 4.0.0 - - org.apache.shiro.integrationtests - shiro-integration-tests - 2.0.0-SNAPSHOT - + + org.apache.shiro.integrationtests + shiro-integration-tests + 2.0.0-SNAPSHOT + - shiro-its-support - Apache Shiro :: ITs :: Support + shiro-its-support + Apache Shiro :: ITs :: Support its.support - - + + org.apache.taglibs taglibs-standard-spec compile - - + + org.apache.taglibs taglibs-standard-impl compile - - - javax.servlet - javax.servlet-api - provided - + + + javax.servlet + javax.servlet-api + provided + org.apache.logging.log4j log4j-slf4j2-impl @@ -59,11 +60,11 @@ runtime - - org.slf4j - jcl-over-slf4j - runtime - + + org.slf4j + jcl-over-slf4j + runtime + org.htmlunit @@ -123,15 +124,15 @@ compile - - - org.apache.shiro - shiro-core - + + + org.apache.shiro + shiro-core + - + diff --git a/integration-tests/support/src/main/java/org/apache/shiro/testing/web/AbstractContainerIT.java b/integration-tests/support/src/main/java/org/apache/shiro/testing/web/AbstractContainerIT.java index b76a06dd4b..3d57d45eb1 100644 --- a/integration-tests/support/src/main/java/org/apache/shiro/testing/web/AbstractContainerIT.java +++ b/integration-tests/support/src/main/java/org/apache/shiro/testing/web/AbstractContainerIT.java @@ -59,17 +59,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +@SuppressWarnings({"checkstyle:ClassDataAbstractionCoupling", "checkstyle:LineLength"}) public abstract class AbstractContainerIT { + protected static final File TEST_KEYSTORE_PATH = setupKeyStore(); + protected static final String TEST_KEYSTORE_PASSWORD = "password"; + protected static EmbeddedJetty jetty; protected static int tlsPort; protected final WebClient webClient = new WebClient(); - protected static final File TEST_KEYSTORE_PATH = setupKeyStore(); - protected static final String TEST_KEYSTORE_PASSWORD = "password"; - + @SuppressWarnings("checkstyle:AnonInnerLength") @BeforeAll public static void startContainer() throws Exception { @@ -81,8 +83,9 @@ public static void startContainer() throws Exception { /** * Overriding with contents of this pull request, to make fragment scanning work. - * https://github.com/mjeanroy/junit-servers/pull/3 + * */ + @SuppressWarnings("checkstyle:LineLength") protected WebAppContext createdWebAppContext() throws Exception { final String path = configuration.getPath(); final String webapp = configuration.getWebapp(); @@ -95,7 +98,7 @@ protected WebAppContext createdWebAppContext() throws Exception { // Useful for WebXmlConfiguration ctx.setBaseResource(newResource(webapp)); - ctx.setConfigurations(new Configuration[]{ + ctx.setConfigurations(new Configuration[] { new WebInfConfiguration(), new WebXmlConfiguration(), new AnnotationConfiguration(), @@ -169,6 +172,7 @@ public boolean accept(File dir, String name) { } }); + assert warFiles != null; assertEquals(1, warFiles.length, "Expected only one war file in target directory, run 'mvn clean' and try again"); return warFiles[0].getAbsolutePath().replaceFirst("\\.war$", ""); diff --git a/integration-tests/support/src/main/resources/test-keystore.jks b/integration-tests/support/src/main/resources/test-keystore.jks index 0cea846c1d..05ff43619c 100644 Binary files a/integration-tests/support/src/main/resources/test-keystore.jks and b/integration-tests/support/src/main/resources/test-keystore.jks differ diff --git a/integration-tests/support/src/main/resources/test-keystore.pem b/integration-tests/support/src/main/resources/test-keystore.pem index bbbad012ab..2b15430818 100644 --- a/integration-tests/support/src/main/resources/test-keystore.pem +++ b/integration-tests/support/src/main/resources/test-keystore.pem @@ -1,22 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDjzCCAnegAwIBAgIEDGro0DANBgkqhkiG9w0BAQsFADBtMRIwEAYDVQQDEwls -b2NhbGhvc3QxEDAOBgNVBAsTB1Vua25vd24xEDAOBgNVBAoTB1Vua25vd24xFDAS -BgNVBAcTC1NwcmluZ2ZpZWxkMRAwDgYDVQQIEwdVbmtub3duMQswCQYDVQQGEwJV -UzAeFw0yMDEwMTUxNTE4MThaFw0zMDEwMTMxNTE4MThaMG0xEjAQBgNVBAMTCWxv -Y2FsaG9zdDEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEUMBIG -A1UEBxMLU3ByaW5nZmllbGQxEDAOBgNVBAgTB1Vua25vd24xCzAJBgNVBAYTAlVT -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmlRyaRblUwPFtvMR+NZa -hW27UEidgyOIXeXS+iwOXbc49B9ADA6utnQybiaKWG5IXMCD1dsPd3Pqx/5Bqmhd -1QQJCQxjx1McieEhXuhON7wqpYlmt+sIVUvPNHFldyuv/CqqZLBLWYj1uaba15E7 -oLPVT+XBRY/uvWR4q6HtweNcBU0m183eaFSzYwpUuhHCgI5V3+qC40eDoCPd6jrA -S6BGldvR+Ysbe5fGQUmL7IUks6YO/AGMRZjmR5pKGE5dmoCPsuusnmHvm8iKfkh0 -KzMDcxpY0ZlwNXIsNfIuIBBBAkGZG2RVmJeOJG6Bv6DCSt3ypOLbb0vHiEm7wLrL -YwIDAQABozcwNTAUBgNVHREEDTALgglsb2NhbGhvc3QwHQYDVR0OBBYEFNDDMtIb -fscg7DRkbQV6ZuKNG7YlMA0GCSqGSIb3DQEBCwUAA4IBAQCLJ6743hPQNx1Sop0v -0Fm+2dm66Xj77aGfB9Xq64/BsQP+imWYuAv0oQq2tgCtXSMBhyfBKQKfEbQcd+m5 -WsiSfkxpvcCWR7Ttc7GuElG6Bb+CqLxLDZuEogIOsVM7MgEfqC5zp94vLhSrLmrl -HzZzisVL/PH0wMpAuD0IRWgdYyPWavBipVHCJYWJRehQon9D+qi1EuwSOZYvq9af -YSfZtkndXrVfpmnO9+xrszO5Yu9qgZfvRLhpal/cmsBpRVyZIWdUj4B1wfPtKT3/ -x+85LN5wNyiRkROf1M0S6mb2Ac7zMJrNI5hKfKM2OOLC/g/ec3NOWH8/7k9FlcRv -alPL +MIIDkzCCAnugAwIBAgIIa39qloKgECwwDQYJKoZIhvcNAQEMBQAwbTESMBAGA1UE +AxMJbG9jYWxob3N0MRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3du +MRQwEgYDVQQHEwtTcHJpbmdmaWVsZDEQMA4GA1UECBMHVW5rbm93bjELMAkGA1UE +BhMCVVMwHhcNMjMwOTEwMTMyMDMwWhcNMzMwOTA3MTMyMDMwWjBtMRIwEAYDVQQD +Ewlsb2NhbGhvc3QxEDAOBgNVBAsTB1Vua25vd24xEDAOBgNVBAoTB1Vua25vd24x +FDASBgNVBAcTC1NwcmluZ2ZpZWxkMRAwDgYDVQQIEwdVbmtub3duMQswCQYDVQQG +EwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKWFVGev4K77elfy +yCx1V51P7Bg1VJi1Q9YNtKFtf97bMUxH4skjKi15FzGmPwfnRwi5tLxUrUCBdUgg +Nw0aUpKKVU5DUsAQsK6X8UA13szMPy878LP7OSlYuS9hdNfGclxCxpfAzUld46/9 +KxKMaIcOcmW8ELIHj14lOiD0wOMzqVVln6J0hoWoeUoVZtIbkGlTbgRKDIlZfkvz ++Q+P4RgV97bB+eMQvapHBTAXtzcfAgBfyfhZdVpFfpXomk/S5HbvL463D99yPzhh +j7oDx2CsYAQZCQR3CuckO1k/NphoBAS4D7Cmf3HrzzIfcK8rxHCw7AWUFx0Bu2dL +g9zRwusCAwEAAaM3MDUwHQYDVR0OBBYEFApTnWJx0Us5I8xUXs+Ax0rQ0GLFMBQG +A1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQwFAAOCAQEAfEkFFkpEo++j +c4iGA03PZ4hpUrXrR3yGg0uBgzJEkqtANrFgIy9sgL7toON7BoyZhq6jzD0Z6wPl +BQRnegX4i3E0URt+LYfC3XE4Tqbg9+txYml3f4u3d/rO0os6jNfPuzOA2a5wFPGp +sHNzTs0jGbKIsEDnAeg2xAFSIh9NPgcXhlewwDKeQB5muydOKpaFdWJMNWQLUSNq +7kfRP/HYcynanPOcj07BOXA/rZGTL2YN2IyQkrXj+Lp8Uu611kXdqQBhyvoXfw8a +jmQlpEHAV5qOOnknfSK2lJ95tUfS3JivHubIS08abpDlYyQ+qDuCIWteHJ4tMnns +hAbs59p5Aw== -----END CERTIFICATE----- diff --git a/lang/pom.xml b/lang/pom.xml index e6729bce46..dc6d6c58bf 100644 --- a/lang/pom.xml +++ b/lang/pom.xml @@ -18,7 +18,8 @@ ~ under the License. --> - + org.apache.shiro @@ -31,7 +32,7 @@ Apache Shiro :: Lang The lang module encapsulates only language-specific utilities that are used by various - other modules. It exists to augment what we would have liked to see in the JDK but does not exist. + other modules. It exists to augment what we would have liked to see in the JDK but does not exist. bundle diff --git a/lang/src/main/java/org/apache/shiro/lang/codec/Base64.java b/lang/src/main/java/org/apache/shiro/lang/codec/Base64.java index 7738259ffb..2b3047f5c5 100644 --- a/lang/src/main/java/org/apache/shiro/lang/codec/Base64.java +++ b/lang/src/main/java/org/apache/shiro/lang/codec/Base64.java @@ -30,13 +30,16 @@ * great work they've done, but also didn't want to force every Shiro user to depend on the commons-codec.jar *

* As per the Apache 2.0 license, the original copyright notice and all author and copyright information have - * remained in tact. + * remained intact. * * @see Wikipedia: Base 64 * @see RFC 2045 * @since 0.9 */ -public class Base64 { +public final class Base64 { + + private Base64() { + } /** * Base64 encodes the specified byte array and then encodes it as a String using Shiro's preferred character diff --git a/lang/src/main/java/org/apache/shiro/lang/codec/CodecSupport.java b/lang/src/main/java/org/apache/shiro/lang/codec/CodecSupport.java index d7fd0c8837..becf3ce9c0 100644 --- a/lang/src/main/java/org/apache/shiro/lang/codec/CodecSupport.java +++ b/lang/src/main/java/org/apache/shiro/lang/codec/CodecSupport.java @@ -33,6 +33,7 @@ * * @since 0.9 */ +@SuppressWarnings("checkstyle:BooleanExpressionComplexity") public abstract class CodecSupport { /** @@ -43,7 +44,7 @@ public abstract class CodecSupport { /** * Converts the specified character array to a byte array using the Shiro's preferred encoding (UTF-8). *

- * This is a convenience method equivalent to calling the {@link #toBytes(String,String)} method with a + * This is a convenience method equivalent to calling the {@link #toBytes(String, String)} method with a * a wrapping String and {@link CodecSupport#PREFERRED_ENCODING PREFERRED_ENCODING}, i.e. *

* toBytes( new String(chars), {@link CodecSupport#PREFERRED_ENCODING PREFERRED_ENCODING} ); @@ -58,7 +59,7 @@ public static byte[] toBytes(char[] chars) { /** * Converts the specified character array into a byte array using the specified character encoding. *

- * This is a convenience method equivalent to calling the {@link #toBytes(String,String)} method with a + * This is a convenience method equivalent to calling the {@link #toBytes(String, String)} method with a * a wrapping String and the specified encoding, i.e. *

* toBytes( new String(chars), encoding ); @@ -97,8 +98,8 @@ public static byte[] toBytes(String source, String encoding) throws CodecExcepti try { return source.getBytes(encoding); } catch (UnsupportedEncodingException e) { - String msg = "Unable to convert source [" + source + "] to byte array using " + - "encoding '" + encoding + "'"; + String msg = "Unable to convert source [" + source + "] to byte array using " + + "encoding '" + encoding + "'"; throw new CodecException(msg, e); } } @@ -149,7 +150,8 @@ public static char[] toChars(byte[] bytes) { /** * Converts the specified byte array to a character array using the specified character encoding. *

- * Effectively calls {@link #toString(byte[], String) toString(bytes,encoding)}.{@link String#toCharArray() toCharArray()}; + * Effectively calls {@link #toString(byte[], String) toString(bytes,encoding)} + * .{@link String#toCharArray() toCharArray()}; * * @param bytes the byte array to convert to a String * @param encoding the character encoding used to encode the bytes. @@ -177,12 +179,12 @@ public static char[] toChars(byte[] bytes, String encoding) throws CodecExceptio * * @param o the object to test to see if it can be easily converted to a byte array * @return {@code true} if the specified object can be easily converted to bytes by instances of this class, - * {@code false} otherwise. + * {@code false} otherwise. * @since 1.0 */ protected boolean isByteSource(Object o) { - return o instanceof byte[] || o instanceof char[] || o instanceof String || - o instanceof ByteSource || o instanceof File || o instanceof InputStream; + return o instanceof byte[] || o instanceof char[] || o instanceof String + || o instanceof ByteSource || o instanceof File || o instanceof InputStream; } /** @@ -272,9 +274,9 @@ protected byte[] toBytes(InputStream in) { if (in == null) { throw new IllegalArgumentException("InputStream argument cannot be null."); } - final int BUFFER_SIZE = 512; - ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE); - byte[] buffer = new byte[BUFFER_SIZE]; + final int bufferSize = 512; + ByteArrayOutputStream out = new ByteArrayOutputStream(bufferSize); + byte[] buffer = new byte[bufferSize]; int bytesRead; try { while ((bytesRead = in.read(buffer)) != -1) { @@ -304,13 +306,13 @@ protected byte[] toBytes(InputStream in) { * @return a byte array representation of the Object argument. */ protected byte[] objectToBytes(Object o) { - String msg = "The " + getClass().getName() + " implementation only supports conversion to " + - "byte[] if the source is of type byte[], char[], String, " + ByteSource.class.getName() + - " File or InputStream. The instance provided as a method " + - "argument is of type [" + o.getClass().getName() + "]. If you would like to convert " + - "this argument type to a byte[], you can 1) convert the argument to one of the supported types " + - "yourself and then use that as the method argument or 2) subclass " + getClass().getName() + - "and override the objectToBytes(Object o) method."; + String msg = "The " + getClass().getName() + " implementation only supports conversion to " + + "byte[] if the source is of type byte[], char[], String, " + ByteSource.class.getName() + + " File or InputStream. The instance provided as a method " + + "argument is of type [" + o.getClass().getName() + "]. If you would like to convert " + + "this argument type to a byte[], you can 1) convert the argument to one of the supported types " + + "yourself and then use that as the method argument or 2) subclass " + getClass().getName() + + "and override the objectToBytes(Object o) method."; throw new CodecException(msg); } diff --git a/lang/src/main/java/org/apache/shiro/lang/codec/H64.java b/lang/src/main/java/org/apache/shiro/lang/codec/H64.java index fd30aa1dda..c9a5914e50 100644 --- a/lang/src/main/java/org/apache/shiro/lang/codec/H64.java +++ b/lang/src/main/java/org/apache/shiro/lang/codec/H64.java @@ -45,25 +45,41 @@ * * @since 1.2 */ -public class H64 { +@SuppressWarnings("checkstyle:MagicNumber") +public final class H64 { - private static final char[] itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); + private static final byte FF = (byte) 0xff; + + private static final char[] ITOA_64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); + + private H64() { + + } private static short toShort(byte b) { - return (short) (b & 0xff); + return (short) (b & FF); } private static int toInt(byte[] bytes, int offset, int numBytes) { if (numBytes < 1 || numBytes > 4) { throw new IllegalArgumentException("numBytes must be between 1 and 4."); } - int val = toShort(bytes[offset]); //1st byte - for (int i = 1; i < numBytes; i++) { //any remaining bytes: + //1st byte + int val = toShort(bytes[offset]); + for (int i = 1; i < numBytes; i++) { + //any remaining bytes: short s = toShort(bytes[offset + i]); switch (i) { - case 1: val |= s << 8; break; - case 2: val |= s << 16; break; - case 3: val |= s << 24; break; + case 1: + val |= s << (2 << 2); + break; + case 2: + val |= s << ((2 << 2) * 2); + break; + case 3: + val |= s << ((2 << 2) * 3); + break; + default: } } return val; @@ -94,7 +110,7 @@ private static void append(Appendable buf, char c) { */ private static void encodeAndAppend(int value, Appendable buf, int numChars) { for (int i = 0; i < numChars; i++) { - append(buf, itoa64[value & 0x3f]); + append(buf, ITOA_64[value & 0x3f]); value >>= 6; } } @@ -106,16 +122,20 @@ private static void encodeAndAppend(int value, Appendable buf, int numChars) { * @return */ public static String encodeToString(byte[] bytes) { - if (bytes == null || bytes.length == 0) return null; + if (bytes == null || bytes.length == 0) { + return null; + } StringBuilder buf = new StringBuilder(); int length = bytes.length; int remainder = length % 3; - int i = 0; //starting byte - int last3ByteIndex = length - remainder; //last byte whose index is a multiple of 3 + //starting byte + int i = 0; + //last byte whose index is a multiple of 3 + int last3ByteIndex = length - remainder; - for(; i < last3ByteIndex; i += 3) { + for (; i < last3ByteIndex; i += 3) { int twentyFourBit = toInt(bytes, i, 3); encodeAndAppend(twentyFourBit, buf, 4); } diff --git a/lang/src/main/java/org/apache/shiro/lang/codec/Hex.java b/lang/src/main/java/org/apache/shiro/lang/codec/Hex.java index 00e4e0ad30..0e2cfd6450 100644 --- a/lang/src/main/java/org/apache/shiro/lang/codec/Hex.java +++ b/lang/src/main/java/org/apache/shiro/lang/codec/Hex.java @@ -18,6 +18,7 @@ */ package org.apache.shiro.lang.codec; + /** * Hexadecimal encoder and decoder. *

@@ -31,7 +32,8 @@ * @see Wikipedia: Hexadecimal * @since 0.9 */ -public class Hex { +@SuppressWarnings("checkstyle:MagicNumber") +public final class Hex { /** * Used to build output as Hex @@ -41,6 +43,10 @@ public class Hex { '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + private Hex() { + + } + /** * Encodes the specified byte array to a character array and then returns that character array * as a String. @@ -85,7 +91,7 @@ public static char[] encode(byte[] data) { * * @param array An array of character bytes containing hexadecimal digits * @return A byte array containing binary data decoded from - * the supplied byte array (representing characters). + * the supplied byte array (representing characters). * @throws IllegalArgumentException Thrown if an odd number of characters is supplied * to this function * @see #decode(char[]) @@ -116,10 +122,11 @@ public static byte[] decode(String hex) { * * @param data An array of characters containing hexadecimal digits * @return A byte array containing binary data decoded from - * the supplied char array. + * the supplied char array. * @throws IllegalArgumentException if an odd number or illegal of characters * is supplied */ + @SuppressWarnings("magicNumber") public static byte[] decode(char[] data) throws IllegalArgumentException { int len = data.length; @@ -151,7 +158,7 @@ public static byte[] decode(char[] data) throws IllegalArgumentException { * @throws IllegalArgumentException if ch is an illegal hex character */ protected static int toDigit(char ch, int index) throws IllegalArgumentException { - int digit = Character.digit(ch, 16); + int digit = Character.digit(ch, 2 << 3); if (digit == -1) { throw new IllegalArgumentException("Illegal hexadecimal character " + ch + " at index " + index); } diff --git a/lang/src/main/java/org/apache/shiro/lang/io/ClassResolvingObjectInputStream.java b/lang/src/main/java/org/apache/shiro/lang/io/ClassResolvingObjectInputStream.java index 941e389de8..1f7f0c73df 100644 --- a/lang/src/main/java/org/apache/shiro/lang/io/ClassResolvingObjectInputStream.java +++ b/lang/src/main/java/org/apache/shiro/lang/io/ClassResolvingObjectInputStream.java @@ -29,8 +29,8 @@ /** * Enables correct ClassLoader lookup in various environments (e.g. JEE Servers, etc.). * - * @since 1.2 * @see SHIRO-334 + * @since 1.2 */ public class ClassResolvingObjectInputStream extends ObjectInputStream { @@ -39,12 +39,12 @@ public ClassResolvingObjectInputStream(InputStream inputStream) throws IOExcepti } /** - * Resolves an {@link ObjectStreamClass} by delegating to Shiro's + * Resolves an {@link ObjectStreamClass} by delegating to Shiro's * {@link ClassUtils#forName(String)} utility method, which is known to work in all ClassLoader environments. - * + * * @param osc the ObjectStreamClass to resolve the class name. * @return the discovered class - * @throws IOException never - declaration retained for subclass consistency + * @throws IOException never - declaration retained for subclass consistency * @throws ClassNotFoundException if the class could not be found in any known ClassLoader */ @Override diff --git a/lang/src/main/java/org/apache/shiro/lang/io/DefaultSerializer.java b/lang/src/main/java/org/apache/shiro/lang/io/DefaultSerializer.java index a48a176cae..a06f7a1421 100644 --- a/lang/src/main/java/org/apache/shiro/lang/io/DefaultSerializer.java +++ b/lang/src/main/java/org/apache/shiro/lang/io/DefaultSerializer.java @@ -18,11 +18,18 @@ */ package org.apache.shiro.lang.io; -import java.io.*; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; /** * Serializer implementation that uses the default JVM serialization mechanism (Object Input/Output Streams). * + * @param the type of target. * @since 0.9 */ public class DefaultSerializer implements Serializer { @@ -50,9 +57,9 @@ public byte[] serialize(T o) throws SerializationException { baos.flush(); return baos.toByteArray(); } catch (IOException e) { - String msg = "Unable to serialize object [" + o + "]. " + - "In order for the DefaultSerializer to serialize this object, the [" + o.getClass().getName() + "] " + - "class must implement java.io.Serializable."; + String msg = "Unable to serialize object [" + o + "]. " + + "In order for the DefaultSerializer to serialize this object, " + + " the [" + o.getClass().getName() + "] class must implement java.io.Serializable."; throw new SerializationException(msg, e); } } diff --git a/lang/src/main/java/org/apache/shiro/lang/io/ResourceUtils.java b/lang/src/main/java/org/apache/shiro/lang/io/ResourceUtils.java index 174f489228..85937e5a4f 100644 --- a/lang/src/main/java/org/apache/shiro/lang/io/ResourceUtils.java +++ b/lang/src/main/java/org/apache/shiro/lang/io/ResourceUtils.java @@ -33,7 +33,7 @@ * @see #getInputStreamForPath(String) * @since 0.2 */ -public class ResourceUtils { +public final class ResourceUtils { /** * Resource path prefix that specifies to load from a classpath location, value is {@code classpath:} @@ -51,7 +51,7 @@ public class ResourceUtils { /** * Private internal log instance. */ - private static final Logger log = LoggerFactory.getLogger(ResourceUtils.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ResourceUtils.class); /** * Prevent instantiation. @@ -66,15 +66,14 @@ private ResourceUtils() { * * @param resourcePath the resource path to check * @return {@code true} if the resource path is not null and starts with one of the recognized - * resource prefixes, {@code false} otherwise. + * resource prefixes, {@code false} otherwise. * @since 0.9 */ @SuppressWarnings({"UnusedDeclaration"}) public static boolean hasResourcePrefix(String resourcePath) { - return resourcePath != null && - (resourcePath.startsWith(CLASSPATH_PREFIX) || - resourcePath.startsWith(URL_PREFIX) || - resourcePath.startsWith(FILE_PREFIX)); + return resourcePath != null + && (resourcePath.startsWith(CLASSPATH_PREFIX) + || resourcePath.startsWith(URL_PREFIX) || resourcePath.startsWith(FILE_PREFIX)); } /** @@ -143,20 +142,20 @@ public static InputStream getInputStreamForPath(String resourcePath) throws IOEx } private static InputStream loadFromFile(String path) throws IOException { - if (log.isDebugEnabled()) { - log.debug("Opening file [" + path + "]..."); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Opening file [" + path + "]..."); } return new FileInputStream(path); } private static InputStream loadFromUrl(String urlPath) throws IOException { - log.debug("Opening url {}", urlPath); + LOGGER.debug("Opening url {}", urlPath); URL url = new URL(urlPath); return url.openStream(); } private static InputStream loadFromClassPath(String path) { - log.debug("Opening resource from class path [{}]", path); + LOGGER.debug("Opening resource from class path [{}]", path); return ClassUtils.getResourceAsStream(path); } @@ -176,7 +175,7 @@ public static void close(InputStream is) { try { is.close(); } catch (IOException e) { - log.warn("Error closing input stream.", e); + LOGGER.warn("Error closing input stream.", e); } } } diff --git a/lang/src/main/java/org/apache/shiro/lang/io/SerializationException.java b/lang/src/main/java/org/apache/shiro/lang/io/SerializationException.java index a10a254450..5292d74791 100644 --- a/lang/src/main/java/org/apache/shiro/lang/io/SerializationException.java +++ b/lang/src/main/java/org/apache/shiro/lang/io/SerializationException.java @@ -26,8 +26,7 @@ * * @since Apr 23, 2008 8:58:22 AM */ -public class SerializationException extends ShiroException -{ +public class SerializationException extends ShiroException { /** * Creates a new SerializationException. diff --git a/lang/src/main/java/org/apache/shiro/lang/io/XmlSerializer.java b/lang/src/main/java/org/apache/shiro/lang/io/XmlSerializer.java index 8e31716488..4788c63821 100644 --- a/lang/src/main/java/org/apache/shiro/lang/io/XmlSerializer.java +++ b/lang/src/main/java/org/apache/shiro/lang/io/XmlSerializer.java @@ -42,6 +42,7 @@ public class XmlSerializer implements Serializer { * Serializes the specified source into a byte[] array by using the * {@link java.beans.XMLEncoder XMLEncoder} to encode the object out to a * {@link java.io.ByteArrayOutputStream ByteArrayOutputStream}, where the resulting byte[] array is returned. + * * @param source the Object to convert into a byte[] array. * @return the byte[] array representation of the XML encoded output. */ @@ -64,6 +65,7 @@ public byte[] serialize(Object source) { * {@link java.io.ByteArrayInputStream ByteArrayInputStream} to wrap the argument and then decode this * stream via an {@link java.beans.XMLDecoder XMLDecoder}, where the * {@link java.beans.XMLDecoder#readObject() readObject} call results in the original Object to return. + * * @param serialized the byte[] array representation of the XML encoded output. * @return the original source Object in reconstituted form. */ diff --git a/lang/src/main/java/org/apache/shiro/lang/package-info.java b/lang/src/main/java/org/apache/shiro/lang/package-info.java new file mode 100644 index 0000000000..76a9d3bec3 --- /dev/null +++ b/lang/src/main/java/org/apache/shiro/lang/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Shiro lang package-info. + */ +package org.apache.shiro.lang; diff --git a/lang/src/main/java/org/apache/shiro/lang/util/Assert.java b/lang/src/main/java/org/apache/shiro/lang/util/Assert.java index c96f3fb624..7bb651e8c7 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/Assert.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/Assert.java @@ -44,7 +44,7 @@ *

  * Assert.notNull(clazz, "The class must not be null");
  * Assert.isTrue(i > 0, "The value must be greater than zero");
- * + *

* Mainly for internal use within the framework; consider Jakarta's Commons Lang * >= 2.0 for a more comprehensive suite of assertion utilities. *

@@ -58,8 +58,9 @@ public abstract class Assert { * Assert a boolean expression, throwing IllegalArgumentException * if the test result is false. *

Assert.isTrue(i > 0, "The value must be greater than zero");
+ * * @param expression a boolean expression - * @param message the exception message to use if the assertion fails + * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if expression is false */ public static void isTrue(boolean expression, String message) { @@ -72,6 +73,7 @@ public static void isTrue(boolean expression, String message) { * Assert a boolean expression, throwing IllegalArgumentException * if the test result is false. *
Assert.isTrue(i > 0);
+ * * @param expression a boolean expression * @throws IllegalArgumentException if expression is false */ @@ -82,7 +84,8 @@ public static void isTrue(boolean expression) { /** * Assert that an object is null . *
Assert.isNull(value, "The value must be null");
- * @param object the object to check + * + * @param object the object to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object is not null */ @@ -95,6 +98,7 @@ public static void isNull(Object object, String message) { /** * Assert that an object is null . *
Assert.isNull(value);
+ * * @param object the object to check * @throws IllegalArgumentException if the object is not null */ @@ -105,7 +109,8 @@ public static void isNull(Object object) { /** * Assert that an object is not null . *
Assert.notNull(clazz, "The class must not be null");
- * @param object the object to check + * + * @param object the object to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object is null */ @@ -118,6 +123,7 @@ public static void notNull(Object object, String message) { /** * Assert that an object is not null . *
Assert.notNull(clazz);
+ * * @param object the object to check * @throws IllegalArgumentException if the object is null */ @@ -129,7 +135,8 @@ public static void notNull(Object object) { * Assert that the given String is not empty; that is, * it must not be null and not the empty String. *
Assert.hasLength(name, "Name must not be empty");
- * @param text the String to check + * + * @param text the String to check * @param message the exception message to use if the assertion fails * @see StringUtils#hasLength */ @@ -143,6 +150,7 @@ public static void hasLength(String text, String message) { * Assert that the given String is not empty; that is, * it must not be null and not the empty String. *
Assert.hasLength(name);
+ * * @param text the String to check * @see StringUtils#hasLength */ @@ -155,7 +163,8 @@ public static void hasLength(String text) { * Assert that the given String has valid text content; that is, it must not * be null and must contain at least one non-whitespace character. *
Assert.hasText(name, "'name' must not be empty");
- * @param text the String to check + * + * @param text the String to check * @param message the exception message to use if the assertion fails * @see StringUtils#hasText */ @@ -169,6 +178,7 @@ public static void hasText(String text, String message) { * Assert that the given String has valid text content; that is, it must not * be null and must contain at least one non-whitespace character. *
Assert.hasText(name, "'name' must not be empty");
+ * * @param text the String to check * @see StringUtils#hasText */ @@ -180,13 +190,14 @@ public static void hasText(String text) { /** * Assert that the given text does not contain the given substring. *
Assert.doesNotContain(name, "rod", "Name must not contain 'rod'");
+ * * @param textToSearch the text to search - * @param substring the substring to find within the text - * @param message the exception message to use if the assertion fails + * @param substring the substring to find within the text + * @param message the exception message to use if the assertion fails */ public static void doesNotContain(String textToSearch, String substring, String message) { - if (StringUtils.hasLength(textToSearch) && StringUtils.hasLength(substring) && - textToSearch.indexOf(substring) != -1) { + if (StringUtils.hasLength(textToSearch) && StringUtils.hasLength(substring) + && textToSearch.contains(substring)) { throw new IllegalArgumentException(message); } } @@ -194,8 +205,9 @@ public static void doesNotContain(String textToSearch, String substring, String /** * Assert that the given text does not contain the given substring. *
Assert.doesNotContain(name, "rod");
+ * * @param textToSearch the text to search - * @param substring the substring to find within the text + * @param substring the substring to find within the text */ public static void doesNotContain(String textToSearch, String substring) { doesNotContain(textToSearch, substring, @@ -207,7 +219,8 @@ public static void doesNotContain(String textToSearch, String substring) { * Assert that an array has elements; that is, it must not be * null and must have at least one element. *
Assert.notEmpty(array, "The array must have elements");
- * @param array the array to check + * + * @param array the array to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object array is null or has no elements */ @@ -221,6 +234,7 @@ public static void notEmpty(Object[] array, String message) { * Assert that an array has elements; that is, it must not be * null and must have at least one element. *
Assert.notEmpty(array);
+ * * @param array the array to check * @throws IllegalArgumentException if the object array is null or has no elements */ @@ -232,7 +246,8 @@ public static void notEmpty(Object[] array) { * Assert that an array has no null elements. * Note: Does not complain if the array is empty! *
Assert.noNullElements(array, "The array must have non-null elements");
- * @param array the array to check + * + * @param array the array to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object array contains a null element */ @@ -250,6 +265,7 @@ public static void noNullElements(Object[] array, String message) { * Assert that an array has no null elements. * Note: Does not complain if the array is empty! *
Assert.noNullElements(array);
+ * * @param array the array to check * @throws IllegalArgumentException if the object array contains a null element */ @@ -261,8 +277,9 @@ public static void noNullElements(Object[] array) { * Assert that a collection has elements; that is, it must not be * null and must have at least one element. *
Assert.notEmpty(collection, "Collection must have elements");
+ * * @param collection the collection to check - * @param message the exception message to use if the assertion fails + * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the collection is null or has no elements */ public static void notEmpty(Collection collection, String message) { @@ -275,6 +292,7 @@ public static void notEmpty(Collection collection, String message) { * Assert that a collection has elements; that is, it must not be * null and must have at least one element. *
Assert.notEmpty(collection, "Collection must have elements");
+ * * @param collection the collection to check * @throws IllegalArgumentException if the collection is null or has no elements */ @@ -287,7 +305,8 @@ public static void notEmpty(Collection collection) { * Assert that a Map has entries; that is, it must not be null * and must have at least one entry. *
Assert.notEmpty(map, "Map must have entries");
- * @param map the map to check + * + * @param map the map to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the map is null or has no entries */ @@ -301,6 +320,7 @@ public static void notEmpty(Map map, String message) { * Assert that a Map has entries; that is, it must not be null * and must have at least one entry. *
Assert.notEmpty(map);
+ * * @param map the map to check * @throws IllegalArgumentException if the map is null or has no entries */ @@ -312,8 +332,9 @@ public static void notEmpty(Map map) { /** * Assert that the provided object is an instance of the provided class. *
Assert.instanceOf(Foo.class, foo);
+ * * @param clazz the required class - * @param obj the object to check + * @param obj the object to check * @throws IllegalArgumentException if the object is not an instance of clazz * @see Class#isInstance */ @@ -324,29 +345,31 @@ public static void isInstanceOf(Class clazz, Object obj) { /** * Assert that the provided object is an instance of the provided class. *
Assert.instanceOf(Foo.class, foo);
- * @param type the type to check against - * @param obj the object to check + * + * @param type the type to check against + * @param obj the object to check * @param message a message which will be prepended to the message produced by - * the function itself, and which may be used to provide context. It should - * normally end in a ": " or ". " so that the function generate message looks - * ok when prepended to it. + * the function itself, and which may be used to provide context. It should + * normally end in a ": " or ". " so that the function generate message looks + * ok when prepended to it. * @throws IllegalArgumentException if the object is not an instance of clazz * @see Class#isInstance */ public static void isInstanceOf(Class type, Object obj, String message) { notNull(type, "Type to check against must not be null"); if (!type.isInstance(obj)) { - throw new IllegalArgumentException(message + - "Object of class [" + (obj != null ? obj.getClass().getName() : "null") + - "] must be an instance of " + type); + throw new IllegalArgumentException(message + + "Object of class [" + (obj != null ? obj.getClass().getName() : "null") + + "] must be an instance of " + type); } } /** * Assert that superType.isAssignableFrom(subType) is true. *
Assert.isAssignable(Number.class, myClass);
+ * * @param superType the super type to check - * @param subType the sub type to check + * @param subType the sub type to check * @throws IllegalArgumentException if the classes are not assignable */ public static void isAssignable(Class superType, Class subType) { @@ -356,12 +379,13 @@ public static void isAssignable(Class superType, Class subType) { /** * Assert that superType.isAssignableFrom(subType) is true. *
Assert.isAssignable(Number.class, myClass);
+ * * @param superType the super type to check against - * @param subType the sub type to check - * @param message a message which will be prepended to the message produced by - * the function itself, and which may be used to provide context. It should - * normally end in a ": " or ". " so that the function generate message looks - * ok when prepended to it. + * @param subType the sub type to check + * @param message a message which will be prepended to the message produced by + * the function itself, and which may be used to provide context. It should + * normally end in a ": " or ". " so that the function generate message looks + * ok when prepended to it. * @throws IllegalArgumentException if the classes are not assignable */ public static void isAssignable(Class superType, Class subType, String message) { @@ -377,8 +401,9 @@ public static void isAssignable(Class superType, Class subType, String message) * if the test result is false. Call isTrue if you wish to * throw IllegalArgumentException on an assertion failure. *
Assert.state(id == null, "The id property must not already be initialized");
+ * * @param expression a boolean expression - * @param message the exception message to use if the assertion fails + * @param message the exception message to use if the assertion fails * @throws IllegalStateException if expression is false */ public static void state(boolean expression, String message) { @@ -393,6 +418,7 @@ public static void state(boolean expression, String message) { *

Call {@link #isTrue(boolean)} if you wish to * throw {@link IllegalArgumentException} on an assertion failure. *

Assert.state(id == null);
+ * * @param expression a boolean expression * @throws IllegalStateException if the supplied expression is false */ diff --git a/lang/src/main/java/org/apache/shiro/lang/util/ByteSource.java b/lang/src/main/java/org/apache/shiro/lang/util/ByteSource.java index f9a1331cda..c75d074bee 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/ByteSource.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/ByteSource.java @@ -41,7 +41,7 @@ public interface ByteSource { * underlying wrapped byte array. * * @return the Hex-formatted String representation of the - * underlying wrapped byte array. + * underlying wrapped byte array. */ String toHex(); @@ -50,7 +50,7 @@ public interface ByteSource { * underlying wrapped byte array. * * @return the Base 64-formatted String representation of the - * underlying wrapped byte array. + * underlying wrapped byte array. */ String toBase64(); @@ -59,7 +59,7 @@ public interface ByteSource { * otherwise. * * @return {@code true} if the underlying wrapped byte array is null or empty (zero length), {@code false} - * otherwise. + * otherwise. * @since 1.2 */ boolean isEmpty(); @@ -70,7 +70,10 @@ public interface ByteSource { * * @since 1.2 */ - public static final class Util { + final class Util { + + private Util() { + } /** * Returns a new {@code ByteSource} instance representing the specified byte array. @@ -138,12 +141,13 @@ public static ByteSource bytes(InputStream stream) { * Returns {@code true} if the specified object can be easily represented as a {@code ByteSource} using * the {@link ByteSource.Util}'s default heuristics, {@code false} otherwise. *

- * This implementation merely returns {@link SimpleByteSource}.{@link SimpleByteSource#isCompatible(Object) isCompatible(source)}. + * This implementation merely returns {@link SimpleByteSource} + * .{@link SimpleByteSource#isCompatible(Object) isCompatible(source)}. * * @param source the object to test to see if it can be easily converted to ByteSource instances using default * heuristics. * @return {@code true} if the specified object can be easily represented as a {@code ByteSource} using - * the {@link ByteSource.Util}'s default heuristics, {@code false} otherwise. + * the {@link ByteSource.Util}'s default heuristics, {@code false} otherwise. */ public static boolean isCompatible(Object source) { return SimpleByteSource.isCompatible(source); @@ -164,9 +168,9 @@ public static ByteSource bytes(Object source) throws IllegalArgumentException { return null; } if (!isCompatible(source)) { - String msg = "Unable to heuristically acquire bytes for object of type [" + - source.getClass().getName() + "]. If this type is indeed a byte-backed data type, you might " + - "want to write your own ByteSource implementation to extract its bytes explicitly."; + String msg = "Unable to heuristically acquire bytes for object of type [" + + source.getClass().getName() + "]. If this type is indeed a byte-backed data type, you might " + + "want to write your own ByteSource implementation to extract its bytes explicitly."; throw new IllegalArgumentException(msg); } if (source instanceof byte[]) { @@ -182,9 +186,9 @@ public static ByteSource bytes(Object source) throws IllegalArgumentException { } else if (source instanceof InputStream) { return bytes((InputStream) source); } else { - throw new IllegalStateException("Encountered unexpected byte source. This is a bug - please notify " + - "the Shiro developer list asap (the isCompatible implementation does not reflect this " + - "method's implementation)."); + throw new IllegalStateException("Encountered unexpected byte source. This is a bug - please notify " + + "the Shiro developer list asap (the isCompatible implementation does not reflect this " + + "method's implementation)."); } } } diff --git a/lang/src/main/java/org/apache/shiro/lang/util/ByteSourceWrapper.java b/lang/src/main/java/org/apache/shiro/lang/util/ByteSourceWrapper.java index 5a0a6c0ba9..bd9270e296 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/ByteSourceWrapper.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/ByteSourceWrapper.java @@ -19,8 +19,6 @@ package org.apache.shiro.lang.util; -import org.apache.shiro.lang.util.ByteSource; - import java.io.Closeable; import java.io.IOException; @@ -28,8 +26,8 @@ * To use try-with-resources idiom, this class supports wrapping existing ByteSource * object or byte array. At end of try block, it gets zeroed out automatically. */ -public class ByteSourceWrapper implements Closeable { - private byte[] bytes; +public final class ByteSourceWrapper implements Closeable { + private final byte[] bytes; private ByteSourceWrapper(byte[] bytes) { this.bytes = bytes; diff --git a/lang/src/main/java/org/apache/shiro/lang/util/ByteUtils.java b/lang/src/main/java/org/apache/shiro/lang/util/ByteUtils.java index df64f99cca..97bc594c65 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/ByteUtils.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/ByteUtils.java @@ -20,22 +20,23 @@ public final class ByteUtils { - private ByteUtils() { - // private utility class - } + private ByteUtils() { + // private utility class + } - /** - * For security, sensitive information in array should be zeroed-out at end of use (SHIRO-349). - * @param value An array holding sensitive data - */ - public static void wipe(Object value) { - if (value instanceof byte[]) { - byte[] array = (byte[]) value; - Arrays.fill(array, (byte) 0); - } else if (value instanceof char[]) { - char[] array = (char[]) value; - Arrays.fill(array, '\u0000'); + /** + * For security, sensitive information in array should be zeroed-out at end of use (SHIRO-349). + * + * @param value An array holding sensitive data + */ + public static void wipe(Object value) { + if (value instanceof byte[]) { + byte[] array = (byte[]) value; + Arrays.fill(array, (byte) 0); + } else if (value instanceof char[]) { + char[] array = (char[]) value; + Arrays.fill(array, '\u0000'); + } } - } } diff --git a/lang/src/main/java/org/apache/shiro/lang/util/ClassUtils.java b/lang/src/main/java/org/apache/shiro/lang/util/ClassUtils.java index dfefacf4c2..10d2705e24 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/ClassUtils.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/ClassUtils.java @@ -36,34 +36,32 @@ * * @since 0.1 */ -public class ClassUtils { - - //TODO - complete JavaDoc +public final class ClassUtils { /** * Private internal log instance. */ - private static final Logger log = LoggerFactory.getLogger(ClassUtils.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ClassUtils.class); /** * SHIRO-767: add a map to mapping primitive data type */ - private static final HashMap> primClasses + private static final HashMap> PRIM_CLASSES = new HashMap<>(8, 1.0F); + static { - primClasses.put("boolean", boolean.class); - primClasses.put("byte", byte.class); - primClasses.put("char", char.class); - primClasses.put("short", short.class); - primClasses.put("int", int.class); - primClasses.put("long", long.class); - primClasses.put("float", float.class); - primClasses.put("double", double.class); - primClasses.put("void", void.class); + PRIM_CLASSES.put("boolean", boolean.class); + PRIM_CLASSES.put("byte", byte.class); + PRIM_CLASSES.put("char", char.class); + PRIM_CLASSES.put("short", short.class); + PRIM_CLASSES.put("int", int.class); + PRIM_CLASSES.put("long", long.class); + PRIM_CLASSES.put("float", float.class); + PRIM_CLASSES.put("double", double.class); + PRIM_CLASSES.put("void", void.class); } - /** * @since 1.0 */ @@ -94,6 +92,10 @@ protected ClassLoader doGetClassLoader() throws Throwable { } }; + private ClassUtils() { + + } + /** * Returns the specified resource by checking the current thread's * {@link Thread#getContextClassLoader() context class loader}, then the @@ -103,7 +105,7 @@ protected ClassLoader doGetClassLoader() throws Throwable { * * @param name the name of the resource to acquire from the classloader(s). * @return the InputStream of the resource found, or null if the resource cannot be found from any - * of the three mentioned ClassLoaders. + * of the three mentioned ClassLoaders. * @since 0.9 */ public static InputStream getResourceAsStream(String name) { @@ -111,24 +113,24 @@ public static InputStream getResourceAsStream(String name) { InputStream is = THREAD_CL_ACCESSOR.getResourceStream(name); if (is == null) { - if (log.isTraceEnabled()) { - log.trace("Resource [" + name + "] was not found via the thread context ClassLoader. Trying the " + - "current ClassLoader..."); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Resource [" + name + "] was not found via the thread context ClassLoader. Trying the " + + "current ClassLoader..."); } is = CLASS_CL_ACCESSOR.getResourceStream(name); } if (is == null) { - if (log.isTraceEnabled()) { - log.trace("Resource [" + name + "] was not found via the current class loader. Trying the " + - "system/application ClassLoader..."); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Resource [" + name + "] was not found via the current class loader. Trying the " + + "system/application ClassLoader..."); } is = SYSTEM_CL_ACCESSOR.getResourceStream(name); } - if (is == null && log.isTraceEnabled()) { - log.trace("Resource [" + name + "] was not found via the thread context, current, or " + - "system/application ClassLoaders. All heuristics have been exhausted. Returning null."); + if (is == null && LOGGER.isTraceEnabled()) { + LOGGER.trace("Resource [" + name + "] was not found via the thread context, current, or " + + "system/application ClassLoaders. All heuristics have been exhausted. Returning null."); } return is; @@ -151,29 +153,29 @@ public static Class forName(String fqcn) throws UnknownClassException { Class clazz = THREAD_CL_ACCESSOR.loadClass(fqcn); if (clazz == null) { - if (log.isTraceEnabled()) { - log.trace("Unable to load class named [" + fqcn + - "] from the thread context ClassLoader. Trying the current ClassLoader..."); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Unable to load class named [" + fqcn + + "] from the thread context ClassLoader. Trying the current ClassLoader..."); } clazz = CLASS_CL_ACCESSOR.loadClass(fqcn); } if (clazz == null) { - if (log.isTraceEnabled()) { - log.trace("Unable to load class named [" + fqcn + "] from the current ClassLoader. " + - "Trying the system/application ClassLoader..."); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Unable to load class named [" + fqcn + "] from the current ClassLoader. " + + "Trying the system/application ClassLoader..."); } clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn); } if (clazz == null) { //SHIRO-767: support for getting primitive data type,such as int,double... - clazz = primClasses.get(fqcn); + clazz = PRIM_CLASSES.get(fqcn); } if (clazz == null) { - String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " + - "system/application ClassLoaders. All heuristics have been exhausted. Class could not be found."; + String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " + + "system/application ClassLoaders. All heuristics have been exhausted. Class could not be found."; throw new UnknownClassException(msg); } @@ -237,7 +239,6 @@ public static Object instantiate(Constructor ctor, Object... args) { } /** - * * @param type * @param annotation * @return @@ -262,15 +263,16 @@ public static List getAnnotatedMethods(final Class type, final Class< /** * @since 1.0 */ - private static interface ClassLoaderAccessor { + private interface ClassLoaderAccessor { Class loadClass(String fqcn); + InputStream getResourceStream(String name); } /** * @since 1.0 */ - private static abstract class ExceptionIgnoringAccessor implements ClassLoaderAccessor { + private abstract static class ExceptionIgnoringAccessor implements ClassLoaderAccessor { public Class loadClass(String fqcn) { Class clazz = null; @@ -280,8 +282,8 @@ public Class loadClass(String fqcn) { //SHIRO-767: Use Class.forName instead of cl.loadClass(), as byte arrays would fail otherwise. clazz = Class.forName(fqcn, false, cl); } catch (ClassNotFoundException e) { - if (log.isTraceEnabled()) { - log.trace("Unable to load clazz named [" + fqcn + "] from class loader [" + cl + "]"); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Unable to load clazz named [" + fqcn + "] from class loader [" + cl + "]"); } } } @@ -301,8 +303,8 @@ protected final ClassLoader getClassLoader() { try { return doGetClassLoader(); } catch (Throwable t) { - if (log.isDebugEnabled()) { - log.debug("Unable to acquire ClassLoader.", t); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Unable to acquire ClassLoader.", t); } } return null; diff --git a/lang/src/main/java/org/apache/shiro/lang/util/Initializable.java b/lang/src/main/java/org/apache/shiro/lang/util/Initializable.java index 37add6f642..07acd0ed1f 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/Initializable.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/Initializable.java @@ -30,8 +30,7 @@ public interface Initializable { /** * Initializes this object. * - * @throws ShiroException - * if an exception occurs during initialization. + * @throws ShiroException if an exception occurs during initialization. */ void init() throws ShiroException; diff --git a/lang/src/main/java/org/apache/shiro/lang/util/LifecycleUtils.java b/lang/src/main/java/org/apache/shiro/lang/util/LifecycleUtils.java index 9d78646b9e..2a57e32735 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/LifecycleUtils.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/LifecycleUtils.java @@ -33,7 +33,7 @@ */ public abstract class LifecycleUtils { - private static final Logger log = LoggerFactory.getLogger(LifecycleUtils.class); + private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleUtils.class); public static void init(Object o) throws ShiroException { if (o instanceof Initializable) { @@ -66,7 +66,7 @@ public static void destroy(Object o) { if (o instanceof Destroyable) { destroy((Destroyable) o); } else if (o instanceof Collection) { - destroy((Collection)o); + destroy((Collection) o); } } @@ -75,9 +75,9 @@ public static void destroy(Destroyable d) { try { d.destroy(); } catch (Throwable t) { - if (log.isDebugEnabled()) { + if (LOGGER.isDebugEnabled()) { String msg = "Unable to cleanly destroy instance [" + d + "] of type [" + d.getClass().getName() + "]."; - log.debug(msg, t); + LOGGER.debug(msg, t); } } } diff --git a/lang/src/main/java/org/apache/shiro/lang/util/Nameable.java b/lang/src/main/java/org/apache/shiro/lang/util/Nameable.java index b645b4a89a..0d6e902c98 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/Nameable.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/Nameable.java @@ -29,6 +29,7 @@ public interface Nameable { /** * Sets the (preferably application unique) name for this component. + * * @param name the preferably application unique name for this component. */ void setName(String name); diff --git a/lang/src/main/java/org/apache/shiro/lang/util/SimpleByteSource.java b/lang/src/main/java/org/apache/shiro/lang/util/SimpleByteSource.java index dbb8d3d3c1..e7351d074e 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/SimpleByteSource.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/SimpleByteSource.java @@ -44,6 +44,7 @@ * * @since 1.0 */ +@SuppressWarnings("checkstyle:BooleanExpressionComplexity") public class SimpleByteSource implements ByteSource { private final byte[] bytes; @@ -122,16 +123,16 @@ public SimpleByteSource(InputStream stream) { * * @param o the object to test to see if it can be easily converted to bytes by instances of this class. * @return {@code true} if the specified object can be easily converted to bytes by instances of this class, - * {@code false} otherwise. + * {@code false} otherwise. * @since 1.2 */ public static boolean isCompatible(Object o) { - return o instanceof byte[] || o instanceof char[] || o instanceof String || - o instanceof ByteSource || o instanceof File || o instanceof InputStream; + return o instanceof byte[] || o instanceof char[] || o instanceof String + || o instanceof ByteSource || o instanceof File || o instanceof InputStream; } public static ByteSource empty() { - return new SimpleByteSource(new byte[]{}); + return new SimpleByteSource(new byte[] {}); } @Override @@ -146,7 +147,7 @@ public boolean isEmpty() { @Override public String toHex() { - if ( this.cachedHex == null ) { + if (this.cachedHex == null) { this.cachedHex = Hex.encodeToString(getBytes()); } return this.cachedHex; @@ -154,7 +155,7 @@ public String toHex() { @Override public String toBase64() { - if ( this.cachedBase64 == null ) { + if (this.cachedBase64 == null) { this.cachedBase64 = Base64.encodeToString(getBytes()); } return this.cachedBase64; diff --git a/lang/src/main/java/org/apache/shiro/lang/util/SoftHashMap.java b/lang/src/main/java/org/apache/shiro/lang/util/SoftHashMap.java index cf20c7d409..47145e5c95 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/SoftHashMap.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/SoftHashMap.java @@ -20,7 +20,14 @@ import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; -import java.util.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Queue; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.locks.ReentrantLock; @@ -29,7 +36,7 @@ /** * A SoftHashMap is a memory-constrained map that stores its values in * {@link SoftReference SoftReference}s. (Contrast this with the JDK's - * {@link WeakHashMap WeakHashMap}, which uses weak references for its keys, which is of little value if you + * {@link java.util.WeakHashMap WeakHashMap}, which uses weak references for its keys, which is of little value if you * want the cache to auto-resize itself based on memory constraints). *

* Having the values wrapped by soft references allows the cache to automatically reduce its size based on memory @@ -42,6 +49,8 @@ *

* This implementation is thread-safe and usable in concurrent environments. * + * @param K + * @param V * @since 1.0 */ public class SoftHashMap implements Map { @@ -60,12 +69,13 @@ public class SoftHashMap implements Map { * The number of strong references to hold internally, that is, the number of instances to prevent * from being garbage collected automatically (unlike other soft references). */ - private final int RETENTION_SIZE; + private final int retentionSize; /** * The FIFO list of strong references (not to be garbage collected), order of last access. */ - private final Queue strongReferences; //guarded by 'strongReferencesLock' + //guarded by 'strongReferencesLock' + private final Queue strongReferences; private final ReentrantLock strongReferencesLock; /** @@ -101,7 +111,7 @@ public SoftHashMap() { @SuppressWarnings({"unchecked"}) public SoftHashMap(int retentionSize) { super(); - RETENTION_SIZE = Math.max(0, retentionSize); + this.retentionSize = Math.max(0, retentionSize); queue = new ReferenceQueue(); strongReferencesLock = new ReentrantLock(); map = new ConcurrentHashMap>(); @@ -113,7 +123,7 @@ public SoftHashMap(int retentionSize) { * size of {@link #DEFAULT_RETENTION_SIZE DEFAULT_RETENTION_SIZE} (100 entries). * * @param source the backing map to populate this {@code SoftHashMap} - * @see #SoftHashMap(Map,int) + * @see #SoftHashMap(java.util.Map, int) */ public SoftHashMap(Map source) { this(DEFAULT_RETENTION_SIZE); @@ -177,7 +187,7 @@ private void addToStrongReferences(V result) { private void trimStrongReferencesIfNecessary() { //trim the strong ref queue if necessary: - while (strongReferences.size() > RETENTION_SIZE) { + while (strongReferences.size() > retentionSize) { strongReferences.poll(); } } @@ -186,11 +196,12 @@ private void trimStrongReferencesIfNecessary() { * Traverses the ReferenceQueue and removes garbage-collected SoftValue objects from the backing map * by looking them up using the SoftValue.key data member. */ + @SuppressWarnings({"unchecked", "SuspiciousMethodCalls"}) private void processQueue() { SoftValue sv; while ((sv = (SoftValue) queue.poll()) != null) { - //noinspection SuspiciousMethodCalls - map.remove(sv.key); // we can access private data! + // we can access private data! + map.remove(sv.key); } } @@ -229,7 +240,6 @@ public Collection values() { processQueue(); Collection keys = map.keySet(); if (keys.isEmpty()) { - //noinspection unchecked return Collections.EMPTY_SET; } Collection values = new ArrayList(keys.size()); @@ -246,7 +256,8 @@ public Collection values() { * Creates a new entry, but wraps the value in a SoftValue instance to enable auto garbage collection. */ public V put(K key, V value) { - processQueue(); // throw out garbage collected values first + // throw out garbage collected values first + processQueue(); SoftValue sv = new SoftValue(value, key, queue); SoftValue previous = map.put(key, sv); addToStrongReferences(value); @@ -254,7 +265,8 @@ public V put(K key, V value) { } public V remove(Object key) { - processQueue(); // throw out garbage collected values first + // throw out garbage collected values first + processQueue(); SoftValue raw = map.remove(key); return raw != null ? raw.get() : null; } @@ -266,17 +278,20 @@ public void clear() { } finally { strongReferencesLock.unlock(); } - processQueue(); // throw out garbage collected values + // throw out garbage collected values + processQueue(); map.clear(); } public int size() { - processQueue(); // throw out garbage collected values first + // throw out garbage collected values first + processQueue(); return map.size(); } public Set> entrySet() { - processQueue(); // throw out garbage collected values first + // throw out garbage collected values first + processQueue(); Collection keys = map.keySet(); if (keys.isEmpty()) { //noinspection unchecked @@ -298,7 +313,7 @@ public Set> entrySet() { * not only the value but also the key to make it easier to find * the entry in the HashMap after it's been garbage collected. */ - private static class SoftValue extends SoftReference { + private static final class SoftValue extends SoftReference { private final K key; diff --git a/lang/src/main/java/org/apache/shiro/lang/util/StringUtils.java b/lang/src/main/java/org/apache/shiro/lang/util/StringUtils.java index b748d32625..fb444958fc 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/StringUtils.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/StringUtils.java @@ -19,7 +19,15 @@ package org.apache.shiro.lang.util; import java.text.ParseException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; + /** *

Simple utility class for String operations useful across the framework. @@ -29,9 +37,8 @@ * * @since 0.9 */ -public class StringUtils { - - //TODO - complete JavaDoc +@SuppressWarnings("checkstyle:CyclomaticComplexity") +public final class StringUtils { /** * Constant representing the empty string, equal to "" @@ -48,6 +55,9 @@ public class StringUtils { */ public static final char DEFAULT_QUOTE_CHAR = '"'; + private StringUtils() { + } + /** * Check whether the given String has actual text. * More specifically, returns true if the string not null, @@ -63,7 +73,7 @@ public class StringUtils { * * @param str the String to check (may be null) * @return true if the String is not null, its length is - * greater than 0, and it does not contain whitespace only + * greater than 0, and it does not contain whitespace only * @see java.lang.Character#isWhitespace */ public static boolean hasText(String str) { @@ -242,7 +252,7 @@ public static String[] tokenizeToStringArray(String str, String delimiters) { * (only applies to tokens that are empty after trimming; StringTokenizer * will not consider subsequent delimiters as token in the first place). * @return an array of the tokens (null if the input String - * was null) + * was null) * @see java.util.StringTokenizer * @see java.lang.String#trim() */ @@ -275,7 +285,7 @@ public static String[] tokenizeToStringArray( * * @param collection the Collection to copy * @return the String array (null if the passed-in - * Collection was null) + * Collection was null) */ @SuppressWarnings({"unchecked"}) public static String[] toStringArray(Collection collection) { @@ -295,8 +305,8 @@ public static String[] splitKeyValue(String aLine) throws ParseException { //fallback to checking for an equals sign split = line.split("=", 2); if (split.length != 2) { - String msg = "Unable to determine Key/Value pair from line [" + line + "]. There is no space from " + - "which the split location could be determined."; + String msg = "Unable to determine Key/Value pair from line [" + line + "]. There is no space from " + + "which the split location could be determined."; throw new ParseException(msg, 0); } @@ -326,10 +336,10 @@ public static String[] splitKeyValue(String aLine) throws ParseException { * Splits a string using the {@link #DEFAULT_DELIMITER_CHAR} (which is {@value #DEFAULT_DELIMITER_CHAR}). * This method also recognizes quoting using the {@link #DEFAULT_QUOTE_CHAR} * (which is {@value #DEFAULT_QUOTE_CHAR}), but does not retain them. - * + * *

This is equivalent of calling {@link #split(String, char, char, char, boolean, boolean)} with * {@code line, DEFAULT_DELIMITER_CHAR, DEFAULT_QUOTE_CHAR, DEFAULT_QUOTE_CHAR, false, true}.

- * + * * @param line the line to split using the {@link #DEFAULT_DELIMITER_CHAR}. * @return the split line, split tokens do not contain quotes and are trimmed. * @see #split(String, char, char, char, boolean, boolean) @@ -356,7 +366,8 @@ public static String[] split(String line, char delimiter, char beginQuoteChar, c *

* This method's implementation is very loosely based (with significant modifications) on * Glen Smith's open-source - * CSVReader.java + * CSVReader.java * file. *

* That file is Apache 2.0 licensed as well, making Glen's code a great starting point for us to modify to @@ -387,9 +398,12 @@ public static String[] split(String aLine, char delimiter, char beginQuoteChar, if (c == beginQuoteChar) { // this gets complex... the quote may end a quoted block, or escape another quote. // do a 1-char lookahead: - if (inQuotes // we are in quotes, therefore there can be escaped quotes in here. - && line.length() > (i + 1) // there is indeed another character to check. - && line.charAt(i + 1) == beginQuoteChar) { // ..and that char. is a quote also. + // we are in quotes, therefore there can be escaped quotes in here. + if (inQuotes + // there is indeed another character to check. + && line.length() > (i + 1) + // ..and that char. is a quote also. + && line.charAt(i + 1) == beginQuoteChar) { // we have two quote chars in a row == one quote char, so consume them both and // put one on the token. we do *not* exit the quoted text. sb.append(line.charAt(i + 1)); @@ -411,7 +425,8 @@ public static String[] split(String aLine, char delimiter, char beginQuoteChar, s = s.trim(); } tokens.add(s); - sb = new StringBuilder(); // start work on next token + // start work on next token + sb = new StringBuilder(); } else { sb.append(c); } @@ -454,7 +469,9 @@ public static String join(Iterator iterator, String separator) { } // two or more elements - StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small + // Java default is 16, probably too small + @SuppressWarnings("checkstyle:MagicNumber") + StringBuilder buf = new StringBuilder(256); if (first != null) { buf.append(first); } @@ -492,6 +509,7 @@ public static Set splitToSet(String delimited, String separator) { /** * Returns the input argument, but ensures the first character is capitalized (if possible). + * * @param in the string to uppercase the first character. * @return the input argument, but with the first character capitalized (if possible). * @since 1.2 diff --git a/lang/src/main/java/org/apache/shiro/lang/util/UnknownClassException.java b/lang/src/main/java/org/apache/shiro/lang/util/UnknownClassException.java index b7891e1188..881d3765dc 100644 --- a/lang/src/main/java/org/apache/shiro/lang/util/UnknownClassException.java +++ b/lang/src/main/java/org/apache/shiro/lang/util/UnknownClassException.java @@ -27,8 +27,7 @@ * * @since 0.1 */ -public class UnknownClassException extends ShiroException -{ +public class UnknownClassException extends ShiroException { /** * Creates a new UnknownClassException. diff --git a/lang/src/main/java/org/apache/shiro/lang/util/package-info.java b/lang/src/main/java/org/apache/shiro/lang/util/package-info.java new file mode 100644 index 0000000000..9d31257920 --- /dev/null +++ b/lang/src/main/java/org/apache/shiro/lang/util/package-info.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Shiro lang.util package-info. + */ +package org.apache.shiro.lang.util; diff --git a/lang/src/test/java/org/apache/shiro/lang/util/ClassUtilsTest.java b/lang/src/test/java/org/apache/shiro/lang/util/ClassUtilsTest.java index 497e58eb08..04883a47b4 100644 --- a/lang/src/test/java/org/apache/shiro/lang/util/ClassUtilsTest.java +++ b/lang/src/test/java/org/apache/shiro/lang/util/ClassUtilsTest.java @@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; - class ClassUtilsTest { @Test diff --git a/pom.xml b/pom.xml index 562489a0c5..72c966bbe3 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + 4.0.0 @@ -327,7 +328,7 @@ org.apache.rat apache-rat-plugin - + **/.externalToolBuilders/* **/infinitest.filters @@ -430,7 +431,8 @@ true - true + true + true ${root.dir}/src/japicmp/postAnalysisScript.groovy @@ -479,7 +481,8 @@ jakarta false - + @@ -543,6 +546,8 @@ true true true + **/Quickstart.java,**/QuickstartGuice.java,**/QuickstartShiroModule.java + test-keystore.jks,test-keystore.pem root.dir=${root.dir} @@ -597,6 +602,9 @@ + + maven-checkstyle-plugin + org.apache.maven.plugins maven-compiler-plugin @@ -741,7 +749,9 @@ - The mission of the Apache Shiro project is to create and maintain an easy to use authentication and authorization framework. + The mission of the Apache Shiro project is to create and maintain an easy to use + authentication and authorization framework. + ${project.url} @@ -1460,7 +1470,9 @@ https://docs.spring.io/spring/docs/2.5.x/javadoc-api/ https://junit.org/junit4/javadoc/4.12/ https://easymock.org/api/ - https://javadoc.io/doc/org.mockito/mockito-core/${mockito.version}/org/mockito/Mockito.html + + https://javadoc.io/doc/org.mockito/mockito-core/${mockito.version}/org/mockito/Mockito.html + https://www.quartz-scheduler.org/api/1.8.6/ @@ -1526,7 +1538,7 @@ false - + **/.externalToolBuilders/* **/infinitest.filters @@ -1789,18 +1801,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + @@ -1826,8 +1838,8 @@ - + apache.snapshots diff --git a/samples/aspectj/pom.xml b/samples/aspectj/pom.xml index af9b9218bc..5387eb5144 100644 --- a/samples/aspectj/pom.xml +++ b/samples/aspectj/pom.xml @@ -18,79 +18,80 @@ ~ under the License. --> - + - + org.apache.shiro.samples shiro-samples 2.0.0-SNAPSHOT - + - 4.0.0 - samples-aspectj - Apache Shiro :: Samples :: AspectJ - - samples.aspectj - + 4.0.0 + samples-aspectj + Apache Shiro :: Samples :: AspectJ + + samples.aspectj + - - - - org.codehaus.mojo - aspectj-maven-plugin - - - - org.apache.shiro - shiro-aspectj - - - - - - aspectj-compile - - compile - test-compile - - - - - - org.aspectj - aspectjtools - ${aspectj.version} - - - - - + + + + org.codehaus.mojo + aspectj-maven-plugin + + + + org.apache.shiro + shiro-aspectj + + + + + + aspectj-compile + + compile + test-compile + + + + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + - - - org.apache.shiro - shiro-aspectj - - - org.apache.commons - commons-lang3 - ${commons.lang3.version} - - - org.slf4j - jcl-over-slf4j - runtime - - - org.apache.logging.log4j - log4j-slf4j2-impl - runtime - - - org.apache.logging.log4j - log4j-core - runtime - - + + + org.apache.shiro + shiro-aspectj + + + org.apache.commons + commons-lang3 + ${commons.lang3.version} + + + org.slf4j + jcl-over-slf4j + runtime + + + org.apache.logging.log4j + log4j-slf4j2-impl + runtime + + + org.apache.logging.log4j + log4j-core + runtime + + diff --git a/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/Account.java b/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/Account.java index 6c7bbfa85b..c58dce8cc4 100644 --- a/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/Account.java +++ b/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/Account.java @@ -29,30 +29,30 @@ public class Account { - private static long _SEQUENCE; + private static long sequence; - private long _id; + private long id; - private String _ownerName; + private String ownerName; - private volatile boolean _isActive; + private volatile boolean isActive; - private double _balance; + private double balance; - private final List _transactions; + private final List transactionList; - private String _createdBy; + private String createdBy; - private Date _creationDate; + private Date creationDate; public Account(String anOwnerName) { - _id = ++_SEQUENCE; - _ownerName = anOwnerName; - _isActive = true; - _balance = 0.0d; - _transactions = new ArrayList(); - _createdBy = "unknown"; - _creationDate = new Date(); + id = ++sequence; + ownerName = anOwnerName; + isActive = true; + balance = 0.0d; + transactionList = new ArrayList(); + createdBy = "unknown"; + creationDate = new Date(); } /** @@ -61,7 +61,7 @@ public Account(String anOwnerName) { * @return The id value. */ public long getId() { - return _id; + return id; } /** @@ -70,7 +70,7 @@ public long getId() { * @return The ownerName value. */ public String getOwnerName() { - return _ownerName; + return ownerName; } /** @@ -79,7 +79,7 @@ public String getOwnerName() { * @return The isActive value. */ public boolean isActive() { - return _isActive; + return isActive; } /** @@ -88,7 +88,7 @@ public boolean isActive() { * @param aIsActive The new value of the isActive attribute. */ public void setActive(boolean aIsActive) { - _isActive = aIsActive; + isActive = aIsActive; } /** @@ -97,7 +97,7 @@ public void setActive(boolean aIsActive) { * @param aOwnerName The new value of the ownerName attribute. */ public void setOwnerName(String aOwnerName) { - _ownerName = aOwnerName; + ownerName = aOwnerName; } /** @@ -106,7 +106,7 @@ public void setOwnerName(String aOwnerName) { * @return The balance value. */ public double getBalance() { - return _balance; + return balance; } /** @@ -115,25 +115,27 @@ public double getBalance() { * @return The transactions value. */ public List getTransactions() { - return _transactions; + return transactionList; } protected void applyTransaction(AccountTransaction aTransaction) throws NotEnoughFundsException, InactiveAccountException { - if (!_isActive) { - throw new InactiveAccountException("Unable to apply " + aTransaction.getType() + " of amount " + aTransaction.getAmount() + " to account " + _id); + if (!isActive) { + throw new InactiveAccountException("Unable to apply " + + aTransaction.getType() + " of amount " + aTransaction.getAmount() + " to account " + id); } - synchronized (_transactions) { + synchronized (transactionList) { if (AccountTransaction.TransactionType.DEPOSIT == aTransaction.getType()) { - _transactions.add(aTransaction); - _balance += aTransaction.getAmount(); + transactionList.add(aTransaction); + balance += aTransaction.getAmount(); } else if (AccountTransaction.TransactionType.WITHDRAWAL == aTransaction.getType()) { - if (_balance < aTransaction.getAmount()) { - throw new NotEnoughFundsException("Unable to withdraw " + aTransaction.getAmount() + "$ from account " + _id + " - current balance is " + _balance); + if (balance < aTransaction.getAmount()) { + throw new NotEnoughFundsException("Unable to withdraw " + + aTransaction.getAmount() + "$ from account " + id + " - current balance is " + balance); } - _transactions.add(aTransaction); - _balance -= aTransaction.getAmount(); + transactionList.add(aTransaction); + balance -= aTransaction.getAmount(); } else { throw new IllegalArgumentException("The transaction passed in has an invalid type: " + aTransaction.getType()); @@ -147,7 +149,7 @@ protected void applyTransaction(AccountTransaction aTransaction) throws NotEnoug * @param aCreatedBy The new value of the createdBy attribute. */ protected void setCreatedBy(String aCreatedBy) { - _createdBy = aCreatedBy; + createdBy = aCreatedBy; } /** @@ -156,7 +158,7 @@ protected void setCreatedBy(String aCreatedBy) { * @return The createdBy value. */ public String getCreatedBy() { - return _createdBy; + return createdBy; } /** @@ -165,22 +167,22 @@ public String getCreatedBy() { * @return The creationDate value. */ public Date getCreationDate() { - return _creationDate; + return creationDate; } /* (non-Javadoc) - * @see java.lang.Object#toString() - */ + * @see java.lang.Object#toString() + */ public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE). - append("id", _id). - append("ownerName", _ownerName). - append("isActive", _isActive). - append("balance", _balance). - append("tx.count", _transactions.size()). - append("createdBy", _createdBy). - append("creationDate", new Timestamp(_creationDate.getTime())). + append("id", id). + append("ownerName", ownerName). + append("isActive", isActive). + append("balance", balance). + append("tx.count", transactionList.size()). + append("createdBy", createdBy). + append("creationDate", new Timestamp(creationDate.getTime())). toString(); } } diff --git a/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/AccountTransaction.java b/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/AccountTransaction.java index 4540bcab8f..a3eab3d819 100644 --- a/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/AccountTransaction.java +++ b/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/AccountTransaction.java @@ -24,25 +24,41 @@ import java.sql.Timestamp; import java.util.Date; -public class AccountTransaction { +public final class AccountTransaction { - private static long _SEQUENCE; + private static long sequence; public enum TransactionType { + /** + * deposit. + */ DEPOSIT, + /** + * withdrawal. + */ WITHDRAWAL } - private long _id; + private long id; - private TransactionType _type; + private TransactionType type; - private long _accountId; + private long accountId; - private double _amount; + private double amount; - private String _createdBy; - private Date _creationDate; + private String createdBy; + + private Date creationDate; + + private AccountTransaction(TransactionType aType, long anAccountId, double anAmount) { + id = ++sequence; + type = aType; + accountId = anAccountId; + amount = anAmount; + createdBy = "unknown"; + creationDate = new Date(); + } public static AccountTransaction createDepositTx(long anAccountId, double anAmount) { return new AccountTransaction(TransactionType.DEPOSIT, anAccountId, anAmount); @@ -52,22 +68,13 @@ public static AccountTransaction createWithdrawalTx(long anAccountId, double anA return new AccountTransaction(TransactionType.WITHDRAWAL, anAccountId, anAmount); } - private AccountTransaction(TransactionType aType, long anAccountId, double anAmount) { - _id = ++_SEQUENCE; - _type = aType; - _accountId = anAccountId; - _amount = anAmount; - _createdBy = "unknown"; - _creationDate = new Date(); - } - /** * Returns the id attribute. * * @return The id value. */ public long getId() { - return _id; + return id; } /** @@ -76,7 +83,7 @@ public long getId() { * @return The type value. */ public TransactionType getType() { - return _type; + return type; } /** @@ -85,7 +92,7 @@ public TransactionType getType() { * @return The accountId value. */ public long getAccountId() { - return _accountId; + return accountId; } /** @@ -94,7 +101,7 @@ public long getAccountId() { * @return The amount value. */ public double getAmount() { - return _amount; + return amount; } /** @@ -103,7 +110,7 @@ public double getAmount() { * @param aCreatedBy The new value of the createdBy attribute. */ protected void setCreatedBy(String aCreatedBy) { - _createdBy = aCreatedBy; + createdBy = aCreatedBy; } /** @@ -112,7 +119,7 @@ protected void setCreatedBy(String aCreatedBy) { * @return The createdBy value. */ public String getCreatedBy() { - return _createdBy; + return createdBy; } /** @@ -121,21 +128,21 @@ public String getCreatedBy() { * @return The creationDate value. */ public Date getCreationDate() { - return _creationDate; + return creationDate; } /* (non-Javadoc) - * @see java.lang.Object#toString() - */ + * @see java.lang.Object#toString() + */ public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE). - append("id", _id). - append("type", _type). - append("accountId", _accountId). - append("amount", _amount). - append("createdBy", _createdBy). - append("creationDate", new Timestamp(_creationDate.getTime())). + append("id", id). + append("type", type). + append("accountId", accountId). + append("amount", amount). + append("createdBy", createdBy). + append("creationDate", new Timestamp(creationDate.getTime())). toString(); } diff --git a/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/BankServerRunner.java b/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/BankServerRunner.java index 162d3405fd..2ac60002fe 100644 --- a/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/BankServerRunner.java +++ b/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/BankServerRunner.java @@ -20,27 +20,27 @@ public class BankServerRunner { - private SecureBankService _bankService; + private SecureBankService bankService; public synchronized void start() throws Exception { - if (_bankService == null) { - _bankService = new SecureBankService(); - _bankService.start(); + if (bankService == null) { + bankService = new SecureBankService(); + bankService.start(); } } public synchronized void stop() { - if (_bankService != null) { + if (bankService != null) { try { - _bankService.dispose(); + bankService.dispose(); } finally { - _bankService = null; + bankService = null; } } } public BankService getBankService() { - return _bankService; + return bankService; } public static void main(String[] args) { diff --git a/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/BankService.java b/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/BankService.java index 9dd7c9d7ba..2afc4c660c 100644 --- a/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/BankService.java +++ b/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/BankService.java @@ -20,36 +20,37 @@ import java.util.Date; +@SuppressWarnings("checkstyle:LineLength") public interface BankService { - public long[] searchAccountIdsByOwner(String anOwnerName); + long[] searchAccountIdsByOwner(String anOwnerName); - public long createNewAccount(String anOwnerName); + long createNewAccount(String anOwnerName); - public double getBalanceOf(long anAccountId) throws AccountNotFoundException; + double getBalanceOf(long anAccountId) throws AccountNotFoundException; - public String getOwnerOf(long anAccountId) throws AccountNotFoundException; + String getOwnerOf(long anAccountId) throws AccountNotFoundException; - public double depositInto(long anAccountId, double anAmount) throws AccountNotFoundException, InactiveAccountException; + double depositInto(long anAccountId, double anAmount) throws AccountNotFoundException, InactiveAccountException; - public double withdrawFrom(long anAccountId, double anAmount) throws AccountNotFoundException, NotEnoughFundsException, InactiveAccountException; + double withdrawFrom(long anAccountId, double anAmount) throws AccountNotFoundException, NotEnoughFundsException, InactiveAccountException; - public TxLog[] getTxHistoryFor(long anAccountId) throws AccountNotFoundException; + TxLog[] getTxHistoryFor(long anAccountId) throws AccountNotFoundException; - public double closeAccount(long anAccountId) throws AccountNotFoundException, InactiveAccountException; + double closeAccount(long anAccountId) throws AccountNotFoundException, InactiveAccountException; - public boolean isAccountActive(long anAccountId) throws AccountNotFoundException; + boolean isAccountActive(long anAccountId) throws AccountNotFoundException; - public static class TxLog { - private Date _creationDate; - private double _amount; - private String _madeBy; + class TxLog { + private Date creationDate; + private double amount; + private String madeBy; public TxLog(Date aCreationDate, double aAmount, String aMadeBy) { super(); - _creationDate = aCreationDate; - _amount = aAmount; - _madeBy = aMadeBy; + creationDate = aCreationDate; + amount = aAmount; + madeBy = aMadeBy; } /** @@ -58,7 +59,7 @@ public TxLog(Date aCreationDate, double aAmount, String aMadeBy) { * @return The creationDate value. */ public Date getCreationDate() { - return _creationDate; + return creationDate; } /** @@ -67,7 +68,7 @@ public Date getCreationDate() { * @return The amount value. */ public double getAmount() { - return _amount; + return amount; } /** @@ -76,7 +77,7 @@ public double getAmount() { * @return The madeBy value. */ public String getMadeBy() { - return _madeBy; + return madeBy; } } diff --git a/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/SecureBankService.java b/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/SecureBankService.java index 41f0e3aa82..8b2529bb16 100644 --- a/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/SecureBankService.java +++ b/samples/aspectj/src/main/java/org/apache/shiro/samples/aspectj/bank/SecureBankService.java @@ -32,86 +32,86 @@ public class SecureBankService implements BankService { - private static final Logger log = LoggerFactory.getLogger(SecureBankService.class); - private volatile boolean _isRunning; - private final List _accounts; - private Map _accountsById; + private static final Logger LOGGER = LoggerFactory.getLogger(SecureBankService.class); + private volatile boolean isRunning; + private final List accounts; + private Map accountsById; /** * Creates a new {@link SecureBankService} instance. */ public SecureBankService() { - _accounts = new ArrayList(); - _accountsById = new HashMap(); + accounts = new ArrayList(); + accountsById = new HashMap(); } /** * Starts this service */ public void start() throws Exception { - _isRunning = true; - log.info("Bank service started"); + isRunning = true; + LOGGER.info("Bank service started"); } /** * Stop this service */ public void dispose() { - log.info("Stopping bank service..."); - _isRunning = false; + LOGGER.info("Stopping bank service..."); + isRunning = false; - synchronized (_accounts) { - _accountsById.clear(); - _accounts.clear(); + synchronized (accounts) { + accountsById.clear(); + accounts.clear(); } - log.info("Bank service stopped"); + LOGGER.info("Bank service stopped"); } /** * Internal utility method that validate the internal state of this service. */ protected void assertServiceState() { - if (!_isRunning) { + if (!isRunning) { throw new IllegalStateException("This bank service is not running"); } } public int getAccountCount() { - return _accounts.size(); + return accounts.size(); } /* (non-Javadoc) - * @see com.connectif.trilogy.root.security.BankService#createNewAccount(java.lang.String) - */ + * @see com.connectif.trilogy.root.security.BankService#createNewAccount(java.lang.String) + */ @RequiresPermissions("bankAccount:create") public long createNewAccount(String anOwnerName) { assertServiceState(); - log.info("Creating new account for " + anOwnerName); + LOGGER.info("Creating new account for " + anOwnerName); - synchronized (_accounts) { + synchronized (accounts) { Account account = new Account(anOwnerName); account.setCreatedBy(getCurrentUsername()); - _accounts.add(account); - _accountsById.put(account.getId(), account); + accounts.add(account); + accountsById.put(account.getId(), account); - log.debug("Created new account: " + account); + LOGGER.debug("Created new account: " + account); return account.getId(); } } /* (non-Javadoc) - * @see com.connectif.trilogy.root.security.BankService#searchAccountIdsByOwner(java.lang.String) - */ + * @see com.connectif.trilogy.root.security.BankService#searchAccountIdsByOwner(java.lang.String) + */ public long[] searchAccountIdsByOwner(String anOwnerName) { assertServiceState(); - log.info("Searching existing accounts for " + anOwnerName); + LOGGER.info("Searching existing accounts for " + anOwnerName); ArrayList matchAccounts = new ArrayList(); - synchronized (_accounts) { - for (Account a : _accounts) { + synchronized (accounts) { + for (Account a : accounts) { if (a.getOwnerName().toLowerCase().contains(anOwnerName.toLowerCase())) { matchAccounts.add(a); } @@ -124,53 +124,53 @@ public long[] searchAccountIdsByOwner(String anOwnerName) { accountIds[index++] = a.getId(); } - log.debug("Found " + accountIds.length + " account(s) matching the name " + anOwnerName); + LOGGER.debug("Found " + accountIds.length + " account(s) matching the name " + anOwnerName); return accountIds; } /* (non-Javadoc) - * @see com.connectif.trilogy.root.security.BankService#getOwnerOf(long) - */ + * @see com.connectif.trilogy.root.security.BankService#getOwnerOf(long) + */ @RequiresPermissions("bankAccount:read") public String getOwnerOf(long anAccountId) throws AccountNotFoundException { assertServiceState(); - log.info("Getting owner of account " + anAccountId); + LOGGER.info("Getting owner of account " + anAccountId); Account a = safelyRetrieveAccountForId(anAccountId); return a.getOwnerName(); } /* (non-Javadoc) - * @see com.connectif.trilogy.root.security.BankService#getBalanceOf(long) - */ + * @see com.connectif.trilogy.root.security.BankService#getBalanceOf(long) + */ @RequiresPermissions("bankAccount:read") public double getBalanceOf(long anAccountId) throws AccountNotFoundException { assertServiceState(); - log.info("Getting balance of account " + anAccountId); + LOGGER.info("Getting balance of account " + anAccountId); Account a = safelyRetrieveAccountForId(anAccountId); return a.getBalance(); } /* (non-Javadoc) - * @see com.connectif.trilogy.root.security.BankService#depositInto(long, double) - */ + * @see com.connectif.trilogy.root.security.BankService#depositInto(long, double) + */ @RequiresPermissions("bankAccount:operate") public double depositInto(long anAccountId, double anAmount) throws AccountNotFoundException, InactiveAccountException { assertServiceState(); - log.info("Making deposit of " + anAmount + " into account " + anAccountId); + LOGGER.info("Making deposit of " + anAmount + " into account " + anAccountId); try { Account a = safelyRetrieveAccountForId(anAccountId); AccountTransaction tx = AccountTransaction.createDepositTx(anAccountId, anAmount); tx.setCreatedBy(getCurrentUsername()); - log.debug("Created a new transaction " + tx); + LOGGER.debug("Created a new transaction " + tx); a.applyTransaction(tx); - log.debug("New balance of account " + a.getId() + " after deposit is " + a.getBalance()); + LOGGER.debug("New balance of account " + a.getId() + " after deposit is " + a.getBalance()); return a.getBalance(); @@ -180,40 +180,41 @@ public double depositInto(long anAccountId, double anAmount) throws AccountNotFo } /* (non-Javadoc) - * @see com.connectif.trilogy.root.security.BankService#withdrawFrom(long, double) - */ + * @see com.connectif.trilogy.root.security.BankService#withdrawFrom(long, double) + */ + @SuppressWarnings("checkstyle:LineLength") @RequiresPermissions("bankAccount:operate") public double withdrawFrom(long anAccountId, double anAmount) throws AccountNotFoundException, NotEnoughFundsException, InactiveAccountException { assertServiceState(); - log.info("Making withdrawal of " + anAmount + " from account " + anAccountId); + LOGGER.info("Making withdrawal of " + anAmount + " from account " + anAccountId); Account a = safelyRetrieveAccountForId(anAccountId); AccountTransaction tx = AccountTransaction.createWithdrawalTx(anAccountId, anAmount); tx.setCreatedBy(getCurrentUsername()); - log.debug("Created a new transaction " + tx); + LOGGER.debug("Created a new transaction " + tx); a.applyTransaction(tx); - log.debug("New balance of account " + a.getId() + " after withdrawal is " + a.getBalance()); + LOGGER.debug("New balance of account " + a.getId() + " after withdrawal is " + a.getBalance()); return a.getBalance(); } /* (non-Javadoc) - * @see com.connectif.trilogy.root.security.BankService#getTxHistoryFor(long) - */ + * @see com.connectif.trilogy.root.security.BankService#getTxHistoryFor(long) + */ @RequiresPermissions("bankAccount:read") public TxLog[] getTxHistoryFor(long anAccountId) throws AccountNotFoundException { assertServiceState(); - log.info("Getting transactions of account " + anAccountId); + LOGGER.info("Getting transactions of account " + anAccountId); Account a = safelyRetrieveAccountForId(anAccountId); TxLog[] txs = new TxLog[a.getTransactions().size()]; int index = 0; for (AccountTransaction tx : a.getTransactions()) { - log.debug("Retrieved transaction " + tx); + LOGGER.debug("Retrieved transaction " + tx); if (TransactionType.DEPOSIT == tx.getType()) { txs[index++] = new TxLog(tx.getCreationDate(), tx.getAmount(), tx.getCreatedBy()); @@ -226,13 +227,13 @@ public TxLog[] getTxHistoryFor(long anAccountId) throws AccountNotFoundException } /* (non-Javadoc) - * @see com.connectif.trilogy.root.security.BankService#closeAccount(long) - */ + * @see com.connectif.trilogy.root.security.BankService#closeAccount(long) + */ @RequiresPermissions("bankAccount:close") public double closeAccount(long anAccountId) throws AccountNotFoundException, InactiveAccountException { assertServiceState(); - log.info("Closing account " + anAccountId); + LOGGER.info("Closing account " + anAccountId); Account a = safelyRetrieveAccountForId(anAccountId); if (!a.isActive()) { @@ -242,11 +243,11 @@ public double closeAccount(long anAccountId) throws AccountNotFoundException, In try { AccountTransaction tx = AccountTransaction.createWithdrawalTx(a.getId(), a.getBalance()); tx.setCreatedBy(getCurrentUsername()); - log.debug("Created a new transaction " + tx); + LOGGER.debug("Created a new transaction " + tx); a.applyTransaction(tx); a.setActive(false); - log.debug("Account " + a.getId() + " is now closed and an amount of " + tx.getAmount() + " is given to the owner"); + LOGGER.debug("Account " + a.getId() + " is now closed and an amount of " + tx.getAmount() + " is given to the owner"); return tx.getAmount(); } catch (NotEnoughFundsException nefe) { @@ -255,13 +256,13 @@ public double closeAccount(long anAccountId) throws AccountNotFoundException, In } /* (non-Javadoc) - * @see com.connectif.trilogy.root.security.BankService#isAccountActive(long) - */ + * @see com.connectif.trilogy.root.security.BankService#isAccountActive(long) + */ @RequiresPermissions("bankAccount:read") public boolean isAccountActive(long anAccountId) throws AccountNotFoundException { assertServiceState(); - log.info("Getting active status of account " + anAccountId); + LOGGER.info("Getting active status of account " + anAccountId); Account a = safelyRetrieveAccountForId(anAccountId); return a.isActive(); @@ -277,15 +278,15 @@ public boolean isAccountActive(long anAccountId) throws AccountNotFoundException */ protected Account safelyRetrieveAccountForId(long anAccountId) throws AccountNotFoundException { Account account = null; - synchronized (_accounts) { - account = _accountsById.get(anAccountId); + synchronized (accounts) { + account = accountsById.get(anAccountId); } if (account == null) { throw new AccountNotFoundException("No account found for the id " + anAccountId); } - log.info("Retrieved account " + account); + LOGGER.info("Retrieved account " + account); return account; } diff --git a/samples/aspectj/src/test/java/org/apache/shiro/samples/aspectj/bank/SecureBankServiceTest.java b/samples/aspectj/src/test/java/org/apache/shiro/samples/aspectj/bank/SecureBankServiceTest.java index 123f631c31..805f381b1f 100644 --- a/samples/aspectj/src/test/java/org/apache/shiro/samples/aspectj/bank/SecureBankServiceTest.java +++ b/samples/aspectj/src/test/java/org/apache/shiro/samples/aspectj/bank/SecureBankServiceTest.java @@ -22,21 +22,29 @@ import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.UnauthorizedException; import org.apache.shiro.ini.IniSecurityManagerFactory; +import org.apache.shiro.lang.util.Factory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; -import org.apache.shiro.lang.util.Factory; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.junit.jupiter.api.Assertions.assertThrows; +@SuppressWarnings({"checkstyle:MemberName", "checkstyle:MethodName", "checkstyle:MagicNumber", "checkstyle:LineLength"}) public class SecureBankServiceTest { private static Logger logger = LoggerFactory.getLogger(SecureBankServiceTest.class); private static SecureBankService service; private static int testCounter; + private Subject _subject; + @BeforeAll public static void setUpClass() throws Exception { Factory factory = new IniSecurityManagerFactory("classpath:shiroBankServiceTest.ini"); @@ -54,8 +62,6 @@ public static void tearDownClass() { } } - private Subject _subject; - @BeforeEach public void setUp() throws Exception { logger.info("\n\n#########################\n### STARTING TEST CASE " + (++testCounter) + "\n"); @@ -172,7 +178,7 @@ void testCloseAccount_zeroBalance() throws Exception { logoutCurrentSubject(); loginAsSuperviser(); double closingBalance = service.closeAccount(accountId); - Assertions.assertEquals(0, (int)closingBalance); + Assertions.assertEquals(0, (int) closingBalance); assertAccount("Chris Smith", false, 0, 1, accountId); } @@ -185,7 +191,7 @@ void testCloseAccount_withBalance() throws Exception { logoutCurrentSubject(); loginAsSuperviser(); double closingBalance = service.closeAccount(accountId); - Assertions.assertEquals(385, (int)closingBalance); + Assertions.assertEquals(385, (int) closingBalance); assertAccount("Gerry Smith", false, 0, 2, accountId); } @@ -223,8 +229,8 @@ protected double makeDepositAndValidateAccount(long anAccountId, int anAmount, S double previousBalance = service.getBalanceOf(anAccountId); int previousTxCount = service.getTxHistoryFor(anAccountId).length; double newBalance = service.depositInto(anAccountId, anAmount); - Assertions.assertEquals((int)previousBalance + anAmount, (int)newBalance); - assertAccount(eOwnerName, true, (int)newBalance, 1 + previousTxCount, anAccountId); + Assertions.assertEquals((int) previousBalance + anAmount, (int) newBalance); + assertAccount(eOwnerName, true, (int) newBalance, 1 + previousTxCount, anAccountId); return newBalance; } @@ -232,8 +238,8 @@ protected double makeWithdrawalAndValidateAccount(long anAccountId, int anAmount double previousBalance = service.getBalanceOf(anAccountId); int previousTxCount = service.getTxHistoryFor(anAccountId).length; double newBalance = service.withdrawFrom(anAccountId, anAmount); - Assertions.assertEquals((int)previousBalance - anAmount, (int)newBalance); - assertAccount(eOwnerName, true, (int)newBalance, 1 + previousTxCount, anAccountId); + Assertions.assertEquals((int) previousBalance - anAmount, (int) newBalance); + assertAccount(eOwnerName, true, (int) newBalance, 1 + previousTxCount, anAccountId); return newBalance; } @@ -241,7 +247,7 @@ protected double makeWithdrawalAndValidateAccount(long anAccountId, int anAmount public static void assertAccount(String eOwnerName, boolean eIsActive, int eBalance, int eTxLogCount, long actualAccountId) throws Exception { Assertions.assertEquals(eOwnerName, service.getOwnerOf(actualAccountId)); Assertions.assertEquals(eIsActive, service.isAccountActive(actualAccountId)); - Assertions.assertEquals(eBalance, (int)service.getBalanceOf(actualAccountId)); + Assertions.assertEquals(eBalance, (int) service.getBalanceOf(actualAccountId)); Assertions.assertEquals(eTxLogCount, service.getTxHistoryFor(actualAccountId).length); } } diff --git a/samples/guice/pom.xml b/samples/guice/pom.xml index 3f6bccd039..9727dc584a 100644 --- a/samples/guice/pom.xml +++ b/samples/guice/pom.xml @@ -17,74 +17,75 @@ ~ specific language governing permissions and limitations ~ under the License. --> - - + + org.apache.shiro.samples shiro-samples - 2.0.0-SNAPSHOT - + 2.0.0-SNAPSHOT + - 4.0.0 - samples-guice - Apache Shiro :: Samples :: Guice Web - war + 4.0.0 + samples-guice + Apache Shiro :: Samples :: Guice Web + war - - - - org.eclipse.jetty - jetty-maven-plugin - - - + + + + org.eclipse.jetty + jetty-maven-plugin + + + - - - org.apache.taglibs - taglibs-standard-spec - compile - - - org.apache.taglibs - taglibs-standard-impl - compile - - - javax.servlet - javax.servlet-api - provided - - - org.apache.logging.log4j - log4j-slf4j2-impl - runtime - - - org.apache.logging.log4j - log4j-core - runtime - - - org.apache.shiro - shiro-core - - - org.apache.shiro - shiro-web - - - org.apache.shiro - shiro-guice - - - com.google.inject.extensions - guice-servlet - - - org.slf4j - jcl-over-slf4j - runtime - + + + org.apache.taglibs + taglibs-standard-spec + compile + + + org.apache.taglibs + taglibs-standard-impl + compile + + + javax.servlet + javax.servlet-api + provided + + + org.apache.logging.log4j + log4j-slf4j2-impl + runtime + + + org.apache.logging.log4j + log4j-core + runtime + + + org.apache.shiro + shiro-core + + + org.apache.shiro + shiro-web + + + org.apache.shiro + shiro-guice + + + com.google.inject.extensions + guice-servlet + + + org.slf4j + jcl-over-slf4j + runtime + org.htmlunit @@ -105,35 +106,35 @@ test - - org.apache.shiro.integrationtests - shiro-its-support - test - - + + org.apache.shiro.integrationtests + shiro-its-support + test + + - - - jdk16 - - [16,) - - - - --illegal-access=permit - --illegal-access=permit - - - - jdk17 - - [17,) - - - - --add-opens java.base/java.lang=ALL-UNNAMED - --add-opens java.base/java.lang=ALL-UNNAMED - - - + + + jdk16 + + [16,) + + + + --illegal-access=permit + --illegal-access=permit + + + + jdk17 + + [17,) + + + + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED + + + diff --git a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroGuiceBootstrap.java b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroGuiceBootstrap.java index 05e1b35e63..12334bb84b 100644 --- a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroGuiceBootstrap.java +++ b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroGuiceBootstrap.java @@ -36,6 +36,7 @@ public void contextInitialized(final ServletContextEvent servletContextEvent) { super.contextInitialized(servletContextEvent); } + @SuppressWarnings("checkstyle:LineLength") @Override protected Injector getInjector() { // return Guice.createInjector(new SampleShiroServletModule(servletContext), ShiroWebModule.guiceFilterModule()); diff --git a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroNativeSessionsServletModule.java b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroNativeSessionsServletModule.java index 271982daa8..003228057e 100644 --- a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroNativeSessionsServletModule.java +++ b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroNativeSessionsServletModule.java @@ -48,6 +48,7 @@ public SampleShiroNativeSessionsServletModule(ServletContext servletContext) { this.servletContext = servletContext; } + @SuppressWarnings("checkstyle:LineLength") @Override protected void configureShiroWeb() { bindConstant().annotatedWith(Names.named("shiro.loginUrl")).to("/login.jsp"); @@ -70,6 +71,7 @@ Ini loadShiroIni() throws MalformedURLException { return Ini.fromResourcePath("url:" + iniUrl.toExternalForm()); } + @SuppressWarnings("checkstyle:MagicNumber") @Override protected void bindSessionManager(AnnotatedBindingBuilder bind) { bind.to(DefaultWebSessionManager.class); @@ -80,23 +82,19 @@ protected void bindSessionManager(AnnotatedBindingBuilder bind) } @Override - protected void bindWebSecurityManager(AnnotatedBindingBuilder bind) - { - try - { - String cipherKey = loadShiroIni().getSectionProperty( "main", "securityManager.rememberMeManager.cipherKey" ); + protected void bindWebSecurityManager(AnnotatedBindingBuilder bind) { + try { + String cipherKey = loadShiroIni().getSectionProperty("main", "securityManager.rememberMeManager.cipherKey"); DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); - rememberMeManager.setCipherKey( Base64.decode( cipherKey ) ); + rememberMeManager.setCipherKey(Base64.decode(cipherKey)); securityManager.setRememberMeManager(rememberMeManager); bind.toInstance(securityManager); - } - catch ( MalformedURLException e ) - { + } catch (MalformedURLException e) { // for now just throw, you could just call // super.bindWebSecurityManager(bind) if you do not need rememberMe functionality - throw new ConfigurationException( "securityManager.rememberMeManager.cipherKey must be set in shiro.ini." ); + throw new ConfigurationException("securityManager.rememberMeManager.cipherKey must be set in shiro.ini."); } } } diff --git a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java index 312a59326c..3826089072 100644 --- a/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java +++ b/samples/guice/src/main/java/org/apache/shiro/samples/guice/SampleShiroServletModule.java @@ -44,6 +44,7 @@ public SampleShiroServletModule(ServletContext servletContext) { this.servletContext = servletContext; } + @SuppressWarnings("checkstyle:LineLength") @Override protected void configureShiroWeb() { bindConstant().annotatedWith(Names.named("shiro.loginUrl")).to("/login.jsp"); @@ -68,23 +69,19 @@ Ini loadShiroIni() throws MalformedURLException { } @Override - protected void bindWebSecurityManager(AnnotatedBindingBuilder bind) - { - try - { - String cipherKey = loadShiroIni().getSectionProperty( "main", "securityManager.rememberMeManager.cipherKey" ); + protected void bindWebSecurityManager(AnnotatedBindingBuilder bind) { + try { + String cipherKey = loadShiroIni().getSectionProperty("main", "securityManager.rememberMeManager.cipherKey"); DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); - rememberMeManager.setCipherKey( Base64.decode( cipherKey ) ); + rememberMeManager.setCipherKey(Base64.decode(cipherKey)); securityManager.setRememberMeManager(rememberMeManager); bind.toInstance(securityManager); - } - catch ( MalformedURLException e ) - { + } catch (MalformedURLException e) { // for now just throw, you could just call // super.bindWebSecurityManager(bind) if you do not need rememberMe functionality - throw new ConfigurationException( "securityManager.rememberMeManager.cipherKey must be set in shiro.ini." ); + throw new ConfigurationException("securityManager.rememberMeManager.cipherKey must be set in shiro.ini."); } diff --git a/samples/guice/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java b/samples/guice/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java index d7bb94ebde..7963a6a801 100644 --- a/samples/guice/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java +++ b/samples/guice/src/test/java/org/apache/shiro/samples/guice/ContainerIntegrationIT.java @@ -39,8 +39,7 @@ public void logOut() throws IOException { final HtmlPage homePage = webClient.getPage(getBaseUri()); try { homePage.getAnchorByHref("/logout").click(); - } - catch (ElementNotFoundException e) { + } catch (ElementNotFoundException e) { //Ignore } } diff --git a/samples/pom.xml b/samples/pom.xml index ea9049337b..b378d6ca61 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + org.apache.shiro diff --git a/samples/quickstart-guice/pom.xml b/samples/quickstart-guice/pom.xml index de44171685..7fa9e70fd2 100644 --- a/samples/quickstart-guice/pom.xml +++ b/samples/quickstart-guice/pom.xml @@ -17,7 +17,8 @@ ~ specific language governing permissions and limitations ~ under the License. --> - + org.apache.shiro.samples shiro-samples diff --git a/samples/quickstart-guice/src/main/java/QuickstartShiroModule.java b/samples/quickstart-guice/src/main/java/QuickstartShiroModule.java index 01caf689f8..0b32d5b4b4 100644 --- a/samples/quickstart-guice/src/main/java/QuickstartShiroModule.java +++ b/samples/quickstart-guice/src/main/java/QuickstartShiroModule.java @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ + import com.google.inject.Provides; import org.apache.shiro.config.Ini; import org.apache.shiro.guice.ShiroModule; diff --git a/samples/quickstart/pom.xml b/samples/quickstart/pom.xml index 3f117014ee..15ce3028ea 100644 --- a/samples/quickstart/pom.xml +++ b/samples/quickstart/pom.xml @@ -18,7 +18,8 @@ ~ under the License. --> - + org.apache.shiro.samples diff --git a/samples/quickstart/src/main/java/Quickstart.java b/samples/quickstart/src/main/java/Quickstart.java index fbba9d2c85..af82f6f261 100644 --- a/samples/quickstart/src/main/java/Quickstart.java +++ b/samples/quickstart/src/main/java/Quickstart.java @@ -27,7 +27,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; - /** * Simple Quickstart application showing how to use Shiro's API. * diff --git a/samples/servlet-plugin/pom.xml b/samples/servlet-plugin/pom.xml index de83d018ed..a251bc5b72 100644 --- a/samples/servlet-plugin/pom.xml +++ b/samples/servlet-plugin/pom.xml @@ -18,7 +18,8 @@ ~ under the License. --> - + org.apache.shiro.samples @@ -64,14 +65,14 @@ - - org.apache.maven.plugins - maven-failsafe-plugin - - - **/ContainerIntegrationIT.java - - + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/ContainerIntegrationIT.java + + diff --git a/samples/servlet-plugin/src/test/java/org/apache/shiro/test/ContainerIntegrationIT.java b/samples/servlet-plugin/src/test/java/org/apache/shiro/test/ContainerIntegrationIT.java index 60d3666be6..e96016857d 100644 --- a/samples/servlet-plugin/src/test/java/org/apache/shiro/test/ContainerIntegrationIT.java +++ b/samples/servlet-plugin/src/test/java/org/apache/shiro/test/ContainerIntegrationIT.java @@ -37,8 +37,7 @@ public void logOut() throws IOException { final HtmlPage homePage = webClient.getPage(getBaseUri()); try { homePage.getAnchorByHref("/logout").click(); - } - catch (ElementNotFoundException e) { + } catch (ElementNotFoundException e) { //Ignore } } diff --git a/samples/spring-boot-3-web/README.md b/samples/spring-boot-3-web/README.md index 8d2474d9b5..bf33169a69 100644 --- a/samples/spring-boot-3-web/README.md +++ b/samples/spring-boot-3-web/README.md @@ -1,7 +1,8 @@ Apache Shiro + Spring Boot 3 Web Example ================================= -A Spring Boot example web application that show the usage of a user login, checking permissions, and annotation protected methods. +A Spring Boot example web application that show the usage of a user login, checking permissions, and annotation +protected methods. Run the Example --------------- diff --git a/samples/spring-boot-3-web/pom.xml b/samples/spring-boot-3-web/pom.xml index 5f16321e36..a56910b0d2 100644 --- a/samples/spring-boot-3-web/pom.xml +++ b/samples/spring-boot-3-web/pom.xml @@ -18,7 +18,8 @@ ~ under the License. --> - + 4.0.0 @@ -36,7 +37,7 @@ samples.spring.boot3.web 3.1.2 ${spring-boot3.version} - + 6.0.11 @@ -79,7 +80,7 @@ true - + jakarta.servlet jakarta.servlet-api diff --git a/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/HelloController.java b/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/HelloController.java index f4dd27a32f..2861c24067 100644 --- a/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/HelloController.java +++ b/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/HelloController.java @@ -27,6 +27,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import jakarta.servlet.http.HttpServletRequest; + import java.util.Collection; import java.util.Map; @@ -47,8 +48,7 @@ public String home(HttpServletRequest request, Model model) { Collection principalMaps = subject.getPrincipals().byType(Map.class); if (CollectionUtils.isEmpty(principalMaps)) { name = subject.getPrincipal().toString(); - } - else { + } else { name = (String) principalMaps.iterator().next().get("username"); } } diff --git a/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/RestrictedErrorController.java b/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/RestrictedErrorController.java index e7a5e5df5a..6fb0bc6e22 100644 --- a/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/RestrictedErrorController.java +++ b/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/RestrictedErrorController.java @@ -28,9 +28,11 @@ import org.springframework.web.context.request.ServletWebRequest; import jakarta.servlet.http.HttpServletRequest; + import java.util.Map; /** + * */ @Controller public class RestrictedErrorController implements ErrorController { diff --git a/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/WebApp.java b/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/WebApp.java index 819db0f0dc..c347ed875e 100644 --- a/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/WebApp.java +++ b/samples/spring-boot-3-web/src/main/java/org/apache/shiro/samples/WebApp.java @@ -41,10 +41,13 @@ import java.util.HashMap; import java.util.Map; +/** + * NOPMD + */ @Configuration @ControllerAdvice @SpringBootApplication -public class WebApp { //NOPMD +public class WebApp { private static Logger log = LoggerFactory.getLogger(WebApp.class); @@ -72,11 +75,9 @@ public String handleException(AuthorizationException e, Model model) { @Bean public Realm realm() { TextConfigurationRealm realm = new TextConfigurationRealm(); - realm.setUserDefinitions("joe.coder=password,user\n" + - "jill.coder=password,admin"); + realm.setUserDefinitions("joe.coder=password,user\n" + "jill.coder=password,admin"); - realm.setRoleDefinitions("admin=read,write\n" + - "user=read"); + realm.setRoleDefinitions("admin=read,write\n" + "user=read"); realm.setCachingEnabled(true); return realm; } @@ -84,7 +85,8 @@ public Realm realm() { @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); - chainDefinition.addPathDefinition("/login.html", "authc"); // need to accept POSTs from the login form + // need to accept POSTs from the login form + chainDefinition.addPathDefinition("/login.html", "authc"); chainDefinition.addPathDefinition("/logout", "logout"); return chainDefinition; } diff --git a/samples/spring-boot-3-web/src/main/resources/application.properties b/samples/spring-boot-3-web/src/main/resources/application.properties index 852dc7493b..8437ddcdf1 100644 --- a/samples/spring-boot-3-web/src/main/resources/application.properties +++ b/samples/spring-boot-3-web/src/main/resources/application.properties @@ -16,11 +16,8 @@ # specific language governing permissions and limitations # under the License. # - -shiro.loginUrl = /login.html - +shiro.loginUrl=/login.html # Let Shiro Manage the sessions -shiro.userNativeSessionManager = true - +shiro.userNativeSessionManager=true # disable URL session rewriting -shiro.sessionManager.sessionIdUrlRewritingEnabled = false \ No newline at end of file +shiro.sessionManager.sessionIdUrlRewritingEnabled=false diff --git a/samples/spring-boot-3-web/src/main/resources/templates/account-info.html b/samples/spring-boot-3-web/src/main/resources/templates/account-info.html index 1fc0ca95ba..52793f37c2 100644 --- a/samples/spring-boot-3-web/src/main/resources/templates/account-info.html +++ b/samples/spring-boot-3-web/src/main/resources/templates/account-info.html @@ -18,22 +18,22 @@ --> - - Account Info - - - -

-

+ + Account Info + + + +
+

- Home + Home -
- -
+
+ +
-

- - - +

+ + + \ No newline at end of file diff --git a/samples/spring-boot-3-web/src/main/resources/templates/fragments/head.html b/samples/spring-boot-3-web/src/main/resources/templates/fragments/head.html index 58ab413e91..27d2d78fea 100644 --- a/samples/spring-boot-3-web/src/main/resources/templates/fragments/head.html +++ b/samples/spring-boot-3-web/src/main/resources/templates/fragments/head.html @@ -23,18 +23,22 @@ - - + +