Initial import.

This commit is contained in:
Mark Paluch
2016-09-11 08:50:51 +02:00
commit b5e09f2d53
98 changed files with 9845 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
*.iml
*.ipr
*.iws
.classpath
.idea
.project
.settings
.checkstyle
bin
build
out
target
/vault/
/download/
/work/

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

1
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@@ -0,0 +1 @@
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip

20
.travis.yml Normal file
View File

@@ -0,0 +1,20 @@
language: java
jdk:
- oraclejdk8
install:
- mkdir -p download
- src/test/bash/create_certificates.sh
- src/test/bash/install_vault.sh
- src/test/bash/local_run_vault.sh &
script: mvn clean verify
after_script:
- pkill vault
cache:
directories:
- '$HOME/.m2/repository'
- 'download'

27
CODE_OF_CONDUCT.adoc Normal file
View File

@@ -0,0 +1,27 @@
= Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses,
without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at spring-code-of-conduct@pivotal.io.
All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident.
This Code of Conduct is adapted from the http://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at http://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/].

202
LICENSE.txt Normal file
View File

@@ -0,0 +1,202 @@
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.

219
README.adoc Normal file
View File

@@ -0,0 +1,219 @@
Spring Vault provides client-side support for accessing, storing and revoking secrets.
With https://www.vaultproject.io[Hashicorp's Vault] you have a central place to manage external secret data for applications across all environments.
Vault can manage static and dynamic secrets such as application data, username/password for remote applications/resources and provide credentials for external services such as MySQL, PostgreSQL, Apache Cassandra, Consul, AWS and more.
== Features
=== Spring Vault
Specifically for Spring applications:
* JavaConfig for Vault Client
* https://www.vaultproject.io/docs/auth/token.html[Token], https://www.vaultproject.io/docs/auth/app-id.html[AppId] authentication,
and https://www.vaultproject.io/docs/auth/aws-ec2.html[AWS-EC2] authentication
== Quick Start
*Prerequisites*
To get started with Vault and this guide you need a
*NIX-like operating systems that provides:
* `wget`, `openssl` and `unzip`
* at least Java 6 and a properly configured `JAVA_HOME` environment variable
*Install Vault*
----
$ src/test/bash/install_vault.sh
----
*Create SSL certificates for Vault*
----
$ src/test/bash/create_certificates.sh
----
NOTE: `create_certificates.sh` creates certificates in `work/ca` and a JKS truststore `work/keystore.jsk`. If you want to run Spring Vault using this quickstart guide you need to configure the truststore to `file:work/keystore.jks`.
*Start Vault server*
----
$ src/test/bash/local_run_vault.sh
----
Vault is started listening on `0.0.0.0:8200` using the `inmem` storage and
`https`.
Vault is sealed and not initialized when starting up
so you need to initialize it first.
----
$ export VAULT_ADDR="https://localhost:8200"
$ export VAULT_SKIP_VERIFY=true # Don't do this for production
$ vault init
----
You should see something like:
----
Key 1: 7149c6a2e16b8833f6eb1e76df03e47f6113a3288b3093faf5033d44f0e70fe701
Key 2: 901c534c7988c18c20435a85213c683bdcf0efcd82e38e2893779f152978c18c02
Key 3: 03ff3948575b1165a20c20ee7c3e6edf04f4cdbe0e82dbff5be49c63f98bc03a03
Key 4: 216ae5cc3ddaf93ceb8e1d15bb9fc3176653f5b738f5f3d1ee00cd7dccbe926e04
Key 5: b2898fc8130929d569c1677ee69dc5f3be57d7c4b494a6062693ce0b1c4d93d805
Initial Root Token: 19aefa97-cccc-bbbb-aaaa-225940e63d76
Vault initialized with 5 keys and a key threshold of 3. Please
securely distribute the above keys. When the Vault is re-sealed,
restarted, or stopped, you must provide at least 3 of these keys
to unseal it again.
Vault does not store the master key. Without at least 3 keys,
your Vault will remain permanently sealed.
----
Vault will initialize and return a set of unsealing keys and the root token.
Pick 3 keys and unseal Vault.
----
$ vault unseal (Key 1)
$ vault unseal (Key 2)
$ vault unseal (Key 3)
----
Vault is now initialized and unsealed.
== Building
==== Build requirements for Vault
Spring Vault requires SSL certificates and a running
Vault instance listening on `localhost:8200`. Certificates and the Vault
setup are scripted, the scripts are located in `src/test/bash`.
The following scripts need to be run prior to building the project for the tests to pass.
$ ./src/test/bash/install_vault.sh
$ ./src/test/bash/create_certificates.sh
$ ./src/test/bash/local_run_vault.sh
Changes to the documentation should be made to the adocs found under `src/main/asciidoc/`
=== Basic Compile and Test
To build the source you will need to install JDK 1.6.
Spring Vault uses Maven for most build-related activities, and you
should be able to get off the ground quite quickly by cloning the
project you are interested in and typing
----
$ ./mvnw install
----
NOTE: You can also install Maven (>=3.3.3) yourself and run the `mvn` command
in place of `./mvnw` in the examples below. If you do that you also
might need to add `-P spring` if your local Maven settings do not
contain repository declarations for spring pre-release artifacts.
NOTE: Be aware that you might need to increase the amount of memory
available to Maven by setting a `MAVEN_OPTS` environment variable with
a value like `-Xmx512m -XX:MaxPermSize=128m`. We try to cover this in
the `.mvn` configuration, so if you find you have to do it to make a
build succeed, please raise a ticket to get the settings added to
source control.
For hints on how to build the project look in `.travis.yml` if there
is one. There should be a "script" and maybe "install" command. Also
look at the "services" section to see if any services need to be
running locally (e.g. mongo or rabbit). Ignore the git-related bits
that you might find in "before_install" since they're related to setting git
credentials and you already have those.
NOTE: If all else fails, build with the command from `.travis.yml` (usually
`./mvnw install`).
=== Documentation
The module has a "distribute" profile, and if you switch
that on it will try to build asciidoc sources from
`src/main/asciidoc`.
=== Working with the code
If you don't have an IDE preference we would recommend that you use
http://www.springsource.com/developer/sts[Spring Tools Suite] or
http://eclipse.org[Eclipse] when working with the code. We use the
http://eclipse.org/m2e/[m2eclipe] eclipse plugin for maven support. Other IDEs and tools
should also work without issue as long as they use Maven 3.3.3 or better.
==== Importing into eclipse with m2eclipse
We recommend the http://eclipse.org/m2e/[m2eclipe] eclipse plugin when working with
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
marketplace".
NOTE: Older versions of m2e do not support Maven 3.3, so once the
projects are imported into Eclipse you will also need to tell
m2eclipse to use the right profile for the projects. If you
see many different errors related to the POMs in the projects, check
that you have an up to date installation. If you can't upgrade m2e,
add the "spring" profile to your `settings.xml`. Alternatively you can
copy the repository settings from the "spring" profile of the parent
pom into your `settings.xml`.
==== Importing into eclipse without m2eclipse
If you prefer not to use m2eclipse you can generate eclipse project metadata using the
following command:
[indent=0]
----
$ ./mvnw eclipse:eclipse
----
The generated eclipse projects can be imported by selecting `import existing projects`
from the `file` menu.
== Contributing
Spring Vault is released under the non-restrictive Apache 2.0 license,
and follows a very standard Github development process, using Github
tracker for issues and merging pull requests into master. If you want
to contribute even something trivial please do not hesitate, but
follow the guidelines below.
=== Sign the Contributor License Agreement
Before we accept a non-trivial patch or pull request we will need you to sign the
https://cla.pivotal.io/sign/spring[Contributor License Agreement].
Signing the contributor's agreement does not grant anyone commit rights to the main
repository, but it does mean that we can accept your contributions, and you will get an
author credit if we do. Active contributors might be asked to join the core team, and
given the ability to merge pull requests.
=== Code of Conduct
This project adheres to the Contributor Covenant https://github.com/spring-framework/spring-vault/blob/master/CODE_OF_CONDUCT.adoc[code of
conduct]. By participating, you are expected to uphold this code. Please report
unacceptable behavior to spring-code-of-conduct@pivotal.io.
=== Code Conventions and Housekeeping
None of these is essential for a pull request, but they will all help. They can also be
added after the original pull request but before a merge.
* Use the Spring Framework code format conventions. If you use Eclipse
you can import formatter settings using the
`eclipse-code-formatter.xml` file from the
https://raw.githubusercontent.com/spring-framework/spring-vault/master/etc/ide/eclipse-code-formatter.xml[project]. If using IntelliJ, you can use the
http://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
Plugin] to import the same file.
* Make sure all new `.java` files to have a Javadoc class comment with at least an
`@author` tag identifying you, and preferably at least a paragraph on what the class is
for.
* Add the ASF license header comment to all new `.java` files (copy from existing files
in the project)
* Add yourself as an `@author` to the .java files that you modify substantially (more
than cosmetic changes).
* Please include unit tests.
* If no-one else is using your branch, please rebase it against the current master (or
other target branch in the main project).
* When writing a commit message please follow http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions],
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
message (where XXXX is the issue number).

View File

@@ -0,0 +1,291 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="12">
<profile kind="CodeFormatterProfile" name="Spring Data" version="12">
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.compiler.source" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.7"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
</profile>
</profiles>

233
mvnw vendored Executable file
View File

@@ -0,0 +1,233 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# 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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
#
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
# for the new JDKs provided by Oracle.
#
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
#
# Oracle JDKs
#
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
#
# Apple JDKs
#
export JAVA_HOME=`/usr/libexec/java_home`
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Migwn, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
local basedir=$(pwd)
local wdir=$(pwd)
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
wdir=$(cd "$wdir/.."; pwd)
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} "$@"

145
mvnw.cmd vendored Normal file
View File

@@ -0,0 +1,145 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
set MAVEN_CMD_LINE_ARGS=%*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

685
pom.xml Normal file
View File

@@ -0,0 +1,685 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-parent</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<name>Spring Vault</name>
<description>Parent project for Spring Vault</description>
<packaging>pom</packaging>
<url>http://projects.spring.io/spring-vault/</url>
<modules>
<module>spring-vault-dependencies</module>
<module>spring-vault-core</module>
<module>spring-vault-distribution</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.2.RELEASE</spring.version>
<java.version>1.6</java.version>
<project.type>multi</project.type>
<dist.id>spring-vault</dist.id>
<project.root>${basedir}</project.root>
</properties>
<inceptionYear>2016</inceptionYear>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<scm>
<url>https://github.com/spring-projects/spring-vault</url>
<connection>scm:git:git://github.com/spring-projects/spring-vault.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-vault.git</developerConnection>
<tag>HEAD</tag>
</scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/spring-projects/spring-vault/issues</url>
</issueManagement>
<ciManagement>
<system>Bamboo</system>
<url>https://build.springframework.org/bamboo/browse/SPRVAULT</url>
</ciManagement>
<developers>
<developer>
<id>sgibb</id>
<name>Spencer Gibb</name>
</developer>
<developer>
<id>mpaluch</id>
<name>Mark Paluch</name>
</developer>
</developers>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
<comments>
Copyright 2016 the original author or authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied.
See the License for the specific language governing permissions and
limitations under the License.
</comments>
</license>
</licenses>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-dependencies</artifactId>
<version>${project.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Tests.java</include>
</includes>
<excludes>
<exclude>**/Abstract*.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java16</artifactId>
<version>1.0</version>
</signature>
</configuration>
<executions>
<execution>
<id>enforce-java-6</id>
<phase>test</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<!--
Default versioned declarations of managed plugins
to be overridden when the distribute profile is active.
If this section was missing, Maven would complain about
missing version numbers for executions without the
profile active.
-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.5.1</version>
<dependencies>
<dependency><!-- add support for ssh/scp -->
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ssh</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.15</version>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.3</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>wagon-maven-plugin</artifactId>
<version>1.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>springNext</id>
<properties>
<spring.version>5.0.0.BUILD-SNAPSHOT</spring.version>
</properties>
</profile>
<profile>
<id>bootstrap</id>
<repositories>
<repository>
<id>repo.spring.io-milestone</id>
<name>Spring Framework Milestone Repository</name>
<url>http://repo.spring.io/libs-milestone-local</url>
</repository>
<repository>
<id>repo.spring.io-release</id>
<name>Spring Framework Release Repository</name>
<url>http://repo.spring.io/libs-release-local</url>
</repository>
<repository>
<id>repo.spring.io-snapshot</id>
<name>Spring Framework Maven Snapshot Repository</name>
<url>http://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</profile>
<profile>
<id>milestone</id>
<distributionManagement>
<repository>
<id>repo.spring.io</id>
<name>Spring Milestone Repository</name>
<url>http://repo.spring.io/libs-milestone-local</url>
</repository>
</distributionManagement>
</profile>
<profile>
<id>central</id>
<distributionManagement>
<snapshotRepository>
<id>sonatype-nexus-snapshots</id>
<name>Sonatype Nexus Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<repository>
<id>sonatype-nexus-staging</id>
<name>Nexus Release Repository</name>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
<build>
<plugins>
<plugin>
<artifactId>maven-gpg-plugin</artifactId>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<!--
Profile to be activated when building the distribution atrifacts.
Generates reference documentation, aggregates JavaDoc etc. Has to be combined with
profiles "release" or "milestone" to deploy artifacts into the appropriate places.
-->
<id>distribute</id>
<properties>
<shared.resources>${project.build.directory}/shared-resources</shared.resources>
<maven.install.skip>true</maven.install.skip>
<skipTests>true</skipTests>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-build-resources</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<scope>provided</scope>
<type>zip</type>
</dependency>
</dependencies>
<build>
<plugins>
<!--
Unpacks the content of spring-data-build-resources into the shared resources folder.
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-shared-resources</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-resources</phase>
</execution>
</executions>
<configuration>
<includeGroupIds>org.springframework.data</includeGroupIds>
<includeArtifactIds>spring-data-build-resources</includeArtifactIds>
<includeTypes>zip</includeTypes>
<excludeTransitive>true</excludeTransitive>
<outputDirectory>${shared.resources}</outputDirectory>
</configuration>
</plugin>
<!--
Configures JavaDoc generation.
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>aggregate-javadoc</id>
<goals>
<goal>aggregate</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
<!--
Copies all namespaces schemas to target/schemas flatten the directory structure.
Depended on by the site.xml assembly descriptor.
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>copy-documentation-resources</id>
<phase>generate-resources</phase>
<configuration>
<target>
<copy todir="${project.root}/target/site/reference/html">
<fileset dir="${shared.resources}/asciidoc" erroronmissingdir="false">
<include name="**/*.css"/>
</fileset>
<flattenmapper/>
</copy>
<copy todir="${project.root}/target/site/reference/html/images">
<fileset dir="${basedir}/src/main/asciidoc" erroronmissingdir="false">
<include name="**/*.png"/>
<include name="**/*.gif"/>
<include name="**/*.jpg"/>
</fileset>
<flattenmapper/>
</copy>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>collect-schema-files</id>
<phase>process-resources</phase>
<configuration>
<target>
<copy todir="${project.build.directory}/schemas">
<fileset dir="${basedir}" erroronmissingdir="false">
<include name="**/src/main/resources/**/config/spring-*.xsd"/>
</fileset>
<flattenmapper/>
</copy>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>rename-reference-docs</id>
<phase>process-resources</phase>
<configuration>
<target>
<copy failonerror="false"
file="${project.build.directory}/generated-docs/index.pdf"
tofile="${project.root}/target/site/reference/pdf/${dist.id}-reference.pdf"/>
<copy failonerror="false"
file="${project.build.directory}/generated-docs/index.epub"
tofile="${project.root}/target/site/reference/epub/${dist.id}-reference.epub"/>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--
JavaDoc
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<breakiterator>true</breakiterator>
<header>${project.name}</header>
<source>${java.version}</source>
<quiet>true</quiet>
<javadocDirectory>${shared.resources}/javadoc</javadocDirectory>
<overview>${shared.resources}/javadoc/overview.html</overview>
<stylesheetfile>${shared.resources}/javadoc/spring-javadoc.css</stylesheetfile>
<!-- copies doc-files subdirectory which contains image resources -->
<docfilessubdirs>true</docfilessubdirs>
<additionalparam>-Xdoclint:none</additionalparam>
<links>
<link>http://docs.spring.io/spring/docs/4.2.x/javadoc-api/</link>
<link>http://docs.spring.io/spring-data/commons/docs/current/api/</link>
<link>http://docs.oracle.com/javase/6/docs/api</link>
</links>
</configuration>
</plugin>
<!--
Asciidoctor
-->
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-pdf</artifactId>
<version>1.5.0-alpha.11</version>
</dependency>
<dependency>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctorj-epub3</artifactId>
<version>1.5.0-alpha.6</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>html</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html5</backend>
<outputDirectory>${project.root}/target/site/reference/html</outputDirectory>
<sectids>false</sectids>
<sourceHighlighter>prettify</sourceHighlighter>
<attributes>
<linkcss>true</linkcss>
<icons>font</icons>
<sectanchors>true</sectanchors>
<stylesheet>spring.css</stylesheet>
</attributes>
</configuration>
</execution>
<execution>
<id>epub</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>epub3</backend>
<sourceHighlighter>coderay</sourceHighlighter>
</configuration>
</execution>
<execution>
<id>pdf</id>
<phase>generate-resources</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>pdf</backend>
<sourceHighlighter>coderay</sourceHighlighter>
</configuration>
</execution>
</executions>
<configuration>
<sourceDirectory>${project.root}/src/main/asciidoc</sourceDirectory>
<sourceDocumentName>index.adoc</sourceDocumentName>
<doctype>book</doctype>
<attributes>
<version>${project.version}</version>
<projectName>${project.name}</projectName>
<projectVersion>${project.version}</projectVersion>
<springVersion>${spring.version}</springVersion>
<allow-uri-read>true</allow-uri-read>
<toclevels>3</toclevels>
<numbered>true</numbered>
</attributes>
</configuration>
</plugin>
<!--
Creates two zip files for download as well as API and reference documentation distribution.
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>static</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptors>
<descriptor>${shared.resources}/assemblies/static-resources.xml</descriptor>
</descriptors>
<finalName>static-resources</finalName>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>wagon-maven-plugin</artifactId>
<configuration>
<fromDir>${project.build.directory}</fromDir>
</configuration>
<executions>
<!-- Distribute static resources -->
<execution>
<id>upload-static-resources</id>
<phase>deploy</phase>
<goals>
<goal>upload</goal>
</goals>
<configuration>
<fromDir>${project.build.directory}/static-resources</fromDir>
<includes>**</includes>
<serverId>static-dot-s2</serverId>
<url>scp://static.springsource.org</url>
<toDir>
/var/www/domains/springsource.org/www/htdocs/autorepo/docs/${dist.id}/${project.version}
</toDir>
<optimize>true</optimize>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
</profiles>
</project>

113
spring-vault-core/pom.xml Normal file
View File

@@ -0,0 +1,113 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-parent</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-vault-core</artifactId>
<name>Spring Vault Core</name>
<description>Spring Vault Core Components</description>
<packaging>jar</packaging>
<url>http://projects.spring.io/spring-vault/</url>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.client.VaultResponseEntity;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
/**
* AppId implementation of {@link ClientAuthentication}. {@link AppIdAuthentication} uses a configured
* {@link AppIdUserIdMechanism} to obtain or calculate a UserId. AppId and UserId are sent in the login request to Vault
* to obtain a {@link VaultToken}.
*
* @author Mark Paluch
* @see AppIdAuthenticationOptions
* @see VaultClient
* @see <a href="https://www.vaultproject.io/docs/auth/app-id.html">Auth Backend: App ID</a>
*/
public class AppIdAuthentication implements ClientAuthentication {
private final static Logger logger = LoggerFactory.getLogger(AppIdAuthentication.class);
private final AppIdAuthenticationOptions options;
private final VaultClient vaultClient;
/**
* Creates a {@link AppIdAuthentication} using {@link AppIdAuthenticationOptions} and {@link VaultClient}.
*
* @param options must not be {@literal null}.
* @param vaultClient must not be {@literal null}.
*/
public AppIdAuthentication(AppIdAuthenticationOptions options, VaultClient vaultClient) {
Assert.notNull(options, "AppIdAuthenticationOptions must not be null");
Assert.notNull(vaultClient, "VaultClient must not be null");
this.options = options;
this.vaultClient = vaultClient;
}
@Override
public VaultToken login() {
return createTokenUsingAppId();
}
private VaultToken createTokenUsingAppId() {
Map<String, String> login = getAppIdLogin(options.getAppId(), options.getUserIdMechanism().createUserId());
VaultResponseEntity<VaultResponse> entity = vaultClient
.postForEntity(String.format("auth/%s/login", options.getPath()), login, VaultResponse.class);
if (!entity.isSuccessful()) {
throw new VaultException(String.format("Cannot login using app-id: %s", entity.getMessage()));
}
VaultResponse body = entity.getBody();
String token = (String) body.getAuth().get("client_token");
logger.debug("Login successful using AppId authentication");
return VaultToken.of(token, body.getLeaseDuration());
}
private Map<String, String> getAppIdLogin(String appId, String userId) {
Map<String, String> login = new HashMap<String, String>();
login.put("app_id", appId);
login.put("user_id", userId);
return login;
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import org.springframework.util.Assert;
/**
* Authentication options for {@link AppIdAuthentication}. Authentication options provide the path, appId and a
* {@link AppIdUserIdMechanism}. {@link AppIdAuthentication} can be constructed using {@link #builder()}.
*
* @author Mark Paluch
* @see AppIdUserIdMechanism
* @see #builder()
*/
public class AppIdAuthenticationOptions {
public final static String DEFAULT_APPID_AUTHENTICATION_PATH = "app-id";
private final String path;
private final String appId;
private final AppIdUserIdMechanism userIdMechanism;
private AppIdAuthenticationOptions(String path, String appId, AppIdUserIdMechanism userIdMechanism) {
this.path = path;
this.appId = appId;
this.userIdMechanism = userIdMechanism;
}
/**
* @return a new {@link AppIdAuthenticationOptionsBuilder}.
*/
public static AppIdAuthenticationOptionsBuilder builder() {
return new AppIdAuthenticationOptionsBuilder();
}
/**
* @return the mount path.
*/
public String getPath() {
return path;
}
/**
* @return the AppId.
*/
public String getAppId() {
return appId;
}
/**
* @return the {@link AppIdUserIdMechanism}.
*/
public AppIdUserIdMechanism getUserIdMechanism() {
return userIdMechanism;
}
/**
* Builder for {@link AppIdAuthenticationOptions}.
*/
public static class AppIdAuthenticationOptionsBuilder {
private String path = DEFAULT_APPID_AUTHENTICATION_PATH;
private String appId;
private AppIdUserIdMechanism userIdMechanism;
AppIdAuthenticationOptionsBuilder() {}
/**
* Configures the mount path.
*
* @param path must not be empty or {@literal null}.
* @return {@code this} {@link AppIdAuthenticationOptionsBuilder}.
* @see #DEFAULT_APPID_AUTHENTICATION_PATH
*/
public AppIdAuthenticationOptionsBuilder path(String path) {
Assert.hasText(path, "Path must not be empty");
this.path = path;
return this;
}
/**
* Configures the AppId.
*
* @param appId must not be empty or {@literal null}.
* @return {@code this} {@link AppIdAuthenticationOptionsBuilder}.
*/
public AppIdAuthenticationOptionsBuilder appId(String appId) {
Assert.hasText(path, "AppId must not be empty");
this.appId = appId;
return this;
}
/**
* Configures the {@link AppIdUserIdMechanism}.
*
* @param userIdMechanism must not be {@literal null}.
* @return {@code this} {@link AppIdAuthenticationOptionsBuilder}.
*/
public AppIdAuthenticationOptionsBuilder userIdMechanism(AppIdUserIdMechanism userIdMechanism) {
Assert.notNull(userIdMechanism, "AppIdUserIdMechanism must not be null");
this.userIdMechanism = userIdMechanism;
return this;
}
/**
* Builds a new {@link AppIdAuthenticationOptions} instance. Requires {@link #userIdMechanism(AppIdUserIdMechanism)}
* to be configured.
*
* @return a new {@link AppIdAuthenticationOptions}.
*/
public AppIdAuthenticationOptions build() {
Assert.hasText(path, "AppId must not be empty");
Assert.notNull(userIdMechanism, "AppIdUserIdMechanism must not be null");
return new AppIdAuthenticationOptions(path, appId, userIdMechanism);
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
/**
* Interface to obtain a UserId for AppId authentication.
*
* @author Mark Paluch
*/
public interface AppIdUserIdMechanism {
/**
* Creates a UserId for AppId authentication.
*
* @return the UserId.
*/
String createUserId();
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.client.VaultResponseEntity;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
/**
* AWS-EC2 login implementation. AWS-EC2 login uses the EC2 identity document and a nonce to login into Vault. AWS-EC2
* login obtains the PKCS#7 signed EC2 identity document and generates a {@link #createNonce() nonce}.
*
* @author Mark Paluch
* @see AwsEc2AuthenticationOptions
* @see <a href="https://www.vaultproject.io/docs/auth/aws-ec2.html">Auth Backend: aws-ec2</a>
*/
public class AwsEc2Authentication implements ClientAuthentication {
private final static Logger logger = LoggerFactory.getLogger(AwsEc2Authentication.class);
private final AwsEc2AuthenticationOptions options;
private final VaultClient vaultClient;
private final RestTemplate restTemplate;
private final AtomicReference<char[]> nonce = new AtomicReference<char[]>();
/**
* Creates a new {@link AwsEc2Authentication}.
*
* @param vaultClient must not be {@literal null}.
*/
public AwsEc2Authentication(VaultClient vaultClient) {
this(AwsEc2AuthenticationOptions.DEFAULT, vaultClient, vaultClient.getRestTemplate());
}
/**
* Creates a new {@link AwsEc2Authentication} specifying {@link AwsEc2AuthenticationOptions}, {@link VaultClient} and
* a {@link RestTemplate}.
*
* @param options must not be {@literal null}.
* @param vaultClient must not be {@literal null}.
* @param restTemplate must not be {@literal null}.
*/
public AwsEc2Authentication(AwsEc2AuthenticationOptions options, VaultClient vaultClient, RestTemplate restTemplate) {
Assert.notNull(options, "AwsEc2AuthenticationOptions must not be null");
Assert.notNull(vaultClient, "VaultEndpoint must not be null");
Assert.notNull(restTemplate, "RestTemplate must not be null");
this.options = options;
this.vaultClient = vaultClient;
this.restTemplate = restTemplate;
}
@Override
public VaultToken login() throws VaultException {
return createTokenUsingAwsEc2();
}
@SuppressWarnings("unchecked")
private VaultToken createTokenUsingAwsEc2() {
String path = String.format("auth/%s/login", options.getPath());
Map<String, String> login = getEc2Login();
VaultResponseEntity<VaultResponse> entity = this.vaultClient.postForEntity(path, login, VaultResponse.class);
if (!entity.isSuccessful()) {
throw new VaultException(String.format("Cannot login using AWS-EC2: %s", entity.getMessage()));
}
VaultResponse body = entity.getBody();
String token = (String) body.getAuth().get("client_token");
if (logger.isDebugEnabled()) {
if (body.getAuth().get("metadata") instanceof Map) {
Map<Object, Object> metadata = (Map<Object, Object>) body.getAuth().get("metadata");
logger.debug(String.format("Login successful using AWS-EC2 authentication for instance %s, AMI %s",
metadata.get("instance_id"), metadata.get("instance_id")));
} else {
logger.debug("Login successful using AWS-EC2 authentication");
}
}
return VaultToken.of(token, body.getLeaseDuration());
}
protected Map<String, String> getEc2Login() {
Map<String, String> login = new HashMap<String, String>();
if (StringUtils.hasText(options.getRole())) {
login.put("role", options.getRole());
}
if (this.nonce.get() == null) {
this.nonce.compareAndSet(null, createNonce());
}
login.put("nonce", new String(this.nonce.get()));
try {
String pkcs7 = restTemplate.getForObject(options.getIdentityDocumentUri(), String.class);
if (StringUtils.hasText(pkcs7)) {
login.put("pkcs7", pkcs7.replaceAll("\\r", "").replace("\\n", ""));
}
return login;
} catch (RestClientException e) {
throw new VaultException(
String.format("Cannot obtain Identity Document from %s", options.getIdentityDocumentUri()), e);
}
}
protected char[] createNonce() {
return UUID.randomUUID().toString().toCharArray();
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import java.net.URI;
import org.springframework.util.Assert;
/**
* Authentication options for {@link AwsEc2Authentication}. Authentication options provide the path, the Identity
* Document URI and an optional role. {@link AwsEc2AuthenticationOptions} can be constructed using {@link #builder()}.
*
* @author Mark Paluch
* @see AwsEc2Authentication
* @see #builder()
*/
public class AwsEc2AuthenticationOptions {
public final static URI DEFAULT_PKCS7_IDENTITY_DOCUMENT_URI = URI
.create("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7");
public final static String DEFAULT_AWS_AUTHENTICATION_PATH = "aws-ec2";
/**
* Default {@link AwsEc2AuthenticationOptions} using {@link #DEFAULT_AWS_AUTHENTICATION_PATH} and
* {@link #DEFAULT_PKCS7_IDENTITY_DOCUMENT_URI}.
*/
public final static AwsEc2AuthenticationOptions DEFAULT = new AwsEc2AuthenticationOptions();
private final String path;
private final URI identityDocumentUri;
private final String role;
private AwsEc2AuthenticationOptions() {
this(DEFAULT_AWS_AUTHENTICATION_PATH, DEFAULT_PKCS7_IDENTITY_DOCUMENT_URI, "");
}
private AwsEc2AuthenticationOptions(String path, URI identityDocumentUri, String role) {
this.path = path;
this.identityDocumentUri = identityDocumentUri;
this.role = role;
}
/**
* @return a new {@link AwsEc2AuthenticationOptionsBuilder}.
*/
public static AwsEc2AuthenticationOptionsBuilder builder() {
return new AwsEc2AuthenticationOptionsBuilder();
}
/**
* @return the mount path.
*/
public String getPath() {
return path;
}
/**
* @return the {@link URI} to the Identity Document.
*/
public URI getIdentityDocumentUri() {
return identityDocumentUri;
}
/**
* @return the role, may be {@literal null}.
*/
public String getRole() {
return role;
}
public static class AwsEc2AuthenticationOptionsBuilder {
private String path = DEFAULT_AWS_AUTHENTICATION_PATH;
private URI identityDocumentUri = DEFAULT_PKCS7_IDENTITY_DOCUMENT_URI;
private String role;
AwsEc2AuthenticationOptionsBuilder() {}
/**
* Configures the mount path.
*
* @param path must not be empty or {@literal null}.
* @return {@code this} {@link AwsEc2AuthenticationOptionsBuilder}.
*/
public AwsEc2AuthenticationOptionsBuilder path(String path) {
Assert.hasText(path, "Path must not be empty");
this.path = path;
return this;
}
/**
* Configures the Identity Document {@link URI}.
*
* @param identityDocumentUri must not be empty or {@literal null}.
* @return {@code this} {@link AwsEc2AuthenticationOptionsBuilder}.
* @see #DEFAULT_PKCS7_IDENTITY_DOCUMENT_URI
*/
public AwsEc2AuthenticationOptionsBuilder identityDocumentUri(URI identityDocumentUri) {
this.identityDocumentUri = identityDocumentUri;
return this;
}
/**
* Configures the name of the role against which the login is being attempted.If role is not specified, then the
* login endpoint looks for a role bearing the name of the AMI ID of the EC2 instance that is trying to login.
*
* @param role may be empty or {@literal null}.
* @return {@code this} {@link AwsEc2AuthenticationOptionsBuilder}.
*/
public AwsEc2AuthenticationOptionsBuilder role(String role) {
this.role = role;
return this;
}
/**
* Builds a new {@link AwsEc2AuthenticationOptions} instance.
*
* @return a new {@link AppIdAuthenticationOptions}.
*/
public AwsEc2AuthenticationOptions build() {
Assert.hasText(path, "AppId must not be empty");
Assert.notNull(identityDocumentUri, "IdentityDocumentUri must not be null");
return new AwsEc2AuthenticationOptions(path, identityDocumentUri, role);
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.support.VaultToken;
/**
* {@link ClientAuthentication} provides {@link VaultToken} to be used for authenticated Vault access. Implementing
* classes usually use a login method to login and return a {@link VaultToken} when implementing {@link #login()}.
*
* @author Mark Paluch
*/
public interface ClientAuthentication {
/**
* Return a {@link VaultToken}. This method can optionally log into Vault to obtain a {@link VaultToken token}.
*
* @return a {@link VaultToken}.
*/
public VaultToken login() throws VaultException;
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.client.VaultResponseEntity;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
/**
* TLS Client Certificate {@link ClientAuthentication}.
*
* @author Mark Paluch
*/
public class ClientCertificateAuthentication implements ClientAuthentication {
private final static Logger logger = LoggerFactory.getLogger(ClientCertificateAuthentication.class);
private final VaultClient vaultClient;
/**
* Creates a {@link ClientCertificateAuthentication} using {@link VaultClient}.
*
* @param vaultClient must not be {@literal null}.
*/
public ClientCertificateAuthentication(VaultClient vaultClient) {
Assert.notNull(vaultClient, "VaultClient must not be null");
this.vaultClient = vaultClient;
}
@Override
public VaultToken login() {
return createTokenUsingTlsCertAuthentication("cert");
}
private VaultToken createTokenUsingTlsCertAuthentication(String path) {
VaultResponseEntity<VaultResponse> response = vaultClient.postForEntity(String.format("auth/%s/login", path),
Collections.emptyMap(), VaultResponse.class);
if (!response.isSuccessful()) {
throw new VaultException(String.format("Cannot login using TLS certificates: %s", response.getMessage()));
}
VaultResponse body = response.getBody();
String token = (String) body.getAuth().get("client_token");
logger.debug("Login successful using TLS certificates");
return VaultToken.of(token, body.getLeaseDuration());
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import org.springframework.vault.support.VaultToken;
/**
* Default implementation of {@link SessionManager}.
* <p>
* Uses a synchronized login method to log into Vault and reuse the resulting {@link VaultToken} throughout session
* lifetime.
*
* @author Mark Paluch
* @see ClientAuthentication
* @see VaultToken
*/
public class DefaultSessionManager implements SessionManager {
private final ClientAuthentication clientAuthentication;
private final Object lock = new Object();
private volatile VaultToken token;
/**
* Creates a new {@link DefaultSessionManager} using a {@link ClientAuthentication}.
*
* @param clientAuthentication must not be {@literal null}.
*/
public DefaultSessionManager(ClientAuthentication clientAuthentication) {
this.clientAuthentication = clientAuthentication;
}
@Override
public VaultToken getSessionToken() {
if (token == null) {
synchronized (lock) {
if (token == null) {
token = clientAuthentication.login();
}
}
}
return token;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import java.io.IOException;
import java.net.InetAddress;
/**
* Mechanism to generate a SHA-256 hashed and hex-encoded representation of the IP address. Can be calculated with
* {@code echo -n 192.168.99.1 | sha256sum}.
*
* @author Mark Paluch
* @see AppIdUserIdMechanism
*/
public class IpAddressUserId implements AppIdUserIdMechanism {
@Override
public String createUserId() {
try {
return Sha256.toSha256(InetAddress.getLocalHost().getHostAddress());
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Mechanism to generate a UserId based on the Mac address. {@link MacAddressUserId} creates a hex-encoded
* representation of the Mac address without any separators (0123456789AB). A network interface hint can be specified
* optionally to select a network interface (index/name).
*
* @author Mark Paluch
* @see AppIdUserIdMechanism
*/
public class MacAddressUserId implements AppIdUserIdMechanism {
private final Logger log = LoggerFactory.getLogger(MacAddressUserId.class);
private final String networkInterfaceHint;
/**
* Creates a new {@link MacAddressUserId} using the {@link NetworkInterface} from the
* {@link InetAddress#getLocalHost()}.
*/
public MacAddressUserId() {
this("");
}
/**
* Creates a new {@link MacAddressUserId} using a {@code networkInterfaceIndex}. The index is applied to
* {@link NetworkInterface#getNetworkInterfaces()} to obtain the desired network interface.
*
* @param networkInterfaceIndex must be greater or equal to zero.
*/
public MacAddressUserId(int networkInterfaceIndex) {
Assert.isTrue(networkInterfaceIndex >= 0, "NetworkInterfaceIndex must be greater or equal to 0");
this.networkInterfaceHint = "" + networkInterfaceIndex;
}
/**
* Creates a new {@link MacAddressUserId} using a {@code networkInterfaceName}. This name is compared with
* {@link NetworkInterface#getName()} and {@link NetworkInterface#getDisplayName()} to obtain the desired network
* interface.
*
* @param networkInterfaceName must not be {@literal null}.
*/
public MacAddressUserId(String networkInterfaceName) {
Assert.notNull(networkInterfaceName, "NetworkInterfaceName must not be null");
this.networkInterfaceHint = networkInterfaceName;
}
@Override
public String createUserId() {
try {
NetworkInterface networkInterface = null;
List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
if (StringUtils.hasText(networkInterfaceHint)) {
try {
networkInterface = getNetworkInterface(Integer.parseInt(networkInterfaceHint), interfaces);
} catch (NumberFormatException e) {
networkInterface = getNetworkInterface((networkInterfaceHint), interfaces);
}
}
if (networkInterface == null) {
if (StringUtils.hasText(networkInterfaceHint)) {
log.warn(String.format("Did not find a NetworkInterface applying hint %s", networkInterfaceHint));
}
InetAddress localHost = InetAddress.getLocalHost();
networkInterface = NetworkInterface.getByInetAddress(localHost);
if (networkInterface == null) {
throw new IllegalStateException(String.format("Cannot determine NetworkInterface for %s", localHost));
}
}
byte[] mac = networkInterface.getHardwareAddress();
if (mac == null) {
throw new IllegalStateException(
String.format("Network interface %s has no hardware address", networkInterface.getName()));
}
return Sha256.toSha256(Sha256.toHexString(mac));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
private NetworkInterface getNetworkInterface(Number hint, List<NetworkInterface> interfaces) {
if (interfaces.size() > hint.intValue() && hint.intValue() >= 0) {
return interfaces.get(hint.intValue());
}
return null;
}
private NetworkInterface getNetworkInterface(String hint, List<NetworkInterface> interfaces) {
for (NetworkInterface anInterface : interfaces) {
if (hint.equals(anInterface.getDisplayName()) || hint.equals(anInterface.getName())) {
return anInterface;
}
}
return null;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import org.springframework.vault.support.VaultToken;
/**
* Strategy interface that encapsulates the creation and management of Vault sessions based on {@link VaultToken}.
* <p>
* {@link SessionManager} is used by {@link org.springframework.vault.core.VaultTemplate} to initiate a session.
* Implementing classes usually use {@link ClientAuthentication} to log into Vault and obtain tokens.
*
* @author Mark Paluch
* @see DefaultSessionManager
* @see ClientAuthentication
*/
public interface SessionManager {
/**
* Obtain a session token.
*
* @return a session token.
*/
VaultToken getSessionToken();
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.springframework.util.Assert;
/**
* Utility to generate a SHA 256 checksum.
*
* @author Mark Paluch
*/
class Sha256 {
private static final Charset US_ASCII = Charset.forName("US-ASCII");
/**
* Generates a hex-encoded SHA256 checksum from the supplied {@code content}.
*
* @param content must not be {@literal null} and not empty.
* @return hex-encoded SHA256 checksum
*/
public static String toSha256(String content) {
Assert.hasText(content, "Content must not be empty");
MessageDigest messageDigest = getMessageDigest("SHA-256");
byte[] digest = messageDigest.digest(content.getBytes(US_ASCII));
return toHexString(digest);
}
/**
* Get a MessageDigest instance for the given algorithm. Throws an IllegalArgumentException if <i>algorithm</i> is
* unknown
*
* @return MessageDigest instance
* @throws IllegalArgumentException if NoSuchAlgorithmException is thrown
*/
private static MessageDigest getMessageDigest(String algorithm) throws IllegalArgumentException {
try {
return MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("No such algorithm [" + algorithm + "]");
}
}
static String toHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
sb.append(String.format("%X", bytes[i]));
}
return sb.toString();
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import org.springframework.util.Assert;
/**
* A static UserId.
*
* @author Mark Paluch
* @see AppIdUserIdMechanism
*/
public class StaticUserId implements AppIdUserIdMechanism {
private final String userId;
/**
* Creates a new {@link StaticUserId} for a given {@code userId}.
*
* @param userId must not be empty or {@literal null}.
*/
public StaticUserId(String userId) {
Assert.hasText(userId, "UserId must not be empty");
this.userId = userId;
}
@Override
public String createUserId() {
return userId;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import org.springframework.util.Assert;
import org.springframework.vault.support.VaultToken;
/**
* Static Token-based {@link ClientAuthentication} method.
*
* @author Mark Paluch
* @see VaultToken
* @see <a href="https://www.vaultproject.io/docs/auth/token.html">Auth Backend: Token</a>
*/
public class TokenAuthentication implements ClientAuthentication {
private final VaultToken token;
/**
* Creates a new {@link TokenAuthentication} with a static {@code token}.
*
* @param token the Vault token, must not be empty or {@literal null}.
*/
public TokenAuthentication(String token) {
Assert.hasText(token, "Token must not be empty");
this.token = VaultToken.of(token);
}
/**
* Creates a new {@link TokenAuthentication} with a static {@code token}.
*
* @param token the Vault token, must not be {@literal null}.
*/
public TokenAuthentication(VaultToken token) {
Assert.notNull(token, "Token must not be null");
this.token = token;
}
@Override
public VaultToken login() {
return token;
}
}

View File

@@ -0,0 +1,5 @@
/**
* Support for authentication and session management.
*/
package org.springframework.vault.authentication;

View File

@@ -0,0 +1,136 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.client;
import java.net.URI;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
/**
* Base class for Vault accessing helpers, defining common properties such as the {@link RestTemplate} to operate on.
* <p>
* Not intended to be used directly. See {@link VaultClient}.
*
* @author Spencer Gibb
* @author Mark Paluch
*/
public abstract class VaultAccessor {
private final RestTemplate restTemplate;
/**
* Create a {@link VaultAccessor} with a {@link RestTemplate}.
*
* @param restTemplate must not be {@literal null}.
*/
protected VaultAccessor(RestTemplate restTemplate) {
Assert.notNull(restTemplate, "RestTemplate must not be null");
this.restTemplate = restTemplate;
}
/**
* Execute a {@link RestTemplateCallback} in the scope of the {@link RestTemplate}.
*
* @param uri must not be {@literal null}.
* @param callback must not be {@literal null}.
* @param <T> return type
* @return the {@link RestTemplateCallback} return value.
*/
protected <T> T doWithRestTemplate(URI uri, RestTemplateCallback<T> callback) {
Assert.notNull(uri, "URI must not be null");
Assert.notNull(callback, "RestTemplateCallback must not be null");
return callback.doWithRestTemplate(uri, getRestTemplate());
}
public <T, S extends T> VaultResponseEntity<S> exchange(URI uri, HttpMethod httpMethod, HttpEntity<?> httpEntity,
Class<T> returnType) {
Assert.notNull(uri, "URI must not be null");
Assert.notNull(httpMethod, "HttpMethod must not be null");
Assert.notNull(returnType, "Return type must not be null");
try {
ResponseEntity<T> response = this.getRestTemplate().exchange(uri, httpMethod, httpEntity, returnType);
return new VaultResponseEntity<S>((S) response.getBody(), response.getStatusCode(), uri,
response.getStatusCode().getReasonPhrase());
} catch (HttpStatusCodeException e) {
return handleCodeException(uri, e);
}
}
public <T, S extends T> VaultResponseEntity<S> exchange(URI uri, HttpMethod httpMethod, HttpEntity<?> httpEntity,
ParameterizedTypeReference<T> returnType) {
Assert.notNull(uri, "URI must not be null");
Assert.notNull(httpMethod, "HttpMethod must not be null");
Assert.notNull(returnType, "Return type must not be null");
try {
ResponseEntity<T> response = this.getRestTemplate().exchange(uri, httpMethod, httpEntity, returnType);
return new VaultResponseEntity<S>((S) response.getBody(), response.getStatusCode(), uri,
response.getStatusCode().getReasonPhrase());
} catch (HttpStatusCodeException e) {
return handleCodeException(uri, e);
}
}
private <T> VaultResponseEntity<T> handleCodeException(URI uri, HttpStatusCodeException e) {
String message = e.getResponseBodyAsString();
if (MediaType.APPLICATION_JSON.includes(e.getResponseHeaders().getContentType())) {
message = VaultErrorMessage.getError(message);
}
return new VaultResponseEntity<T>(null, e.getStatusCode(), uri, message);
}
/**
* @return the underlying {@link RestTemplate}.
*/
public RestTemplate getRestTemplate() {
return restTemplate;
}
/**
* A callback for executing arbitrary operations on the {@link RestTemplate}.
*
* @author Mark Paluch
*/
public interface RestTemplateCallback<T> {
/**
* @param uri must not be {@literal null}.
* @param restTemplate must not be {@literal null}.
* @return a result object or null if none.
*/
T doWithRestTemplate(URI uri, RestTemplate restTemplate);
}
}

View File

@@ -0,0 +1,276 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.client;
import java.net.URI;
import java.util.Map;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
/**
* Implementation of the low-level Vault client. This client uses the Vault HTTP API to issue requests using different
* {@link HttpMethod HTTP methods}. {@link VaultClient} is configured with an {@link VaultEndpoint} and
* {@link RestTemplate}. It does not maintain any session or token state. See {@link VaultTemplate} and
* {@link org.springframework.vault.authentication.SessionManager} for authenticated and stateful Vault access.
* <p>
* {@link VaultClient} encapsulates base URI and path construction and uses {@link VaultAccessor} for request and error
* handling by returning {@link VaultResponseEntity} for requests.
*
* @author Mark Paluch
* @see VaultResponseEntity
* @see VaultTemplate
*/
public class VaultClient extends VaultAccessor {
public static final String VAULT_TOKEN = "X-Vault-Token";
private final VaultEndpoint endpoint;
/**
* Creates a new {@link VaultClient} with a default a {@link RestTemplate} and {@link VaultEndpoint}.
*
* @see VaultEndpoint
*/
public VaultClient() {
this(new RestTemplate(), new VaultEndpoint());
}
/**
* Creates a new {@link VaultClient} for a {@link RestTemplate} and {@link VaultEndpoint}.
*
* @param restTemplate must not be {@literal null}.
* @param endpoint must not be {@literal null}.
*/
public VaultClient(RestTemplate restTemplate, VaultEndpoint endpoint) {
super(restTemplate);
Assert.notNull(endpoint, "VaultEndpoint must not be null");
this.endpoint = endpoint;
}
/**
* Retrieve a resource by GETting from the path, and returns the response as {@link VaultResponseEntity}.
*
* @param path the path.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
public <T, S extends T> VaultResponseEntity<S> getForEntity(String path, Class<T> responseType) {
return exchange(path, HttpMethod.GET, new HttpEntity<Object>(null), responseType, null);
}
/**
* Retrieve a resource by GETting from the path, and returns the response as {@link VaultResponseEntity}.
*
* @param path the path.
* @param vaultToken the {@link VaultToken}.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
public <T, S extends T> VaultResponseEntity<S> getForEntity(String path, VaultToken vaultToken,
Class<T> responseType) {
return exchange(path, HttpMethod.GET, new HttpEntity<Object>(null, createHeaders(vaultToken)), responseType, null);
}
/**
* Issue a POST request using the given object to the path, and returns the response as {@link VaultResponseEntity}.
*
* @param path the path.
* @param request the Object to be POSTed, may be {@code null}.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
public <T, S extends T> VaultResponseEntity<S> postForEntity(String path, Object request, Class<T> responseType) {
return exchange(path, HttpMethod.POST, new HttpEntity<Object>(request), responseType, null);
}
/**
* Issue a POST request using the given object to the path, and returns the response as {@link VaultResponseEntity}.
*
* @param path the path.
* @param vaultToken the {@link VaultToken}.
* @param request the Object to be POSTed, may be {@code null}.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
public <T, S extends T> VaultResponseEntity<S> postForEntity(String path, VaultToken vaultToken, Object request,
Class<T> responseType) {
return exchange(path, HttpMethod.POST, new HttpEntity<Object>(request, createHeaders(vaultToken)), responseType,
null);
}
/**
* Create a new resource by PUTting the given object to the path, and returns the response as
* {@link VaultResponseEntity}.
*
* @param path the path.
* @param request the Object to be PUT.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
public <T, S extends T> VaultResponseEntity<S> putForEntity(String path, Object request, Class<T> responseType) {
return exchange(path, HttpMethod.PUT, new HttpEntity<Object>(request), responseType, null);
}
/**
* Create a new resource by PUTting the given object to the path, and returns the response as
* {@link VaultResponseEntity}.
*
* @param path the path.
* @param vaultToken the {@link VaultToken}.
* @param request the Object to be PUT.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
public <T, S extends T> VaultResponseEntity<S> putForEntity(String path, VaultToken vaultToken, Object request,
Class<T> responseType) {
return exchange(path, HttpMethod.PUT, new HttpEntity<Object>(request, createHeaders(vaultToken)), responseType,
null);
}
/**
* Delete a resource by DELETEing from the path, and returns the response as {@link VaultResponseEntity}.
*
* @param path the path.
* @param vaultToken the {@link VaultToken}.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
public <T, S extends T> VaultResponseEntity<S> deleteForEntity(String path, VaultToken vaultToken,
Class<T> responseType) {
return exchange(path, HttpMethod.DELETE, new HttpEntity<Object>(null, createHeaders(vaultToken)), responseType,
null);
}
/**
* Execute the HTTP method to the given URI template, writing the given request entity to the request, and returns the
* response as {@link VaultResponseEntity}.
* <p>
* URI Template variables are using the given URI variables, if any.
*
* @param pathTemplate the path template.
* @param method the HTTP method (GET, POST, etc).
* @param requestEntity the entity (headers and/or body) to write to the request, may be {@code null}.
* @param responseType the type of the return value.
* @param uriVariables the variables to expand in the template.
* @return the response as entity.
*/
public <T, S extends T> VaultResponseEntity<S> exchange(String pathTemplate, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
URI uri = uriVariables != null ? buildUri(pathTemplate, uriVariables) : getEndpoint().createUri(pathTemplate);
return exchange(uri, method, requestEntity, responseType);
}
/**
* Execute the HTTP method to the given path template, writing the given request entity to the request, and returns
* the response as {@link VaultResponseEntity}. The given {@link ParameterizedTypeReference} is used to pass generic
* type information:
*
* <pre class="code">
* ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean = new ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt;() {};
* ResponseEntity&lt;List&lt;MyBean&gt;&gt; response = client.exchange(&quot;http://example.com&quot;, HttpMethod.GET, null, myBean, null);
* </pre>
*
* @param pathTemplate the path template.
* @param method the HTTP method (GET, POST, etc).
* @param requestEntity the entity (headers and/or body) to write to the request, may be {@code null}.
* @param responseType the type of the return value.
* @param uriVariables the variables to expand in the template.
* @return the response as entity.
*/
public <T, S extends T> VaultResponseEntity<S> exchange(String pathTemplate, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables)
throws RestClientException {
URI uri = uriVariables != null ? buildUri(pathTemplate, uriVariables) : getEndpoint().createUri(pathTemplate);
return exchange(uri, method, requestEntity, responseType);
}
/**
* Executes a {@link RestTemplateCallback}. Allows to interact with the underlying {@link RestTemplate} and benefit
* from optional parameter expansion.
*
* @param pathTemplate the path template.
* @param uriVariables the variables to expand in the template
* @param callback the request.
* @return the {@link RestTemplateCallback} return value.
*/
public <T> T doWithRestTemplate(String pathTemplate, Map<String, ?> uriVariables, RestTemplateCallback<T> callback) {
URI uri = uriVariables != null ? buildUri(pathTemplate, uriVariables) : getEndpoint().createUri(pathTemplate);
return super.doWithRestTemplate(uri, callback);
}
/**
* @return the configured {@link VaultEndpoint}.
*/
public VaultEndpoint getEndpoint() {
return endpoint;
}
/**
* Build the Vault {@link URI} based on the given {@link VaultEndpoint} and {@code pathTemplate}. URI template
* variables will be expanded using {@code uriVariables}.
*
* @param pathTemplate must not be empty or {@literal null}.
* @param uriVariables must not be {@literal null}.
* @see org.springframework.web.util.UriComponentsBuilder
* @return
*/
protected URI buildUri(String pathTemplate, Map<String, ?> uriVariables) {
Assert.hasText(pathTemplate, "Path must not be empty");
return getRestTemplate().getUriTemplateHandler().expand(getEndpoint().createUriString(pathTemplate), uriVariables);
}
/**
* Create {@link HttpHeaders} for a {@link VaultToken}.
*
* @param vaultToken must not be {@literal null}.
* @return {@link HttpHeaders} for a {@link VaultToken}.
*/
public static HttpHeaders createHeaders(VaultToken vaultToken) {
Assert.notNull(vaultToken, "Vault Token must not be null");
HttpHeaders headers = new HttpHeaders();
headers.add(VAULT_TOKEN, vaultToken.getToken());
return headers;
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.client;
import java.io.Serializable;
import java.net.URI;
import org.springframework.util.Assert;
import lombok.EqualsAndHashCode;
/**
* Value object that defines Vault connection coordinates.
* <p>
* A {@link VaultEndpoint} defines the hostname, TCP port and the protocol scheme (HTTP or HTTPS).
*
* @author Mark Paluch
*/
@EqualsAndHashCode
public class VaultEndpoint implements Serializable {
public static final String API_VERSION = "v1";
/**
* Vault server host.
*/
private String host = "localhost";
/**
* Vault server port.
*/
private int port = 8200;
/**
* Protocol scheme. Can be either "http" or "https".
*/
private String scheme = "https";
/**
* @return the hostname.
*/
public String getHost() {
return host;
}
/**
* Sets the hostname.
*
* @param host must not be empty or {@literal null}.
*/
public void setHost(String host) {
this.host = host;
}
/**
* @return the port.
*/
public int getPort() {
return port;
}
/**
* @param port must be a valid port in the range of 1-65535
*/
public void setPort(int port) {
Assert.isTrue(port >= 1 && port <= 65535, "Port must be a valid port in the range between 1 and 65535");
this.port = port;
}
/**
* @return the protocol scheme.
*/
public String getScheme() {
return scheme;
}
/**
* @param scheme must be {@literal http} or {@literal https}.
*/
public void setScheme(String scheme) {
Assert.isTrue("http".equals(scheme) || "https".equals(scheme), "Scheme must be http or https");
this.scheme = scheme;
}
/**
* Build the Vault {@link URI} based on the given {@code path}.
*
* @param path must not be empty or {@literal null}.
* @return constructed {@link URI}.
*/
public URI createUri(String path) {
return URI.create(createUriString(path));
}
/**
* Build the Vault URI string based on the given {@code path}.
*
* @param path must not be empty or {@literal null}.
* @return constructed URI String.
*/
public String createUriString(String path) {
Assert.hasText(path, "Path must not be empty");
return String.format("%s://%s:%s/%s/%s", getScheme(), getHost(), getPort(), API_VERSION, path);
}
@Override
public String toString() {
return String.format("%://%:%d", scheme, host, port);
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.client;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Utility to obtain a Vault error message.
*
* @author Mark Paluch
*/
class VaultErrorMessage {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
/**
* Obtain the error message from a JSON response.
*
* @param json
* @return
*/
static String getError(String json) {
if (json.contains("\"errors\":")) {
try {
Map<String, Object> map = OBJECT_MAPPER.readValue(json.getBytes(),
Map.class);
if (map.containsKey("errors")) {
Collection<String> errors = (Collection<String>) map.get("errors");
if (errors.size() == 1) {
return errors.iterator().next();
}
return errors.toString();
}
}
catch (IOException o_O) {
// ignore
}
}
return json;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.client;
import org.springframework.core.NestedRuntimeException;
/**
* The Spring Vault specific {@link NestedRuntimeException} implementation.
*
* @author Mark Paluch
*/
@SuppressWarnings("serial")
public class VaultException extends NestedRuntimeException {
/**
* Creates a {@code VaultException} with the specified detail message.
*
* @param msg the detail message
*/
public VaultException(String msg) {
super(msg);
}
/**
* Creates a {@code NestedRuntimeException} with the specified detail message and nested exception.
*
* @param msg the detail message
* @param cause the nested exception
*/
public VaultException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.client;
import java.net.URI;
import org.springframework.http.HttpStatus;
/**
* Encapsulates the client response used in {@link VaultAccessor}. Consists of the body, status code the location and a
* message. The {@code body} is empty for all non-successful results. This class is immutable.
*
* @author Mark Paluch
*/
public class VaultResponseEntity<T> {
private final T body;
private final HttpStatus statusCode;
private final URI uri;
private final String message;
VaultResponseEntity(T body, HttpStatus statusCode, URI uri, String message) {
this.body = body;
this.statusCode = statusCode;
this.uri = uri;
this.message = message;
}
/**
* @return {@literal true} if the request was completed successfully.
*/
public boolean isSuccessful() {
return statusCode.is2xxSuccessful();
}
/**
* @return {@literal true} if the request returned a body.
*/
public boolean hasBody() {
return body != null;
}
/**
* @return the body of this entity.
*/
public T getBody() {
return body;
}
/**
* @return the {@link HttpStatus} of this entity.
*/
public HttpStatus getStatusCode() {
return statusCode;
}
/**
* @return the request {@link URI} of this entity.
*/
public URI getUri() {
return uri;
}
/**
* @return the message of this entity. {@literal null} for successful responses but provided usually when the response
* yielded an error.
*/
public String getMessage() {
return message;
}
}

View File

@@ -0,0 +1,5 @@
/**
* Spring Vault Client abstraction.
*/
package org.springframework.vault.client;

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.config;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.util.Assert;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.DefaultSessionManager;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.DefaultVaultClientFactory;
import org.springframework.vault.core.VaultClientFactory;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.web.client.RestTemplate;
/**
* Base class for Spring Vault configuration using JavaConfig.
*
* @author Spencer Gibb
* @author Mark Paluch
*/
@Configuration
public abstract class AbstractVaultConfiguration {
/**
* @return Vault endpoint coordinates for HTTP/HTTPS communication, must not be {@literal null}.
*/
public abstract VaultEndpoint vaultEndpoint();
/**
* Annotate with {@link Bean} in case you want to expose a {@link ClientAuthentication} instance to the
* {@link org.springframework.context.ApplicationContext}.
*
* @return the {@link ClientAuthentication} to use. Must not be {@literal null}.
*/
public abstract ClientAuthentication clientAuthentication();
/**
* Annotate with {@link Bean} in case you want to expose a {@link SessionManager} instance to the
* {@link org.springframework.context.ApplicationContext}.
*
* @return the {@link SessionManager} for Vault session management.
* @see SessionManager
* @see DefaultSessionManager
*/
public SessionManager sessionManager() {
ClientAuthentication clientAuthentication = clientAuthentication();
Assert.notNull(clientAuthentication, "ClientAuthentication must not be null");
return new DefaultSessionManager(clientAuthentication);
}
/**
* @return {@link ClientOptions} to configure communication parameters.
* @see ClientOptions
*/
public ClientOptions clientOptions() {
return new ClientOptions();
}
/**
* @return SSL configuration options. Defaults to {@link SslConfiguration#NONE}.
* @see SslConfiguration
* @see SslConfiguration#NONE
*/
public SslConfiguration sslConfiguration() {
return SslConfiguration.NONE;
}
/**
* Creates a {@link ClientFactoryWrapper} containing a {@link ClientHttpRequestFactory}.
* {@link ClientHttpRequestFactory} is not exposed as root bean because {@link ClientHttpRequestFactory} is configured
* with {@link ClientOptions} and {@link SslConfiguration} which are not necessarily applicable for the whole
* application.
*
* @return the {@link ClientFactoryWrapper} to wrap a {@link ClientHttpRequestFactory} instance.
* @see #clientOptions()
* @see #sslConfiguration()
*/
@Bean
public ClientFactoryWrapper clientHttpRequestFactoryWrapper() {
return new ClientFactoryWrapper(ClientHttpRequestFactoryFactory.create(clientOptions(), sslConfiguration()));
}
/**
* @return the {@link VaultClient}
* @see #clientHttpRequestFactoryWrapper()
* @see #vaultEndpoint()
*/
@Bean
public VaultClient vaultClient() {
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactoryWrapper().getClientHttpRequestFactory());
return new VaultClient(restTemplate, vaultEndpoint());
}
/**
* Creates the {@link VaultClientFactory} to be used with {@link VaultTemplate}. Uses by default
* {@link DefaultVaultClientFactory} with the configured {@link #vaultClient()} instance.
*
* @return
*/
@Bean
public VaultClientFactory vaultClientFactory() {
return new DefaultVaultClientFactory(vaultClient());
}
/**
* Creates a {@link VaultTemplate}.
*
* @return
* @see #vaultClientFactory()
* @see #sessionManager()
*/
@Bean
public VaultTemplate vaultTemplate() {
return new VaultTemplate(vaultClientFactory(), sessionManager());
}
/**
* Wrapper for {@link ClientHttpRequestFactory} to not expose the bean globally.
*/
public static class ClientFactoryWrapper implements InitializingBean, DisposableBean {
private final ClientHttpRequestFactory clientHttpRequestFactory;
public ClientFactoryWrapper(ClientHttpRequestFactory clientHttpRequestFactory) {
this.clientHttpRequestFactory = clientHttpRequestFactory;
}
@Override
public void destroy() throws Exception {
if (clientHttpRequestFactory instanceof DisposableBean) {
((DisposableBean) clientHttpRequestFactory).destroy();
}
}
@Override
public void afterPropertiesSet() throws Exception {
if (clientHttpRequestFactory instanceof InitializingBean) {
((InitializingBean) clientHttpRequestFactory).afterPropertiesSet();
}
}
public ClientHttpRequestFactory getClientHttpRequestFactory() {
return clientHttpRequestFactory;
}
}
}

View File

@@ -0,0 +1,282 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.config;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
import org.springframework.http.client.OkHttpClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.squareup.okhttp.OkHttpClient;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
/**
* Factory for {@link ClientHttpRequestFactory} that supports Apache HTTP Components, OkHttp, Netty and the JDK HTTP
* client (in that order). This factory configures a {@link ClientHttpRequestFactory} depending on the available
* dependencies.
*
* @author Mark Paluch
*/
public class ClientHttpRequestFactoryFactory {
private static final Logger logger = LoggerFactory.getLogger(ClientHttpRequestFactoryFactory.class);
private static final boolean HTTP_COMPONENTS_PRESENT = ClassUtils.isPresent("org.apache.http.client.HttpClient",
ClientHttpRequestFactoryFactory.class.getClassLoader());
private static final boolean OKHTTP_PRESENT = ClassUtils.isPresent("com.squareup.okhttp.OkHttpClient",
ClientHttpRequestFactoryFactory.class.getClassLoader());
private static final boolean NETTY_PRESENT = ClassUtils.isPresent("io.netty.channel.nio.NioEventLoopGroup",
ClientHttpRequestFactoryFactory.class.getClassLoader());
/**
* Creates a {@link ClientHttpRequestFactory} for the given {@link ClientOptions} and {@link SslConfiguration}.
*
* @param options must not be {@literal null}
* @param sslConfiguration must not be {@literal null}
* @return a new {@link ClientHttpRequestFactory}. Lifecycle beans must be initialized after obtaining.
*/
public static ClientHttpRequestFactory create(ClientOptions options, SslConfiguration sslConfiguration) {
Assert.notNull(options, "ClientOptions must not be null");
Assert.notNull(sslConfiguration, "SslConfiguration must not be null");
try {
if (HTTP_COMPONENTS_PRESENT) {
return HttpComponents.usingHttpComponents(options, sslConfiguration);
}
if (OKHTTP_PRESENT) {
return OkHttp.usingOkHttp(options, sslConfiguration);
}
if (NETTY_PRESENT) {
return Netty.usingNetty(options, sslConfiguration);
}
} catch (GeneralSecurityException e) {
throw new IllegalStateException(e);
} catch (IOException e) {
throw new IllegalStateException(e);
}
if (hasSslConfiguration(sslConfiguration)) {
logger.warn("VaultProperties has SSL configured but the SSL configuration "
+ "must be applied outside the Vault Client to use the JDK HTTP client");
}
return new SimpleClientHttpRequestFactory();
}
private static SSLContext getSSLContext(SslConfiguration sslConfiguration)
throws GeneralSecurityException, IOException {
KeyManager[] keyManagers = sslConfiguration.getKeyStore() != null
? createKeyManagerFactory(sslConfiguration.getKeyStore(), sslConfiguration.getKeyStorePassword())
.getKeyManagers()
: null;
TrustManager[] trustManagers = sslConfiguration.getTrustStore() != null
? createTrustManagerFactory(sslConfiguration.getTrustStore(), sslConfiguration.getTrustStorePassword())
.getTrustManagers()
: null;
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
}
private static KeyManagerFactory createKeyManagerFactory(Resource keystoreFile, String storePassword)
throws GeneralSecurityException, IOException {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
loadKeyStore(keystoreFile, storePassword, keyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, StringUtils.hasText(storePassword) ? storePassword.toCharArray() : new char[0]);
return keyManagerFactory;
}
private static TrustManagerFactory createTrustManagerFactory(Resource trustFile, String storePassword)
throws GeneralSecurityException, IOException {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
loadKeyStore(trustFile, storePassword, trustStore);
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
return trustManagerFactory;
}
private static void loadKeyStore(Resource keyStoreResource, String storePassword, KeyStore keyStore)
throws IOException, NoSuchAlgorithmException, CertificateException {
InputStream inputStream = null;
try {
inputStream = keyStoreResource.getInputStream();
keyStore.load(inputStream, StringUtils.hasText(storePassword) ? storePassword.toCharArray() : null);
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
private static boolean hasSslConfiguration(SslConfiguration sslConfiguration) {
return sslConfiguration.getTrustStore() != null || sslConfiguration.getKeyStore() != null;
}
/**
* {@link ClientHttpRequestFactory} for Apache Http Components.
*
* @author Mark Paluch
*/
static class HttpComponents {
static ClientHttpRequestFactory usingHttpComponents(ClientOptions options, SslConfiguration sslConfiguration)
throws GeneralSecurityException, IOException {
HttpClientBuilder httpClientBuilder = HttpClients.custom();
if (hasSslConfiguration(sslConfiguration)) {
SSLContext sslContext = getSSLContext(sslConfiguration);
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);
httpClientBuilder.setSSLSocketFactory(sslSocketFactory);
httpClientBuilder.setSSLContext(sslContext);
}
RequestConfig requestConfig = RequestConfig.custom() //
.setConnectTimeout(options.getConnectionTimeout()) //
.setSocketTimeout(options.getReadTimeout()) //
.setAuthenticationEnabled(true) //
.build();
httpClientBuilder.setDefaultRequestConfig(requestConfig);
return new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
}
}
/**
* {@link ClientHttpRequestFactory} for the {@link OkHttpClient}.
*
* @author Mark Paluch
*/
static class OkHttp {
static ClientHttpRequestFactory usingOkHttp(ClientOptions options, SslConfiguration sslConfiguration)
throws GeneralSecurityException, IOException {
final OkHttpClient okHttpClient = new OkHttpClient();
OkHttpClientHttpRequestFactory requestFactory = new OkHttpClientHttpRequestFactory(okHttpClient) {
@Override
public void destroy() throws IOException {
if (okHttpClient.getCache() != null) {
okHttpClient.getCache().close();
}
okHttpClient.getDispatcher().getExecutorService().shutdown();
}
};
if (hasSslConfiguration(sslConfiguration)) {
okHttpClient.setSslSocketFactory(getSSLContext(sslConfiguration).getSocketFactory());
}
requestFactory.setConnectTimeout(options.getConnectionTimeout());
requestFactory.setReadTimeout(options.getReadTimeout());
return requestFactory;
}
}
/**
* {@link ClientHttpRequestFactory} for Netty.
*
* @author Mark Paluch
*/
static class Netty {
static ClientHttpRequestFactory usingNetty(ClientOptions options, SslConfiguration sslConfiguration)
throws GeneralSecurityException, IOException {
final Netty4ClientHttpRequestFactory requestFactory = new Netty4ClientHttpRequestFactory();
if (hasSslConfiguration(sslConfiguration)) {
SslContextBuilder sslContextBuilder = SslContextBuilder //
.forClient();
if (sslConfiguration.getTrustStore() != null) {
sslContextBuilder.trustManager(
createTrustManagerFactory(sslConfiguration.getTrustStore(), sslConfiguration.getTrustStorePassword()));
}
if (sslConfiguration.getKeyStore() != null) {
sslContextBuilder.keyManager(
createKeyManagerFactory(sslConfiguration.getKeyStore(), sslConfiguration.getKeyStorePassword()));
}
requestFactory.setSslContext(sslContextBuilder.sslProvider(SslProvider.JDK).build());
}
requestFactory.setConnectTimeout(options.getConnectionTimeout());
requestFactory.setReadTimeout(options.getReadTimeout());
return requestFactory;
}
}
}

View File

@@ -0,0 +1,5 @@
/**
* Spring configuration for Vault.
*/
package org.springframework.vault.config;

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import org.springframework.util.Assert;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.web.client.RestTemplate;
/**
* Default implementation of {@link VaultClientFactory}. Returns the provided {@link VaultClient}.
*
* @author Mark Paluch
*/
public class DefaultVaultClientFactory implements VaultClientFactory {
private final VaultClient vaultClient;
/**
* Creates a new {@link DefaultVaultClientFactory} returning always the same {@link VaultClient}.
*
* @param vaultClient must not be {@literal null}.
*/
public DefaultVaultClientFactory(VaultClient vaultClient) {
Assert.notNull(vaultClient, "VaultClient must not be null");
this.vaultClient = vaultClient;
}
/**
* Creates a new {@link DefaultVaultClientFactory} using a default {@link VaultClient} and {@link VaultEndpoint}. Will
* use Vault at {@code https://localhost:8200} .
*
* @see VaultClient
* @see VaultEndpoint
*/
public DefaultVaultClientFactory() {
this(new VaultClient(new RestTemplate(), new VaultEndpoint()));
}
@Override
public VaultClient getVaultClient() {
return vaultClient;
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import org.springframework.vault.client.VaultClient;
/**
* The strategy to produce a {@link VaultClient} instance(s).
*
* @author Mark Paluch
*/
public interface VaultClientFactory {
/**
* @return a {@link VaultClient}.
*/
VaultClient getVaultClient();
}

View File

@@ -0,0 +1,251 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import java.util.List;
import java.util.Map;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.vault.client.VaultAccessor.RestTemplateCallback;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultResponseEntity;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultResponseSupport;
/**
* Interface that specifies a basic set of Vault operations, implemented by {@link VaultTemplate}. This is the main
* entry point to interact with Vault in an authenticated and unauthenticated context with configured
* {@link VaultClient} instances.
* <p>
* {@link VaultOperations} resolves {@link VaultClient} instances and allows execution of callback methods on various
* levels. Callbacks can execute requests within a {@link #doWithVault(SessionCallback) session}, the
* {@link #doWithVault(ClientCallback) client (without requiring a session)} and a
* {@link #doWithRestTemplate(String, Map, RestTemplateCallback) low-level}
* {@link org.springframework.web.client.RestTemplate} level.
*
* @author Mark Paluch
* @see #doWithVault(ClientCallback)
* @see #doWithVault(SessionCallback)
* @see #doWithRestTemplate(String, Map, RestTemplateCallback)
* @see VaultClient
* @see VaultTemplate
* @see VaultTokenOperations
* @see org.springframework.vault.authentication.SessionManager
*/
public interface VaultOperations {
/**
* @return the operations interface administrative Vault access.
*/
VaultSysOperations opsForSys();
/**
* @return the operations interface to interact with Vault token.
*/
VaultTokenOperations opsForToken();
/**
* Read from a secret backend. Reading data using this method is suitable for secret backends that do not require a
* request body.
*
* @param path must not be {@literal null}.
* @return the data. May be {@literal null} if the path does not exist.
*/
VaultResponse read(String path);
/**
* Read from a secret backend. Reading data using this method is suitable for secret backends that do not require a
* request body.
*
* @param path must not be {@literal null}.
* @param responseType must not be {@literal null}.
* @return the data. May be {@literal null} if the path does not exist.
*/
<T> VaultResponseSupport<T> read(String path, Class<T> responseType);
/**
* Enumerate keys from a secret backend.
*
* @param path must not be {@literal null}.
* @return the data. May be {@literal null} if the path does not exist.
*/
List<String> list(String path);
/**
* Write to a secret backend.
*
* @param path must not be {@literal null}.
* @return the configuration data. May be empty but never {@literal null}.
*/
VaultResponse write(String path, Object body);
/**
* Delete a path in the secret backend.
*
* @param path must not be {@literal null}.
*/
void delete(String path);
/**
* Executes a Vault {@link ClientCallback}. Allows to interact with Vault using {@link VaultClient} without requiring a session.
*
* @param clientCallback the request.
* @return the {@link ClientCallback} return value.
*/
<T> T doWithVault(ClientCallback<T> clientCallback);
/**
* Executes a Vault {@link SessionCallback}. Allows to interact with Vault in an authenticated session.
*
* @param sessionCallback the request.
* @return the {@link SessionCallback} return value.
*/
<T> T doWithVault(SessionCallback<T> sessionCallback);
/**
* Executes {@link RestTemplateCallback}. Expands the {@code pathTemplate} to an {@link java.net.URI} and allows
* low-level interaction with the underlying {@link org.springframework.web.client.RestTemplate}.
*
* @param pathTemplate the path of the resource, e.g. {@code transit/{key}/foo}, must not be empty or {@literal null}.
* @param variables the variables for expansion of the {@code pathTemplate}, must not be {@literal null}.
* @param callback the request callback.
* @return the {@link RestTemplateCallback} return value.
*/
<T> T doWithRestTemplate(String pathTemplate, Map<String, ?> variables, RestTemplateCallback<T> callback);
/**
* A callback for executing arbitrary operations on the {@link VaultClient}.
*
* @author Mark Paluch
*/
public interface ClientCallback<T> {
/**
* Callback method.
*
* @param client session to use, must not be {@literal null}.
* @return a result object or null if none.
*/
T doWithVault(VaultClient client);
}
/**
* A callback for executing arbitrary operations on the {@link VaultSession}.
*
* @author Mark Paluch
*/
public interface SessionCallback<T> {
/**
* Callback method.
*
* @param session session to use, must not be {@literal null}.
* @return a result object or null if none.
*/
T doWithVault(VaultSession session);
}
/**
* An authenticated Vault session. {@link VaultSession} exposes request accessor methods to be executed in an
* authenticated context.
*
* @author Mark Paluch
*/
public interface VaultSession {
/**
* Retrieve a resource by GETting from the path, and returns the response as {@link VaultResponseEntity}.
*
* @param path the path.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
<T, S extends T> VaultResponseEntity<S> getForEntity(String path, Class<T> responseType);
/**
* Issue a POST request using the given object to the path, and returns the response as {@link VaultResponseEntity}.
*
* @param path the path.
* @param request the Object to be POSTed, may be {@code null}.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
<T, S extends T> VaultResponseEntity<S> postForEntity(String path, Object request, Class<T> responseType);
/**
* Create a new resource by PUTting the given object to the path, and returns the response as
* {@link VaultResponseEntity}.
*
* @param path the path.
* @param request the Object to be PUT.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
<T, S extends T> VaultResponseEntity<S> putForEntity(String path, Object request, Class<T> responseType);
/**
* Delete a resource by DELETEing from the path, and returns the response as {@link VaultResponseEntity}.
*
* @param path the path.
* @param responseType the type of the return value
* @return the response as entity.
* @see VaultResponseEntity
*/
<T, S extends T> VaultResponseEntity<S> deleteForEntity(String path, Class<T> responseType);
/**
* Execute the HTTP method to the given URI template, writing the given request entity to the request, and returns
* the response as {@link VaultResponseEntity}.
* <p>
* URI Template variables are using the given URI variables, if any.
*
* @param pathTemplate the path template.
* @param method the HTTP method (GET, POST, etc).
* @param requestEntity the entity (headers and/or body) to write to the request, may be {@code null}.
* @param responseType the type of the return value.
* @param uriVariables the variables to expand in the template.
* @return the response as entity.
*/
<T, S extends T> VaultResponseEntity<S> exchange(String pathTemplate, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables);
/**
* Execute the HTTP method to the given path template, writing the given request entity to the request, and returns
* the response as {@link VaultResponseEntity}. The given {@link ParameterizedTypeReference} is used to pass generic
* type information:
*
* <pre class="code">
* ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean = new ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt;() {};
* ResponseEntity&lt;List&lt;MyBean&gt;&gt; response = session.exchange(&quot;http://example.com&quot;, HttpMethod.GET, null, myBean,
* null);
* </pre>
*
* @param pathTemplate the path template.
* @param method the HTTP method (GET, POST, etc).
* @param requestEntity the entity (headers and/or body) to write to the request, may be {@code null}.
* @param responseType the type of the return value.
* @param uriVariables the variables to expand in the template.
* @return the response as entity.
*/
<T, S extends T> VaultResponseEntity<S> exchange(String pathTemplate, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables);
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import java.util.Map;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.support.VaultHealthResponse;
import org.springframework.vault.support.VaultInitializationRequest;
import org.springframework.vault.support.VaultInitializationResponse;
import org.springframework.vault.support.VaultMount;
import org.springframework.vault.support.VaultUnsealStatus;
/**
* Interface that specified a basic set of Vault operations, implemented by {@link VaultTemplate}. Request errors are
* wrapped within {@link VaultException}.
*
* @author Mark Paluch
*/
public interface VaultSysOperations {
/**
* @return {@literal true} if Vault is initialized.
*/
boolean isInitialized() throws VaultException;
/**
* Initializes Vault with a {@link VaultInitializationRequest}.
*
* @param vaultInitializationRequest must not be {@literal null}.
* @return the {@link VaultInitializationResponse}.
*/
VaultInitializationResponse initialize(VaultInitializationRequest vaultInitializationRequest) throws VaultException;
/**
* Seals vault.
*/
void seal() throws VaultException;
/**
* Unseal vault. See {@link VaultUnsealStatus#getProgress()} for progress.
*
* @param keyShare must not be empty and not {@literal null}.
* @return the {@link VaultUnsealStatus}.
*/
VaultUnsealStatus unseal(String keyShare) throws VaultException;
/**
* @return the {@link VaultUnsealStatus}.
*/
VaultUnsealStatus getUnsealStatus() throws VaultException;
/**
* Mounts a secret backend {@link VaultMount} to {@code path}.
*
* @param path must not be empty or {@literal null}.
* @param vaultMount must not be {@literal null}.
*/
void mount(String path, VaultMount vaultMount) throws VaultException;
/**
* @return {@link Map} of all secret backend {@link VaultMount mounts}.
*/
Map<String, VaultMount> getMounts() throws VaultException;
/**
* Unmounts the secret backend mount at {@code path}.
*
* @param path must not be empty or {@literal null}.
*/
void unmount(String path) throws VaultException;
/**
* Mounts an auth backend {@link VaultMount} to {@code path}.
*
* @param path must not be empty or {@literal null}.
* @param vaultMount must not be {@literal null}.
*/
void authMount(String path, VaultMount vaultMount) throws VaultException;
/**
* @return {@link Map} of all auth backend {@link VaultMount mounts}.
*/
Map<String, VaultMount> getAuthMounts() throws VaultException;
/**
* Unmounts the auth backend mount at {@code path}.
*
* @param path must not be empty or {@literal null}.
*/
void authUnmount(String path) throws VaultException;
/**
* @return the {@link VaultHealthResponse}.
*/
VaultHealthResponse health() throws VaultException;
}

View File

@@ -0,0 +1,335 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.vault.client.VaultAccessor;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.client.VaultResponseEntity;
import org.springframework.vault.core.VaultOperations.ClientCallback;
import org.springframework.vault.core.VaultOperations.SessionCallback;
import org.springframework.vault.support.VaultHealthResponse;
import org.springframework.vault.support.VaultInitializationRequest;
import org.springframework.vault.support.VaultInitializationResponse;
import org.springframework.vault.support.VaultMount;
import org.springframework.vault.support.VaultResponseSupport;
import org.springframework.vault.support.VaultUnsealStatus;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Default implementation of {@link VaultSysOperations}.
*
* @author Mark Paluch
*/
public class VaultSysTemplate implements VaultSysOperations {
private static final GetUnsealStatus GET_UNSEAL_STATUS = new GetUnsealStatus();
private static final Seal SEAL = new Seal();
private static final GetMounts GET_MOUNTS = new GetMounts("sys/mounts");
private static final GetMounts GET_AUTH_MOUNTS = new GetMounts("sys/auth");
private static final Health HEALTH = new Health();
private final VaultOperations vaultOperations;
/**
* Creates a new {@link VaultSysTemplate} with the given {@link VaultOperations}.
*
* @param vaultOperations must not be {@literal null}.
*/
public VaultSysTemplate(VaultOperations vaultOperations) {
Assert.notNull(vaultOperations);
this.vaultOperations = vaultOperations;
}
@Override
public boolean isInitialized() {
return vaultOperations.doWithVault(new ClientCallback<Boolean>() {
@Override
public Boolean doWithVault(VaultClient client) {
VaultResponseEntity<Map<String, Boolean>> response = client.getForEntity("sys/init", Map.class);
if (response.isSuccessful() && response.hasBody()) {
return response.getBody().get("initialized");
}
throw new VaultException(buildExceptionMessage(response));
}
});
}
@Override
public VaultInitializationResponse initialize(final VaultInitializationRequest vaultInitializationRequest) {
Assert.notNull(vaultInitializationRequest, "VaultInitialization must not be null");
return vaultOperations.doWithVault(new ClientCallback<VaultInitializationResponse>() {
@Override
public VaultInitializationResponse doWithVault(VaultClient client) {
VaultResponseEntity<VaultInitializationResponse> response = client.putForEntity("sys/init",
vaultInitializationRequest, VaultInitializationResponse.class);
if (response.isSuccessful() && response.hasBody()) {
return response.getBody();
}
throw new VaultException(buildExceptionMessage(response));
}
});
}
@Override
public void seal() {
vaultOperations.doWithVault(SEAL);
}
@Override
public VaultUnsealStatus unseal(final String keyShare) {
return vaultOperations.doWithVault(new ClientCallback<VaultUnsealStatus>() {
@Override
public VaultUnsealStatus doWithVault(VaultClient client) {
VaultResponseEntity<VaultUnsealStatus> response = client.putForEntity("sys/unseal",
Collections.singletonMap("key", keyShare), VaultUnsealStatus.class);
if (response.isSuccessful() && response.hasBody()) {
return response.getBody();
}
throw new VaultException(buildExceptionMessage(response));
}
});
}
@Override
public VaultUnsealStatus getUnsealStatus() {
return vaultOperations.doWithVault(GET_UNSEAL_STATUS);
}
@Override
public void mount(final String path, final VaultMount vaultMount) {
Assert.hasText(path, "Path must not be empty");
Assert.notNull(vaultMount, "VaultMount must not be null");
vaultOperations.doWithVault(new SessionCallback<Void>() {
@Override
public Void doWithVault(VaultOperations.VaultSession session) {
VaultResponseEntity<Map<?, ?>> response = session.exchange("sys/mounts/{mount}", HttpMethod.PUT,
new HttpEntity<VaultMount>(vaultMount), Map.class, Collections.singletonMap("mount", path));
if (response.isSuccessful()) {
return null;
}
throw new VaultException(buildExceptionMessage(response));
}
});
}
@Override
public Map<String, VaultMount> getMounts() {
return vaultOperations.doWithVault(GET_MOUNTS);
}
@Override
public void unmount(final String path) {
Assert.hasText(path, "Path must not be empty");
vaultOperations.doWithVault(new SessionCallback<Void>() {
@Override
public Void doWithVault(VaultOperations.VaultSession session) {
VaultResponseEntity<Map<?, ?>> response = session.exchange("sys/mounts/{mount}", HttpMethod.DELETE, null,
Map.class, Collections.singletonMap("mount", path));
if (response.isSuccessful()) {
return null;
}
throw new VaultException(buildExceptionMessage(response));
}
});
}
@Override
public void authMount(final String path, final VaultMount vaultMount) throws VaultException {
Assert.hasText(path, "Path must not be empty");
Assert.notNull(vaultMount, "VaultMount must not be null");
vaultOperations.doWithVault(new SessionCallback<Void>() {
@Override
public Void doWithVault(VaultOperations.VaultSession session) {
VaultResponseEntity<Map<?, ?>> response = session.exchange("sys/auth/{mount}", HttpMethod.PUT,
new HttpEntity<VaultMount>(vaultMount), Map.class, Collections.singletonMap("mount", path));
if (response.isSuccessful()) {
return null;
}
throw new VaultException(buildExceptionMessage(response));
}
});
}
@Override
public Map<String, VaultMount> getAuthMounts() throws VaultException {
return vaultOperations.doWithVault(GET_AUTH_MOUNTS);
}
@Override
public void authUnmount(final String path) throws VaultException {
Assert.hasText(path, "Path must not be empty");
vaultOperations.doWithVault(new SessionCallback<Void>() {
@Override
public Void doWithVault(VaultOperations.VaultSession session) {
VaultResponseEntity<Map<?, ?>> response = session.exchange("sys/auth/{mount}", HttpMethod.DELETE, null,
Map.class, Collections.singletonMap("mount", path));
if (response.isSuccessful()) {
return null;
}
throw new VaultException(buildExceptionMessage(response));
}
});
}
@Override
public VaultHealthResponse health() {
return vaultOperations.doWithRestTemplate("sys/health", Collections.<String, Object> emptyMap(), HEALTH);
}
private static String buildExceptionMessage(VaultResponseEntity<?> response) {
if (StringUtils.hasText(response.getMessage())) {
return String.format("Status %s URI %s: %s", response.getStatusCode(), response.getUri(), response.getMessage());
}
return String.format("Status %s URI %s", response.getStatusCode(), response.getUri());
}
private static class GetUnsealStatus implements ClientCallback<VaultUnsealStatus> {
@Override
public VaultUnsealStatus doWithVault(VaultClient client) {
VaultResponseEntity<VaultUnsealStatus> response = client.getForEntity("sys/seal-status", VaultUnsealStatus.class);
if (response.isSuccessful() && response.hasBody()) {
return response.getBody();
}
throw new VaultException(buildExceptionMessage(response));
}
}
private static class Seal implements SessionCallback<Void> {
@Override
public Void doWithVault(VaultOperations.VaultSession session) {
VaultResponseEntity<Map> response = session.putForEntity("sys/seal", null, Map.class);
if (!response.isSuccessful()) {
throw new VaultException(buildExceptionMessage(response));
}
return null;
}
}
private static class GetMounts implements SessionCallback<Map<String, VaultMount>> {
private static final ParameterizedTypeReference<VaultResponseSupport<Map<String, VaultMount>>> MOUNT_TYPE_REF = new ParameterizedTypeReference<VaultResponseSupport<Map<String, VaultMount>>>() {
};
private final String path;
public GetMounts(String path) {
this.path = path;
}
@Override
public Map<String, VaultMount> doWithVault(VaultOperations.VaultSession session) {
VaultResponseEntity<VaultResponseSupport<Map<String, VaultMount>>> response = session.exchange(path, HttpMethod.GET, null,
MOUNT_TYPE_REF, Collections.<String, Object>emptyMap());
if (response.isSuccessful() && response.hasBody()) {
return response.getBody().getData();
}
throw new VaultException(buildExceptionMessage(response));
}
}
private static class Health implements VaultAccessor.RestTemplateCallback<VaultHealthResponse> {
@Override
public VaultHealthResponse doWithRestTemplate(URI uri, RestTemplate restTemplate) {
try {
ResponseEntity<VaultHealthResponse> healthResponse = restTemplate.exchange(uri, HttpMethod.GET, null,
VaultHealthResponse.class);
return healthResponse.getBody();
} catch (HttpStatusCodeException responseError) {
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(responseError.getResponseBodyAsString(), VaultHealthResponse.class);
} catch (Exception jsonError) {
throw responseError;
}
}
}
}
}

View File

@@ -0,0 +1,373 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.DefaultSessionManager;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.client.VaultAccessor.RestTemplateCallback;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.client.VaultResponseEntity;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultResponseSupport;
/**
* This class encapsulates main Vault interaction. {@link VaultTemplate} will log into Vault on initialization and use
* the token throughout the whole lifetime.
*
* @author Mark Paluch
* @see VaultClientFactory
* @see SessionManager
*/
public class VaultTemplate implements InitializingBean, VaultOperations {
private VaultClientFactory vaultClientFactory;
private SessionManager sessionManager;
/**
* Creates a new {@link VaultTemplate} without setting {@link VaultClientFactory} and {@link SessionManager}.
*/
public VaultTemplate() {}
/**
* Creates a new {@link VaultTemplate} with a {@link VaultClient} and {@link ClientAuthentication}.
*
* @param vaultClient must not be {@literal null}.
* @param clientAuthentication must not be {@literal null}.
*/
public VaultTemplate(VaultClient vaultClient, ClientAuthentication clientAuthentication) {
Assert.notNull(vaultClient, "VaultClientFactory must not be null");
Assert.notNull(clientAuthentication, "ClientAuthentication must not be null");
this.vaultClientFactory = new DefaultVaultClientFactory(vaultClient);
this.sessionManager = new DefaultSessionManager(clientAuthentication);
}
/**
* Creates a new {@link VaultTemplate} with a {@link VaultClientFactory} and {@link SessionManager}.
*
* @param vaultClientFactory must not be {@literal null}.
* @param sessionManager must not be {@literal null}.
*/
public VaultTemplate(VaultClientFactory vaultClientFactory, SessionManager sessionManager) {
Assert.notNull(vaultClientFactory, "VaultClientFactory must not be null");
Assert.notNull(sessionManager, "SessionManager must not be null");
this.vaultClientFactory = vaultClientFactory;
this.sessionManager = sessionManager;
}
/**
* Set the {@link VaultClientFactory}.
*
* @param vaultClientFactory must not be {@literal null}.
*/
public void setVaultClientFactory(VaultClientFactory vaultClientFactory) {
Assert.notNull(vaultClientFactory, "VaultClientFactory must not be null");
this.vaultClientFactory = vaultClientFactory;
}
/**
* Set the {@link SessionManager}.
*
* @param sessionManager must not be {@literal null}.
*/
public void setSessionManager(SessionManager sessionManager) {
Assert.notNull(sessionManager, "SessionManager must not be null");
this.sessionManager = sessionManager;
}
@Override
public void afterPropertiesSet() {
Assert.notNull(vaultClientFactory, "VaultClientFactory must not be null");
Assert.notNull(sessionManager, "SessionManager must not be null");
}
@Override
public VaultTokenOperations opsForToken() {
return new VaultTokenTemplate(this);
}
@Override
public VaultSysOperations opsForSys() {
return new VaultSysTemplate(this);
}
@Override
public <T> T doWithVault(ClientCallback<T> clientCallback) {
Assert.notNull(clientCallback, "ClientCallback must not be null!");
Assert.state(vaultClientFactory != null, "VaultClientFactory must not be null");
Assert.state(sessionManager != null, "SessionManager must not be null");
return clientCallback.doWithVault(vaultClientFactory.getVaultClient());
}
@Override
public <T> T doWithVault(SessionCallback<T> sessionCallback) {
Assert.notNull(sessionCallback, "SessionCallback must not be null!");
Assert.state(vaultClientFactory != null, "VaultClientFactory must not be null");
Assert.state(sessionManager != null, "SessionManager must not be null");
VaultClient vaultClient = vaultClientFactory.getVaultClient();
return sessionCallback.doWithVault(new DefaultVaultSession(sessionManager, vaultClient));
}
@Override
public <T> T doWithRestTemplate(String pathTemplate, Map<String, ?> uriVariables, RestTemplateCallback<T> callback) {
Assert.notNull(callback, "RestTemplateCallback must not be null!");
Assert.state(vaultClientFactory != null, "VaultClientFactory must not be null");
return vaultClientFactory.getVaultClient().doWithRestTemplate(pathTemplate, uriVariables, callback);
}
@Override
public VaultResponse read(String path) {
Assert.hasText(path, "Path must not be empty");
return doRead(path, VaultResponse.class);
}
@SuppressWarnings("unchecked")
@Override
public <T> VaultResponseSupport<T> read(final String path, final Class<T> responseType) {
final ParameterizedTypeReference<VaultResponseSupport<T>> ref = getTypeReference(responseType);
return doWithVault(new SessionCallback<VaultResponseSupport<T>>() {
@Override
public VaultResponseSupport<T> doWithVault(VaultSession session) {
VaultResponseEntity<VaultResponseSupport<T>> entity = session.exchange(path, HttpMethod.GET, null, ref, null);
if (entity.isSuccessful() && entity.hasBody()) {
return entity.getBody();
}
if (entity.getStatusCode() == HttpStatus.NOT_FOUND) {
return null;
}
throw new VaultException(buildExceptionMessage(entity));
}
});
}
@Override
public List<String> list(String path) {
Assert.hasText(path, "Path must not be empty");
VaultListResponse read = doRead(String.format("%s?list=true", path.endsWith("/") ? path : (path + "/")),
VaultListResponse.class);
if (read == null) {
return Collections.emptyList();
}
return (List) read.getData().get("keys");
}
@Override
public VaultResponse write(final String path, final Object body) {
Assert.hasText(path, "Path must not be empty");
return doWithVault(new SessionCallback<VaultResponse>() {
@Override
public VaultResponse doWithVault(VaultSession session) {
VaultResponseEntity<VaultResponse> entity = session.postForEntity(path, body, VaultResponse.class);
if (entity.isSuccessful()) {
if (entity.hasBody()) {
return entity.getBody();
}
return null;
}
throw new VaultException(buildExceptionMessage(entity));
}
});
}
@Override
public void delete(final String path) {
Assert.hasText(path, "Path must not be empty");
doWithVault(new SessionCallback<VaultResponse>() {
@Override
public VaultResponse doWithVault(VaultSession session) {
VaultResponseEntity<VaultResponse> entity = session.deleteForEntity(path, VaultResponse.class);
if (entity.isSuccessful()) {
return null;
}
throw new VaultException(buildExceptionMessage(entity));
}
});
}
private <T> ParameterizedTypeReference<VaultResponseSupport<T>> getTypeReference(final Class<T> responseType) {
final Type supportType = new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return new Type[] { responseType };
}
@Override
public Type getRawType() {
return VaultResponseSupport.class;
}
@Override
public Type getOwnerType() {
return VaultResponseSupport.class;
}
};
return new ParameterizedTypeReference<VaultResponseSupport<T>>() {
@Override
public Type getType() {
return supportType;
}
};
}
private <T> T doRead(final String path, final Class<T> responseType) {
return doWithVault(new SessionCallback<T>() {
@Override
public T doWithVault(VaultSession session) {
VaultResponseEntity<T> entity = session.getForEntity(path, responseType);
if (entity.isSuccessful() && entity.hasBody()) {
return entity.getBody();
}
if (entity.getStatusCode() == HttpStatus.NOT_FOUND) {
return null;
}
throw new VaultException(buildExceptionMessage(entity));
}
});
}
private static String buildExceptionMessage(VaultResponseEntity<?> response) {
if (StringUtils.hasText(response.getMessage())) {
return String.format("Status %s URI %s: %s", response.getStatusCode(), response.getUri(), response.getMessage());
}
return String.format("Status %s URI %s", response.getStatusCode(), response.getUri());
}
private static class DefaultVaultSession implements VaultSession {
private final SessionManager sessionManager;
private final VaultClient vaultClient;
DefaultVaultSession(SessionManager sessionManager, VaultClient vaultClient) {
this.sessionManager = sessionManager;
this.vaultClient = vaultClient;
}
@Override
public <T, S extends T> VaultResponseEntity<S> getForEntity(String path, Class<T> responseType) {
return vaultClient.getForEntity(path, sessionManager.getSessionToken(), responseType);
}
@Override
public <T, S extends T> VaultResponseEntity<S> putForEntity(String path, Object request, Class<T> responseType) {
return vaultClient.putForEntity(path, sessionManager.getSessionToken(), request, responseType);
}
@Override
public <T, S extends T> VaultResponseEntity<S> postForEntity(String path, Object request, Class<T> responseType) {
return vaultClient.postForEntity(path, sessionManager.getSessionToken(), request, responseType);
}
@Override
public <T, S extends T> VaultResponseEntity<S> deleteForEntity(String path, Class<T> responseType) {
return vaultClient.deleteForEntity(path, sessionManager.getSessionToken(), responseType);
}
@Override
public <T, S extends T> VaultResponseEntity<S> exchange(String pathTemplate, HttpMethod method,
HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) {
HttpEntity<?> requestEntityToUse = getHttpEntity(requestEntity);
return vaultClient.exchange(pathTemplate, method, requestEntityToUse, responseType, uriVariables);
}
@Override
public <T, S extends T> VaultResponseEntity<S> exchange(String pathTemplate, HttpMethod method,
HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) {
HttpEntity<?> requestEntityToUse = getHttpEntity(requestEntity);
return vaultClient.exchange(pathTemplate, method, requestEntityToUse, responseType, uriVariables);
}
private HttpEntity<?> getHttpEntity(HttpEntity<?> requestEntity) {
HttpHeaders httpHeaders = VaultClient.createHeaders(sessionManager.getSessionToken());
HttpEntity<?> requestEntityToUse = requestEntity;
if (requestEntityToUse != null) {
requestEntityToUse = new HttpEntity<Object>(requestEntityToUse.getBody(), httpHeaders);
} else {
requestEntityToUse = new HttpEntity<Object>(httpHeaders);
}
return requestEntityToUse;
}
}
private static class VaultListResponse extends VaultResponseSupport<Map<String, Object>> {}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.support.VaultToken;
import org.springframework.vault.support.VaultTokenRequest;
import org.springframework.vault.support.VaultTokenResponse;
/**
* Interface that specifies token-related operations.
*
* @author Mark Paluch
*/
public interface VaultTokenOperations {
/**
* Creates a new token.
*
* @return a {@link VaultTokenResponse}
*/
VaultTokenResponse create() throws VaultException;
/**
* Creates a new token for the given {@link VaultTokenRequest}.
*
* @param request must not be {@literal null}.
* @return a {@link VaultTokenResponse}
*/
VaultTokenResponse create(VaultTokenRequest request) throws VaultException;
/**
* Creates a new orphan token.
*
* @return a {@link VaultTokenResponse}
*/
VaultTokenResponse createOrphan();
/**
* Creates a new orphan token for the given {@link VaultTokenRequest}.
*
* @param request must not be {@literal null}.
* @return a {@link VaultTokenResponse}
*/
VaultTokenResponse createOrphan(VaultTokenRequest request);
/**
* Renews a {@link VaultToken}.
*
* @param vaultToken must not be {@literal null}.
* @return a {@link VaultTokenResponse}
*/
VaultTokenResponse renew(VaultToken vaultToken);
/**
* Revokes a {@link VaultToken}.
*
* @param vaultToken must not be {@literal null}.
*/
void revoke(VaultToken vaultToken);
/**
* Revokes a {@link VaultToken} but not its child tokens.
*
* @param vaultToken must not be {@literal null}.
*/
void revokeOrphan(VaultToken vaultToken);
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.client.VaultResponseEntity;
import org.springframework.vault.core.VaultOperations.SessionCallback;
import org.springframework.vault.core.VaultOperations.VaultSession;
import org.springframework.vault.support.VaultToken;
import org.springframework.vault.support.VaultTokenRequest;
import org.springframework.vault.support.VaultTokenResponse;
/**
* Default implementation of {@link VaultTokenOperations}.
*
* @author Mark Paluch
*/
public class VaultTokenTemplate implements VaultTokenOperations {
private final VaultOperations vaultOperations;
/**
* Creates a new {@link VaultTokenTemplate} with the given {@link VaultOperations}.
*
* @param vaultOperations must not be {@literal null}.
*/
public VaultTokenTemplate(VaultOperations vaultOperations) {
Assert.notNull(vaultOperations);
this.vaultOperations = vaultOperations;
}
@Override
public VaultTokenResponse create() {
return create(new VaultTokenRequest());
}
@Override
public VaultTokenResponse create(VaultTokenRequest request) {
Assert.notNull(request, "VaultTokenRequest must not be null");
return vaultOperations.doWithVault(new CreateToken("auth/token/create", request));
}
@Override
public VaultTokenResponse createOrphan() {
return createOrphan(new VaultTokenRequest());
}
@Override
public VaultTokenResponse createOrphan(VaultTokenRequest request) {
Assert.notNull(request, "VaultTokenRequest must not be null");
return vaultOperations.doWithVault(new CreateToken("auth/token/create-orphan", request));
}
@Override
public VaultTokenResponse renew(VaultToken vaultToken) {
Assert.notNull(vaultToken, "VaultToken must not be null");
return vaultOperations.doWithVault(new RenewToken(String.format("auth/token/renew/%s", vaultToken.getToken())));
}
@Override
public void revoke(VaultToken vaultToken) {
Assert.notNull(vaultToken, "VaultToken must not be null");
vaultOperations.doWithVault(new RevokeToken(String.format("auth/token/revoke/%s", vaultToken.getToken())));
}
@Override
public void revokeOrphan(VaultToken vaultToken) {
Assert.notNull(vaultToken, "VaultToken must not be null");
vaultOperations.doWithVault(new RevokeToken(String.format("auth/token/revoke-orphan/%s", vaultToken.getToken())));
}
private static String buildExceptionMessage(VaultResponseEntity<?> response) {
if (StringUtils.hasText(response.getMessage())) {
return String.format("Status %s URI %s: %s", response.getStatusCode(), response.getUri(), response.getMessage());
}
return String.format("Status %s URI %s", response.getStatusCode(), response.getUri());
}
private static class CreateToken implements SessionCallback<VaultTokenResponse> {
private final String path;
private final VaultTokenRequest request;
public CreateToken(String path, VaultTokenRequest request) {
this.path = path;
this.request = request;
}
@Override
public VaultTokenResponse doWithVault(VaultSession session) {
VaultResponseEntity<VaultTokenResponse> response = session.postForEntity(path, request, VaultTokenResponse.class);
if (response.isSuccessful() && response.hasBody()) {
return response.getBody();
}
throw new VaultException(buildExceptionMessage(response));
}
}
private static class RevokeToken implements SessionCallback<Void> {
private final String path;
RevokeToken(String path) {
this.path = path;
}
@Override
public Void doWithVault(VaultSession session) {
VaultResponseEntity<VaultTokenResponse> response = session.postForEntity(path, null, VaultTokenResponse.class);
if (response.isSuccessful()) {
return null;
}
throw new VaultException(buildExceptionMessage(response));
}
}
private static class RenewToken implements SessionCallback<VaultTokenResponse> {
private final String path;
RenewToken(String path) {
this.path = path;
}
@Override
public VaultTokenResponse doWithVault(VaultSession session) {
VaultResponseEntity<VaultTokenResponse> response = session.postForEntity(path, null, VaultTokenResponse.class);
if (response.isSuccessful() && response.hasBody()) {
return response.getBody();
}
throw new VaultException(buildExceptionMessage(response));
}
}
}

View File

@@ -0,0 +1,4 @@
/**
* Vault core support.
*/
package org.springframework.vault.core;

View File

@@ -0,0 +1,5 @@
/**
* Spring support for <a href="https://vaultproject.io">Hashicorp Vault</a>.
*/
package org.springframework.vault;

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import java.util.concurrent.TimeUnit;
/**
* Client options for Vault.
*
* @author Mark Paluch
*/
public class ClientOptions {
/**
* Connection timeout;
*/
private final int connectionTimeout;
/**
* Read timeout;
*/
private final int readTimeout;
/**
* Creates new {@link ClientOptions} with default timeouts of {@literal 5} {@link TimeUnit#SECONDS} connection timeout
* and {@literal 15} {@link TimeUnit#SECONDS} read timeout.
*/
public ClientOptions() {
this((int) TimeUnit.SECONDS.toMillis(5), (int) TimeUnit.SECONDS.toMillis(15));
}
/**
* Creates new {@link ClientOptions}.
*
* @param connectionTimeout connection timeout in {@link TimeUnit#MILLISECONDS}, must be greater {@literal 0}.
* @param readTimeout read timeout in {@link TimeUnit#MILLISECONDS}, must be greater {@literal 0}.
*/
public ClientOptions(int connectionTimeout, int readTimeout) {
this.connectionTimeout = connectionTimeout;
this.readTimeout = readTimeout;
}
/**
* @return the connection timeout in {@link TimeUnit#MILLISECONDS}.
*/
public int getConnectionTimeout() {
return connectionTimeout;
}
/**
* @return the read timeout in {@link TimeUnit#MILLISECONDS}.
*/
public int getReadTimeout() {
return readTimeout;
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
/**
* SSL configuration.
* <p>
* Provides configuration for a key store and trust store for TLS certificate verification. Key store and trust store
* may be left unconfigured if the JDK trust store contains all necessary certificates to verify TLS certificates. The
* key store is used for Client Certificate authentication.
*
* @author Mark Paluch
* @see Resource
* @see java.security.KeyStore
* @see org.springframework.vault.authentication.ClientCertificateAuthentication
*/
public class SslConfiguration {
/**
* Default {@link SslConfiguration} without a KeyStore/TrustStore configured.
*/
public static final SslConfiguration NONE = new SslConfiguration(null, null, null, null);
/**
* Trust store that holds certificates and private keys.
*/
private final Resource keyStore;
/**
* Password used to access the key store.
*/
private final String keyStorePassword;
/**
* Trust store that holds SSL certificates.
*/
private final Resource trustStore;
/**
* Password used to access the trust store.
*/
private final String trustStorePassword;
public SslConfiguration(Resource keyStore, String keyStorePassword, Resource trustStore, String trustStorePassword) {
this.keyStore = keyStore;
this.keyStorePassword = keyStorePassword;
this.trustStore = trustStore;
this.trustStorePassword = trustStorePassword;
}
/**
* Creates a new {@link SslConfiguration} for the given trust store.
*
* @param trustStore resource pointing to an existing trust store, must not be {@literal null}.
* @param trustStorePassword may be {@literal null}.
* @see java.security.KeyStore
*/
public static SslConfiguration forTrustStore(Resource trustStore, String trustStorePassword) {
Assert.notNull(trustStore, "TrustStore must not be null");
Assert.notNull(trustStore.exists(), String.format("TrustStore %s does not exist", trustStore));
return new SslConfiguration(null, null, trustStore, trustStorePassword);
}
/**
* Creates a new {@link SslConfiguration} for the given key store.
*
* @param keyStore resource pointing to an existing key store, must not be {@literal null}.
* @param keyStorePassword may be {@literal null}.
* @see java.security.KeyStore
*/
public static SslConfiguration forKeyStore(Resource keyStore, String keyStorePassword) {
Assert.notNull(keyStore, "KeyStore must not be null");
Assert.notNull(keyStore.exists(), String.format("KeyStore %s does not exist", keyStore));
return new SslConfiguration(keyStore, keyStorePassword, null, null);
}
/**
* Creates a new {@link SslConfiguration} for the given truststore.
*
* @param keyStore resource pointing to an existing keystore, must not be {@literal null}.
* @param keyStorePassword may be {@literal null}.
* @see java.security.KeyStore
*/
public SslConfiguration create(Resource keyStore, String keyStorePassword, Resource trustStore,
String trustStorePassword) {
Assert.notNull(keyStore, "KeyStore must not be null");
Assert.notNull(keyStore.exists(), String.format("KeyStore %s does not exist", trustStore));
Assert.notNull(trustStore, "TrustStore must not be null");
Assert.notNull(trustStore.exists(), String.format("TrustStore %s does not exist", trustStore));
return new SslConfiguration(keyStore, keyStorePassword, trustStore, trustStorePassword);
}
/**
* @return the {@link java.security.KeyStore key store} resource or {@literal null} if not configured.
*/
public Resource getKeyStore() {
return keyStore;
}
/**
* @return the key store password or {@literal null} if not configured.
*/
public String getKeyStorePassword() {
return keyStorePassword;
}
/**
* @return the {@link java.security.KeyStore key store} resource or {@literal null} if not configured.
*/
public Resource getTrustStore() {
return trustStore;
}
/**
* @return the trust store password or {@literal null} if not configured.
*/
public String getTrustStorePassword() {
return trustStorePassword;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to bind HTTP API responses for sys/health.
*
* @author Stuart Ingram
* @author Bill Koch
*/
public class VaultHealthResponse {
private boolean initialized;
private boolean sealed;
private boolean standby;
@JsonProperty("server_time_utc") private int serverTimeUtc;
public boolean isInitialized() {
return initialized;
}
public void setInitialized(boolean initialized) {
this.initialized = initialized;
}
public boolean isSealed() {
return sealed;
}
public void setSealed(boolean sealed) {
this.sealed = sealed;
}
public boolean isStandby() {
return standby;
}
public void setStandby(boolean standby) {
this.standby = standby;
}
public int getServerTimeUtc() {
return serverTimeUtc;
}
public void setServerTimeUtc(int serverTimeUtc) {
this.serverTimeUtc = serverTimeUtc;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to bind Vault HTTP Initialization API requests.
*
* @author Mark Paluch
*/
public class VaultInitializationRequest {
@JsonProperty("secret_shares") private int secretShares;
@JsonProperty("secret_threshold") private int secretThreshold;
public VaultInitializationRequest() {}
public VaultInitializationRequest(int secretShares, int secretThreshold) {
this.secretShares = secretShares;
this.secretThreshold = secretThreshold;
}
public int getSecretShares() {
return secretShares;
}
public void setSecretShares(int secretShares) {
this.secretShares = secretShares;
}
public int getSecretThreshold() {
return secretThreshold;
}
public void setSecretThreshold(int secretThreshold) {
this.secretThreshold = secretThreshold;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to bind Vault HTTP Initialization API responses.
*
* @author Mark Paluch
*/
public class VaultInitializationResponse {
private List<String> keys = new ArrayList<String>();
@JsonProperty("root_token") private String rootToken;
public VaultInitializationResponse() {}
public List<String> getKeys() {
return keys;
}
public void setKeys(List<String> keys) {
this.keys = keys;
}
public String getRootToken() {
return rootToken;
}
public void setRootToken(String rootToken) {
this.rootToken = rootToken;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import java.util.Map;
/**
* Value object to bind Vault HTTP Mount API requests/responses.
*
* @author Mark Paluch
*/
public class VaultMount {
private String type;
private String description;
private Map<String, Object> config;
public VaultMount() {}
public VaultMount(String type) {
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Map<String, Object> getConfig() {
return config;
}
public void setConfig(Map<String, Object> config) {
this.config = config;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Value object to bind generic Vault HTTP API responses.
* <p>
* This class binds the data element to a {@link Map} for generic response handling.
*
* @author Spencer Gibb
* @author Mark Paluch
*/
@JsonIgnoreProperties(ignoreUnknown = false)
public class VaultResponse extends VaultResponseSupport<Map<String, Object>> {}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to bind generic Vault HTTP API responses.
*
* @param <T>
* @author Spencer Gibb
* @author Mark Paluch
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class VaultResponseSupport<T> {
private Map<String, Object> auth;
private T data;
private Map<String, String> metadata;
@JsonProperty("wrap_info") private Map<String, String> wrapInfo;
@JsonProperty("lease_duration") private long leaseDuration;
@JsonProperty("lease_id") private String leaseId;
@JsonProperty("request_id") private String requestId;
private boolean renewable;
public Map<String, Object> getAuth() {
return auth;
}
public void setAuth(Map<String, Object> auth) {
this.auth = auth;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Map<String, String> getMetadata() {
return metadata;
}
public void setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
}
public long getLeaseDuration() {
return leaseDuration;
}
public void setLeaseDuration(long leaseDuration) {
this.leaseDuration = leaseDuration;
}
public String getLeaseId() {
return leaseId;
}
public void setLeaseId(String leaseId) {
this.leaseId = leaseId;
}
public boolean isRenewable() {
return renewable;
}
public void setRenewable(boolean renewable) {
this.renewable = renewable;
}
public Map<String, String> getWrapInfo() {
return wrapInfo;
}
public void setWrapInfo(Map<String, String> wrapInfo) {
this.wrapInfo = wrapInfo;
}
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import org.springframework.util.Assert;
import lombok.EqualsAndHashCode;
/**
* Value object for a Vault token.
*
* @author Mark Paluch
*/
@EqualsAndHashCode
public class VaultToken {
private final String token;
private final long leaseDuration;
private VaultToken(String token, long leaseDuration) {
this.token = token;
this.leaseDuration = leaseDuration;
}
/**
* Creates a new {@link VaultToken}.
*
* @param token must not be {@literal null}.
* @return the created {@link VaultToken}
*/
public static VaultToken of(String token) {
return of(token, 0);
}
/**
* Creates a new {@link VaultToken} with a {@code leaseDuration}.
*
* @param token must not be {@literal null}.
* @return the created {@link VaultToken}
*/
public static VaultToken of(String token, long leaseDuration) {
Assert.hasText(token, "Token must not be empty");
return new VaultToken(token, leaseDuration);
}
/**
* @return the token value.
*/
public String getToken() {
return token;
}
/**
* @return the lease duration. May be {@literal 0} if none.
*/
public long getLeaseDuration() {
return leaseDuration;
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to bind Vault HTTP Token API requests.
*
* @author Mark Paluch
*/
public class VaultTokenRequest {
private String id;
private List<String> policies;
private Map<String, String> meta;
@JsonProperty("no_parent") private Boolean noParent;
@JsonProperty("no_default_policy") private Boolean noDefaultPolicy;
private Boolean renewable;
private String ttl;
@JsonProperty("explicit_max_ttl") private String explicitMaxTtl;
@JsonProperty("display_name") private String displayName;
@JsonProperty("num_uses") private Integer numUses;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<String> getPolicies() {
return policies;
}
public void setPolicies(List<String> policies) {
this.policies = policies;
}
public Map<String, String> getMeta() {
return meta;
}
public void setMeta(Map<String, String> meta) {
this.meta = meta;
}
public Boolean getNoParent() {
return noParent;
}
public void setNoParent(Boolean noParent) {
this.noParent = noParent;
}
public Boolean getNoDefaultPolicy() {
return noDefaultPolicy;
}
public void setNoDefaultPolicy(Boolean noDefaultPolicy) {
this.noDefaultPolicy = noDefaultPolicy;
}
public Boolean getRenewable() {
return renewable;
}
public void setRenewable(Boolean renewable) {
this.renewable = renewable;
}
public String getTtl() {
return ttl;
}
public void setTtl(String ttl) {
this.ttl = ttl;
}
public String getExplicitMaxTtl() {
return explicitMaxTtl;
}
public void setExplicitMaxTtl(String explicitMaxTtl) {
this.explicitMaxTtl = explicitMaxTtl;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public Integer getNumUses() {
return numUses;
}
public void setNumUses(Integer numUses) {
this.numUses = numUses;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
/**
* Value object to bind Vault HTTP Token API responses.
*
* @author Mark Paluch
*/
public class VaultTokenResponse extends VaultResponse {
/**
* Returns a {@link VaultToken} from the {@link VaultResponse}.
*
* @return the {@link VaultToken}.
*/
public VaultToken getToken() {
return VaultToken.of((String) getAuth().get("client_token"),
((Number) getAuth().get("lease_duration")).longValue());
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.support;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Value object to bind Vault HTTP Unseal API responses.
*
* @author Mark Paluch
*/
public class VaultUnsealStatus {
private boolean sealed;
@JsonProperty("t") private int secretThreshold;
@JsonProperty("n") private int secretShares;
private int progress;
public VaultUnsealStatus() {}
public int getSecretShares() {
return secretShares;
}
public void setSecretShares(int secretShares) {
this.secretShares = secretShares;
}
public int getSecretThreshold() {
return secretThreshold;
}
public void setSecretThreshold(int secretThreshold) {
this.secretThreshold = secretThreshold;
}
public boolean isSealed() {
return sealed;
}
public void setSealed(boolean sealed) {
this.sealed = sealed;
}
public int getProgress() {
return progress;
}
public void setProgress(int progress) {
this.progress = progress;
}
}

View File

@@ -0,0 +1,4 @@
/**
* Classes supporting the Vault packages, such as value objects.
*/
package org.springframework.vault.support;

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import static org.assertj.core.api.Assertions.*;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.support.VaultToken;
import org.springframework.vault.util.IntegrationTestSupport;
import org.springframework.vault.util.Settings;
import org.springframework.vault.util.TestRestTemplateFactory;
/**
* Integration tests for {@link AppIdAuthentication}.
*
* @author Mark Paluch
*/
public class AppIdAuthenticationIntegrationTests extends IntegrationTestSupport {
@Before
public void before() throws Exception {
if (!prepare().hasAuth("app-id")) {
prepare().mountAuth("app-id");
}
prepare().getVaultOperations().doWithVault(new VaultOperations.SessionCallback<Object>() {
@Override
public Object doWithVault(VaultOperations.VaultSession session) {
Map<String, String> appIdData = new HashMap<String, String>();
appIdData.put("value", "dummy"); // policy
appIdData.put("display_name", "this is my test application");
session.postForEntity("auth/app-id/map/app-id/myapp", appIdData, Map.class);
Map<String, String> userIdData = new HashMap<String, String>();
userIdData.put("value", "myapp"); // name of the app-id
userIdData.put("cidr_block", "0.0.0.0/0");
session.postForEntity("auth/app-id/map/user-id/static-userid-value", userIdData, Map.class);
return null;
}
});
}
@Test
public void shouldLoginSuccessfully() throws Exception {
AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder().appId("myapp") //
.userIdMechanism(new StaticUserId("static-userid-value")) //
.build();
VaultClient vaultClient = new VaultClient(TestRestTemplateFactory.create(Settings.createSslConfiguration()),
new VaultEndpoint());
AppIdAuthentication authentication = new AppIdAuthentication(options, vaultClient);
VaultToken login = authentication.login();
assertThat(login.getToken()).isNotEmpty();
}
@Test(expected = VaultException.class)
public void loginShouldFail() throws Exception {
AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder().appId("wrong") //
.userIdMechanism(new StaticUserId("wrong")) //
.build();
VaultClient vaultClient = new VaultClient(TestRestTemplateFactory.create(Settings.createSslConfiguration()),
new VaultEndpoint());
new AppIdAuthentication(options, vaultClient).login();
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.RestTemplate;
/**
* Unit tests for {@link AppIdAuthentication}.
*
* @author Mark Paluch
*/
public class AppIdAuthenticationUnitTests {
private VaultClient vaultClient;
private MockRestServiceServer mockRest;
@Before
public void before() throws Exception {
RestTemplate restTemplate = new RestTemplate();
mockRest = MockRestServiceServer.createServer(restTemplate);
vaultClient = new VaultClient(restTemplate, new VaultEndpoint());
}
@Test
public void loginShouldObtainTokenWithStaticUserId() throws Exception {
AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder().appId("hello") //
.userIdMechanism(new StaticUserId("world")) //
.build();
mockRest.expect(requestTo("https://localhost:8200/v1/auth/app-id/login")) //
.andExpect(method(HttpMethod.POST)) //
.andExpect(jsonPath("$.app_id").value("hello")) //
.andExpect(jsonPath("$.user_id").value("world")) //
.andRespond(withSuccess().contentType(MediaType.APPLICATION_JSON)
.body("{" + "\"auth\":{\"client_token\":\"my-token\"}" + "}"));
AppIdAuthentication authentication = new AppIdAuthentication(options, vaultClient);
VaultToken login = authentication.login();
assertThat(login.getToken()).isEqualTo("my-token");
}
@Test(expected = VaultException.class)
public void loginShouldFail() throws Exception {
AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder().appId("hello") //
.userIdMechanism(new StaticUserId("world")) //
.build();
mockRest.expect(requestTo("https://localhost:8200/v1/auth/app-id/login")) //
.andRespond(withServerError());
new AppIdAuthentication(options, vaultClient).login();
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
import java.util.Collections;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.RestTemplate;
/**
* Unit tests for {@link AwsEc2Authentication}.
*
* @author Mark Paluch
*/
public class AwsEc2AuthenticationUnitTests {
private VaultClient vaultClient;
private MockRestServiceServer mockRest;
@Before
public void before() throws Exception {
RestTemplate restTemplate = new RestTemplate();
mockRest = MockRestServiceServer.createServer(restTemplate);
vaultClient = new VaultClient(restTemplate, new VaultEndpoint());
}
@Test
public void shouldObtainIdentityDocument() throws Exception {
mockRest.expect(requestTo("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7")) //
.andExpect(method(HttpMethod.GET)) //
.andRespond(withSuccess().body("Hello, world"));
AwsEc2Authentication authentication = new AwsEc2Authentication(vaultClient);
assertThat(authentication.getEc2Login()).containsEntry("pkcs7", "Hello, world").containsKey("nonce").hasSize(2);
}
@Test
public void shouldContainRole() throws Exception {
AwsEc2AuthenticationOptions options = AwsEc2AuthenticationOptions.builder().role("ami").build();
mockRest.expect(requestTo("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7")) //
.andExpect(method(HttpMethod.GET)) //
.andRespond(withSuccess().body("Hello, world"));
AwsEc2Authentication authentication = new AwsEc2Authentication(options, vaultClient, vaultClient.getRestTemplate());
assertThat(authentication.getEc2Login()) //
.containsEntry("pkcs7", "Hello, world") //
.containsEntry("role", "ami") //
.containsKey("nonce").hasSize(3);
}
@Test
public void shouldLogin() throws Exception {
mockRest.expect(requestTo("https://localhost:8200/v1/auth/aws-ec2/login")) //
.andExpect(method(HttpMethod.POST)) //
.andExpect(jsonPath("$.pkcs7").value("value")) //
.andRespond(withSuccess().contentType(MediaType.APPLICATION_JSON)
.body("{" + "\"auth\":{\"client_token\":\"my-token\"}" + "}"));
AwsEc2Authentication authentication = new AwsEc2Authentication(vaultClient) {
@Override
protected Map<String, String> getEc2Login() {
return Collections.singletonMap("pkcs7", "value");
}
};
VaultToken vaultToken = authentication.login();
assertThat(vaultToken.getToken()).isEqualTo("my-token");
}
@Test(expected = VaultException.class)
public void loginShouldFailWhileObtainingIdentityDocument() throws Exception {
mockRest.expect(requestTo("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7")) //
.andRespond(withServerError());
new AwsEc2Authentication(vaultClient).login();
}
@Test(expected = VaultException.class)
public void loginShouldFail() throws Exception {
mockRest.expect(requestTo("https://localhost:8200/v1/auth/aws-ec2/login")) //
.andRespond(withServerError());
new AwsEc2Authentication(vaultClient) {
@Override
protected Map<String, String> getEc2Login() {
return Collections.singletonMap("pkcs7", "value");
}
}.login();
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.vault.util.Settings.*;
import java.io.File;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Map;
import org.assertj.core.util.Files;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.vault.support.VaultToken;
import org.springframework.vault.util.IntegrationTestSupport;
import org.springframework.vault.util.Settings;
import org.springframework.web.client.RestTemplate;
/**
* Integration tests for {@link ClientCertificateAuthentication}.
*
* @author Mark Paluch
*/
public class ClientCertificateAuthenticationIntegrationTests extends IntegrationTestSupport {
@Before
public void before() throws Exception {
if (!prepare().hasAuth("cert")) {
prepare().mountAuth("cert");
}
prepare().getVaultOperations().doWithVault(new VaultOperations.SessionCallback<Object>() {
@Override
public Object doWithVault(VaultOperations.VaultSession session) {
File workDir = findWorkDir();
String certificate = Files.contentOf(new File(workDir, "ca/certs/client.cert.pem"),
Charset.forName("US-ASCII"));
session.postForEntity("auth/cert/certs/my-role", Collections.singletonMap("certificate", certificate),
Map.class);
return null;
}
});
}
@Test
public void shouldLoginSuccessfully() throws Exception {
ClientHttpRequestFactory clientHttpRequestFactory = ClientHttpRequestFactoryFactory.create(new ClientOptions(),
prepareCertAuthenticationMethod());
VaultClient vaultClient = new VaultClient(new RestTemplate(clientHttpRequestFactory), new VaultEndpoint());
ClientCertificateAuthentication authentication = new ClientCertificateAuthentication(vaultClient);
VaultToken login = authentication.login();
assertThat(login.getToken()).isNotEmpty();
}
@Test(expected = VaultException.class)
public void loginShouldFail() throws Exception {
ClientHttpRequestFactory clientHttpRequestFactory = ClientHttpRequestFactoryFactory.create(new ClientOptions(),
Settings.createSslConfiguration());
VaultClient vaultClient = new VaultClient(new RestTemplate(clientHttpRequestFactory), new VaultEndpoint());
new ClientCertificateAuthentication(vaultClient).login();
}
private SslConfiguration prepareCertAuthenticationMethod() {
SslConfiguration original = createSslConfiguration();
SslConfiguration sslConfiguration = new SslConfiguration(
new FileSystemResource(new File(findWorkDir(), "client-cert.jks")), "changeit", original.getTrustStore(),
original.getTrustStorePassword());
return sslConfiguration;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.support.VaultToken;
import org.springframework.web.client.RestTemplate;
/**
* Unit tests for {@link ClientCertificateAuthentication}.
*
* @author Mark Paluch
*/
public class ClientCertificateAuthenticationUnitTests {
private VaultClient vaultClient;
private MockRestServiceServer mockRest;
private AppIdAuthentication sut;
@Before
public void before() throws Exception {
RestTemplate restTemplate = new RestTemplate();
mockRest = MockRestServiceServer.createServer(restTemplate);
vaultClient = new VaultClient(restTemplate, new VaultEndpoint());
}
@Test
public void loginShouldObtainToken() throws Exception {
mockRest.expect(requestTo("https://localhost:8200/v1/auth/cert/login")) //
.andExpect(method(HttpMethod.POST)) //
.andRespond(withSuccess().contentType(MediaType.APPLICATION_JSON)
.body("{" + "\"auth\":{\"client_token\":\"my-token\"}" + "}"));
ClientCertificateAuthentication sut = new ClientCertificateAuthentication(vaultClient);
VaultToken login = sut.login();
assertThat(login.getToken()).isEqualTo("my-token");
}
@Test(expected = VaultException.class)
public void loginShouldFail() throws Exception {
mockRest.expect(requestTo("https://localhost:8200/v1/auth/cert/login")) //
.andRespond(withServerError());
new ClientCertificateAuthentication(vaultClient).login();
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import static org.assertj.core.api.Assertions.*;
import java.util.regex.Pattern;
import org.junit.Test;
/**
* Unit tests for {@link IpAddressUserId}.
*
* @author Mark Paluch
*/
public class IpAddressUserIdTests {
@Test
public void shouldGenerateUppercaseSha256HexString() throws Exception {
String userId = new IpAddressUserId().createUserId();
assertThat(userId).matches(Pattern.compile("[0-9A-F]+")).doesNotMatch(Pattern.compile("[a-f]"));
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.authentication;
import static org.assertj.core.api.Assertions.*;
import static org.junit.Assume.*;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.regex.Pattern;
import org.junit.Test;
import org.springframework.util.CollectionUtils;
/**
* Unit tests for {@link MacAddressUserId}.
*
* @author Mark Paluch
*/
public class MacAddressUserIdUnitTests {
@Test
public void shouldGenerateUppercaseSha256HexString() throws Exception {
String userId = new MacAddressUserId().createUserId();
assertThat(userId).matches(Pattern.compile("[0-9A-F]+")).doesNotMatch(Pattern.compile("[a-f]"));
}
@Test
public void shouldGenerateUserIdFromNetworkInterfaceIndex() throws Exception {
int index = getValidNetworkInterfaceIndex();
assumeTrue(index != -1);
String userId = new MacAddressUserId(index).createUserId();
assertThat(userId).matches(Pattern.compile("[0-9A-F]+")).doesNotMatch(Pattern.compile("[a-f]"));
}
/**
* Obtain index for {@link NetworkInterface} with a HardwareAddress.
*
* @return -1 if none, otherwise index.
* @throws SocketException
*/
private int getValidNetworkInterfaceIndex() throws SocketException {
NetworkInterface[] networkInterfaces = CollectionUtils.toArray(NetworkInterface.getNetworkInterfaces(),
new NetworkInterface[0]);
int index = -1;
for (int i = 0; i < networkInterfaces.length; i++) {
if (networkInterfaces[i].getHardwareAddress() != null) {
index = i;
break;
}
}
return index;
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.config;
import static org.assertj.core.api.AssertionsForClassTypes.*;
import org.junit.Test;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.Netty4ClientHttpRequestFactory;
import org.springframework.http.client.OkHttpClientHttpRequestFactory;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory.HttpComponents;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory.Netty;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory.OkHttp;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.util.Settings;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
/**
* Integration tests for {@link ClientHttpRequestFactory}.
*
* @author Mark Paluch
*/
public class ClientHttpRequestFactoryFactoryIntegrationTests {
private final String url = new VaultEndpoint().createUriString("sys/health");
@Test
public void httpComponentsClientShouldWork() throws Exception {
ClientHttpRequestFactory factory = HttpComponents.usingHttpComponents(new ClientOptions(),
Settings.createSslConfiguration());
RestTemplate template = new RestTemplate(factory);
String response = request(template);
assertThat(factory).isInstanceOf(HttpComponentsClientHttpRequestFactory.class);
assertThat(response).isNotNull().contains("initialized");
((DisposableBean) factory).destroy();
}
@Test
public void nettyClientShouldWork() throws Exception {
ClientHttpRequestFactory factory = Netty.usingNetty(new ClientOptions(), Settings.createSslConfiguration());
((InitializingBean) factory).afterPropertiesSet();
RestTemplate template = new RestTemplate(factory);
String response = request(template);
assertThat(factory).isInstanceOf(Netty4ClientHttpRequestFactory.class);
assertThat(response).isNotNull().contains("initialized");
((DisposableBean) factory).destroy();
}
@Test
public void okHttpClientShouldWork() throws Exception {
ClientHttpRequestFactory factory = OkHttp.usingOkHttp(new ClientOptions(), Settings.createSslConfiguration());
RestTemplate template = new RestTemplate(factory);
String response = request(template);
assertThat(factory).isInstanceOf(OkHttpClientHttpRequestFactory.class);
assertThat(response).isNotNull().contains("initialized");
((DisposableBean) factory).destroy();
}
private String request(RestTemplate template) {
// Uninitialized and sealed can cause status 500
try {
ResponseEntity<String> responseEntity = template.exchange(url, HttpMethod.GET, null, String.class);
return responseEntity.getBody();
} catch (HttpStatusCodeException e) {
return e.getResponseBodyAsString();
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.DefaultSessionManager;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.config.AbstractVaultConfiguration;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.vault.util.Settings;
/**
* Test configuration for Vault integration tests.
*
* @author Mark Paluch
*/
@Configuration
class VaultIntegrationTestConfiguration extends AbstractVaultConfiguration {
@Override
public VaultEndpoint vaultEndpoint() {
return new VaultEndpoint();
}
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication(Settings.token());
}
@Override
public SslConfiguration sslConfiguration() {
return Settings.createSslConfiguration();
}
}
class test {
@Bean
public VaultTemplate vaultTemplate() {
VaultTemplate vaultTemplate = new VaultTemplate();
vaultTemplate.setSessionManager(sessionManager());
vaultTemplate.setVaultClientFactory(clientFactory());
return vaultTemplate;
}
@Bean
public DefaultVaultClientFactory clientFactory() {
return new DefaultVaultClientFactory();
}
@Bean
public DefaultSessionManager sessionManager() {
return new DefaultSessionManager(new TokenAuthentication(""));
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import static org.assertj.core.api.Assertions.*;
import java.util.Collections;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.vault.support.VaultMount;
import org.springframework.vault.support.VaultUnsealStatus;
import org.springframework.vault.util.IntegrationTestSupport;
/**
* Integration tests for {@link VaultSysTemplate} through {@link VaultSysOperations}.
*
* @author Mark Paluch
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
public class VaultSysTemplateIntegrationTests extends IntegrationTestSupport {
@Autowired private VaultOperations vaultOperations;
private VaultSysOperations adminOperations;
@Before
public void before() throws Exception {
adminOperations = vaultOperations.opsForSys();
}
@Test
public void getMountsShouldContainSecretBackend() {
Map<String, VaultMount> mounts = adminOperations.getMounts();
assertThat(mounts).containsKey("secret/");
VaultMount secret = mounts.get("secret/");
assertThat(secret.getDescription()).isEqualTo("generic secret storage");
assertThat(secret.getType()).isEqualTo("generic");
}
@Test
public void mountShouldMountGenericSecret() {
if (adminOperations.getMounts().containsKey("other/")) {
adminOperations.unmount("other");
}
VaultMount mount = new VaultMount("generic");
mount.setConfig(Collections.singletonMap("default_lease_ttl", (Object) "1h"));
mount.setDescription("hello, world");
adminOperations.mount("other", mount);
Map<String, VaultMount> mounts = adminOperations.getMounts();
assertThat(mounts).containsKey("other/");
VaultMount secret = mounts.get("other/");
assertThat(secret.getDescription()).isEqualTo(mount.getDescription());
assertThat(secret.getConfig()).containsEntry("default_lease_ttl", 3600);
assertThat(secret.getType()).isEqualTo("generic");
}
@Test
public void getAuthMountsShouldContainSecretBackend() {
Map<String, VaultMount> mounts = adminOperations.getAuthMounts();
assertThat(mounts).containsKey("token/");
VaultMount secret = mounts.get("token/");
assertThat(secret.getDescription()).isEqualTo("token based credentials");
assertThat(secret.getType()).isEqualTo("token");
}
@Test
public void authMountShouldMountGenericSecret() {
if (adminOperations.getAuthMounts().containsKey("other/")) {
adminOperations.authUnmount("other");
}
VaultMount mount = new VaultMount("userpass");
mount.setDescription("hello, world");
adminOperations.authMount("other", mount);
Map<String, VaultMount> mounts = adminOperations.getAuthMounts();
assertThat(mounts).containsKey("other/");
VaultMount secret = mounts.get("other/");
assertThat(secret.getDescription()).isEqualTo(mount.getDescription());
assertThat(secret.getType()).isEqualTo("userpass");
}
@Test
public void isInitializedShouldReturnTrue() {
assertThat(adminOperations.isInitialized()).isTrue();
}
@Test
public void getUnsealStatusShouldReturnStatus() {
VaultUnsealStatus unsealStatus = adminOperations.getUnsealStatus();
assertThat(unsealStatus.isSealed()).isFalse();
assertThat(unsealStatus.getProgress()).isEqualTo(0);
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import static org.assertj.core.api.Assertions.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultResponseSupport;
import org.springframework.vault.util.IntegrationTestSupport;
/**
* Integration tests for {@link VaultTemplate} using the {@code generic} backend.
*
* @author Mark Paluch
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
public class VaultTemplateGenericIntegrationTests extends IntegrationTestSupport {
@Autowired private VaultOperations vaultOperations;
@Test
public void readShouldReturnAbsentKey() throws Exception {
VaultResponse read = vaultOperations.read("secret/absent");
assertThat(read).isNull();
}
@Test
public void readShouldReturnExistingKey() throws Exception {
vaultOperations.write("secret/mykey", Collections.singletonMap("hello", "world"));
VaultResponse read = vaultOperations.read("secret/mykey");
assertThat(read).isNotNull();
assertThat(read.getData()).containsEntry("hello", "world");
}
@Test
public void readObjectShouldReadDomainClass() throws Exception {
Map<String, String> data = new HashMap<String, String>();
data.put("firstname", "Walter");
data.put("password", "Secret");
vaultOperations.write("secret/mykey", data);
VaultResponseSupport<Person> read = vaultOperations.read("secret/mykey", Person.class);
assertThat(read).isNotNull();
Person person = read.getData();
assertThat(person.getFirstname()).isEqualTo("Walter");
assertThat(person.getPassword()).isEqualTo("Secret");
}
@Test
public void listShouldReturnExistingKey() throws Exception {
vaultOperations.write("secret/mykey", Collections.singletonMap("hello", "world"));
List<String> keys = vaultOperations.list("secret");
assertThat(keys).contains("mykey");
}
@Test
public void deleteShouldRemoveKey() throws Exception {
vaultOperations.write("secret/mykey", Collections.singletonMap("hello", "world"));
vaultOperations.delete("secret/mykey");
VaultResponse read = vaultOperations.read("secret/mykey");
assertThat(read).isNull();
}
static class Person {
String firstname;
String password;
public String getFirstname() {
return firstname;
}
public String getPassword() {
return password;
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import static org.assertj.core.api.Assertions.*;
import java.util.Collections;
import org.apache.commons.codec.binary.Base64;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.vault.support.VaultMount;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.util.IntegrationTestSupport;
/**
* Integration tests for {@link VaultTemplate} using the {@code transit} backend.
*
* @author Mark Paluch
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
public class VaultTemplateTransitIntegrationTests extends IntegrationTestSupport {
@Autowired private VaultOperations vaultOperations;
@Before
public void before() throws Exception {
VaultSysOperations adminOperations = vaultOperations.opsForSys();
if (!adminOperations.getMounts().containsKey("transit/")) {
adminOperations.mount("transit", new VaultMount("transit"));
vaultOperations.write("transit/keys/mykey", null);
vaultOperations.write("transit/keys/derived", Collections.singletonMap("derived", true));
}
}
@Test
public void shouldEncrypt() throws Exception {
VaultResponse response = vaultOperations.write("transit/encrypt/mykey",
Collections.singletonMap("plaintext", Base64.encodeBase64String("that message is secret".getBytes())));
assertThat((String) response.getData().get("ciphertext")).isNotEmpty();
}
@Test
public void shouldEncryptAndDecrypt() throws Exception {
VaultResponse response = vaultOperations.write("transit/encrypt/mykey",
Collections.singletonMap("plaintext", Base64.encodeBase64String("that message is secret".getBytes())));
VaultResponse decrypted = vaultOperations.write("transit/decrypt/mykey",
Collections.singletonMap("ciphertext", response.getData().get("ciphertext")));
assertThat((String) decrypted.getData().get("plaintext")).isEqualTo(Base64.encodeBase64String("that message is secret".getBytes()));
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.core;
import static org.assertj.core.api.Assertions.*;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultException;
import org.springframework.vault.client.VaultResponseEntity;
import org.springframework.vault.support.VaultTokenRequest;
import org.springframework.vault.support.VaultTokenResponse;
import org.springframework.vault.util.IntegrationTestSupport;
/**
* Integration tests for {@link VaultTokenTemplate} through {@link VaultTokenOperations}.
*
* @author Mark Paluch
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
public class VaultTokenTemplateIntegrationTests extends IntegrationTestSupport {
@Autowired private VaultOperations vaultOperations;
private VaultTokenOperations tokenOperations;
@Before
public void before() throws Exception {
tokenOperations = vaultOperations.opsForToken();
}
@Test
public void createTokenShouldCreateANewToken() {
VaultTokenResponse tokenResponse = tokenOperations.create();
assertThat(tokenResponse.getAuth()).containsKey("client_token");
}
@Test
public void createTokenShouldCreateACustomizedToken() {
VaultTokenRequest tokenRequest = new VaultTokenRequest();
tokenRequest.setDisplayName("display");
tokenRequest.setExplicitMaxTtl("1h");
tokenRequest.setTtl("30m");
tokenRequest.setPolicies(Collections.singletonList("root"));
tokenRequest.setNumUses(2);
tokenRequest.setRenewable(true);
tokenRequest.setId("HELLO-WORLD");
VaultTokenResponse tokenResponse = tokenOperations.create(tokenRequest);
assertThat(tokenResponse.getAuth()).containsEntry("client_token", tokenRequest.getId());
}
@Test
public void createOrphanTokenShouldCreateAToken() {
VaultTokenResponse tokenResponse = tokenOperations.createOrphan();
assertThat(tokenResponse.getAuth()).containsKey("client_token");
}
@Test
public void createOrphanTokenShouldCreateACustomizedToken() {
VaultTokenRequest tokenRequest = new VaultTokenRequest();
tokenRequest.setDisplayName("display");
tokenRequest.setExplicitMaxTtl("1h");
tokenRequest.setTtl("30m");
tokenRequest.setPolicies(Collections.singletonList("root"));
tokenRequest.setNumUses(2);
tokenRequest.setRenewable(true);
tokenRequest.setId("HELLO-WORLD");
VaultTokenResponse tokenResponse = tokenOperations.createOrphan(tokenRequest);
assertThat(tokenResponse.getAuth()).containsEntry("client_token", tokenRequest.getId());
}
@Test
public void renewShouldRenewToken() {
VaultTokenRequest tokenRequest = new VaultTokenRequest();
tokenRequest.setDisplayName("display");
tokenRequest.setExplicitMaxTtl("1h");
tokenRequest.setTtl("30m");
VaultTokenResponse tokenResponse = tokenOperations.create(tokenRequest);
VaultTokenResponse renew = tokenOperations.renew(tokenResponse.getToken());
assertThat(renew.getAuth()).containsKey("client_token");
}
@Test(expected = VaultException.class)
public void renewShouldFailForNonRenewableRenewTokens() {
VaultTokenResponse tokenResponse = tokenOperations.create();
VaultTokenResponse renew = tokenOperations.renew(tokenResponse.getToken());
assertThat(renew.getAuth()).containsKey("client_token");
}
@Test
public void revokeShouldRevokeToken() {
final VaultTokenResponse tokenResponse = tokenOperations.create();
tokenOperations.revoke(tokenResponse.getToken());
VaultResponseEntity<String> response = lookupSelf(tokenResponse);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
assertThat(response.getMessage()).isEqualTo("permission denied");
}
@Test
public void createdTokenShouldBeUsableWithVaultClient() {
final VaultTokenResponse tokenResponse = tokenOperations.create();
VaultResponseEntity<String> response = lookupSelf(tokenResponse);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private VaultResponseEntity<String> lookupSelf(final VaultTokenResponse tokenResponse) {
return vaultOperations.doWithVault(new VaultOperations.ClientCallback<VaultResponseEntity<String>>() {
@Override
public VaultResponseEntity<String> doWithVault(VaultClient client) {
return client.getForEntity("/auth/token/lookup-self", tokenResponse.getToken(), String.class);
}
});
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.demo;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.VaultResponseSupport;
/**
* @author Mark Paluch
*/
public class VaultApp {
public static void main(String[] args) {
VaultTemplate vaultTemplate = new VaultTemplate(new VaultClient(),
new TokenAuthentication("00000000-0000-0000-0000-000000000000"));
Secrets secrets = new Secrets();
secrets.username = "hello";
secrets.password = "world";
vaultTemplate.write("secret/myapp", secrets);
VaultResponseSupport<Secrets> response = vaultTemplate.read("secret/myapp", Secrets.class);
System.out.println(response.getData().getUsername());
vaultTemplate.delete("secret/myapp");
}
public static class Secrets {
String username;
String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.util;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;
/**
* Helper to check whether a TCP connection can be established.
*
* @author Mark Paluch
*/
public class CanConnect {
/**
* Performs a check whether a connection can be established to the
* {@code socketAddress}.
*
* @param socketAddress the address to test, must not be {@literal null}.
* @return {@literal true}, if a connection can be established
*/
public static boolean to(SocketAddress socketAddress) {
Socket socket = new Socket();
try {
socket.connect(socketAddress, (int) TimeUnit.SECONDS.toMillis(1));
return true;
}
catch (IOException e) {
return false;
}
finally {
try {
socket.close();
}
catch (IOException o_O) {
}
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.util;
import org.junit.Rule;
/**
* Base class for integration tests using Vault.
*
* @author Mark Paluch
*/
public abstract class IntegrationTestSupport {
@Rule public final VaultRule vaultRule = new VaultRule();
public final PrepareVault prepare() {
return vaultRule.prepare();
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.util;
import java.util.Collections;
import org.springframework.util.Assert;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultSysOperations;
import org.springframework.vault.support.VaultInitializationRequest;
import org.springframework.vault.support.VaultInitializationResponse;
import org.springframework.vault.support.VaultMount;
import org.springframework.vault.support.VaultToken;
import org.springframework.vault.support.VaultTokenRequest;
import org.springframework.vault.support.VaultTokenResponse;
import org.springframework.vault.support.VaultUnsealStatus;
/**
* @author Mark Paluch
*/
public class PrepareVault {
private final VaultOperations vaultOperations;
private final VaultSysOperations adminOperations;
public PrepareVault(VaultOperations vaultOperations) {
this.vaultOperations = vaultOperations;
this.adminOperations = vaultOperations.opsForSys();
}
/**
* Initialize Vault and unseal the vault.
*
* @return the root token.
*/
public VaultToken initializeVault() {
int createKeys = 2;
int requiredKeys = 2;
VaultInitializationResponse initialized = vaultOperations.opsForSys()
.initialize(new VaultInitializationRequest(createKeys, requiredKeys));
for (int i = 0; i < requiredKeys; i++) {
VaultUnsealStatus unsealStatus = vaultOperations.opsForSys().unseal(initialized.getKeys().get(i));
if (!unsealStatus.isSealed()) {
break;
}
}
return VaultToken.of(initialized.getRootToken());
}
/**
* Create a token for the given {@code tokenId} and {@code policy}.
*
* @param tokenId
* @param policy
* @return
*/
public VaultToken createToken(String tokenId, String policy) {
VaultTokenRequest tokenRequest = new VaultTokenRequest();
tokenRequest.setId(tokenId);
if (policy != null) {
tokenRequest.setPolicies(Collections.singletonList(policy));
}
VaultTokenResponse vaultTokenResponse = vaultOperations.opsForToken().create(tokenRequest);
return vaultTokenResponse.getToken();
}
/**
* Check whether Vault is available (vault created and unsealed).
*
* @return
*/
public boolean isAvailable() {
return adminOperations.isInitialized() && !adminOperations.health().isSealed();
}
/**
* Mount an auth backend.
*
* @param authBackend
*/
public void mountAuth(String authBackend) {
Assert.hasText(authBackend, "AuthBackend must not be empty");
adminOperations.authMount(authBackend, new VaultMount(authBackend));
}
/**
* Check whether a auth-backend is enabled.
*
* @param authBackend
* @return
*/
public boolean hasAuth(String authBackend) {
Assert.hasText(authBackend, "AuthBackend must not be empty");
return adminOperations.getAuthMounts().containsKey(authBackend + "/");
}
/**
* Mount an secret backend.
*
* @param secretBackend
*/
public void mountSecret(String secretBackend) {
Assert.hasText(secretBackend, "SecretBackend must not be empty");
adminOperations.mount(secretBackend, new VaultMount(secretBackend));
}
/**
* Check whether a auth-backend is enabled.
*
* @param secretBackend
* @return
*/
public boolean hasSecret(String secretBackend) {
Assert.hasText(secretBackend, "SecretBackend must not be empty");
return adminOperations.getMounts().containsKey(secretBackend);
}
public VaultOperations getVaultOperations() {
return vaultOperations;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.util;
import java.io.File;
import org.springframework.core.io.FileSystemResource;
import org.springframework.vault.support.VaultToken;
import org.springframework.vault.support.SslConfiguration;
/**
* Utility to retrieve settings during test.
*
* @author Mark Paluch
*/
public class Settings {
/**
* @return the vault properties.
*/
public static SslConfiguration createSslConfiguration() {
File workDir = findWorkDir();
return SslConfiguration.forTrustStore(new FileSystemResource(new File(workDir, "keystore.jks")), "changeit");
}
/**
* Find the {@code work} directory, starting at the {@code user.dir} directory. Search
* is performed by walking the parent directories.
* @return the {@link File} pointing to the {@code work} directory
* @throws IllegalStateException If the {@code work} directory cannot be found.
*/
public static File findWorkDir() {
return findWorkDir(new File(System.getProperty("user.dir")));
}
/**
* Find the {@code work} directory, starting at the given {@code directory}. Search
* is performed by walking the parent directories.
* @return the {@link File} pointing to the {@code work} directory
* @throws IllegalStateException If the {@code work} directory cannot be found.
*/
public static File findWorkDir(File directory) {
File searchLevel = directory;
while (searchLevel.getParentFile() != null
&& searchLevel.getParentFile() != searchLevel) {
File work = new File(searchLevel, "work");
if (work.isDirectory() && work.exists()) {
return work;
}
searchLevel = searchLevel.getParentFile();
}
throw new IllegalStateException(String.format(
"Cannot find work directory in %s or any parent directories",
directory.getAbsoluteFile()));
}
/**
* @return the token to use during tests.
*/
public static VaultToken token() {
return VaultToken.of(System.getProperty("vault.token",
"00000000-0000-0000-0000-000000000000"));
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.util;
import java.util.concurrent.atomic.AtomicReference;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.util.Assert;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
/**
* Factory for {@link RestTemplate}. The template caches the {@link ClientHttpRequestFactory} once it was initialized.
* Changes to timeouts or the SSL configuration won't be applied once a {@link ClientHttpRequestFactory} was created for
* the first time.
*
* @author Mark Paluch
*/
public class TestRestTemplateFactory {
private static final AtomicReference<ClientHttpRequestFactory> factoryCache = new AtomicReference<ClientHttpRequestFactory>();
/**
* Create a new {@link RestTemplate} using the {@link SslConfiguration}. The underlying
* {@link ClientHttpRequestFactory} is cached. See {@link #create(ClientHttpRequestFactory)} to create
* {@link RestTemplate} for a given {@link ClientHttpRequestFactory}.
*
* @param sslConfiguration must not be {@literal null}.
* @return
*/
public static RestTemplate create(SslConfiguration sslConfiguration) {
Assert.notNull(sslConfiguration, "SslConfiguration must not be null!");
try {
initializeClientHttpRequestFactory(sslConfiguration);
return create(factoryCache.get());
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
/**
* Create a new {@link RestTemplate} using the {@link ClientHttpRequestFactory}. The {@link RestTemplate} will throw
* {@link org.springframework.web.client.HttpStatusCodeException exceptions} in error cases and behave in that aspect
* like the regular {@link org.springframework.web.client.RestTemplate}.
*
* @param requestFactory must not be {@literal null}.
* @return
*/
public static RestTemplate create(ClientHttpRequestFactory requestFactory) {
Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null!");
RestTemplate RestTemplate = new RestTemplate();
RestTemplate.setErrorHandler(new DefaultResponseErrorHandler());
RestTemplate.setRequestFactory(requestFactory);
return RestTemplate;
}
private static void initializeClientHttpRequestFactory(SslConfiguration sslConfiguration) throws Exception {
if (factoryCache.get() != null) {
return;
}
final ClientHttpRequestFactory clientHttpRequestFactory = ClientHttpRequestFactoryFactory
.create(new ClientOptions(), sslConfiguration);
if (factoryCache.compareAndSet(null, clientHttpRequestFactory)) {
if (clientHttpRequestFactory instanceof InitializingBean) {
((InitializingBean) clientHttpRequestFactory).afterPropertiesSet();
}
if (clientHttpRequestFactory instanceof DisposableBean) {
Runtime.getRuntime().addShutdownHook(new Thread("ClientHttpRequestFactory Shutdown Hook") {
@Override
public void run() {
try {
((DisposableBean) clientHttpRequestFactory).destroy();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.vault.util;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import org.junit.rules.ExternalResource;
import org.springframework.util.Assert;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.DefaultVaultClientFactory;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.vault.support.VaultToken;
/**
* Vault rule to ensure a running and prepared Vault.
*
* @author Mark Paluch
*/
public class VaultRule extends ExternalResource {
private final VaultEndpoint vaultEndpoint;
private final PrepareVault prepareVault;
private VaultToken token;
/**
* Create a new {@link VaultRule} with default SSL configuration and endpoint.
*
* @see Settings#createSslConfiguration()
* @see VaultEndpoint
*/
public VaultRule() {
this(Settings.createSslConfiguration(), new VaultEndpoint());
}
/**
* Create a new {@link VaultRule} with the given {@link SslConfiguration} and {@link VaultEndpoint}.
*
* @param sslConfiguration must not be {@literal null}.
* @param vaultEndpoint must not be {@literal null}.
*/
public VaultRule(SslConfiguration sslConfiguration, VaultEndpoint vaultEndpoint) {
Assert.notNull(sslConfiguration, "SslConfiguration must not be null");
Assert.notNull(vaultEndpoint, "VaultEndpoint must not be null");
VaultClient vaultClient = new VaultClient(TestRestTemplateFactory.create(sslConfiguration), vaultEndpoint);
DefaultVaultClientFactory clientFactory = new DefaultVaultClientFactory(vaultClient);
VaultTemplate vaultTemplate = new VaultTemplate(clientFactory, new PreparingSessionManager());
this.token = Settings.token();
this.prepareVault = new PrepareVault(vaultTemplate);
this.vaultEndpoint = vaultEndpoint;
}
@Override
public void before() {
Socket socket = null;
try {
socket = new Socket();
socket.connect(new InetSocketAddress(InetAddress.getByName("localhost"), vaultEndpoint.getPort()));
socket.close();
} catch (Exception ex) {
throw new IllegalStateException(
String.format("Vault is not running on localhost:%d which is required to run a test using @Rule %s",
vaultEndpoint.getPort(), getClass().getSimpleName()));
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {}
}
}
if (!this.prepareVault.isAvailable()) {
this.token = prepareVault.initializeVault();
this.prepareVault.createToken(Settings.token().getToken(), "root");
this.token = Settings.token();
}
}
public PrepareVault prepare() {
return prepareVault;
}
private class PreparingSessionManager implements SessionManager {
@Override
public VaultToken getSessionToken() {
return token;
}
}
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
</encoder>
</appender>
<!--
<logger name="org.springframework" level="debug" />
-->
<root level="error">
<appender-ref ref="console" />
</root>
</configuration>

View File

@@ -0,0 +1 @@
handlers = org.slf4j.bridge.SLF4JBridgeHandler

View File

@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-dependencies</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>spring-vault-dependencies</name>
<description>Spring Vault Dependencies</description>
<properties>
<spring.version>4.3.2.RELEASE</spring.version>
<httpclient.version>4.5.2</httpclient.version>
<httpcore.version>4.4.4</httpcore.version>
<netty.version>4.1.4.Final</netty.version>
<okhttp.version>2.7.5</okhttp.version>
<jackson.version>2.8.2</jackson.version>
<slf4j.version>1.7.21</slf4j.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Vault -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-vault-core</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Spring Framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- HTTP Client Libraries -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>${httpcore.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-vault-distribution</artifactId>
<packaging>pom</packaging>
<name>Spring Vault - Distribution</name>
<description>Distribution build for Spring Vault</description>
<parent>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-parent</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<project.root>${basedir}/..</project.root>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>wagon-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,26 @@
= Spring Vault - Reference Documentation
Mark Paluch;
:revnumber: {version}
:revdate: {localdate}
:toc:
:toc-placement!:
(C) 2016 The original authors.
NOTE: _Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically._
toc::[]
:leveloffset: +1
include::preface.adoc[]
include::new-features.adoc[]
:leveloffset: -1
[[reference]]
= Reference Documentation
:leveloffset: +1
include::reference/introduction.adoc[]
include::reference/vault.adoc[]
:leveloffset: -1

View File

@@ -0,0 +1,8 @@
[[new-features]]
= New & Noteworthy
[[new-features.1-0-0]]
== What's new in Spring Vault 1.0
* Initial Vault support.

View File

@@ -0,0 +1,62 @@
[[preface]]
= Preface
The Spring Vault project applies core Spring concepts to the development of solutions using Hashicorp Vault. We provide a "template" as a high-level abstraction for storing and querying documents. You will notice similarities to the REST support in the Spring Framework.
This document is the reference guide for Spring Vault. It explains Vault concepts and semantics and the syntax.
This section provides some basic introduction to Spring and Vault. The rest of the document refers only to Spring Vault features and assumes the user is familiar with Hashicorp Vault as well as Spring concepts.
[[get-started:first-steps:spring]]
== Knowing Spring
Spring Vault uses Spring framework's http://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/spring-core.html[core] functionality, such as the http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/beans.html[IoC] container, http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/validation.html#core-convert[type conversion system], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/expressions.html[expression language], http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/jmx.html[JMX integration], and portable http://docs.spring.io/spring/docs/{springVersion}/spring-framework-reference/html/dao.html#dao-exceptions[DAO exception hierarchy]. While it is not important to know the Spring APIs, understanding the concepts behind them is. At a minimum, the idea behind IoC should be familiar for whatever IoC container you choose to use.
The core functionality of the Vault support can be used directly, with no need to invoke the IoC services of the Spring Container. This is much like `RestTemplate` which can be used 'standalone' without any other services of the Spring container. To leverage all the features of Spring Vault document, such as the session support, you will need to configure some parts of the library using Spring.
To learn more about Spring, you can refer to the comprehensive (and sometimes disarming) documentation that explains in detail the Spring Framework. There are a lot of articles, blog entries and books on the matter - take a look at the Spring framework http://spring.io/docs[home page ] for more information.
[[get-started:first-steps:vault]]
== Knowing Vault
Security and working with secrets is a concern of every developer working with databases, user credentials or API keys. Vault steps in by providing a secure storage combined with access control, revocation, key rolling and auditing. In short: Vault is a service for securely accessing and storing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, and more.
The jumping off ground for learning about Vault is www.vaultproject.io. Here is a list of useful resources:
* The manual introduces Vault and contains links to getting started guides, reference documentation and tutorials.
* The online shell provides a convenient way to interact with a Vault instance in combination with the online tutorial.
* https://www.vaultproject.io/intro/index.html[Hashicorp Vault Introduction]
* https://www.vaultproject.io/docs/index.html[Hashicorp Vault Documentation]
[[requirements]]
== Requirements
Spring Vault 1.x binaries requires JDK level 6.0 and above, and http://spring.io/docs[Spring Framework] {springVersion} and above.
In terms of Vault, https://www.vaultproject.io/[Vault] at least 0.5.
== Additional Help Resources
Learning a new framework is not always straight forward. In this section, we try to provide what we think is an easy to follow guide for starting with Spring Vault module. However, if you encounter issues or you are just looking for advice, feel free to use one of the links below:
[[get-started:help]]
=== Support
There are a few support options available:
[[get-started:help:community]]
==== Community Forum
Spring Vault on Stackoverflow http://stackoverflow.com/questions/tagged/spring-vault[Stackoverflow] is a tag for all Spring Vault users to share information and help each other. Note that registration is needed *only* for posting.
[[get-started:help:professional]]
==== Professional Support
Professional, from-the-source support, with guaranteed response time, is available from http://pivotal.io/[Pivotal Sofware, Inc.], the company behind Spring Vault and Spring.
[[get-started:up-to-date]]
=== Following Development
For information on the Spring Vault source code repository, nightly builds and snapshot artifacts please see the http://projects.spring.io/spring-vault/[Spring Vault homepage]. You can help make Spring Vault best serve the needs of the Spring community by interacting with developers through the Community on http://stackoverflow.com/questions/tagged/spring-vault[Stackoverflow]. If you encounter a bug or want to suggest an improvement, please create a ticket on the Spring Vault issue https://github.com/spring-projects/spring-vault/issues[tracker]. To stay up to date with the latest news and announcements in the Spring ecosystem, subscribe to the Spring Community http://spring.io[Portal]. Lastly, you can follow the Spring http://spring.io/blog[blog ]or the project team on Twitter (http://twitter.com/springcentral[SpringCentral]).

View File

@@ -0,0 +1,13 @@
[[introduction]]
= Introduction
== Document Structure
This part of the reference documentation explains the core functionality offered by Spring Vault.
<<vault.core>> introduces the Vault module feature set.
Spring Vault provides client-side support for accessing, storing and revoking secrets.
With https://www.vaultproject.io[Hashicorp's Vault] you have a central place to manage external secret data for applications across all environments.
Vault can manage static and dynamic secrets such as application data, username/password for remote applications/resources and provide credentials for external services such as MySQL, PostgreSQL, Apache Cassandra, Consul, AWS and more.

View File

@@ -0,0 +1,547 @@
[[vault.core]]
= Vault support
The Vault support contains a wide range of features which are summarized below.
* Spring configuration support using Java based @Configuration classes
* `VaultTemplate` helper class that increases productivity performing common
Vault operations. Includes integrated object mapping between Vault responses and POJOs.
For most tasks, you will find yourself using `VaultTemplate` that leverages the
rich communication functionality. `VaultTemplate` is the place to look for
accessing functionality such as reading data from Vault or issuing administrative commands. `VaultTemplate` also provides callback methods so that it is easy for you to
get a hold of the low-level API artifacts such as `RestTemplate` to communicate
directly with Vault.
[[vault.core.getting-started]]
== Getting Started
Spring Vault support requires Vault 0.5 or higher and Java SE 6 or higher.
An easy way to bootstrap setting up a working environment is to create a Spring based project in http://spring.io/tools/sts[STS].
First you need to set up a running Vault server. Refer to the https://www.vaultproject.io/intro/[Vault] for an explanation on how to startup a Vault instance.
To create a Spring project in STS go to File -> New -> Spring Template Project -> Simple Spring Utility Project -> press Yes when prompted. Then enter a project and a package name such as org.spring.vault.example.
Then add the following to pom.xml dependencies section.
[source,xml]
----
<dependencies>
<!-- other dependency elements omitted -->
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>{version}</version>
</dependency>
</dependencies>
----
Also change the version of Spring in the pom.xml to be
[source,xml]
----
<spring.framework.version>{springVersion}</spring.framework.version>
----
You will also need to add the location of the Spring Milestone repository for maven to your pom.xml which is at the same level of your <dependencies/> element
[source,xml]
----
<repositories>
<repository>
<id>spring-milestone</id>
<name>Spring Maven MILESTONE Repository</name>
<url>http://repo.spring.io/libs-milestone</url>
</repository>
</repositories>
----
The repository is also http://repo.spring.io/milestone/org/springframework/data/[browseable here].
You may also want to set the logging level to `DEBUG` to see some additional information, edit the log4j.properties file to have
[source]
----
log4j.category.org.springframework.vault=DEBUG
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n
----
Create a simple `Secrets` class to persist:
.Mapped data object
====
[source,java]
----
package org.spring.vault.example;
public class Secrets {
String username;
String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
----
====
And a main application to run
.Example application using Spring Vault
====
[source,java]
----
package org.springframework.vault.example;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.VaultClient;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.VaultResponseSupport;
public class VaultApp {
public static void main(String[] args) {
VaultTemplate vaultTemplate = new VaultTemplate(new VaultClient(),
new TokenAuthentication("00000000-0000-0000-0000-000000000000"));
Secrets secrets = new Secrets();
secrets.username = "hello";
secrets.password = "world";
vaultTemplate.write("secret/myapp", secrets);
VaultResponseSupport<Secrets> response = vaultTemplate.read("secret/myapp", Secrets.class);
System.out.println(response.getData().getUsername());
vaultTemplate.delete("secret/myapp");
}
}
----
====
Even in this simple example, there are few things to take notice of
* You can instantiate the central helper class of Spring Vault,
<<vault-template,`VaultTemplate`>>, using the `org.springframework.vault.client.VaultClient`
object and the `ClientAuthentication`.
* The mapper works against standard POJO objects without the need for any
additional metadata (though you can optionally provide that information).
* Mapping conventions can use field access. Notice the `Secrets` class has only getters.
* If the constructor argument names match the field names of the stored document,
they will be used to instantiate the object
[[vault.core.connection]]
== Connecting to Vault with Spring
One of the first tasks when using Vault and Spring is to create a `org.springframework.vault.client.VaultClient` object using the IoC container.
[[vault.core.vault-java-config]]
=== Registering a Vault instance using Java based metadata
An example of using Java based bean metadata to register common Vault support classes.
.Registering a Spring Vault objects using Java based bean metadata
====
[source,java]
----
@Configuration
public class AppConfig extends AbstractVaultConfiguration {
/**
* Specify an endpoint for connecting to Vault.
*/
@Override
public VaultEndpoint vaultEndpoint() {
return new VaultEndpoint();
}
/**
* Configure a client authentication.
* Please consider a more secure authentication method
* for production use.
*/
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("…");
}
}
----
====
[[vault.core.template]]
== Introduction to VaultTemplate
The class `VaultTemplate`, located in the package `org.springframework.vault.core`,
is the central class of the Spring's Vault support providing a rich feature set to
interact with Vault. The template offers convenience operations to read, write and
delete data in Vault and provides a mapping between your domain objects and Vault data.
NOTE: Once configured, `VaultTemplate` is thread-safe and can be reused across multiple instances.
The mapping between Vault documents and domain classes is done by delegating to
`RestTemplate`. Spring Web support provides the mapping infrastructure.
The `VaultTemplate` class implements the interface `VaultOperations`.
In as much as possible, the methods on `VaultOperations` are named after methods
available on the Vault API to make the API familiar to existing Vault developers
who are used to the API and CLI. For example, you will find methods such as
"write", "delete", "read", and "revoke".
The design goal was to make it as easy as possible to transition between
the use of the Vault API and `VaultOperations`. A major difference in between
the two APIs is that `VaultOperations` can be passed domain objects instead of JSON Key-Value pairs.
NOTE: The preferred way to reference the operations on `VaultTemplate` instance is via its interface `VaultOperations`.
While there are many convenience methods on `VaultTemplate` to help you easily
perform common tasks if you should need to access the Vault API directly to access
functionality not explicitly exposed by the `VaultTemplate` you can use one of
several execute callback methods to access underlying APIs. The execute callbacks
will give you a reference to either a `RestTemplate` or a `VaultClient` object. Please see the section <<vault.core.executioncallback,Execution Callbacks>> for more information.
Now let's look at a examples of how to work with the `VaultTemplate` in the context of the Spring container.
[[vault.core.template.instantiating]]
=== Instantiating VaultTemplate
You can use Java to create and register an instance of `VaultTemplate` as shown below.
.Registering a `VaultTemplate` object
====
[source,java]
----
@Configuration
class AppConfig {
@Bean
public VaultTemplate vaultTemplate() {
VaultTemplate vaultTemplate = new VaultTemplate();
vaultTemplate.setSessionManager(sessionManager());
vaultTemplate.setVaultClientFactory(clientFactory());
return vaultTemplate;
}
@Bean
public DefaultVaultClientFactory clientFactory() {
return new DefaultVaultClientFactory();
}
@Bean
public DefaultSessionManager sessionManager() {
return new DefaultSessionManager(new TokenAuthentication("…"));
}
}
----
====
There are several overloaded constructors of `VaultTemplate`. These are
* `VaultTemplate(VaultClient, ClientAuthentication)` - takes the `VaultClient` object and client authentication
* `VaultTemplate(VaultClientFactory, SessionManager)` - takes a client factory for resource management and a `SessionManager`.
[[vault.core.clients]]
=== Client support
[[vault.core.authentication]]
== Vault authentication
Different organizations have different requirements for security
and authentication. Vault reflects that need by shipping multiple authentication
methods. Spring Vault supports multiple authentications mechanisms.
=== Token authentication
Tokens are the core method for authentication within Vault.
Token authentication requires a static token to be provided.
NOTE: Token authentication is the default authentication method.
If a token is disclosed an unintended party, it gains access to Vault and
can access secrets for the intended client.
====
[source,java]
----
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("…");
}
// …
}
----
====
See also: https://www.vaultproject.io/docs/concepts/tokens.html[Vault Documentation: Tokens]
=== AppId authentication
Vault supports https://www.vaultproject.io/docs/auth/app-id.html[AppId]
authentication that consists of two hard to guess tokens. The AppId
defaults to `spring.application.name` that is statically configured.
The second token is the UserId which is a part determined by the application,
usually related to the runtime environment. IP address, Mac address or a
Docker container name are good examples. Spring Vault supports
IP address, Mac address and static UserId's (e.g. supplied via System properties).
The IP and Mac address are represented as Hex-encoded SHA256 hash.
IP address-based UserId's use the local host's IP address.
====
[source,java]
----
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder().appId("myapp") //
.userIdMechanism(new IpAddressUserId()) //
.build();
return new AppIdAuthentication(options, vaultClient());
}
// …
}
----
====
The corresponding command to generate the IP address UserId from a command line is:
----
$ echo -n 192.168.99.1 | sha256sum
----
NOTE: Including the line break of `echo` leads to a different hash value
so make sure to include the `-n` flag.
Mac address-based UserId's obtain their network device from the
localhost-bound device. The configuration also allows specifying
a `network-interface` hint to pick the right device. The value of
`network-interface` is optional and can be either an interface
name or interface index (0-based).
====
[source,java]
----
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder().appId("myapp") //
.userIdMechanism(new MacAddressUserId()) //
.build();
return new AppIdAuthentication(options, vaultClient());
}
// …
}
----
====
The corresponding command to generate the IP address UserId from a command line is:
----
$ echo -n 0AFEDE1234AC | sha256sum
----
NOTE: The Mac address is specified uppercase and without colons.
Including the line break of `echo` leads to a different hash value
so make sure to include the `-n` flag.
==== Custom UserId
A more advanced approach lets you implementing your own `AppIdUserIdMechanism`.
This class must be on your classpath and must implement
the `org.springframework.vault.authentication.AppIdUserIdMechanism` interface
and the `createUserId` method. Spring Vault will obtain the UserId
by calling `createUserId` each time it authenticates using AppId to
obtain a token.
[source,java]
.MyUserIdMechanism.java
----
public class MyUserIdMechanism implements AppIdUserIdMechanism {
@Override
public String createUserId() {
String userId = ...
return userId;
}
}
----
See also: https://www.vaultproject.io/docs/auth/app-id.html[Vault Documentation: Using the App ID auth backend]
=== AWS-EC2 authentication
The https://www.vaultproject.io/docs/auth/aws-ec2.html[aws-ec2]
auth backend provides a secure introduction mechanism
for AWS EC2 instances, allowing automated retrieval of a Vault
token. Unlike most Vault authentication backends, this backend
does not require first-deploying, or provisioning security-sensitive
credentials (tokens, username/password, client certificates, etc.).
Instead, it treats AWS as a Trusted Third Party and uses the
cryptographically signed dynamic metadata information that uniquely
represents each EC2 instance.
====
[source,java]
----
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
return new AwsEc2Authentication(vaultClient());
}
// …
}
----
====
AWS-EC2 authentication enables nonce by default to follow
the Trust On First Use (TOFU) principle. Any unintended party that
gains access to the PKCS#7 identity metadata can authenticate
against Vault.
During the first login, Spring Vault generates a nonce
that is stored in the auth backend aside the instance Id.
Re-authentication requires the same nonce to be sent. Any other
party does not have the nonce and can raise an alert in Vault for
further investigation.
The nonce is kept in memory and is lost during application restart.
AWS-EC2 authentication roles are optional and default to the AMI.
You can configure the authentication role by setting
it in `AwsEc2AuthenticationOptions`.
See also: https://www.vaultproject.io/docs/auth/aws-ec2.html[Vault Documentation: Using the aws-ec2 auth backend]
=== TLS certificate authentication
The `cert` auth backend allows authentication using SSL/TLS client
certificates that are either signed by a CA or self-signed.
To enable `cert` authentication you need to:
1. Use SSL, see <<vault.client-ssl>>
2. Configure a Java `Keystore` that contains the client
certificate and the private key
====
[source,java]
----
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
return new ClientCertificateAuthentication(options, vaultClient());
}
// …
}
----
====
See also: https://www.vaultproject.io/docs/auth/cert.html[Vault Documentation: Using the cert auth backend]
[[vault.client-ssl]]
== Vault Client SSL configuration
SSL can be configured using `SslConfiguration` by setting various properties.
You can set either `javax.net.ssl.trustStore` to configure
JVM-wide SSL settings or configure `SslConfiguration`
to set SSL settings only for Spring Vault.
====
[source,java]
----
SslConfiguration sslConfiguration = new SslConfiguration( <1>
new FileSystemResource("client-cert.jks"), "changeit",
new FileSystemResource("truststore.jks"), "changeit");
SslConfiguration.forTrustStore(new FileSystemResource("keystore.jks"), <2>
"changeit")
SslConfiguration.forKeyStore(new FileSystemResource("keystore.jks"), <3>
"changeit")
----
<1> Full configuration.
<2> Configuring only trust store settings.
<3> Configuring only key store settings.
====
Please note that providing `SslConfiguration` can be only
applied when either Apache Http Components or the OkHttp client
is on your class-path.
[[vault.core.executioncallback]]
== Execution callbacks
One common design feature of all Spring template classes is that all functionality is routed into one of the templates execute callback methods. This helps ensure that exceptions and any resource management that maybe required are performed consistency. While this was of much greater need in the case of JDBC and JMS than with Vault, it still offers a single spot for access and logging to occur. As such, using the execute callback is the preferred way to access the Vault API to perform uncommon operations that we've not exposed as methods on `VaultTemplate`.
Here is a list of execute callback methods.
* `<T> T` *doWithVault* `(ClientCallback<T> clientCallback)` Executes the given `ClientCallback`, allows to interact with Vault using `VaultClient` without requiring a session.
* `<T> T` *doWithVault* `(SessionCallback<T> sessionCallback)` Executes the given `SessionCallback`, allows to interact with Vault in an authenticated session..
* `<T> T` *doWithRestTemplate* `(String pathTemplate, Map<String, ?> variables, RestTemplateCallback<T> callback)` Expands the `pathTemplate` to an `java.net.URI` and allows low-level interaction with the underlying `org.springframework.web.client.RestTemplate`.
Here is an example that uses the `ClientCallback` to initialize Vault:
====
[source,java]
----
return vaultTemplate.doWithVault(new ClientCallback<VaultInitializationResponse>() {
@Override
public VaultInitializationResponse doWithVault(VaultClient client) {
VaultResponseEntity<VaultInitializationResponse> response = client.putForEntity("sys/init",
vaultInitializationRequest, VaultInitializationResponse.class);
if (response.isSuccessful() && response.hasBody()) {
return response.getBody();
}
return null.
}
});
----
====

View File

@@ -0,0 +1,5 @@
Spring Vault Changelog
=============================
Changes in version 1.0.0.M1 (?)
-----------------------------------------

View File

@@ -0,0 +1,216 @@
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.
=======================================================================
To the extent any open source subcomponents are licensed under the EPL and/or other
similar licenses that require the source code and/or modifications to
source code to be made available (as would be noted above), you may obtain a
copy of the source code corresponding to the binaries for such open source
components and modifications thereto, if any, (the "Source Files"), by
downloading the Source Files from http://www.springsource.org/download,
or by sending a request, with your name and address to: VMware, Inc., 3401 Hillview
Avenue, Palo Alto, CA 94304, United States of America or email info@vmware.com. All
such requests should clearly specify: OPEN SOURCE FILES REQUEST, Attention General
Counsel. VMware shall mail a copy of the Source Files to you on a CD or equivalent
physical medium. This offer to obtain a copy of the Source Files is valid for three
years from the date you acquired this Software product.

View File

@@ -0,0 +1,10 @@
Spring Vault 1.10 M1
Copyright (c) [2016] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
You may not use this product except in compliance with the License.
This product may include a number of subcomponents with
separate copyright notices and license terms. Your use of the source
code for the these subcomponents is subject to the terms and
conditions of the subcomponent's license, as noted in the LICENSE file.

View File

@@ -0,0 +1,103 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
CA_DIR=work/ca
KEYSTORE_FILE=work/keystore.jks
CLIENT_CERT_KEYSTORE=work/client-cert.jks
if [[ -d work/ca ]] ; then
rm -Rf ${CA_DIR}
fi
if [[ -f ${KEYSTORE_FILE} ]] ; then
rm -Rf ${KEYSTORE_FILE}
fi
if [[ -f ${CLIENT_CERT_KEYSTORE} ]] ; then
rm -Rf ${CLIENT_CERT_KEYSTORE}
fi
mkdir -p ${CA_DIR}/private ${CA_DIR}/certs ${CA_DIR}/crl ${CA_DIR}/csr ${CA_DIR}/newcerts ${CA_DIR}/intermediate
echo "[INFO] Generating CA private key"
# Less bits = less secure = faster to generate
openssl genrsa -passout pass:changeit -aes256 -out ${CA_DIR}/private/ca.key.pem 2048
chmod 400 ${CA_DIR}/private/ca.key.pem
echo "[INFO] Generating CA certificate"
openssl req -config ${DIR}/openssl.cnf \
-key ${CA_DIR}/private/ca.key.pem \
-new -x509 -days 7300 -sha256 -extensions v3_ca \
-out ${CA_DIR}/certs/ca.cert.pem \
-passin pass:changeit \
-subj "/C=NN/ST=Unknown/L=Unknown/O=spring-cloud-vault-config/CN=CA Certificate"
echo "[INFO] Prepare CA database"
echo 1000 > ${CA_DIR}/serial
touch ${CA_DIR}/index.txt
echo "[INFO] Generating server private key"
openssl genrsa -aes256 \
-passout pass:changeit \
-out ${CA_DIR}/private/localhost.key.pem 2048
openssl rsa -in ${CA_DIR}/private/localhost.key.pem \
-out ${CA_DIR}/private/localhost.decrypted.key.pem \
-passin pass:changeit
chmod 400 ${CA_DIR}/private/localhost.key.pem
echo "[INFO] Generating server certificate request"
openssl req -config ${DIR}/openssl.cnf \
-key ${CA_DIR}/private/localhost.key.pem \
-passin pass:changeit \
-new -sha256 -out ${CA_DIR}/csr/localhost.csr.pem \
-subj "/C=NN/ST=Unknown/L=Unknown/O=spring-cloud-vault-config/CN=localhost"
echo "[INFO] Signing certificate request"
openssl ca -config ${DIR}/openssl.cnf \
-extensions server_cert -days 375 -notext -md sha256 \
-passin pass:changeit \
-batch \
-in ${CA_DIR}/csr/localhost.csr.pem \
-out ${CA_DIR}/certs/localhost.cert.pem
echo "[INFO] Generating client auth private key"
openssl genrsa -aes256 \
-passout pass:changeit \
-out ${CA_DIR}/private/client.key.pem 2048
openssl rsa -in ${CA_DIR}/private/client.key.pem \
-out ${CA_DIR}/private/client.decrypted.key.pem \
-passin pass:changeit
chmod 400 ${CA_DIR}/private/client.key.pem
echo "[INFO] Generating client certificate request"
openssl req -config ${DIR}/openssl.cnf \
-key ${CA_DIR}/private/client.key.pem \
-passin pass:changeit \
-new -sha256 -out ${CA_DIR}/csr/client.csr.pem \
-subj "/C=NN/ST=Unknown/L=Unknown/O=spring-cloud-vault-config/CN=client"
echo "[INFO] Signing certificate request"
openssl ca -config ${DIR}/openssl.cnf \
-extensions usr_cert -days 375 -notext -md sha256 \
-passin pass:changeit \
-batch \
-in ${CA_DIR}/csr/client.csr.pem \
-out ${CA_DIR}/certs/client.cert.pem
echo "[INFO] Creating PKCS12 file with client certificate"
openssl pkcs12 -export -clcerts \
-in ${CA_DIR}/certs/client.cert.pem \
-inkey ${CA_DIR}/private/client.decrypted.key.pem \
-passout pass:changeit \
-out ${CA_DIR}/client.p12
${JAVA_HOME}/bin/keytool -importcert -keystore ${KEYSTORE_FILE} -file ${CA_DIR}/certs/ca.cert.pem -noprompt -storepass changeit
${JAVA_HOME}/bin/keytool -importkeystore \
-srckeystore ${CA_DIR}/client.p12 -srcstoretype PKCS12 -srcstorepass changeit\
-destkeystore ${CLIENT_CERT_KEYSTORE} -deststoretype JKS \
-noprompt -storepass changeit

12
src/test/bash/env.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
###########################################################################
# Vault environment settings. Source this file. #
###########################################################################
export VAULT_TOKEN=00000000-0000-0000-0000-000000000000
export VAULT_ADDR=https://localhost:8200
export VAULT_SKIP_VERIFY=false
export VAULT_CAPATH=${DIR}/work/ca/certs/ca.cert.pem

46
src/test/bash/install_vault.sh Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/bash
###########################################################################
# Download and Install Vault #
# This script is prepared for caching of the download directory #
###########################################################################
VAULT_VER="0.6.1"
UNAME=$(uname -s | tr '[:upper:]' '[:lower:]')
VAULT_ZIP="vault_${VAULT_VER}_${UNAME}_amd64.zip"
IGNORE_CERTS="${IGNORE_CERTS:-no}"
# cleanup
mkdir -p vault
mkdir -p download
if [[ ! -f "download/${VAULT_ZIP}" ]] ; then
cd download
# install Vault
if [[ "${IGNORE_CERTS}" == "no" ]] ; then
echo "Downloading Vault with certs verification"
wget "https://releases.hashicorp.com/vault/${VAULT_VER}/${VAULT_ZIP}"
else
echo "WARNING... Downloading Vault WITHOUT certs verification"
wget "https://releases.hashicorp.com/vault/${VAULT_VER}/${VAULT_ZIP}" --no-check-certificate
fi
if [[ $? != 0 ]] ; then
echo "Cannot download Vault"
exit 1
fi
cd ..
fi
cd vault
if [[ -f vault ]] ; then
rm vault
fi
unzip ../download/${VAULT_ZIP}
chmod a+x vault
# check
./vault --version

View File

@@ -0,0 +1,11 @@
#!/bin/bash
###########################################################################
# Start Vault on localhost:8200 #
###########################################################################
BASEDIR=`dirname $0`/../../..
./vault/vault server -config=${BASEDIR}/src/test/bash/vault.conf
exit $?

106
src/test/bash/openssl.cnf Normal file
View File

@@ -0,0 +1,106 @@
[ ca ]
# `man ca`
default_ca = CA_default
[ CA_default ]
# Directory and file locations.
dir = work/ca
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# The root key and root certificate.
private_key = $dir/private/ca.key.pem
certificate = $dir/certs/ca.cert.pem
# For certificate revocation lists.
crlnumber = $dir/crlnumber
crl = $dir/crl/ca.crl.pem
crl_extensions = crl_ext
default_crl_days = 30
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 375
preserve = no
policy = policy_strict
[ policy_strict ]
# The root CA should only sign intermediate certificates that match.
# See the POLICY FORMAT section of `man ca`.
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
# Options for the `req` tool (`man req`).
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
# SHA-1 is deprecated, so use SHA-2 instead.
default_md = sha256
# Extension to add when the -x509 option is used.
x509_extensions = v3_ca
[ req_distinguished_name ]
# See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
# Optionally, specify some defaults.
countryName_default = NN
stateOrProvinceName_default = Vault Test
localityName_default =
0.organizationName_default = spring-cloud-vault-config
#organizationalUnitName_default =
#emailAddress_default = info@spring-cloud-vault-config.dummy
[ v3_ca ]
# Extensions for a typical CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_intermediate_ca ]
# Extensions for a typical intermediate CA (`man x509v3_config`).
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ usr_cert ]
# Extensions for client certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "OpenSSL Generated Client Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
[ server_cert ]
# Extensions for server certificates (`man x509v3_config`).
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

10
src/test/bash/vault.conf Normal file
View File

@@ -0,0 +1,10 @@
backend "inmem" {
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "work/ca/certs/localhost.cert.pem"
tls_key_file = "work/ca/private/localhost.decrypted.key.pem"
}
disable_mlock = true