From 89c74fa4f06ba870414a8a247a056feb58ee60f1 Mon Sep 17 00:00:00 2001 From: Yoko Date: Mon, 10 Jun 2019 09:54:21 -0700 Subject: [PATCH 01/20] upgrade guide warning about downgrading (#6836) * upgrade guide warning about downgrading * Changed the wording --- website/source/docs/upgrading/index.html.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/website/source/docs/upgrading/index.html.md b/website/source/docs/upgrading/index.html.md index 51f15ce142..2b3ac46139 100644 --- a/website/source/docs/upgrading/index.html.md +++ b/website/source/docs/upgrading/index.html.md @@ -14,9 +14,13 @@ These are general upgrade instructions for Vault for both non-HA and HA setups. _Please ensure that you also read any version-specific upgrade notes which can be found in the sidebar._ -**Always** back up your data before upgrading! Vault does not make -backwards-compatibility guarantees for its data store. If you need to roll back -to a previous version of Vault, you should roll back your data store as well. +!> **IMPORTANT NOTE:** Always back up your data before upgrading! Vault does not +make backward-compatibility guarantees for its data store. Simply replacing the +newly-installed Vault binary with the previous version will not cleanly +downgrade Vault, as upgrades may perform changes to the underlying data +structure that make the data incompatible with a downgrade. If you need to roll +back to a previous version of Vault, you should roll back your data store as +well. ## Testing the Upgrade From c8b701938c2251ec1e420bf5cb939d964bd40985 Mon Sep 17 00:00:00 2001 From: Yoko Date: Mon, 10 Jun 2019 10:41:55 -0700 Subject: [PATCH 02/20] Adding vault kv command doc (#6845) * Adding vault kv command doc * Update website/source/docs/commands/kv/delete.html.md Co-Authored-By: Becca Petrin * Update website/source/docs/commands/kv/delete.html.md Co-Authored-By: Becca Petrin * Update website/source/docs/commands/kv/destroy.html.md Co-Authored-By: Becca Petrin * Update website/source/docs/commands/kv/destroy.html.md Co-Authored-By: Becca Petrin * Update website/source/docs/commands/kv/undelete.html.md Co-Authored-By: Becca Petrin * Update website/source/docs/commands/kv/delete.html.md Co-Authored-By: Becca Petrin --- .../source/docs/commands/kv/delete.html.md | 47 ++++++ .../source/docs/commands/kv/destroy.html.md | 44 ++++++ .../commands/kv/enable-versioning.html.md | 35 +++++ website/source/docs/commands/kv/get.html.md | 68 +++++++++ website/source/docs/commands/kv/index.html.md | 114 ++++++++++++++ website/source/docs/commands/kv/list.html.md | 45 ++++++ .../source/docs/commands/kv/metadata.html.md | 140 ++++++++++++++++++ website/source/docs/commands/kv/patch.html.md | 72 +++++++++ website/source/docs/commands/kv/put.html.md | 69 +++++++++ .../source/docs/commands/kv/rollback.html.md | 51 +++++++ .../source/docs/commands/kv/undelete.html.md | 45 ++++++ website/source/layouts/docs.erb | 15 ++ 12 files changed, 745 insertions(+) create mode 100644 website/source/docs/commands/kv/delete.html.md create mode 100644 website/source/docs/commands/kv/destroy.html.md create mode 100644 website/source/docs/commands/kv/enable-versioning.html.md create mode 100644 website/source/docs/commands/kv/get.html.md create mode 100644 website/source/docs/commands/kv/index.html.md create mode 100644 website/source/docs/commands/kv/list.html.md create mode 100644 website/source/docs/commands/kv/metadata.html.md create mode 100644 website/source/docs/commands/kv/patch.html.md create mode 100644 website/source/docs/commands/kv/put.html.md create mode 100644 website/source/docs/commands/kv/rollback.html.md create mode 100644 website/source/docs/commands/kv/undelete.html.md diff --git a/website/source/docs/commands/kv/delete.html.md b/website/source/docs/commands/kv/delete.html.md new file mode 100644 index 0000000000..1a364c892a --- /dev/null +++ b/website/source/docs/commands/kv/delete.html.md @@ -0,0 +1,47 @@ +--- +layout: "docs" +page_title: "kv delete - Command" +sidebar_title: "delete" +sidebar_current: "docs-commands-kv-delete" +description: |- + The "kv delete" command disables a secrets engine at a given PATH. The + argument corresponds to the enabled PATH of the engine, not the TYPE! All + secrets created by this engine are revoked and its Vault data is removed. +--- + +# kv delete + +The `kv delete` command deletes the data for the provided path in +the key/value secrets engine. If using K/V Version 2, its versioned data will +not be fully removed, but marked as deleted and will no longer be returned in +normal get requests. + +## Examples + +Delete the latest version of the key "creds": + +```text +$ vault kv delete secret/creds +Success! Data deleted (if it existed) at: secret/creds +``` + +**[K/V Version 2]** Delete version 11 of key "creds": + +```text +$ vault kv delete -versions=11 secret/creds +Success! Data deleted (if it existed) at: secret/creds +``` + +## Usage + +There are no flags beyond the [standard set of flags](/docs/commands/index.html) +included on all commands. + + +### Command Options + +- `-versions` `([]int: )` - The versions to be deleted. The versioned +data will not be deleted, but it will no longer be returned in normal get +requests. + +~> **NOTE:** This command option is only for K/V v2. diff --git a/website/source/docs/commands/kv/destroy.html.md b/website/source/docs/commands/kv/destroy.html.md new file mode 100644 index 0000000000..3f0fce38bb --- /dev/null +++ b/website/source/docs/commands/kv/destroy.html.md @@ -0,0 +1,44 @@ +--- +layout: "docs" +page_title: "kv destroy - Command" +sidebar_title: "destroy" +sidebar_current: "docs-commands-kv-destroy" +description: |- + The "kv destroy" command permanently removes the specified version data for + the provided key and version numbers from the key-value store. +--- + +# kv destroy + +~> **NOTE:** This is a [K/V Version 2](/docs/secrets/kv/kv-v2.html) secrets +engine command, and not available for Version 1. + +The `secrets enable` command permanently removes the specified versions' data +from the key/value secrets engine. If no key exists at the path, no action is +taken. + + +## Examples + +Destroy version 11 of the key "creds": + +```text +$ vault kv destroy -versions=11 secret/creds +Success! Data written to: secret/destroy/creds +``` + +## Usage + +There are no flags beyond the [standard set of flags](/docs/commands/index.html) +included on all commands. + +### Output Options + +- `-format` `(string: "table")` - Print the output in the given format. Valid + formats are "table", "json", or "yaml". This can also be specified via the + `VAULT_FORMAT` environment variable. + +### Command Options + +- `-versions` `([]int: )` - The versions to destroy. Their data will +be permanently deleted. diff --git a/website/source/docs/commands/kv/enable-versioning.html.md b/website/source/docs/commands/kv/enable-versioning.html.md new file mode 100644 index 0000000000..f7c31ecbab --- /dev/null +++ b/website/source/docs/commands/kv/enable-versioning.html.md @@ -0,0 +1,35 @@ +--- +layout: "docs" +page_title: "kv enable-versioning - Command" +sidebar_title: "enable-versioning" +sidebar_current: "docs-commands-kv-enable-versioning" +description: |- + The "kv enable-versioning" command turns on versioning for the backend + at the provided path. +--- + +# kv enable-versioning + +The `kv enable-versioning` command turns on versioning for an existing +non-versioned key/value secrets engine (K/V Version 1) at its path. + +## Examples + +This command turns on versioning for the K/V Version 1 secrets engine enabled at +"secret". + +```text +$ vault kv enable-versioning secret +Success! Tuned the secrets engine at: secret/ +``` + +## Usage + +There are no flags beyond the [standard set of flags](/docs/commands/index.html) +included on all commands. + +### Output Options + +- `-format` `(string: "table")` - Print the output in the given format. Valid + formats are "table", "json", or "yaml". This can also be specified via the + `VAULT_FORMAT` environment variable. diff --git a/website/source/docs/commands/kv/get.html.md b/website/source/docs/commands/kv/get.html.md new file mode 100644 index 0000000000..d10d70d49a --- /dev/null +++ b/website/source/docs/commands/kv/get.html.md @@ -0,0 +1,68 @@ +--- +layout: "docs" +page_title: "kv get - Command" +sidebar_title: "get" +sidebar_current: "docs-commands-kv-get" +description: |- + The "kv get" command retrieves the value from Vault's key-value store at the + given key name. If no key exists with that name, an error is returned. If a + key exists with that name but has no data, nothing is returned. +--- + +# kv get + +The `kv get` command retrieves the value from K/V secrets engine at the given +key name. If no key exists with that name, an error is returned. If a key exists +with the name but has no data, nothing is returned. + +## Examples + +Retrieve the data of the key "creds": + +```text +$ vault kv get secret/creds +====== Metadata ====== +Key Value +--- ----- +created_time 2019-06-06T06:03:26.595978Z +deletion_time n/a +destroyed false +version 5 + +====== Data ====== +Key Value +--- ----- +passcode my-long-passcode +``` + +If K/V Version 1 secrets engine is enabled at "secret", the output has no +metadata since there is no versioning information associated with the data: + +```text +$ vault kv get secret/creds +====== Data ====== +Key Value +--- ----- +passcode my-long-passcode +``` + +## Usage + +There are no flags beyond the [standard set of flags](/docs/commands/index.html) +included on all commands. + +### Output Options + +- `-field` `(string: "")` - Print only the field with the given name. Specifying + this option will take precedence over other formatting directives. The result + will not have a trailing newline making it ideal for piping to other + processes. + +- `-format` `(string: "table")` - Print the output in the given format. Valid + formats are "table", "json", or "yaml". This can also be specified via the + `VAULT_FORMAT` environment variable. + +### Command Options + +- `-version` `(int: 0)` - Specifies the version to return. If not set the + latest version is returned. diff --git a/website/source/docs/commands/kv/index.html.md b/website/source/docs/commands/kv/index.html.md new file mode 100644 index 0000000000..ac749ab070 --- /dev/null +++ b/website/source/docs/commands/kv/index.html.md @@ -0,0 +1,114 @@ +--- +layout: "docs" +page_title: "kv - Command" +sidebar_title: "kv" +sidebar_current: "docs-commands-kv" +description: |- + The "kv" command groups subcommands for interacting with Vault's key/value + secret engine. +--- + +# kv + +The `kv` command groups subcommands for interacting with Vault's key/value +secrets engine (both [K/V Version 1](/docs/secrets/kv/kv-v1.html) and [K/V +Version 2](/docs/secrets/kv/kv-v2.html). + + +## Examples + +Create or update the key named "creds" in the K/V Version 2 enabled at "secret" +with the value "passcode=my-long-passcode": + +```text +$ vault kv put secret/creds passcode=my-long-passcode +Key Value +--- ----- +created_time 2019-06-06T04:07:33.279432Z +deletion_time n/a +destroyed false +version 1 +``` + +Read this value back: + +```text +$ vault kv get secret/creds +====== Metadata ====== +Key Value +--- ----- +created_time 2019-06-06T04:07:33.279432Z +deletion_time n/a +destroyed false +version 1 + +====== Data ====== +Key Value +--- ----- +passcode my-long-passcode +``` + +Get metadata for the key named "creds": + +```text +$ vault kv metadata get secret/creds +======= Metadata ======= +Key Value +--- ----- +cas_required false +created_time 2019-06-06T04:07:33.279432Z +current_version 1 +max_versions 0 +oldest_version 0 +updated_time 2019-06-06T04:07:33.279432Z + +====== Version 1 ====== +Key Value +--- ----- +created_time 2019-06-06T04:07:33.279432Z +deletion_time n/a +destroyed false +``` + + +Get a specific version of the key named "creds": + +```text +$ vault kv get -version=1 secret/creds +====== Metadata ====== +Key Value +--- ----- +created_time 2019-06-06T04:07:33.279432Z +deletion_time n/a +destroyed false +version 1 + +====== Data ====== +Key Value +--- ----- +passcode my-long-passcode +``` + + +## Usage + +```text +Usage: vault kv [options] [args] + + # ... + +Subcommands: + delete Deletes versions in the KV store + destroy Permanently removes one or more versions in the KV store + enable-versioning Turns on versioning for a KV store + get Retrieves data from the KV store + list List data or secrets + metadata Interact with Vault's Key-Value storage + patch Sets or updates data in the KV store without overwriting + put Sets or updates data in the KV store + rollback Rolls back to a previous version of data + undelete Undeletes versions in the KV store +``` + +For more information, examples, and usage about a subcommand, click on the name +of the subcommand in the sidebar. diff --git a/website/source/docs/commands/kv/list.html.md b/website/source/docs/commands/kv/list.html.md new file mode 100644 index 0000000000..e0a90269da --- /dev/null +++ b/website/source/docs/commands/kv/list.html.md @@ -0,0 +1,45 @@ +--- +layout: "docs" +page_title: "kv list - Command" +sidebar_title: "list" +sidebar_current: "docs-commands-kv-list" +description: |- + The "kv list" command lists data from Vault's K/V secrets engine at the given + path. +--- + +# kv list + +The `kv list` command returns a list of key names at the specified location. +Folders are suffixed with /. The input must be a folder; list on a file will not +return a value. Note that no policy-based filtering is performed on keys; do not +encode sensitive information in key names. The values themselves are not +accessible via this command. + +Use this command to list all existing key names at a specific path. + +## Examples + +List values under the key "my-app": + +```text +$ vault kv list secret/my-app/ +Keys +---- +admin_creds +domain +eng_creds +qa_creds +release +``` + +## Usage + +There are no flags beyond the [standard set of flags](/docs/commands/index.html) +included on all commands. + +### Output Options + +- `-format` `(string: "table")` - Print the output in the given format. Valid + formats are "table", "json", or "yaml". This can also be specified via the + `VAULT_FORMAT` environment variable. diff --git a/website/source/docs/commands/kv/metadata.html.md b/website/source/docs/commands/kv/metadata.html.md new file mode 100644 index 0000000000..3c07b36946 --- /dev/null +++ b/website/source/docs/commands/kv/metadata.html.md @@ -0,0 +1,140 @@ +--- +layout: "docs" +page_title: "kv metadata - Command" +sidebar_title: "metadata" +sidebar_current: "docs-commands-kv-metadata" +description: |- + The "kv metadata" command has subcommands for interacting with the metadata + endpoint in Vault's key-value store. +--- + +# kv metadata + +~> **NOTE:** This is a [K/V Version 2](/docs/secrets/kv/kv-v2.html) secrets +engine command, and not available for Version 1. + + +The `kv metadata` command has subcommands for interacting with the metadata and +versions for the versioned secrets (K/V Version 2 secrets engine) at the +specified path. + + +## Usage + +```text +Usage: vault kv metadata [options] [args] + + # ... + +Subcommands: + delete Deletes all versions and metadata for a key in the KV store + get Retrieves key metadata from the KV store + put Sets or updates key settings in the KV store +``` + +### kv metadata delete + +The `kv metadata delete` command deletes all versions and metadata for the +provided key. + +#### Examples + +Deletes all versions and metadata of the key "creds": + +```text +$ vault kv metadata delete secret/creds +Success! Data deleted (if it existed) at: secret/metadata/creds +``` + + +### kv metadata get + +The `kv metadata get` command retrieves the metadata of the versioned secrets at +the given key name. If no key exists with that name, an error is returned. + +#### Examples + +Retrieves the metadata of the key name, "creds": + +```text +$ vault kv metadata get secret/creds +======= Metadata ======= +Key Value +--- ----- +cas_required false +created_time 2019-06-06T04:07:33.279432Z +current_version 5 +max_versions 0 +oldest_version 0 +updated_time 2019-06-06T06:03:26.595978Z + +====== Version 1 ====== +Key Value +--- ----- +created_time 2019-06-06T04:07:33.279432Z +deletion_time n/a +destroyed false + +====== Version 2 ====== +Key Value +--- ----- +created_time 2019-06-06T06:03:12.187441Z +deletion_time n/a +destroyed false + +... +``` + + +### kv metadata put + +The `kv metadata put` command can be used to create a blank key in the K/V v2 +secrets engine or to update key configuration for a specified key. + + +#### Examples + +Create a key in the K/V v2 with no data at the key "creds": + +```text +$ vault kv metadata put secret/creds +Success! Data written to: secret/metadata/creds +``` + +Set the maximum number of versions to keep for the key "creds": + +```text +$ vault kv metadata put -max-versions=5 secret/creds +Success! Data written to: secret/metadata/creds +``` + +**NOTE:** If not set, the backend’s configured max version is used. Once a key +has more than the configured allowed versions the oldest version will be +permanently deleted. + + +Require Check-and-Set for the key "creds": + +```text +$ vault kv metadata put -cas-required secret/creds +``` + +**NOTE:** When check-and-set is required, the key will require the `cas` +parameter to be set on all write requests. Otherwise, the backend’s +configuration will be used. + +#### Output Options + +- `-format` `(string: "table")` - Print the output in the given format. Valid + formats are "table", "json", or "yaml". This can also be specified via the + `VAULT_FORMAT` environment variable. + +#### Subcommand Options + +- `-cas-required` `(bool: false)` - If true the key will require the cas + parameter to be set on all write requests. If false, the backend’s + configuration will be used. The default is false. + +- `-max-versions` `(int: 0)` - The number of versions to keep per key. If not + set, the backend’s configured max version is used. Once a key has more than the + configured allowed versions the oldest version will be permanently deleted. diff --git a/website/source/docs/commands/kv/patch.html.md b/website/source/docs/commands/kv/patch.html.md new file mode 100644 index 0000000000..64cb3d208b --- /dev/null +++ b/website/source/docs/commands/kv/patch.html.md @@ -0,0 +1,72 @@ +--- +layout: "docs" +page_title: "kv patch - Command" +sidebar_title: "patch" +sidebar_current: "docs-commands-kv-patch" +description: |- + The "kv patch" command writes the data to the given path in the key-value + store. The data can be of any type. +--- + +# kv patch + +~> **NOTE:** This is a [K/V Version 2](/docs/secrets/kv/kv-v2.html) secrets +engine command, and not available for Version 1. + + +The `kv patch` command writes the data to the given path in the K/V v2 secrets +engine. The data can be of any type. Unlike the `kv put` command, the `patch` +command combines the change with existing data instead of replacing them. +Therefore, this command makes it easy to make a partial updates to an existing +data. + +## Examples + +If you wish to add an additional key-value (`ttl=48h`) to the existing data at +the key "creds": + +```text +$ vault kv patch secret/creds ttl=48h +Key Value +--- ----- +created_time 2019-06-06T16:46:22.090654Z +deletion_time n/a +destroyed false +version 6 +``` + +**NOTE:** The `kv put` command requires both the existing data and +the data you wish to add in order to accomplish the same result. + +```text +$ vault kv put secret/creds ttl=48h passcode=my-long-passcode +``` + +The data can also be consumed from a file on disk by prefixing with the "@" +symbol. For example: + +```text +$ vault kv patch secret/creds @data.json +``` + +Or it can be read from stdin using the "-" symbol: + +```text +$ echo "abcd1234" | vault kv patch secret/foo bar=- +``` + +## Usage + +There are no flags beyond the [standard set of flags](/docs/commands/index.html) +included on all commands. + +### Output Options + +- `-field` `(string: "")` - Print only the field with the given name. Specifying + this option will take precedence over other formatting directives. The result + will not have a trailing newline making it ideal for piping to other + processes. + +- `-format` `(string: "table")` - Print the output in the given format. Valid + formats are "table", "json", or "yaml". This can also be specified via the + `VAULT_FORMAT` environment variable. diff --git a/website/source/docs/commands/kv/put.html.md b/website/source/docs/commands/kv/put.html.md new file mode 100644 index 0000000000..25c58e2a0c --- /dev/null +++ b/website/source/docs/commands/kv/put.html.md @@ -0,0 +1,69 @@ +--- +layout: "docs" +page_title: "kv put - Command" +sidebar_title: "put" +sidebar_current: "docs-commands-kv-put" +description: |- + The "kv put" command writes the data to the given path in the K/V secrets + engine. The data can be of any type. +--- + +# kv put + +The `kv put` command writes the data to the given path in the K/V secrets +engine. + +If working with K/V v2, this command creates a new version of a secret at the +specified location. If working with K/V v1, this command stores the given secret +at the specified location. + +Regardless of the K/V version, if the value does not yet exist at the specified +path, the calling token must have an ACL policy granting the "create" +capability. If the value already exists, the calling token must have an ACL +policy granting the "update" capability. + + +## Examples + +Writes the data to the key "creds": + +```text +$ vault kv put secret/creds passcode=my-long-passcode +``` + +The data can also be consumed from a file on disk by prefixing with the "@" +symbol. For example: + +```text +$ vault kv put secret/foo @data.json +``` + +Or it can be read from stdin using the "-" symbol: + +```text +$ echo "abcd1234" | vault kv put secret/foo bar=- +``` + +## Usage + +There are no flags beyond the [standard set of flags](/docs/commands/index.html) +included on all commands. + +### Output Options + +- `-field` `(string: "")` - Print only the field with the given name. Specifying + this option will take precedence over other formatting directives. The result + will not have a trailing newline making it ideal for piping to other + processes. + +- `-format` `(string: "table")` - Print the output in the given format. Valid + formats are "table", "json", or "yaml". This can also be specified via the + `VAULT_FORMAT` environment variable. + +### Command Options + +- `-cas` `(int: 0)` - Specifies to use a Check-And-Set operation. If not set the + write will be allowed. If set to 0 a write will only be allowed if the key + doesn’t exist. If the index is non-zero the write will only be allowed if the + key’s current version matches the version specified in the cas parameter. The + default is -1. diff --git a/website/source/docs/commands/kv/rollback.html.md b/website/source/docs/commands/kv/rollback.html.md new file mode 100644 index 0000000000..5441561e3a --- /dev/null +++ b/website/source/docs/commands/kv/rollback.html.md @@ -0,0 +1,51 @@ +--- +layout: "docs" +page_title: "kv rollback - Command" +sidebar_title: "rollback" +sidebar_current: "docs-commands-kv-rollback" +description: |- + The "kv rollback" command restores a given previous version to the current + version at the given path. +--- + +# kv rollback + +~> **NOTE:** This is a [K/V Version 2](/docs/secrets/kv/kv-v2.html) secrets +engine command, and not available for Version 1. + + +The `kv rollback` command restores a given previous version to the current +version at the given path. The value is written as a new version; for instance, +if the current version is 5 and the rollback version is 2, the data from version +2 will become version 6. This command makes it easy to restore unintentionally +overwritten data. + +## Examples + +Restores the version 2 of the data at key "creds": + +```text +$ vault kv rollback -version=2 secret/creds +Key Value +--- ----- +created_time 2019-06-06T17:07:19.299831Z +deletion_time n/a +destroyed false +version 6 +``` + +## Usage + +There are no flags beyond the [standard set of flags](/docs/commands/index.html) +included on all commands. + +### Output Options + +- `-format` `(string: "table")` - Print the output in the given format. Valid + formats are "table", "json", or "yaml". This can also be specified via the + `VAULT_FORMAT` environment variable. + +### Command Options + +- `-version` `(int: 0)` - Specifies the version number that should be made +current again. diff --git a/website/source/docs/commands/kv/undelete.html.md b/website/source/docs/commands/kv/undelete.html.md new file mode 100644 index 0000000000..e945530956 --- /dev/null +++ b/website/source/docs/commands/kv/undelete.html.md @@ -0,0 +1,45 @@ +--- +layout: "docs" +page_title: "kv undelete - Command" +sidebar_title: "undelete" +sidebar_current: "docs-commands-kv-undelete" +description: |- + The "kv undelete" command undeletes the data for the provided version and path + in the key-value store. This restores the data, allowing it to be returned on + get requests. +--- + +# kv undelete + +~> **NOTE:** This is a [K/V Version 2](/docs/secrets/kv/kv-v2.html) secrets +engine command, and not available for Version 1. + + +The `kv undelete` command undoes the deletes of the data for the provided version +and path in the key-value store. This restores the data, allowing it to be +returned on get requests. + +## Examples + +Undelete version 3 of the key "creds": + +```text +$ vault kv undelete -versions=3 secret/creds +Success! Data written to: secret/undelete/creds +``` + +## Usage + +There are no flags beyond the [standard set of flags](/docs/commands/index.html) +included on all commands. + +### Output Options + +- `-format` `(string: "table")` - Print the output in the given format. Valid + formats are "table", "json", or "yaml". This can also be specified via the + `VAULT_FORMAT` environment variable. + +### Command Options + +- `-versions` `([]int: )` - Specifies the version number that should +be made current again. diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index e7282bfed5..10b3806439 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -100,6 +100,21 @@ ] }, 'delete', + { + category: 'kv', + content: [ + 'delete', + 'destroy', + 'enable-versioning', + 'get', + 'list', + 'metadata', + 'patch', + 'put', + 'rollback', + 'undelete' + ] + }, { category: 'lease', content: ['renew', 'revoke'] From 1f3ec37a57d82a7288e19187d1660a039d9bf350 Mon Sep 17 00:00:00 2001 From: ncabatoff Date: Mon, 10 Jun 2019 14:07:16 -0400 Subject: [PATCH 03/20] Add core.coreNumber field, used to differentiate multiple cores/clusters when running tests (#6855) This is not used or exposed in prod. Remove some test-specific code from the cluster-building helpers. The corresponding additions go on the ent side. --- helper/testhelpers/testhelpers.go | 55 +++++++++++++++++++------------ vault/core.go | 2 ++ vault/mount.go | 1 + vault/testing.go | 7 ++++ 4 files changed, 44 insertions(+), 21 deletions(-) diff --git a/helper/testhelpers/testhelpers.go b/helper/testhelpers/testhelpers.go index 3a02fbcd75..9e3afe03ce 100644 --- a/helper/testhelpers/testhelpers.go +++ b/helper/testhelpers/testhelpers.go @@ -265,10 +265,6 @@ func ConfClusterAndCore(t testing.T, conf *vault.CoreConfig, opts *vault.TestClu "approle": credAppRole.Factory, "userpass": credUserpass.Factory, } - coreConfig.LogicalBackends = map[string]logical.Factory{ - "local-kv": PassthroughWithLocalPathsFactory, - "leased-kv": vault.LeasedPassthroughBackendFactory, - } vault.AddNoopAudit(&coreConfig) cluster := vault.NewTestCluster(t, &coreConfig, opts) cluster.Start() @@ -281,13 +277,6 @@ func ConfClusterAndCore(t testing.T, conf *vault.CoreConfig, opts *vault.TestClu return cluster, core } -func GetClusterAndCore(t testing.T, logger log.Logger, handlerFunc func(*vault.HandlerProperties) http.Handler) (*vault.TestCluster, *vault.TestClusterCore) { - return ConfClusterAndCore(t, &vault.CoreConfig{}, &vault.TestClusterOptions{ - Logger: logger, - HandlerFunc: handlerFunc, - }) -} - func GetPerfReplicatedClusters(t testing.T, conf *vault.CoreConfig, opts *vault.TestClusterOptions) *ReplicatedTestClusters { ret := &ReplicatedTestClusters{} @@ -305,13 +294,18 @@ func GetPerfReplicatedClusters(t testing.T, conf *vault.CoreConfig, opts *vault. // Set this lower so that state populates quickly to standby nodes cluster.HeartbeatInterval = 2 * time.Second - opts1 := *opts - opts1.Logger = logger.Named("perf-pri") - ret.PerfPrimaryCluster, _ = ConfClusterAndCore(t, conf, &opts1) + numCores := opts.NumCores + if numCores == 0 { + numCores = vault.DefaultNumCores + } - opts2 := *opts - opts1.Logger = logger.Named("perf-sec") - ret.PerfSecondaryCluster, _ = ConfClusterAndCore(t, conf, &opts2) + localopts := *opts + localopts.Logger = logger.Named("perf-pri") + ret.PerfPrimaryCluster, _ = ConfClusterAndCore(t, conf, &localopts) + + localopts.Logger = logger.Named("perf-sec") + localopts.FirstCoreNumber += numCores + ret.PerfSecondaryCluster, _ = ConfClusterAndCore(t, conf, &localopts) SetupTwoClusterPerfReplication(t, ret.PerfPrimaryCluster, ret.PerfSecondaryCluster) @@ -319,6 +313,12 @@ func GetPerfReplicatedClusters(t testing.T, conf *vault.CoreConfig, opts *vault. } func GetFourReplicatedClusters(t testing.T, handlerFunc func(*vault.HandlerProperties) http.Handler) *ReplicatedTestClusters { + return GetFourReplicatedClustersWithConf(t, &vault.CoreConfig{}, &vault.TestClusterOptions{ + HandlerFunc: handlerFunc, + }) +} + +func GetFourReplicatedClustersWithConf(t testing.T, conf *vault.CoreConfig, opts *vault.TestClusterOptions) *ReplicatedTestClusters { ret := &ReplicatedTestClusters{} logger := log.New(&log.LoggerOptions{ @@ -328,13 +328,26 @@ func GetFourReplicatedClusters(t testing.T, handlerFunc func(*vault.HandlerPrope // Set this lower so that state populates quickly to standby nodes cluster.HeartbeatInterval = 2 * time.Second - ret.PerfPrimaryCluster, _ = GetClusterAndCore(t, logger.Named("perf-pri"), handlerFunc) + numCores := opts.NumCores + if numCores == 0 { + numCores = vault.DefaultNumCores + } - ret.PerfSecondaryCluster, _ = GetClusterAndCore(t, logger.Named("perf-sec"), handlerFunc) + localopts := *opts + localopts.Logger = logger.Named("perf-pri") + ret.PerfPrimaryCluster, _ = ConfClusterAndCore(t, conf, &localopts) - ret.PerfPrimaryDRCluster, _ = GetClusterAndCore(t, logger.Named("perf-pri-dr"), handlerFunc) + localopts.Logger = logger.Named("perf-sec") + localopts.FirstCoreNumber += numCores + ret.PerfSecondaryCluster, _ = ConfClusterAndCore(t, conf, &localopts) - ret.PerfSecondaryDRCluster, _ = GetClusterAndCore(t, logger.Named("perf-sec-dr"), handlerFunc) + localopts.Logger = logger.Named("perf-pri-dr") + localopts.FirstCoreNumber += numCores + ret.PerfPrimaryDRCluster, _ = ConfClusterAndCore(t, conf, &localopts) + + localopts.Logger = logger.Named("perf-sec-dr") + localopts.FirstCoreNumber += numCores + ret.PerfSecondaryDRCluster, _ = ConfClusterAndCore(t, conf, &localopts) builder := &ReplicatedTestClustersBuilder{clusters: ret} builder.setupFourClusterReplication(t) diff --git a/vault/core.go b/vault/core.go index f2858956e9..755b368303 100644 --- a/vault/core.go +++ b/vault/core.go @@ -427,6 +427,8 @@ type Core struct { // Stores request counters counters counters + + coreNumber int } // CoreConfig is used to parameterize a core diff --git a/vault/mount.go b/vault/mount.go index 5b055d1fe7..6177f4a3f2 100644 --- a/vault/mount.go +++ b/vault/mount.go @@ -1199,6 +1199,7 @@ func (c *Core) newLogicalBackend(ctx context.Context, entry *MountEntry, sysView BackendUUID: entry.BackendAwareUUID, } + ctx = context.WithValue(ctx, "core_number", c.coreNumber) b, err := f(ctx, config) if err != nil { return nil, err diff --git a/vault/testing.go b/vault/testing.go index ca0b318950..fbf9423d30 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -989,6 +989,7 @@ type TestClusterOptions struct { TempDir string CACert []byte CAKey *ecdsa.PrivateKey + FirstCoreNumber int } var DefaultNumCores = 3 @@ -1019,6 +1020,11 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te numCores = opts.NumCores } + var firstCoreNumber int + if opts != nil { + firstCoreNumber = opts.FirstCoreNumber + } + certIPs := []net.IP{ net.IPv6loopback, net.ParseIP("127.0.0.1"), @@ -1371,6 +1377,7 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te if err != nil { t.Fatalf("err: %v", err) } + c.coreNumber = firstCoreNumber + i cores = append(cores, c) coreConfigs = append(coreConfigs, &localConfig) if opts != nil && opts.HandlerFunc != nil { From f06bfa3087ab51bf7a12d339fc744e453546aad8 Mon Sep 17 00:00:00 2001 From: Mark Gritter Date: Mon, 10 Jun 2019 13:59:13 -0500 Subject: [PATCH 04/20] Be more permissive in what URLs to allow; added test for unix socket. (#6859) --- physical/consul/consul.go | 4 +--- physical/consul/consul_test.go | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/physical/consul/consul.go b/physical/consul/consul.go index e050cf08ee..4db2b36287 100644 --- a/physical/consul/consul.go +++ b/physical/consul/consul.go @@ -237,9 +237,7 @@ func NewConsulBackend(conf map[string]string, logger log.Logger) (physical.Backe logger.Debug("config address parsed", "scheme", parts[0]) logger.Debug("config scheme parsed", "address", parts[1]) } - } else { - return nil, errors.New("address should be host[:port], not URL") - } + } // allow "unix:" or whatever else consul supports in the future } } if scheme, ok := conf["scheme"]; ok { diff --git a/physical/consul/consul_test.go b/physical/consul/consul_test.go index fa45f972f1..b474fb9ae5 100644 --- a/physical/consul/consul_test.go +++ b/physical/consul/consul_test.go @@ -231,6 +231,24 @@ func TestConsul_newConsulBackend(t *testing.T) { max_parallel: 4, consistencyMode: "strong", }, + { + name: "Unix socket", + consulConfig: map[string]string{ + "address": "unix:///tmp/.consul.http.sock", + }, + address: "/tmp/.consul.http.sock", + scheme: "http", // Default, not overridden? + + // Defaults + checkTimeout: 5 * time.Second, + redirectAddr: "http://127.0.0.1:8200", + path: "vault/", + service: "vault", + token: "", + max_parallel: 4, + disableReg: false, + consistencyMode: "default", + }, { name: "Scheme in address", consulConfig: map[string]string{ From 37f4b6596258abaf5c504bc8af419b9ea282a667 Mon Sep 17 00:00:00 2001 From: Michel Vocks Date: Tue, 11 Jun 2019 10:05:44 +0200 Subject: [PATCH 05/20] Fixed wrong rekey recovery backup client API URL (#6841) * Fixed wrong rekey recovery backup client API URL * Fixed wrong rekey recovery backup client API URL delete * Changed output for recovery backup key delete --- api/sys_rekey.go | 4 ++-- command/operator_rekey.go | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/api/sys_rekey.go b/api/sys_rekey.go index 55f1a703d4..153e486c6d 100644 --- a/api/sys_rekey.go +++ b/api/sys_rekey.go @@ -234,7 +234,7 @@ func (c *Sys) RekeyRetrieveBackup() (*RekeyRetrieveResponse, error) { } func (c *Sys) RekeyRetrieveRecoveryBackup() (*RekeyRetrieveResponse, error) { - r := c.c.NewRequest("GET", "/v1/sys/rekey/recovery-backup") + r := c.c.NewRequest("GET", "/v1/sys/rekey/recovery-key-backup") ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() @@ -275,7 +275,7 @@ func (c *Sys) RekeyDeleteBackup() error { } func (c *Sys) RekeyDeleteRecoveryBackup() error { - r := c.c.NewRequest("DELETE", "/v1/sys/rekey/recovery-backup") + r := c.c.NewRequest("DELETE", "/v1/sys/rekey/recovery-key-backup") ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() diff --git a/command/operator_rekey.go b/command/operator_rekey.go index 724d9ad3ca..d437a92591 100644 --- a/command/operator_rekey.go +++ b/command/operator_rekey.go @@ -685,12 +685,22 @@ func (c *OperatorRekeyCommand) printUnsealKeys(client *api.Client, status *api.R if len(resp.PGPFingerprints) > 0 && resp.Backup { c.UI.Output("") - c.UI.Output(wrapAtLength(fmt.Sprintf( - "The encrypted unseal keys are backed up to \"core/unseal-keys-backup\"" + - "in the storage backend. Remove these keys at any time using " + - "\"vault operator rekey -backup-delete\". Vault does not automatically " + - "remove these keys.", - ))) + switch strings.ToLower(strings.TrimSpace(c.flagTarget)) { + case "barrier": + c.UI.Output(wrapAtLength(fmt.Sprintf( + "The encrypted unseal keys are backed up to \"core/unseal-keys-backup\" " + + "in the storage backend. Remove these keys at any time using " + + "\"vault operator rekey -backup-delete\". Vault does not automatically " + + "remove these keys.", + ))) + case "recovery", "hsm": + c.UI.Output(wrapAtLength(fmt.Sprintf( + "The encrypted unseal keys are backed up to \"core/recovery-keys-backup\" " + + "in the storage backend. Remove these keys at any time using " + + "\"vault operator rekey -backup-delete -target=recovery\". Vault does not automatically " + + "remove these keys.", + ))) + } } switch status.VerificationRequired { From 380f460eff23af251c237fdcbf12da50017b35ef Mon Sep 17 00:00:00 2001 From: Sam Salisbury Date: Tue, 11 Jun 2019 15:55:53 +0100 Subject: [PATCH 06/20] ci: break config into separate files (#6849) * ci: break config into separate files * Untangle githooks * githooks: fix whitespace * .hooks/pre-commit: add ui -> lint-staged check - We no longer require dependency on husky with this change. * ui: remove husky dependency and config - The previous commit obviates the need for it. - We will now have to manage these hooks by hand, but this removes the conflict between husky-installed hooks and those in the .hooks dir. * ui: update yarn.lock with husky removed * .hooks/pre-commit: always use subshell + docs - Always use subshell means we consistently exit from the same place which feels less complex. - Docs are necessary for horrible bash like this I think... * Makefile: remove old husky githooks - Husky has installed a handler for every single git hook. - This causes warnings on every git operation. - Eventually we can remove this, but better not to confuse people with these messages for now. * ci: fix go build tags * Makefile: improve compatibility of rm call - Looks like the xargs in Travis does something different to the one on my mac, this more verbose call should be safe everywhere. * ci: fix make target names * ci: fix test-ui invocation * Makefile: simplify husky hook cleanup * ci: more focussed readme --- .circleci/.gitattributes | 1 + .circleci/.gitignore | 1 + .circleci/Makefile | 47 ++ .circleci/README.md | 117 ++++ .circleci/config.yml | 661 +++++++++++++----- .circleci/config/@config.yml | 53 ++ .circleci/config/commands/go_test.yml | 55 ++ .circleci/config/jobs/build-go-dev.yml | 21 + .circleci/config/jobs/go-mod-download.yml | 15 + .../config/jobs/install-ui-dependencies.yml | 13 + .circleci/config/jobs/test-go-race.yml | 17 + .circleci/config/jobs/test-go.yml | 17 + .circleci/config/jobs/test-ui.yml | 33 + .circleci/config/workflows/ci.yml | 16 + .hooks/pre-commit | 104 +++ Makefile | 7 + ui/package.json | 6 - ui/yarn.lock | 57 +- 18 files changed, 992 insertions(+), 249 deletions(-) create mode 100644 .circleci/.gitattributes create mode 100644 .circleci/.gitignore create mode 100644 .circleci/Makefile create mode 100644 .circleci/README.md create mode 100644 .circleci/config/@config.yml create mode 100644 .circleci/config/commands/go_test.yml create mode 100644 .circleci/config/jobs/build-go-dev.yml create mode 100644 .circleci/config/jobs/go-mod-download.yml create mode 100644 .circleci/config/jobs/install-ui-dependencies.yml create mode 100644 .circleci/config/jobs/test-go-race.yml create mode 100644 .circleci/config/jobs/test-go.yml create mode 100644 .circleci/config/jobs/test-ui.yml create mode 100644 .circleci/config/workflows/ci.yml create mode 100755 .hooks/pre-commit diff --git a/.circleci/.gitattributes b/.circleci/.gitattributes new file mode 100644 index 0000000000..2dd06ee5f7 --- /dev/null +++ b/.circleci/.gitattributes @@ -0,0 +1 @@ +config.yml linguist-generated diff --git a/.circleci/.gitignore b/.circleci/.gitignore new file mode 100644 index 0000000000..3018b3a681 --- /dev/null +++ b/.circleci/.gitignore @@ -0,0 +1 @@ +.tmp/ diff --git a/.circleci/Makefile b/.circleci/Makefile new file mode 100644 index 0000000000..db2b3d7581 --- /dev/null +++ b/.circleci/Makefile @@ -0,0 +1,47 @@ +.PHONY: default +default: ci-config + +.PHONY: check-circleci-installed +check-circleci-installed: + @command -v circleci > /dev/null 2>&1 || { \ + echo "Please install circleci-cli, see https://circleci.com/docs/2.0/local-cli/#installation"; \ + exit 1; } + +.PHONY: ci-config +# ci-config is just an alias for config.yml for now +ci-config: config.yml + +CONFIG_SOURCE_DIR := config/ +CONFIG_SOURCE := $(shell find config/) Makefile +OUT := config.yml +TMP := .tmp/config.yml.tmp +CONFIG_21 := .tmp/config.2.1.tmp + +# Ensure the .tmp dir exists. +$(shell [ -d .tmp ] || mkdir .tmp) + +define GEN_CONFIG + @circleci config pack $(CONFIG_SOURCE_DIR) > $(CONFIG_21) + @echo "### Generated by 'make ci-config' do not manually edit this file." > $@ + @circleci config process $(CONFIG_21) >> $@ +endef + +$(OUT): $(CONFIG_SOURCE) check-circleci-installed + $(GEN_CONFIG) + @echo "$@ updated" + +$(TMP): $(CONFIG_SOURCE) check-circleci-installed + $(GEN_CONFIG) + +.PHONY: config-up-to-date +config-up-to-date: $(TMP) # Note this must not depend on $(OUT)! + @if diff config.yml $<; then \ + echo "Generated $(OUT) is up to date!"; \ + else \ + echo "Generated $(OUT) is out of date, run make ci-config to update."; \ + exit 1; \ + fi + +.PHONY: ci-verify +ci-verify: config-up-to-date + @circleci config validate config.yml diff --git a/.circleci/README.md b/.circleci/README.md new file mode 100644 index 0000000000..ea7ed54a2b --- /dev/null +++ b/.circleci/README.md @@ -0,0 +1,117 @@ +# CircleCI config + +This directory contains both the source code (under `./config/`) +and the generated single-file `config.yml` +which defines the CircleCI workflows for this project. + +The Makefile in this directory generates the `./config.yml` +in CircleCI 2.0 syntax, +from the tree rooted at `./config/`, +which contains files in CircleCI 2.1 syntax. +CircleCI supports [generating a single config file from many], +using the `$ circleci config pack` command. +It also supports [expanding 2.1 syntax to 2.0 syntax] +using the `$ circleci config process` command. + +[generating a single config file from many]: https://circleci.com/docs/2.0/local-cli/#packing-a-config +[expanding 2.1 syntax to 2.0 syntax]: https://circleci.com/docs/2.0/local-cli/#processing-a-config + +## Prerequisites + +You will need the [CircleCI CLI tool] installed and working, +at least version `0.1.5607`. + +``` +$ circleci version +0.1.5607+f705856 +``` + +NOTE: It is recommended to [download this tool directly from GitHub Releases]. +Do not install it using Homebrew, as this version cannot be easily updated. +It is also not recommended to pipe curl to bash (which CircleCI recommend) for security reasons! + +[CircleCI CLI tool]: https://circleci.com/docs/2.0/local-cli/ +[download this tool directly from GitHub Releases]: https://github.com/CircleCI-Public/circleci-cli/releases + +## How to make changes + +Before making changes, be sure to understand the layout +of the `./config/` file tree, as well as circleci 2.1 syntax. +See the [Syntax and layout] section below. + +To update the config, you should edit, add or remove files +in the `./config/` directory, +and then run `make ci-config`. +If that's successful, +you should then commit every `*.yml` file in the tree rooted in this directory. +That is: you should commit both the source under `./config/` +and the generated file `./config.yml` at the same time, in the same commit. +Do not edit the `./config.yml` file directly, as you will lose your changes +next time `make ci-config` is run. + +[Syntax and layout]: #syntax-and-layout + +### Verifying `./config.yml` + +To check whether or not the current `./config.yml` is up to date with the source, +and whether it is valid, run `$ make ci-verify`. +Note that `$ make ci-verify` should be run in CI, +as well as by a local git commit hook, +to ensure we never commit files that are invalid or out of date. + +#### Example shell session + +```sh +$ make ci-config +config.yml updated +$ git add -A . # The -A makes sure to include deletions/renames etc. +$ git commit -m "ci: blah blah blah" +Changes detected in .circleci/, running 'make -C .circleci ci-verify' +--> Generated config.yml is up to date! +--> Config file at config.yml is valid. +``` + +### Syntax and layout + +It is important to understand the layout of the config directory. +Read the documentation on [packing a config] for a full understanding +of how multiple YAML files are merged by the circleci CLI tool. + +[packing a config]: https://circleci.com/docs/2.0/local-cli/#packing-a-config + +Here is an example file tree (with comments added afterwards): + +```sh +$ tree . +. +├── Makefile +├── README.md # This file. +├── config # The source code for config.yml is rooted here. +│   ├── @config.yml # Files beginning with @ are treated specially by `circleci config pack` +│   ├── commands # Subdirectories of config become top-level keys. +│   │   └── go_test.yml # Filenames (minus .yml) become top-level keys under their parent (in this case "commands"). +│ │ # The contents of go_test.yml therefore are placed at: .commands.go_test: +│   └── jobs # jobs also becomes a top-level key under config... +│   ├── build-go-dev.yml # ...and likewise filenames become keys under their parent. +│   ├── go-mod-download.yml +│   ├── install-ui-dependencies.yml +│   ├── test-go-race.yml +│   ├── test-go.yml +│   └── test-ui.yml +└── config.yml # The generated file in 2.0 syntax. +``` + +About those `@` files... Preceding a filename with `@` +indicates to `$ circleci config pack` that the contents of this YAML file +should be at the top-level, rather than underneath a key named after their filename. +This naming convention is unfortunate as it breaks autocompletion in bash, +but there we go. + +### Why not just use YAML references? + +YAML references only work within a single file, +this is because `circleci config pack` is not a text-level packer, +but rather stitches together the structures defined in each YAML +file according to certain rules. +Therefore it must parse each file separately, +and YAML references are handled by the parser. diff --git a/.circleci/config.yml b/.circleci/config.yml index 790a973d90..6cceda575f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,228 +1,511 @@ +### Generated by 'make ci-config' do not manually edit this file. version: 2 - -references: - images: - go: &GOLANG_IMAGE golang:1.12.4-stretch # Pin Go to patch version (ex: 1.2.3) - node: &NODE_IMAGE node:10-stretch # Pin Node.js to major version (ex: 10) - - environment: &ENVIRONMENT - CIRCLECI_CLI_VERSION: 0.1.5546 # Pin CircleCI CLI to patch version (ex: 1.2.3) - GO_VERSION: 1.12.4 # Pin Go to patch version (ex: 1.2.3) - GOTESTSUM_VERSION: 0.3.3 # Pin gotestsum to patch version (ex: 1.2.3) - - cache: - go-sum: &GO_SUM_CACHE_KEY go-sum-v1-{{ checksum "go.sum" }} - yarn-lock: &YARN_LOCK_CACHE_KEY yarn-lock-v1-{{ checksum "ui/yarn.lock" }} - jobs: install-ui-dependencies: docker: - - image: *NODE_IMAGE + - image: node:10-stretch working_directory: /src steps: - - checkout - - restore_cache: - key: *YARN_LOCK_CACHE_KEY - - run: - name: Install UI dependencies - command: | - set -eux -o pipefail - - cd ui - yarn install --ignore-optional - npm rebuild node-sass - - save_cache: - key: *YARN_LOCK_CACHE_KEY - paths: - - ui/node_modules + - checkout + - restore_cache: + key: yarn-lock-v1-{{ checksum "ui/yarn.lock" }} + - run: + command: | + set -eux -o pipefail + cd ui + yarn install --ignore-optional + npm rebuild node-sass + name: Install UI dependencies + - save_cache: + key: yarn-lock-v1-{{ checksum "ui/yarn.lock" }} + paths: + - ui/node_modules go-mod-download: docker: - - image: *GOLANG_IMAGE + - image: golang:1.12.4-stretch working_directory: /src steps: - - checkout - - restore_cache: - key: *GO_SUM_CACHE_KEY - - run: - name: Download Go modules - command: go mod download - - run: - name: Verify checksums of Go modules - command: go mod verify - - save_cache: - key: *GO_SUM_CACHE_KEY - paths: - - /go/pkg/mod - + - add_ssh_keys: + fingerprints: + - c6:96:98:82:dc:04:6c:39:dd:ac:83:05:e3:15:1c:98 + - checkout + - restore_cache: + key: go-sum-v1-{{ checksum "go.sum" }} + - run: + command: go mod download + name: Download Go modules + - run: + command: go mod verify + name: Verify checksums of Go modules + - save_cache: + key: go-sum-v1-{{ checksum "go.sum" }} + paths: + - /go/pkg/mod build-go-dev: docker: - - image: *GOLANG_IMAGE + - image: golang:1.12.4-stretch working_directory: /src steps: - - checkout - - restore_cache: - key: *GO_SUM_CACHE_KEY - - run: - name: Build dev binary - command: | - set -eux -o pipefail + - checkout + - restore_cache: + key: go-sum-v1-{{ checksum "go.sum" }} + - attach_workspace: + at: . + - run: + command: | + set -eux -o pipefail - # Move dev UI assets to expected location - rm -rf ./pkg - mkdir ./pkg - - # Build dev binary - make bootstrap dev - - persist_to_workspace: - root: . - paths: - - bin + # Move dev UI assets to expected location + rm -rf ./pkg + mkdir ./pkg + # Build dev binary + make bootstrap dev + name: Build dev binary + - persist_to_workspace: + paths: + - bin + root: . test-ui: docker: - - image: *NODE_IMAGE + - image: node:10-stretch working_directory: /src resource_class: medium+ steps: - - checkout - - restore_cache: - key: *YARN_LOCK_CACHE_KEY - - attach_workspace: - at: . - - run: - name: Test UI - command: | - set -eux -o pipefail + - checkout + - restore_cache: + key: yarn-lock-v1-{{ checksum "ui/yarn.lock" }} + - attach_workspace: + at: . + - run: + command: | + set -eux -o pipefail - # Install Chrome - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub \ - | apt-key add - - echo "deb http://dl.google.com/linux/chrome/deb/ stable main" \ - | tee /etc/apt/sources.list.d/google-chrome.list - apt-get update - apt-get -y install google-chrome-stable - rm /etc/apt/sources.list.d/google-chrome.list - rm -rf /var/lib/apt/lists/* /var/cache/apt/* + # Install Chrome + wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub \ + | apt-key add - + echo "deb http://dl.google.com/linux/chrome/deb/ stable main" \ + | tee /etc/apt/sources.list.d/google-chrome.list + apt-get update + apt-get -y install google-chrome-stable + rm /etc/apt/sources.list.d/google-chrome.list + rm -rf /var/lib/apt/lists/* /var/cache/apt/* - # Add ./bin to the PATH so vault binary can be run by Ember tests - export PATH="${PWD}"/bin:${PATH} - - # Run Ember tests - cd ui - mkdir -p test-results/qunit - yarn run test-oss - - store_artifacts: - path: ui/test-results - - store_test_results: - path: ui/test-results - - test-ui-browserstack: - docker: - - image: *NODE_IMAGE - steps: - - checkout - - restore_cache: - key: *YARN_LOCK_CACHE_KEY - - attach_workspace: - at: . - - run: - name: Run BrowserStack tests - command: | - set -eux -o pipefail - - # Add ./bin to the PATH so vault binary can be run by Ember tests - export PATH="${PWD}"/bin:${PATH} - - make test-ui-browserstack + # Add ./bin to the PATH so vault binary can be run by Ember tests + export PATH="${PWD}/bin:${PATH}" + # Run Ember tests + cd ui + mkdir -p test-results/qunit + yarn run test-oss + name: Test UI + - store_artifacts: + path: ui/test-results + - store_test_results: + path: ui/test-results test-go: machine: true - environment: - <<: *ENVIRONMENT - GO_TAGS: + working_directory: ~/src parallelism: 2 + steps: + - checkout + - run: + command: | + set -eux -o pipefail + + sudo mkdir /go + sudo chown -R circleci:circleci /go + name: Allow circleci user to restore Go modules cache + - restore_cache: + key: go-sum-v1-{{ checksum "go.sum" }} + - run: + command: | + set -eux -o pipefail + + # Install Go + curl -sSLO "https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz" + sudo rm -rf /usr/local/go + sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-amd64.tar.gz" + rm -f "go${GO_VERSION}.linux-amd64.tar.gz" + export GOPATH=/go + export PATH="${PATH}:${GOPATH}/bin:/usr/local/go/bin" + + # Install CircleCI CLI + curl -sSL \ + "https://github.com/CircleCI-Public/circleci-cli/releases/download/v${CIRCLECI_CLI_VERSION}/circleci-cli_${CIRCLECI_CLI_VERSION}_linux_amd64.tar.gz" \ + | sudo tar --overwrite -xz \ + -C /usr/local/bin \ + "circleci-cli_${CIRCLECI_CLI_VERSION}_linux_amd64/circleci" + + # Split Go tests by prior test times + package_names=$(go list \ + -tags "${GO_TAGS}" \ + ./... \ + | grep -v /integ \ + | grep -v /vendor/ \ + | sort \ + | circleci tests split --split-by=timings --timings-type=classname) + + # Install gotestsum + curl -sSL "https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_linux_amd64.tar.gz" \ + | sudo tar --overwrite -xz -C /usr/local/bin gotestsum + + # Run tests + make prep + mkdir -p test-results/go-test + CGO_ENABLED= \ + VAULT_ADDR= \ + VAULT_TOKEN= \ + VAULT_DEV_ROOT_TOKEN_ID= \ + VAULT_ACC= \ + gotestsum --format=short-verbose --junitfile test-results/go-test/results.xml -- \ + -tags "${GO_TAGS}" \ + -timeout=40m \ + -parallel=20 \ + \ + ${package_names} + name: Run Go tests + no_output_timeout: 20m + - store_artifacts: + path: test-results + - store_test_results: + path: test-results + environment: + - CIRCLECI_CLI_VERSION: 0.1.5546 + - GO_TAGS: null + - GO_VERSION: 1.12.4 + - GOTESTSUM_VERSION: 0.3.3 + test-go-race: + machine: true working_directory: ~/src steps: - - checkout - - run: - name: Allow circleci user to restore Go modules cache - command: | - set -eux -o pipefail + - checkout + - run: + command: | + set -eux -o pipefail - sudo mkdir /go - sudo chown -R circleci:circleci /go - - restore_cache: - key: *GO_SUM_CACHE_KEY - - run: - name: Run Go tests - no_output_timeout: 20m - command: | - set -eux -o pipefail + sudo mkdir /go + sudo chown -R circleci:circleci /go + name: Allow circleci user to restore Go modules cache + - restore_cache: + key: go-sum-v1-{{ checksum "go.sum" }} + - run: + command: | + set -eux -o pipefail - # Install Go - curl -sSLO "https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz" - sudo rm -rf /usr/local/go - sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-amd64.tar.gz" - rm -f "go${GO_VERSION}.linux-amd64.tar.gz" - export GOPATH=/go - export PATH="${PATH}:${GOPATH}/bin:/usr/local/go/bin" + # Install Go + curl -sSLO "https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz" + sudo rm -rf /usr/local/go + sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-amd64.tar.gz" + rm -f "go${GO_VERSION}.linux-amd64.tar.gz" + export GOPATH=/go + export PATH="${PATH}:${GOPATH}/bin:/usr/local/go/bin" - # Install CircleCI CLI - curl -sSL \ - "https://github.com/CircleCI-Public/circleci-cli/releases/download/v${CIRCLECI_CLI_VERSION}/circleci-cli_${CIRCLECI_CLI_VERSION}_linux_amd64.tar.gz" \ - | sudo tar --overwrite -xz \ - -C /usr/local/bin \ - "circleci-cli_${CIRCLECI_CLI_VERSION}_linux_amd64/circleci" + # Install CircleCI CLI + curl -sSL \ + "https://github.com/CircleCI-Public/circleci-cli/releases/download/v${CIRCLECI_CLI_VERSION}/circleci-cli_${CIRCLECI_CLI_VERSION}_linux_amd64.tar.gz" \ + | sudo tar --overwrite -xz \ + -C /usr/local/bin \ + "circleci-cli_${CIRCLECI_CLI_VERSION}_linux_amd64/circleci" - # Split Go tests by prior test times - package_names=$(go list \ - -tags "${GO_TAGS}" \ - ./... \ - | grep -v /vendor/ \ - | sort \ - | circleci tests split --split-by=timings --timings-type=classname) + # Split Go tests by prior test times + package_names=$(go list \ + -tags "${GO_TAGS}" \ + ./... \ + | grep -v /integ \ + | grep -v /vendor/ \ + | sort \ + | circleci tests split --split-by=timings --timings-type=classname) - # Install gotestsum - curl -sSL "https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_linux_amd64.tar.gz" \ - | sudo tar --overwrite -xz -C /usr/local/bin gotestsum - - # Run tests - make prep - mkdir -p test-results/go-test - CGO_ENABLED= \ - VAULT_ADDR= \ - VAULT_TOKEN= \ - VAULT_DEV_ROOT_TOKEN_ID= \ - VAULT_ACC= \ - gotestsum --format=short-verbose --junitfile test-results/go-test/results.xml -- \ - -tags "${GO_TAGS}" \ - -timeout=40m \ - -parallel=20 \ - ${package_names} - - store_artifacts: - path: test-results - - store_test_results: - path: test-results + # Install gotestsum + curl -sSL "https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_linux_amd64.tar.gz" \ + | sudo tar --overwrite -xz -C /usr/local/bin gotestsum + # Run tests + make prep + mkdir -p test-results/go-test + CGO_ENABLED= \ + VAULT_ADDR= \ + VAULT_TOKEN= \ + VAULT_DEV_ROOT_TOKEN_ID= \ + VAULT_ACC= \ + gotestsum --format=short-verbose --junitfile test-results/go-test/results.xml -- \ + -tags "${GO_TAGS}" \ + -timeout=40m \ + -parallel=20 \ + -race \ + ${package_names} + name: Run Go tests + no_output_timeout: 20m + - store_artifacts: + path: test-results + - store_test_results: + path: test-results + environment: + - CIRCLECI_CLI_VERSION: 0.1.5546 + - GO_TAGS: null + - GO_VERSION: 1.12.4 + - GOTESTSUM_VERSION: 0.3.3 workflows: - version: 2 - ci: jobs: - - install-ui-dependencies - - go-mod-download - - build-go-dev: - requires: - - go-mod-download - - test-ui: - requires: - - install-ui-dependencies - - build-go-dev - - test-go: - requires: - - build-go-dev - - test-ui-browserstack: - requires: - - install-ui-dependencies - - build-go-dev + - install-ui-dependencies + - go-mod-download + - build-go-dev: + requires: + - go-mod-download + - test-ui: + requires: + - install-ui-dependencies + - build-go-dev + - test-go: + requires: + - build-go-dev + - test-go-race: + requires: + - build-go-dev + version: 2 + +# Original config.yml file: +# commands: +# go_test: +# description: run go tests +# parameters: +# extra_flags: +# default: \"\" +# type: string +# steps: +# - run: +# command: | +# set -eux -o pipefail +# +# # Install Go +# curl -sSLO \"https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz\" +# sudo rm -rf /usr/local/go +# sudo tar -C /usr/local -xzf \"go${GO_VERSION}.linux-amd64.tar.gz\" +# rm -f \"go${GO_VERSION}.linux-amd64.tar.gz\" +# export GOPATH=/go +# export PATH=\"${PATH}:${GOPATH}/bin:/usr/local/go/bin\" +# +# # Install CircleCI CLI +# curl -sSL \\ +# \"https://github.com/CircleCI-Public/circleci-cli/releases/download/v${CIRCLECI_CLI_VERSION}/circleci-cli_${CIRCLECI_CLI_VERSION}_linux_amd64.tar.gz\" \\ +# | sudo tar --overwrite -xz \\ +# -C /usr/local/bin \\ +# \"circleci-cli_${CIRCLECI_CLI_VERSION}_linux_amd64/circleci\" +# +# # Split Go tests by prior test times +# package_names=$(go list \\ +# -tags \"${GO_TAGS}\" \\ +# ./... \\ +# | grep -v /integ \\ +# | grep -v /vendor/ \\ +# | sort \\ +# | circleci tests split --split-by=timings --timings-type=classname) +# +# # Install gotestsum +# curl -sSL \"https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_linux_amd64.tar.gz\" \\ +# | sudo tar --overwrite -xz -C /usr/local/bin gotestsum +# +# # Run tests +# make prep +# mkdir -p test-results/go-test +# CGO_ENABLED= \\ +# VAULT_ADDR= \\ +# VAULT_TOKEN= \\ +# VAULT_DEV_ROOT_TOKEN_ID= \\ +# VAULT_ACC= \\ +# gotestsum --format=short-verbose --junitfile test-results/go-test/results.xml -- \\ +# -tags \"${GO_TAGS}\" \\ +# -timeout=40m \\ +# -parallel=20 \\ +# << parameters.extra_flags >> \\ +# ${package_names} +# name: Run Go tests +# no_output_timeout: 20m +# restore_go_cache: +# steps: +# - restore_cache: +# key: go-sum-v1-{{ checksum \"go.sum\" }} +# restore_yarn_cache: +# steps: +# - restore_cache: +# key: yarn-lock-v1-{{ checksum \"ui/yarn.lock\" }} +# save_go_cache: +# steps: +# - save_cache: +# key: go-sum-v1-{{ checksum \"go.sum\" }} +# paths: +# - /go/pkg/mod +# save_yarn_cache: +# steps: +# - save_cache: +# key: yarn-lock-v1-{{ checksum \"ui/yarn.lock\" }} +# paths: +# - ui/node_modules +# executors: +# go: +# docker: +# - image: golang:1.12.4-stretch +# working_directory: /src +# go-machine: +# environment: +# CIRCLECI_CLI_VERSION: 0.1.5546 +# GO_TAGS: null +# GO_VERSION: 1.12.4 +# GOTESTSUM_VERSION: 0.3.3 +# machine: true +# working_directory: ~/src +# node: +# docker: +# - image: node:10-stretch +# working_directory: /src +# jobs: +# build-go-dev: +# executor: go +# steps: +# - checkout +# - restore_go_cache +# - attach_workspace: +# at: . +# - run: +# command: | +# set -eux -o pipefail +# +# # Move dev UI assets to expected location +# rm -rf ./pkg +# mkdir ./pkg +# +# # Build dev binary +# make bootstrap dev +# name: Build dev binary +# - persist_to_workspace: +# paths: +# - bin +# root: . +# go-mod-download: +# executor: go +# steps: +# - add_ssh_keys: +# fingerprints: +# - c6:96:98:82:dc:04:6c:39:dd:ac:83:05:e3:15:1c:98 +# - checkout +# - restore_go_cache +# - run: +# command: go mod download +# name: Download Go modules +# - run: +# command: go mod verify +# name: Verify checksums of Go modules +# - save_go_cache +# install-ui-dependencies: +# executor: node +# steps: +# - checkout +# - restore_yarn_cache +# - run: +# command: | +# set -eux -o pipefail +# +# cd ui +# yarn install --ignore-optional +# npm rebuild node-sass +# name: Install UI dependencies +# - save_yarn_cache +# test-go: +# executor: go-machine +# parallelism: 2 +# steps: +# - checkout +# - run: +# command: | +# set -eux -o pipefail +# +# sudo mkdir /go +# sudo chown -R circleci:circleci /go +# name: Allow circleci user to restore Go modules cache +# - restore_go_cache +# - go_test +# - store_artifacts: +# path: test-results +# - store_test_results: +# path: test-results +# test-go-race: +# executor: go-machine +# steps: +# - checkout +# - run: +# command: | +# set -eux -o pipefail +# +# sudo mkdir /go +# sudo chown -R circleci:circleci /go +# name: Allow circleci user to restore Go modules cache +# - restore_go_cache +# - go_test: +# extra_flags: -race +# - store_artifacts: +# path: test-results +# - store_test_results: +# path: test-results +# test-ui: +# executor: node +# resource_class: medium+ +# steps: +# - checkout +# - restore_yarn_cache +# - attach_workspace: +# at: . +# - run: +# command: | +# set -eux -o pipefail +# +# # Install Chrome +# wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub \\ +# | apt-key add - +# echo \"deb http://dl.google.com/linux/chrome/deb/ stable main\" \\ +# | tee /etc/apt/sources.list.d/google-chrome.list +# apt-get update +# apt-get -y install google-chrome-stable +# rm /etc/apt/sources.list.d/google-chrome.list +# rm -rf /var/lib/apt/lists/* /var/cache/apt/* +# +# # Add ./bin to the PATH so vault binary can be run by Ember tests +# export PATH=\"${PWD}/bin:${PATH}\" +# +# # Run Ember tests +# cd ui +# mkdir -p test-results/qunit +# yarn run test-oss +# name: Test UI +# - store_artifacts: +# path: ui/test-results +# - store_test_results: +# path: ui/test-results +# references: +# cache: +# go-sum: go-sum-v1-{{ checksum \"go.sum\" }} +# yarn-lock: yarn-lock-v1-{{ checksum \"ui/yarn.lock\" }} +# images: +# go: golang:1.12.4-stretch +# node: node:10-stretch +# version: 2.1 +# workflows: +# ci: +# jobs: +# - install-ui-dependencies +# - go-mod-download +# - build-go-dev: +# requires: +# - go-mod-download +# - test-ui: +# requires: +# - install-ui-dependencies +# - build-go-dev +# - test-go: +# requires: +# - build-go-dev +# - test-go-race: +# requires: +# - build-go-dev \ No newline at end of file diff --git a/.circleci/config/@config.yml b/.circleci/config/@config.yml new file mode 100644 index 0000000000..c4ef96b68b --- /dev/null +++ b/.circleci/config/@config.yml @@ -0,0 +1,53 @@ +--- +version: 2.1 + +references: + images: + go: &GOLANG_IMAGE golang:1.12.4-stretch # Pin Go to patch version (ex: 1.2.3) + node: &NODE_IMAGE node:10-stretch # Pin Node.js to major version (ex: 10) + + cache: + go-sum: &GO_SUM_CACHE_KEY go-sum-v1-{{ checksum "go.sum" }} + yarn-lock: &YARN_LOCK_CACHE_KEY yarn-lock-v1-{{ checksum "ui/yarn.lock" }} + +# more commands defined in commands/ +commands: + restore_yarn_cache: + steps: + - restore_cache: + key: *YARN_LOCK_CACHE_KEY + save_yarn_cache: + steps: + - save_cache: + key: *YARN_LOCK_CACHE_KEY + paths: + - ui/node_modules + restore_go_cache: + steps: + - restore_cache: + key: *GO_SUM_CACHE_KEY + save_go_cache: + steps: + - save_cache: + key: *GO_SUM_CACHE_KEY + paths: + - /go/pkg/mod + +executors: + go: + docker: + - image: *GOLANG_IMAGE + working_directory: /src + go-machine: + machine: true + environment: + CIRCLECI_CLI_VERSION: 0.1.5546 # Pin CircleCI CLI to patch version (ex: 1.2.3) + GO_VERSION: 1.12.4 # Pin Go to patch version (ex: 1.2.3) + GOTESTSUM_VERSION: 0.3.3 # Pin gotestsum to patch version (ex: 1.2.3) + GO_TAGS: + working_directory: ~/src + node: + docker: + - image: *NODE_IMAGE + working_directory: /src + diff --git a/.circleci/config/commands/go_test.yml b/.circleci/config/commands/go_test.yml new file mode 100644 index 0000000000..bfae0c3d69 --- /dev/null +++ b/.circleci/config/commands/go_test.yml @@ -0,0 +1,55 @@ +description: run go tests +parameters: + extra_flags: + type: string + default: "" +steps: + - run: + name: Run Go tests + no_output_timeout: 20m + command: | + set -eux -o pipefail + + # Install Go + curl -sSLO "https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz" + sudo rm -rf /usr/local/go + sudo tar -C /usr/local -xzf "go${GO_VERSION}.linux-amd64.tar.gz" + rm -f "go${GO_VERSION}.linux-amd64.tar.gz" + export GOPATH=/go + export PATH="${PATH}:${GOPATH}/bin:/usr/local/go/bin" + + # Install CircleCI CLI + curl -sSL \ + "https://github.com/CircleCI-Public/circleci-cli/releases/download/v${CIRCLECI_CLI_VERSION}/circleci-cli_${CIRCLECI_CLI_VERSION}_linux_amd64.tar.gz" \ + | sudo tar --overwrite -xz \ + -C /usr/local/bin \ + "circleci-cli_${CIRCLECI_CLI_VERSION}_linux_amd64/circleci" + + # Split Go tests by prior test times + package_names=$(go list \ + -tags "${GO_TAGS}" \ + ./... \ + | grep -v /integ \ + | grep -v /vendor/ \ + | sort \ + | circleci tests split --split-by=timings --timings-type=classname) + + # Install gotestsum + curl -sSL "https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_linux_amd64.tar.gz" \ + | sudo tar --overwrite -xz -C /usr/local/bin gotestsum + + # Run tests + make prep + mkdir -p test-results/go-test + CGO_ENABLED= \ + VAULT_ADDR= \ + VAULT_TOKEN= \ + VAULT_DEV_ROOT_TOKEN_ID= \ + VAULT_ACC= \ + gotestsum --format=short-verbose --junitfile test-results/go-test/results.xml -- \ + -tags "${GO_TAGS}" \ + -timeout=40m \ + -parallel=20 \ + << parameters.extra_flags >> \ + ${package_names} + diff --git a/.circleci/config/jobs/build-go-dev.yml b/.circleci/config/jobs/build-go-dev.yml new file mode 100644 index 0000000000..59729bc698 --- /dev/null +++ b/.circleci/config/jobs/build-go-dev.yml @@ -0,0 +1,21 @@ +executor: go +steps: + - checkout + - restore_go_cache + - attach_workspace: + at: . + - run: + name: Build dev binary + command: | + set -eux -o pipefail + + # Move dev UI assets to expected location + rm -rf ./pkg + mkdir ./pkg + + # Build dev binary + make bootstrap dev + - persist_to_workspace: + root: . + paths: + - bin diff --git a/.circleci/config/jobs/go-mod-download.yml b/.circleci/config/jobs/go-mod-download.yml new file mode 100644 index 0000000000..adfaf0ad80 --- /dev/null +++ b/.circleci/config/jobs/go-mod-download.yml @@ -0,0 +1,15 @@ +executor: go +steps: + - add_ssh_keys: + fingerprints: + # "CircleCI SSH Checkout" SSH key associated with hashicorp-ci GitHub user + - "c6:96:98:82:dc:04:6c:39:dd:ac:83:05:e3:15:1c:98" + - checkout + - restore_go_cache + - run: + name: Download Go modules + command: go mod download + - run: + name: Verify checksums of Go modules + command: go mod verify + - save_go_cache diff --git a/.circleci/config/jobs/install-ui-dependencies.yml b/.circleci/config/jobs/install-ui-dependencies.yml new file mode 100644 index 0000000000..2b04e176bb --- /dev/null +++ b/.circleci/config/jobs/install-ui-dependencies.yml @@ -0,0 +1,13 @@ +executor: node +steps: + - checkout + - restore_yarn_cache + - run: + name: Install UI dependencies + command: | + set -eux -o pipefail + + cd ui + yarn install --ignore-optional + npm rebuild node-sass + - save_yarn_cache diff --git a/.circleci/config/jobs/test-go-race.yml b/.circleci/config/jobs/test-go-race.yml new file mode 100644 index 0000000000..df16fc6164 --- /dev/null +++ b/.circleci/config/jobs/test-go-race.yml @@ -0,0 +1,17 @@ +executor: go-machine +steps: + - checkout + - run: + name: Allow circleci user to restore Go modules cache + command: | + set -eux -o pipefail + + sudo mkdir /go + sudo chown -R circleci:circleci /go + - restore_go_cache + - go_test: + extra_flags: "-race" + - store_artifacts: + path: test-results + - store_test_results: + path: test-results diff --git a/.circleci/config/jobs/test-go.yml b/.circleci/config/jobs/test-go.yml new file mode 100644 index 0000000000..031f7bc249 --- /dev/null +++ b/.circleci/config/jobs/test-go.yml @@ -0,0 +1,17 @@ +executor: go-machine +parallelism: 2 +steps: + - checkout + - run: + name: Allow circleci user to restore Go modules cache + command: | + set -eux -o pipefail + + sudo mkdir /go + sudo chown -R circleci:circleci /go + - restore_go_cache + - go_test + - store_artifacts: + path: test-results + - store_test_results: + path: test-results diff --git a/.circleci/config/jobs/test-ui.yml b/.circleci/config/jobs/test-ui.yml new file mode 100644 index 0000000000..813f800d20 --- /dev/null +++ b/.circleci/config/jobs/test-ui.yml @@ -0,0 +1,33 @@ +executor: node +resource_class: medium+ +steps: + - checkout + - restore_yarn_cache + - attach_workspace: + at: . + - run: + name: Test UI + command: | + set -eux -o pipefail + + # Install Chrome + wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub \ + | apt-key add - + echo "deb http://dl.google.com/linux/chrome/deb/ stable main" \ + | tee /etc/apt/sources.list.d/google-chrome.list + apt-get update + apt-get -y install google-chrome-stable + rm /etc/apt/sources.list.d/google-chrome.list + rm -rf /var/lib/apt/lists/* /var/cache/apt/* + + # Add ./bin to the PATH so vault binary can be run by Ember tests + export PATH="${PWD}/bin:${PATH}" + + # Run Ember tests + cd ui + mkdir -p test-results/qunit + yarn run test-oss + - store_artifacts: + path: ui/test-results + - store_test_results: + path: ui/test-results diff --git a/.circleci/config/workflows/ci.yml b/.circleci/config/workflows/ci.yml new file mode 100644 index 0000000000..dda98aea45 --- /dev/null +++ b/.circleci/config/workflows/ci.yml @@ -0,0 +1,16 @@ +jobs: + - install-ui-dependencies + - go-mod-download + - build-go-dev: + requires: + - go-mod-download + - test-ui: + requires: + - install-ui-dependencies + - build-go-dev + - test-go: + requires: + - build-go-dev + - test-go-race: + requires: + - build-go-dev diff --git a/.hooks/pre-commit b/.hooks/pre-commit new file mode 100755 index 0000000000..fd2533885f --- /dev/null +++ b/.hooks/pre-commit @@ -0,0 +1,104 @@ +#!/usr/bin/env bash + +# READ THIS BEFORE MAKING CHANGES: +# +# If you want to add a new pre-commit check, here are the rules: +# +# 1. Create a bash function for your check (see e.g. ui_lint below). +# NOTE: Each function will be called in a sub-shell so you can freely +# change directory without worrying about interference. +# 2. Add the name of the function to the CHECKS variable. +# 3. If no changes relevant to your new check are staged, then +# do not output anything at all - this would be annoying noise. +# In this case, call 'return 0' from your check function to return +# early without blocking the commit. +# 4. If any non-trivial check-specific thing has to be invoked, +# then output '==> [check description]' as the first line of +# output. Each sub-check should output '--> [subcheck description]' +# after it has run, indicating success or failure. +# 5. Call 'block [reason]' to block the commit. This ensures the last +# line of output calls out that the commit was blocked - which may not +# be obvious from random error messages generated in 4. +# +# At the moment, there are no automated tests for this hook, so please run it +# locally to check you have not broken anything - breaking this will interfere +# with other peoples' workflows significantly, so be sure, check everything twice. + +set -euo pipefail + +# Call block to block the commit with a message. +block() { + echo "$@" + echo "Commit blocked - see errors above." + exit 1 +} + +# Add all check functions to this space separated list. +# They are executed in this order (see end of file). +CHECKS="ui_lint circleci_verify" + +# Run ui linter if changes in that dir detected. +ui_lint() { + local DIR=ui LINTER=node_modules/.bin/lint-staged + + # Silently succeed if no changes staged for $DIR + if git diff --name-only --cached --exit-code -- $DIR/; then + return 0 + fi + + # Silently succeed if the linter has not been installed. + # We assume that if you're doing UI dev, you will have installed the linter + # by running yarn. + if [ ! -x $DIR/$LINTER ]; then + return 0 + fi + + echo "==> Changes detected in $DIR/: Running linter..." + + # Run the linter from the UI dir. + cd $DIR + $LINTER || block "UI lint failed" +} + +# Check .circleci/config.yml is up to date and valid, and that all changes are +# included together in this commit. +circleci_verify() { + # Change to the root dir of the repo. + cd "$(git rev-parse --show-toplevel)" + + # Fail early if we accidentally used '.yaml' instead of '.yml' + if ! git diff --name-only --cached --exit-code -- '.circleci/***.yaml'; then + # This is just for consistency, as I keep making this mistake - Sam. + block "ERROR: File(s) with .yaml extension detected. Please rename them .yml instead." + fi + + # Succeed early if no changes to yml files in .circleci/ are currently staged. + # make ci-verify is slow so we really don't want to run it unnecessarily. + if git diff --name-only --cached --exit-code -- '.circleci/***.yml'; then + return 0 + fi + # Make sure to add no explicit output before this line, as it would just be noise + # for those making non-circleci changes. + echo "==> Verifying config changes in .circleci/" + echo "--> OK: All files are .yml not .yaml" + + # Ensure commit includes _all_ files in .circleci/ + # So not only are the files up to date, but we are also committing them in one go. + if ! git diff --name-only --exit-code -- '.circleci/***.yml'; then + echo "ERROR: Some .yml diffs in .circleci/ are staged, others not." + block "Please commit the entire .circleci/ directory together, or omit it altogether." + fi + + echo "--> OK: All .yml files in .circleci are staged." + + if ! make -C .circleci ci-verify; then + block "ERROR: make ci-verify failed" + fi + + echo "--> OK: make ci-verify succeeded." +} + +for CHECK in $CHECKS; do + # Force each check into a subshell to avoid crosstalk. + ( $CHECK ) || exit $? +done diff --git a/Makefile b/Makefile index 6869df88c0..c2c413547c 100644 --- a/Makefile +++ b/Makefile @@ -102,8 +102,15 @@ vet: prep: fmtcheck @sh -c "'$(CURDIR)/scripts/goversioncheck.sh' '$(GO_VERSION_MIN)'" @go generate $(go list ./... | grep -v /vendor/) + @# Remove old (now broken) husky git hooks. + @[ ! -d .git/hooks ] || grep -l '^# husky$$' .git/hooks/* | xargs rm -f @if [ -d .git/hooks ]; then cp .hooks/* .git/hooks/; fi +ci-config: + @$(MAKE) -C .circleci +ci-verify: + @$(MAKE) -C .circleci ci-verify + # bootstrap the build by downloading additional tools bootstrap: @for tool in $(EXTERNAL_TOOLS) ; do \ diff --git a/ui/package.json b/ui/package.json index 64927bf403..fd28037278 100644 --- a/ui/package.json +++ b/ui/package.json @@ -139,7 +139,6 @@ "@storybook/ember": "^5.0.5", "@storybook/ember-cli-storybook": "meirish/ember-cli-storybook#6bd58326d8c21e986d390b541ae5e49089d61b93", "babel-loader": "^8.0.5", - "husky": "^1.1.3", "jsdoc-to-markdown": "^4.0.1", "lint-staged": "^8.0.4" }, @@ -154,10 +153,5 @@ "lib/replication" ] }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, "dependencies": {} } diff --git a/ui/yarn.lock b/ui/yarn.lock index fe4a3c8f9f..b202bcc58f 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -5479,11 +5479,6 @@ ci-info@^1.3.0, ci-info@^1.4.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.5.0.tgz#38327c69e98dab18487744b84e5d6e841a09a1a7" integrity sha512-Bx/xWOzip4whERIvC97aIHjWCa8FxEn0ezng0oVn4kma6p+90Fbs3bTcJw6ZL0da2EPHydxsXJPZxNUv5oWb1Q== -ci-info@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" - integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== - cidr-regex@^2.0.8: version "2.0.9" resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-2.0.9.tgz#9c17bb2b18e15af07f7d0c3b994b961d687ed1c9" @@ -6194,7 +6189,7 @@ cosmiconfig@^5.0.1: js-yaml "^3.9.0" parse-json "^4.0.0" -cosmiconfig@^5.0.2, cosmiconfig@^5.0.6: +cosmiconfig@^5.0.2: version "5.0.7" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.7.tgz#39826b292ee0d78eda137dfa3173bd1c21a43b04" integrity sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA== @@ -8578,19 +8573,6 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.9.0.tgz#adb7ce62cf985071f60580deb4a88b9e34712d01" - integrity sha512-BbUMBiX4hqiHZUA5+JujIjNb6TyAlp2D5KLheMjMluwOuzcnylDL4AxZYLLn1n2AGB49eSWwyKvvEQoRpnAtmA== - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -10242,22 +10224,6 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/husky/-/husky-1.1.3.tgz#3ccfdb4d7332896bf7cd0e618c6fb8be09d9de4b" - integrity sha512-6uc48B/A2Mqi65yeg37d/TPcTb0bZ1GTkMYOM0nXLOPuPaTRhXCeee80/noOrbavWd12x72Tusja7GJ5rzvV6g== - dependencies: - cosmiconfig "^5.0.6" - execa "^0.9.0" - find-up "^3.0.0" - get-stdin "^6.0.0" - is-ci "^1.2.1" - pkg-dir "^3.0.0" - please-upgrade-node "^3.1.1" - read-pkg "^4.0.1" - run-node "^1.0.0" - slash "^2.0.0" - iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" @@ -10676,13 +10642,6 @@ is-ci@^1.0.10: dependencies: ci-info "^1.3.0" -is-ci@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" - integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== - dependencies: - ci-info "^1.5.0" - is-cidr@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-2.0.6.tgz#4b01c9693d8e18399dacd18a4f3d60ea5871ac60" @@ -14388,7 +14347,7 @@ pkg-up@2.0.0, pkg-up@^2.0.0: dependencies: find-up "^2.1.0" -please-upgrade-node@^3.0.2, please-upgrade-node@^3.1.1: +please-upgrade-node@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac" integrity sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ== @@ -15344,7 +15303,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -read-pkg@^4.0.0, read-pkg@^4.0.1: +read-pkg@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" integrity sha1-ljYlN48+HE1IyFhytabsfV0JMjc= @@ -16031,11 +15990,6 @@ run-async@^2.2.0: dependencies: is-promise "^2.1.0" -run-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" - integrity sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A== - run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -16511,11 +16465,6 @@ slash@^1.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" From 1ac329a9c7dc2e27f3a7972edefad1ba163ed081 Mon Sep 17 00:00:00 2001 From: Calvin Leung Huang Date: Tue, 11 Jun 2019 13:13:03 -0700 Subject: [PATCH 07/20] core: add generic request forwarding bits to oss (#6866) --- sdk/logical/system_view.go | 1 + vault/dynamic_system_view.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/sdk/logical/system_view.go b/sdk/logical/system_view.go index 550b74a915..2be420d3a6 100644 --- a/sdk/logical/system_view.go +++ b/sdk/logical/system_view.go @@ -72,6 +72,7 @@ type SystemView interface { type ExtendedSystemView interface { Auditor() Auditor + ForwardGenericRequest(context.Context, *Request) (*Response, error) } type StaticSystemView struct { diff --git a/vault/dynamic_system_view.go b/vault/dynamic_system_view.go index fd45b3a725..1cd74af4e5 100644 --- a/vault/dynamic_system_view.go +++ b/vault/dynamic_system_view.go @@ -16,6 +16,12 @@ import ( "github.com/hashicorp/vault/sdk/version" ) +type ctxKeyForwardedRequestMountAccessor struct{} + +func (c ctxKeyForwardedRequestMountAccessor) String() string { + return "forwarded-req-mount-accessor" +} + type dynamicSystemView struct { core *Core mountEntry *MountEntry @@ -33,6 +39,17 @@ func (e extendedSystemView) Auditor() logical.Auditor { } } +func (e extendedSystemView) ForwardGenericRequest(ctx context.Context, req *logical.Request) (*logical.Response, error) { + // Forward the request if allowed + if couldForward(e.core) { + ctx = namespace.ContextWithNamespace(ctx, e.mountEntry.Namespace()) + ctx = context.WithValue(ctx, ctxKeyForwardedRequestMountAccessor{}, e.mountEntry.Accessor) + return forward(ctx, e.core, req) + } + + return nil, logical.ErrReadOnly +} + func (d dynamicSystemView) DefaultLeaseTTL() time.Duration { def, _ := d.fetchTTLs() return def From 485b4d173e543aa52d93bb8df973101699d2369c Mon Sep 17 00:00:00 2001 From: Calvin Leung Huang Date: Tue, 11 Jun 2019 14:07:04 -0700 Subject: [PATCH 08/20] core: add ForwardGenericRequest to StaticSystemView to satisfy ExtendedSystemView (#6867) --- sdk/logical/system_view.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sdk/logical/system_view.go b/sdk/logical/system_view.go index 2be420d3a6..52fc2bd6ac 100644 --- a/sdk/logical/system_view.go +++ b/sdk/logical/system_view.go @@ -105,6 +105,10 @@ func (d StaticSystemView) Auditor() Auditor { return noopAuditor{} } +func (d StaticSystemView) ForwardGenericRequest(ctx context.Context, req *Request) (*Response, error) { + return nil, errors.New("ForwardGenericRequest is not implemented in StaticSystemView") +} + func (d StaticSystemView) DefaultLeaseTTL() time.Duration { return d.DefaultLeaseTTLVal } From cd0e64e4300bcfc27637cea417d8018c7701c0c6 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Wed, 12 Jun 2019 09:38:35 -0400 Subject: [PATCH 09/20] Bump Go version in build Dockerfile --- scripts/cross/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/cross/Dockerfile b/scripts/cross/Dockerfile index 2ce7ea669a..d367ab2e39 100644 --- a/scripts/cross/Dockerfile +++ b/scripts/cross/Dockerfile @@ -21,7 +21,7 @@ RUN apt-get update -y && apt-get install -y -q nodejs yarn=1.12.1-1 RUN rm -rf /var/lib/apt/lists/* -ENV GOVERSION 1.12.4 +ENV GOVERSION 1.12.6 RUN mkdir /goroot && mkdir /gopath RUN curl https://storage.googleapis.com/golang/go${GOVERSION}.linux-amd64.tar.gz \ | tar xvzf - -C /goroot --strip-components=1 From df32def64b365cb9f266b73411d1557ca359f80f Mon Sep 17 00:00:00 2001 From: Brian Kassouf Date: Wed, 12 Jun 2019 08:56:16 -0700 Subject: [PATCH 10/20] Clear the Barrier AEAD cache on keyring reload (#6870) * Clear the barrier's AEAD cache on keyring reload * Update barrier_aes_gcm_test.go --- vault/barrier_aes_gcm.go | 1 + vault/barrier_aes_gcm_test.go | 79 +++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/vault/barrier_aes_gcm.go b/vault/barrier_aes_gcm.go index d186879ab2..fa02abbc38 100644 --- a/vault/barrier_aes_gcm.go +++ b/vault/barrier_aes_gcm.go @@ -287,6 +287,7 @@ func (b *AESGCMBarrier) ReloadKeyring(ctx context.Context) error { } // Setup the keyring and finish + b.cache = make(map[uint32]cipher.AEAD) b.keyring = keyring return nil } diff --git a/vault/barrier_aes_gcm_test.go b/vault/barrier_aes_gcm_test.go index 38960355b4..b466bb3054 100644 --- a/vault/barrier_aes_gcm_test.go +++ b/vault/barrier_aes_gcm_test.go @@ -516,3 +516,82 @@ func TestEncrypt_BarrierEncryptor(t *testing.T) { t.Fatalf("bad: %s", plain) } } + +func TestAESGCMBarrier_ReloadKeyring(t *testing.T) { + inm, err := inmem.NewInmem(nil, logger) + if err != nil { + t.Fatalf("err: %v", err) + } + b, err := NewAESGCMBarrier(inm) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Initialize and unseal + key, _ := b.GenerateKey() + b.Initialize(context.Background(), key) + b.Unseal(context.Background(), key) + + keyringRaw, err := inm.Get(context.Background(), keyringPath) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Encrypt something to test cache invalidation + _, err = b.Encrypt(context.Background(), "foo", []byte("quick brown fox")) + if err != nil { + t.Fatalf("err: %v", err) + } + + { + // Create a second barrier and rotate the keyring + b2, err := NewAESGCMBarrier(inm) + if err != nil { + t.Fatalf("err: %v", err) + } + b2.Unseal(context.Background(), key) + _, err = b2.Rotate(context.Background()) + if err != nil { + t.Fatalf("err: %v", err) + } + } + + // Reload the keyring on the first + err = b.ReloadKeyring(context.Background()) + if err != nil { + t.Fatalf("err: %v", err) + } + + if b.keyring.ActiveTerm() != 2 { + t.Fatal("failed to reload keyring") + } + if len(b.cache) != 0 { + t.Fatal("failed to clear cache") + } + + // Encrypt something to test cache invalidation + _, err = b.Encrypt(context.Background(), "foo", []byte("quick brown fox")) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Restore old keyring to test rolling back + err = inm.Put(context.Background(), keyringRaw) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Reload the keyring on the first + err = b.ReloadKeyring(context.Background()) + if err != nil { + t.Fatalf("err: %v", err) + } + + if b.keyring.ActiveTerm() != 1 { + t.Fatal("failed to reload keyring") + } + if len(b.cache) != 0 { + t.Fatal("failed to clear cache") + } + +} From 0e77af2c5973893f57865173cbf0b13ff6047cd1 Mon Sep 17 00:00:00 2001 From: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com> Date: Wed, 12 Jun 2019 13:34:26 -0400 Subject: [PATCH 11/20] Fix multiline jwt config setup example (#6873) --- website/source/docs/auth/jwt.html.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/source/docs/auth/jwt.html.md b/website/source/docs/auth/jwt.html.md index 8a60daf6c3..d6718b8004 100644 --- a/website/source/docs/auth/jwt.html.md +++ b/website/source/docs/auth/jwt.html.md @@ -267,9 +267,9 @@ list of available configuration options, please see the [API documentation](/api ```text $ vault write auth/jwt/config \ - oidc_discovery_url="https://myco.auth0.com/" - oidc_client_id="m5i8bj3iofytj", - oidc_client_secret="f4ubv72nfiu23hnsj", + oidc_discovery_url="https://myco.auth0.com/" \ + oidc_client_id="m5i8bj3iofytj" \ + oidc_client_secret="f4ubv72nfiu23hnsj" \ default_role="demo" ``` From 99eb461f98ca8f495f6e4550267366613970db6d Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Thu, 13 Jun 2019 13:41:18 -0400 Subject: [PATCH 12/20] Update vendoring --- vendor/github.com/hashicorp/vault/api/sys_rekey.go | 4 ++-- vendor/github.com/hashicorp/vault/sdk/logical/system_view.go | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/vendor/github.com/hashicorp/vault/api/sys_rekey.go b/vendor/github.com/hashicorp/vault/api/sys_rekey.go index 55f1a703d4..153e486c6d 100644 --- a/vendor/github.com/hashicorp/vault/api/sys_rekey.go +++ b/vendor/github.com/hashicorp/vault/api/sys_rekey.go @@ -234,7 +234,7 @@ func (c *Sys) RekeyRetrieveBackup() (*RekeyRetrieveResponse, error) { } func (c *Sys) RekeyRetrieveRecoveryBackup() (*RekeyRetrieveResponse, error) { - r := c.c.NewRequest("GET", "/v1/sys/rekey/recovery-backup") + r := c.c.NewRequest("GET", "/v1/sys/rekey/recovery-key-backup") ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() @@ -275,7 +275,7 @@ func (c *Sys) RekeyDeleteBackup() error { } func (c *Sys) RekeyDeleteRecoveryBackup() error { - r := c.c.NewRequest("DELETE", "/v1/sys/rekey/recovery-backup") + r := c.c.NewRequest("DELETE", "/v1/sys/rekey/recovery-key-backup") ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() diff --git a/vendor/github.com/hashicorp/vault/sdk/logical/system_view.go b/vendor/github.com/hashicorp/vault/sdk/logical/system_view.go index 550b74a915..52fc2bd6ac 100644 --- a/vendor/github.com/hashicorp/vault/sdk/logical/system_view.go +++ b/vendor/github.com/hashicorp/vault/sdk/logical/system_view.go @@ -72,6 +72,7 @@ type SystemView interface { type ExtendedSystemView interface { Auditor() Auditor + ForwardGenericRequest(context.Context, *Request) (*Response, error) } type StaticSystemView struct { @@ -104,6 +105,10 @@ func (d StaticSystemView) Auditor() Auditor { return noopAuditor{} } +func (d StaticSystemView) ForwardGenericRequest(ctx context.Context, req *Request) (*Response, error) { + return nil, errors.New("ForwardGenericRequest is not implemented in StaticSystemView") +} + func (d StaticSystemView) DefaultLeaseTTL() time.Duration { return d.DefaultLeaseTTLVal } From 263b96ef4ec1ece537a52fdce0f5d01669116b29 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 14 Jun 2019 10:17:04 -0400 Subject: [PATCH 13/20] Tokenhelper v2 (#6662) This provides an sdk util for common token fields and parsing and plumbs it into token store roles. --- CHANGELOG.md | 7 + audit/format.go | 4 + audit/format_json_test.go | 28 +- audit/format_jsonx_test.go | 30 +- sdk/helper/tokenutil/tokenutil.go | 233 +++++++++ sdk/logical/auth.go | 5 + sdk/plugin/pb/backend.pb.go | 324 +++++++------ sdk/plugin/pb/backend.proto | 3 + sdk/plugin/pb/translation.go | 2 + .../external_tests/policy/no_default_test.go | 89 ++++ vault/request_handling.go | 7 +- vault/token_store.go | 456 ++++++++++-------- vault/token_store_test.go | 455 +++++++++++++++-- .../sdk/helper/tokenhelper/tokenhelper.go | 207 ++++++++ .../hashicorp/vault/sdk/logical/auth.go | 5 + .../vault/sdk/plugin/pb/backend.pb.go | 324 +++++++------ .../vault/sdk/plugin/pb/backend.proto | 3 + .../vault/sdk/plugin/pb/translation.go | 2 + vendor/modules.txt | 1 + 19 files changed, 1603 insertions(+), 582 deletions(-) create mode 100644 sdk/helper/tokenutil/tokenutil.go create mode 100644 vault/external_tests/policy/no_default_test.go create mode 100644 vendor/github.com/hashicorp/vault/sdk/helper/tokenhelper/tokenhelper.go diff --git a/CHANGELOG.md b/CHANGELOG.md index db986e1852..98cdc474f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ CHANGES: + * auth/token: Token store roles use new, common token fields for the values + that overlap with other auth backends. `period`, `explicit_max_ttl`, and + `bound_cidrs` will continue to work, with priority being given to the + `token_` prefixed versions of those parameters. They will also be returned + when doing a read on the role if they were used to provide values initially; + however, in Vault 1.4 if `period` or `explicit_max_ttl` is zero they will no + longer be returned. (`explicit_max_ttl` was already not returned if empty.) * Due to underlying changes in Go version 1.12 and Go > 1.11.5, Vault is now stricter about what characters it will accept in path names. Whereas before it would filter out unprintable characters (and this could be turned off), diff --git a/audit/format.go b/audit/format.go index c5f57ff0d6..1c0ddb3fb9 100644 --- a/audit/format.go +++ b/audit/format.go @@ -153,6 +153,7 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config TokenPolicies: auth.TokenPolicies, IdentityPolicies: auth.IdentityPolicies, ExternalNamespacePolicies: auth.ExternalNamespacePolicies, + NoDefaultPolicy: auth.NoDefaultPolicy, Metadata: auth.Metadata, EntityID: auth.EntityID, RemainingUses: req.ClientTokenRemainingUses, @@ -352,6 +353,7 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config TokenPolicies: resp.Auth.TokenPolicies, IdentityPolicies: resp.Auth.IdentityPolicies, ExternalNamespacePolicies: resp.Auth.ExternalNamespacePolicies, + NoDefaultPolicy: resp.Auth.NoDefaultPolicy, Metadata: resp.Auth.Metadata, NumUses: resp.Auth.NumUses, EntityID: resp.Auth.EntityID, @@ -397,6 +399,7 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config TokenPolicies: auth.TokenPolicies, IdentityPolicies: auth.IdentityPolicies, ExternalNamespacePolicies: auth.ExternalNamespacePolicies, + NoDefaultPolicy: auth.NoDefaultPolicy, Metadata: auth.Metadata, RemainingUses: req.ClientTokenRemainingUses, EntityID: auth.EntityID, @@ -496,6 +499,7 @@ type AuditAuth struct { TokenPolicies []string `json:"token_policies,omitempty"` IdentityPolicies []string `json:"identity_policies,omitempty"` ExternalNamespacePolicies map[string][]string `json:"external_namespace_policies,omitempty"` + NoDefaultPolicy bool `json:"no_default_policy,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` NumUses int `json:"num_uses,omitempty"` RemainingUses int `json:"remaining_uses,omitempty"` diff --git a/audit/format_json_test.go b/audit/format_json_test.go index a937eb342b..a1e32a1103 100644 --- a/audit/format_json_test.go +++ b/audit/format_json_test.go @@ -38,12 +38,13 @@ func TestFormatJSON_formatRequest(t *testing.T) { }{ "auth, request": { &logical.Auth{ - ClientToken: "foo", - Accessor: "bar", - EntityID: "foobarentity", - DisplayName: "testtoken", - Policies: []string{"root"}, - TokenType: logical.TokenTypeService, + ClientToken: "foo", + Accessor: "bar", + DisplayName: "testtoken", + EntityID: "foobarentity", + NoDefaultPolicy: true, + Policies: []string{"root"}, + TokenType: logical.TokenTypeService, }, &logical.Request{ Operation: logical.UpdateOperation, @@ -64,12 +65,13 @@ func TestFormatJSON_formatRequest(t *testing.T) { }, "auth, request with prefix": { &logical.Auth{ - ClientToken: "foo", - Accessor: "bar", - EntityID: "foobarentity", - DisplayName: "testtoken", - Policies: []string{"root"}, - TokenType: logical.TokenTypeService, + ClientToken: "foo", + Accessor: "bar", + EntityID: "foobarentity", + DisplayName: "testtoken", + NoDefaultPolicy: true, + Policies: []string{"root"}, + TokenType: logical.TokenTypeService, }, &logical.Request{ Operation: logical.UpdateOperation, @@ -141,5 +143,5 @@ func TestFormatJSON_formatRequest(t *testing.T) { } } -const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"metadata":null,"entity_id":"foobarentity","token_type":"service"},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"} +const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"no_default_policy":true,"metadata":null,"entity_id":"foobarentity","token_type":"service"},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"} ` diff --git a/audit/format_jsonx_test.go b/audit/format_jsonx_test.go index d5239277a6..6c46d3d9db 100644 --- a/audit/format_jsonx_test.go +++ b/audit/format_jsonx_test.go @@ -37,12 +37,13 @@ func TestFormatJSONx_formatRequest(t *testing.T) { }{ "auth, request": { &logical.Auth{ - ClientToken: "foo", - Accessor: "bar", - EntityID: "foobarentity", - DisplayName: "testtoken", - Policies: []string{"root"}, - TokenType: logical.TokenTypeService, + ClientToken: "foo", + Accessor: "bar", + DisplayName: "testtoken", + EntityID: "foobarentity", + NoDefaultPolicy: true, + Policies: []string{"root"}, + TokenType: logical.TokenTypeService, }, &logical.Request{ ID: "request", @@ -64,17 +65,18 @@ func TestFormatJSONx_formatRequest(t *testing.T) { errors.New("this is an error"), "", "", - fmt.Sprintf(`bar%stesttokenfoobarentityrootservicethis is an error%sbarbarrequestrootupdate/footrue127.0.0.160request`, + fmt.Sprintf(`bar%stesttokenfoobarentitytruerootservicethis is an error%sbarbarrequestrootupdate/footrue127.0.0.160request`, fooSalted, fooSalted), }, "auth, request with prefix": { &logical.Auth{ - ClientToken: "foo", - Accessor: "bar", - EntityID: "foobarentity", - DisplayName: "testtoken", - Policies: []string{"root"}, - TokenType: logical.TokenTypeService, + ClientToken: "foo", + Accessor: "bar", + DisplayName: "testtoken", + NoDefaultPolicy: true, + EntityID: "foobarentity", + Policies: []string{"root"}, + TokenType: logical.TokenTypeService, }, &logical.Request{ ID: "request", @@ -96,7 +98,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) { errors.New("this is an error"), "", "@cee: ", - fmt.Sprintf(`bar%stesttokenfoobarentityrootservicethis is an error%sbarbarrequestrootupdate/footrue127.0.0.160request`, + fmt.Sprintf(`bar%stesttokenfoobarentitytruerootservicethis is an error%sbarbarrequestrootupdate/footrue127.0.0.160request`, fooSalted, fooSalted), }, } diff --git a/sdk/helper/tokenutil/tokenutil.go b/sdk/helper/tokenutil/tokenutil.go new file mode 100644 index 0000000000..5945fd47e0 --- /dev/null +++ b/sdk/helper/tokenutil/tokenutil.go @@ -0,0 +1,233 @@ +package tokenutil + +import ( + "errors" + "fmt" + "time" + + sockaddr "github.com/hashicorp/go-sockaddr" + "github.com/hashicorp/vault/sdk/framework" + "github.com/hashicorp/vault/sdk/helper/parseutil" + "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/hashicorp/vault/sdk/logical" +) + +// TokenParams contains a set of common parameters that auth plugins can use +// for setting token behavior +type TokenParams struct { + // The set of CIDRs that tokens generated using this role will be bound to + TokenBoundCIDRs []*sockaddr.SockAddrMarshaler `json:"token_bound_cidrs"` + + // If set, the token entry will have an explicit maximum TTL set, rather + // than deferring to role/mount values + TokenExplicitMaxTTL time.Duration `json:"token_explicit_max_ttl" mapstructure:"token_explicit_max_ttl"` + + // The max TTL to use for the token + TokenMaxTTL time.Duration `json:"token_max_ttl" mapstructure:"token_max_ttl"` + + // If set, core will not automatically add default to the policy list + TokenNoDefaultPolicy bool `json:"token_no_default_policy" mapstructure:"token_no_default_policy"` + + // The maximum number of times a token issued from this role may be used. + TokenNumUses int `json:"token_num_uses" mapstructure:"token_num_uses"` + + // If non-zero, tokens created using this role will be able to be renewed + // forever, but will have a fixed renewal period of this value + TokenPeriod time.Duration `json:"token_period" mapstructure:"token_period"` + + // The policies to set + TokenPolicies []string `json:"token_policies" mapstructure:"token_policies"` + + // The type of token this role should issue + TokenType logical.TokenType `json:"token_type" mapstructure:"token_type"` + + // The TTL to user for the token + TokenTTL time.Duration `json:"token_ttl" mapstructure:"token_ttl"` +} + +// AddTokenFields adds fields to an existing role. It panics if it would +// overwrite an existing field. +func AddTokenFields(m map[string]*framework.FieldSchema) { + AddTokenFieldsWithAllowList(m, nil) +} + +// AddTokenFields adds fields to an existing role. It panics if it would +// overwrite an existing field. Allowed can be use to restrict the set, e.g. if +// there would be conflicts. +func AddTokenFieldsWithAllowList(m map[string]*framework.FieldSchema, allowed []string) { + r := TokenFields() + for k, v := range r { + if len(allowed) > 0 && !strutil.StrListContains(allowed, k) { + continue + } + if _, has := m[k]; has { + panic(fmt.Sprintf("adding role field %s would overwrite existing field", k)) + } + m[k] = v + } +} + +// TokenFields provides a set of field schemas for the parameters +func TokenFields() map[string]*framework.FieldSchema { + return map[string]*framework.FieldSchema{ + "token_bound_cidrs": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: `Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.`, + }, + + "token_explicit_max_ttl": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: tokenExplicitMaxTTLHelp, + }, + + "token_max_ttl": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: "The maximum lifetime of the generated token", + }, + + "token_no_default_policy": &framework.FieldSchema{ + Type: framework.TypeBool, + Description: "If true, the 'default' policy will not automatically be added to generated tokens", + }, + + "token_period": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: tokenPeriodHelp, + }, + + "token_policies": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "Comma-separated list of policies", + }, + + "token_type": &framework.FieldSchema{ + Type: framework.TypeString, + Default: "default-service", + Description: "The type of token to generate, service or batch", + }, + + "token_ttl": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: "The initial ttl of the token to generate", + }, + + "token_num_uses": &framework.FieldSchema{ + Type: framework.TypeInt, + Description: "The maximum number of times a token may be used, a value of zero means unlimited", + }, + } +} + +// ParseTokenFields provides common field parsing functionality into a TokenFields struct +func (t *TokenParams) ParseTokenFields(req *logical.Request, d *framework.FieldData) error { + if boundCIDRsRaw, ok := d.GetOk("token_bound_cidrs"); ok { + boundCIDRs, err := parseutil.ParseAddrs(boundCIDRsRaw.([]string)) + if err != nil { + return err + } + t.TokenBoundCIDRs = boundCIDRs + } + + if explicitMaxTTLRaw, ok := d.GetOk("token_explicit_max_ttl"); ok { + t.TokenExplicitMaxTTL = time.Duration(explicitMaxTTLRaw.(int)) * time.Second + } + + if maxTTLRaw, ok := d.GetOk("token_max_ttl"); ok { + t.TokenMaxTTL = time.Duration(maxTTLRaw.(int)) * time.Second + } + if t.TokenMaxTTL < 0 { + return errors.New("'token_max_ttl' cannot be negative") + } + + if noDefaultRaw, ok := d.GetOk("token_no_default_policy"); ok { + t.TokenNoDefaultPolicy = noDefaultRaw.(bool) + } + + if periodRaw, ok := d.GetOk("token_period"); ok { + t.TokenPeriod = time.Duration(periodRaw.(int)) * time.Second + } + if t.TokenPeriod < 0 { + return errors.New("'token_period' cannot be negative") + } + + if policiesRaw, ok := d.GetOk("token_policies"); ok { + t.TokenPolicies = policiesRaw.([]string) + } + + if tokenTypeRaw, ok := d.GetOk("token_type"); ok { + var tokenType logical.TokenType + tokenTypeStr := tokenTypeRaw.(string) + switch tokenTypeStr { + case "service": + tokenType = logical.TokenTypeService + case "batch": + tokenType = logical.TokenTypeBatch + case "", "default", "default-service": + tokenType = logical.TokenTypeDefaultService + case "default-batch": + tokenType = logical.TokenTypeDefaultBatch + default: + return fmt.Errorf("invalid 'token_type' value %q", tokenTypeStr) + } + t.TokenType = tokenType + } + + if ttlRaw, ok := d.GetOk("token_ttl"); ok { + t.TokenTTL = time.Duration(ttlRaw.(int)) * time.Second + } + if t.TokenTTL < 0 { + return errors.New("'token_ttl' cannot be negative") + } + if t.TokenTTL > 0 && t.TokenMaxTTL > 0 && t.TokenTTL > t.TokenMaxTTL { + return errors.New("'token_ttl' cannot be greater than 'token_max_ttl'") + } + + if tokenNumUses, ok := d.GetOk("token_num_uses"); ok { + t.TokenNumUses = tokenNumUses.(int) + } + if t.TokenNumUses < 0 { + return errors.New("'token_num_uses' cannot be negative") + } + + return nil +} + +// PopulateTokenData adds information from TokenParams into the map +func (t *TokenParams) PopulateTokenData(m map[string]interface{}) { + m["token_bound_cidrs"] = t.TokenBoundCIDRs + m["token_explicit_max_ttl"] = t.TokenExplicitMaxTTL.Seconds() + m["token_max_ttl"] = t.TokenMaxTTL.Seconds() + m["token_no_default_policy"] = t.TokenNoDefaultPolicy + m["token_period"] = t.TokenPeriod.Seconds() + m["token_policies"] = t.TokenPolicies + m["token_type"] = t.TokenType.String() + m["token_ttl"] = t.TokenTTL.Seconds() + m["token_num_uses"] = t.TokenNumUses +} + +// PopulateTokenAuth populates Auth with parameters +func (t *TokenParams) PopulateTokenAuth(auth *logical.Auth) { + auth.BoundCIDRs = t.TokenBoundCIDRs + auth.ExplicitMaxTTL = t.TokenExplicitMaxTTL + auth.MaxTTL = t.TokenMaxTTL + auth.NoDefaultPolicy = t.TokenNoDefaultPolicy + auth.Period = t.TokenPeriod + auth.Policies = t.TokenPolicies + auth.TokenType = t.TokenType + auth.TTL = t.TokenTTL + auth.NumUses = t.TokenNumUses +} + +const ( + tokenPeriodHelp = `If set, tokens created via this role +will have no max lifetime; instead, their +renewal period will be fixed to this value. +This takes an integer number of seconds, +or a string duration (e.g. "24h").` + tokenExplicitMaxTTLHelp = `If set, tokens created via this role +carry an explicit maximum TTL. During renewal, +the current maximum TTL values of the role +and the mount are not checked for changes, +and any updates to these values will have +no effect on the token being renewed.` +) diff --git a/sdk/logical/auth.go b/sdk/logical/auth.go index 89aa916590..2bfb6e0015 100644 --- a/sdk/logical/auth.go +++ b/sdk/logical/auth.go @@ -38,6 +38,11 @@ type Auth struct { // different namespaces indexed by respective namespace identifiers ExternalNamespacePolicies map[string][]string `json:"external_namespace_policies" mapstructure:"external_namespace_policies" structs:"external_namespace_policies"` + // Indicates that the default policy should not be added by core when + // creating a token. The default policy will still be added if it's + // explicitly defined. + NoDefaultPolicy bool `json:"no_default_policy" mapstructure:"no_default_policy" structs:"no_default_policy"` + // Metadata is used to attach arbitrary string-type metadata to // an authenticated user. This metadata will be outputted into the // audit log. diff --git a/sdk/plugin/pb/backend.pb.go b/sdk/plugin/pb/backend.pb.go index 2db00c7e9e..28ff0c455e 100644 --- a/sdk/plugin/pb/backend.pb.go +++ b/sdk/plugin/pb/backend.pb.go @@ -526,7 +526,9 @@ type Auth struct { // TTL is a hard limit and cannot be exceeded, also counts for periodic tokens. ExplicitMaxTTL int64 `sentinel:"" protobuf:"varint,16,opt,name=explicit_max_ttl,json=explicitMaxTtl,proto3" json:"explicit_max_ttl,omitempty"` // TokenType is the type of token being requested - TokenType uint32 `sentinel:"" protobuf:"varint,17,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` + TokenType uint32 `sentinel:"" protobuf:"varint,17,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` + // Whether the default policy should be added automatically by core + NoDefaultPolicy bool `sentinel:"" protobuf:"varint,18,opt,name=no_default_policy,json=noDefaultPolicy,proto3" json:"no_default_policy,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -676,6 +678,13 @@ func (m *Auth) GetTokenType() uint32 { return 0 } +func (m *Auth) GetNoDefaultPolicy() bool { + if m != nil { + return m.NoDefaultPolicy + } + return false +} + type TokenEntry struct { ID string `sentinel:"" protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Accessor string `sentinel:"" protobuf:"bytes,2,opt,name=accessor,proto3" json:"accessor,omitempty"` @@ -2713,164 +2722,165 @@ func init() { func init() { proto.RegisterFile("sdk/plugin/pb/backend.proto", fileDescriptor_4dbf1dfe0c11846b) } var fileDescriptor_4dbf1dfe0c11846b = []byte{ - // 2499 bytes of a gzipped FileDescriptorProto + // 2519 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xdb, 0x72, 0x1b, 0xc7, 0xd1, 0x2e, 0x00, 0xc4, 0xa9, 0x71, 0x22, 0x46, 0xb4, 0xfe, 0x15, 0x24, 0xff, 0x82, 0xd7, 0x91, - 0x0c, 0x2b, 0x36, 0x68, 0x51, 0x71, 0x2c, 0x27, 0x65, 0xa7, 0x68, 0x8a, 0x96, 0x19, 0x93, 0x36, - 0x6b, 0x09, 0xc5, 0x39, 0x55, 0xc1, 0x83, 0xdd, 0x21, 0xb8, 0xc5, 0xc5, 0xee, 0x66, 0x76, 0x96, - 0x22, 0xae, 0xf2, 0x16, 0x79, 0x8d, 0xdc, 0xa6, 0x72, 0x93, 0xbb, 0x94, 0x2b, 0xf7, 0x79, 0x8d, - 0x3c, 0x43, 0x6a, 0x7a, 0x66, 0x4f, 0x00, 0x68, 0xc9, 0x55, 0xce, 0xdd, 0x4c, 0x77, 0xcf, 0xa9, - 0xe7, 0xeb, 0xaf, 0x7b, 0x76, 0xe1, 0x6e, 0xe4, 0x5c, 0xee, 0x86, 0x5e, 0x3c, 0x77, 0xfd, 0xdd, - 0x70, 0xb6, 0x3b, 0xa3, 0xf6, 0x25, 0xf3, 0x9d, 0x71, 0xc8, 0x03, 0x11, 0x90, 0x72, 0x38, 0x1b, - 0xdc, 0x9f, 0x07, 0xc1, 0xdc, 0x63, 0xbb, 0x28, 0x99, 0xc5, 0xe7, 0xbb, 0xc2, 0x5d, 0xb0, 0x48, - 0xd0, 0x45, 0xa8, 0x8c, 0x06, 0x03, 0x39, 0x83, 0x17, 0xcc, 0x5d, 0x9b, 0x7a, 0xbb, 0xae, 0xc3, - 0x7c, 0xe1, 0x8a, 0xa5, 0xd6, 0x19, 0x79, 0x9d, 0x5a, 0x45, 0x69, 0xcc, 0x3a, 0x54, 0x0f, 0x17, - 0xa1, 0x58, 0x9a, 0x43, 0xa8, 0x7d, 0xc1, 0xa8, 0xc3, 0x38, 0xb9, 0x0d, 0xb5, 0x0b, 0x6c, 0x19, - 0xa5, 0x61, 0x65, 0xd4, 0xb4, 0x74, 0xcf, 0xfc, 0x03, 0xc0, 0xa9, 0x1c, 0x73, 0xc8, 0x79, 0xc0, - 0xc9, 0x1d, 0x68, 0x30, 0xce, 0xa7, 0x62, 0x19, 0x32, 0xa3, 0x34, 0x2c, 0x8d, 0x3a, 0x56, 0x9d, - 0x71, 0x3e, 0x59, 0x86, 0x8c, 0xfc, 0x1f, 0xc8, 0xe6, 0x74, 0x11, 0xcd, 0x8d, 0xf2, 0xb0, 0x24, - 0x67, 0x60, 0x9c, 0x9f, 0x44, 0xf3, 0x64, 0x8c, 0x1d, 0x38, 0xcc, 0xa8, 0x0c, 0x4b, 0xa3, 0x0a, - 0x8e, 0x39, 0x08, 0x1c, 0x66, 0xfe, 0xa5, 0x04, 0xd5, 0x53, 0x2a, 0x2e, 0x22, 0x42, 0x60, 0x8b, - 0x07, 0x81, 0xd0, 0x8b, 0x63, 0x9b, 0x8c, 0xa0, 0x17, 0xfb, 0x34, 0x16, 0x17, 0xf2, 0x54, 0x36, - 0x15, 0xcc, 0x31, 0xca, 0xa8, 0x5e, 0x15, 0x93, 0xb7, 0xa1, 0xe3, 0x05, 0x36, 0xf5, 0xa6, 0x91, - 0x08, 0x38, 0x9d, 0xcb, 0x75, 0xa4, 0x5d, 0x1b, 0x85, 0x67, 0x4a, 0x46, 0x1e, 0x41, 0x3f, 0x62, - 0xd4, 0x9b, 0xbe, 0xe4, 0x34, 0x4c, 0x0d, 0xb7, 0xd4, 0x84, 0x52, 0xf1, 0x0d, 0xa7, 0xa1, 0xb6, - 0x35, 0xff, 0x51, 0x83, 0xba, 0xc5, 0xfe, 0x14, 0xb3, 0x48, 0x90, 0x2e, 0x94, 0x5d, 0x07, 0x4f, - 0xdb, 0xb4, 0xca, 0xae, 0x43, 0xc6, 0x40, 0x2c, 0x16, 0x7a, 0x72, 0x69, 0x37, 0xf0, 0x0f, 0xbc, - 0x38, 0x12, 0x8c, 0xeb, 0x33, 0x6f, 0xd0, 0x90, 0x7b, 0xd0, 0x0c, 0x42, 0xc6, 0x51, 0x86, 0x0e, - 0x68, 0x5a, 0x99, 0x40, 0x1e, 0x3c, 0xa4, 0xe2, 0xc2, 0xd8, 0x42, 0x05, 0xb6, 0xa5, 0xcc, 0xa1, - 0x82, 0x1a, 0x55, 0x25, 0x93, 0x6d, 0x62, 0x42, 0x2d, 0x62, 0x36, 0x67, 0xc2, 0xa8, 0x0d, 0x4b, - 0xa3, 0xd6, 0x1e, 0x8c, 0xc3, 0xd9, 0xf8, 0x0c, 0x25, 0x96, 0xd6, 0x90, 0x7b, 0xb0, 0x25, 0xfd, - 0x62, 0xd4, 0xd1, 0xa2, 0x21, 0x2d, 0xf6, 0x63, 0x71, 0x61, 0xa1, 0x94, 0xec, 0x41, 0x5d, 0xdd, - 0x69, 0x64, 0x34, 0x86, 0x95, 0x51, 0x6b, 0xcf, 0x90, 0x06, 0xfa, 0x94, 0x63, 0x05, 0x83, 0xe8, - 0xd0, 0x17, 0x7c, 0x69, 0x25, 0x86, 0xe4, 0x2d, 0x68, 0xdb, 0x9e, 0xcb, 0x7c, 0x31, 0x15, 0xc1, - 0x25, 0xf3, 0x8d, 0x26, 0xee, 0xa8, 0xa5, 0x64, 0x13, 0x29, 0x22, 0x7b, 0xf0, 0x46, 0xde, 0x64, - 0x4a, 0x6d, 0x9b, 0x45, 0x51, 0xc0, 0x0d, 0x40, 0xdb, 0x5b, 0x39, 0xdb, 0x7d, 0xad, 0x92, 0xd3, - 0x3a, 0x6e, 0x14, 0x7a, 0x74, 0x39, 0xf5, 0xe9, 0x82, 0x19, 0x2d, 0x35, 0xad, 0x96, 0x7d, 0x45, - 0x17, 0x8c, 0xdc, 0x87, 0xd6, 0x22, 0x88, 0x7d, 0x31, 0x0d, 0x03, 0xd7, 0x17, 0x46, 0x1b, 0x2d, - 0x00, 0x45, 0xa7, 0x52, 0x42, 0xde, 0x04, 0xd5, 0x53, 0x60, 0xec, 0x28, 0xbf, 0xa2, 0x04, 0xe1, - 0xf8, 0x00, 0xba, 0x4a, 0x9d, 0xee, 0xa7, 0x8b, 0x26, 0x1d, 0x94, 0xa6, 0x3b, 0xf9, 0x00, 0x9a, - 0x88, 0x07, 0xd7, 0x3f, 0x0f, 0x8c, 0x1e, 0xfa, 0xed, 0x56, 0xce, 0x2d, 0x12, 0x13, 0x47, 0xfe, - 0x79, 0x60, 0x35, 0x5e, 0xea, 0x16, 0xf9, 0x04, 0xee, 0x16, 0xce, 0xcb, 0xd9, 0x82, 0xba, 0xbe, - 0xeb, 0xcf, 0xa7, 0x71, 0xc4, 0x22, 0x63, 0x1b, 0x11, 0x6e, 0xe4, 0x4e, 0x6d, 0x25, 0x06, 0x2f, - 0x22, 0x16, 0x91, 0xbb, 0xd0, 0x54, 0x41, 0x3a, 0x75, 0x1d, 0xa3, 0x8f, 0x5b, 0x6a, 0x28, 0xc1, - 0x91, 0x43, 0xde, 0x81, 0x5e, 0x18, 0x78, 0xae, 0xbd, 0x9c, 0x06, 0x57, 0x8c, 0x73, 0xd7, 0x61, - 0x06, 0x19, 0x96, 0x46, 0x0d, 0xab, 0xab, 0xc4, 0x5f, 0x6b, 0xe9, 0xa6, 0xd0, 0xb8, 0x85, 0x86, - 0x6b, 0xa1, 0x31, 0x06, 0xb0, 0x03, 0xdf, 0x67, 0x36, 0xc2, 0x6f, 0x07, 0x4f, 0xd8, 0x95, 0x27, - 0x3c, 0x48, 0xa5, 0x56, 0xce, 0x62, 0xf0, 0x39, 0xb4, 0xf3, 0x50, 0x20, 0xdb, 0x50, 0xb9, 0x64, - 0x4b, 0x0d, 0x7f, 0xd9, 0x24, 0x43, 0xa8, 0x5e, 0x51, 0x2f, 0x66, 0x08, 0x79, 0x0d, 0x44, 0x35, - 0xc4, 0x52, 0x8a, 0x5f, 0x94, 0x9f, 0x96, 0xcc, 0xbf, 0x57, 0x61, 0x4b, 0x82, 0x8f, 0x7c, 0x08, - 0x1d, 0x8f, 0xd1, 0x88, 0x4d, 0x83, 0x50, 0x2e, 0x10, 0xe1, 0x54, 0xad, 0xbd, 0x6d, 0x39, 0xec, - 0x58, 0x2a, 0xbe, 0x56, 0x72, 0xab, 0xed, 0xe5, 0x7a, 0x32, 0xa4, 0x5d, 0x5f, 0x30, 0xee, 0x53, - 0x6f, 0x8a, 0xc1, 0xa0, 0x02, 0xac, 0x9d, 0x08, 0x9f, 0xc9, 0xa0, 0x58, 0xc5, 0x51, 0x65, 0x1d, - 0x47, 0x03, 0x68, 0xa0, 0xef, 0x5c, 0x16, 0xe9, 0x60, 0x4f, 0xfb, 0x64, 0x0f, 0x1a, 0x0b, 0x26, - 0xa8, 0x8e, 0x35, 0x19, 0x12, 0xb7, 0x93, 0x98, 0x19, 0x9f, 0x68, 0x85, 0x0a, 0x88, 0xd4, 0x6e, - 0x2d, 0x22, 0x6a, 0xeb, 0x11, 0x31, 0x80, 0x46, 0x0a, 0xba, 0xba, 0xba, 0xe1, 0xa4, 0x2f, 0x69, - 0x36, 0x64, 0xdc, 0x0d, 0x1c, 0xa3, 0x81, 0x40, 0xd1, 0x3d, 0x49, 0x92, 0x7e, 0xbc, 0x50, 0x10, - 0x6a, 0x2a, 0x92, 0xf4, 0xe3, 0xc5, 0x3a, 0x62, 0x60, 0x05, 0x31, 0x3f, 0x81, 0x2a, 0xf5, 0x5c, - 0x1a, 0x61, 0x08, 0xc9, 0x9b, 0xd5, 0x7c, 0x3f, 0xde, 0x97, 0x52, 0x4b, 0x29, 0xc9, 0x13, 0xe8, - 0xcc, 0x79, 0x10, 0x87, 0x53, 0xec, 0xb2, 0xc8, 0x68, 0xe3, 0x69, 0x57, 0xad, 0xdb, 0x68, 0xb4, - 0xaf, 0x6c, 0x64, 0x04, 0xce, 0x82, 0xd8, 0x77, 0xa6, 0xb6, 0xeb, 0xf0, 0xc8, 0xe8, 0xa0, 0xf3, - 0x00, 0x45, 0x07, 0x52, 0x22, 0x43, 0x4c, 0x85, 0x40, 0xea, 0xe0, 0x2e, 0xda, 0x74, 0x50, 0x7a, - 0x9a, 0x78, 0xf9, 0xa7, 0xd0, 0x4f, 0x12, 0x53, 0x66, 0xd9, 0x43, 0xcb, 0xed, 0x44, 0x91, 0x1a, - 0x8f, 0x60, 0x9b, 0x5d, 0x4b, 0x0a, 0x75, 0xc5, 0x74, 0x41, 0xaf, 0xa7, 0x42, 0x78, 0x3a, 0xa4, - 0xba, 0x89, 0xfc, 0x84, 0x5e, 0x4f, 0x84, 0x27, 0xe3, 0x5f, 0xad, 0x8e, 0xf1, 0xdf, 0xc7, 0x64, - 0xd4, 0x44, 0x89, 0x8c, 0xff, 0xc1, 0x2f, 0xa1, 0x53, 0xb8, 0xc2, 0x0d, 0x40, 0xde, 0xc9, 0x03, - 0xb9, 0x99, 0x07, 0xef, 0xbf, 0xb6, 0x00, 0xf0, 0x2e, 0xd5, 0xd0, 0xd5, 0x0c, 0x90, 0xbf, 0xe0, - 0xf2, 0x86, 0x0b, 0xa6, 0x9c, 0xf9, 0x42, 0x83, 0x51, 0xf7, 0xbe, 0x17, 0x87, 0x49, 0x0e, 0xa8, - 0xe6, 0x72, 0xc0, 0x7b, 0xb0, 0x25, 0x31, 0x67, 0xd4, 0x32, 0xaa, 0xce, 0x76, 0x84, 0xe8, 0x54, - 0xc8, 0x44, 0xab, 0xb5, 0x40, 0xa8, 0xaf, 0x07, 0x42, 0x1e, 0x61, 0x8d, 0x22, 0xc2, 0xde, 0x86, - 0x8e, 0xcd, 0x19, 0xe6, 0xa3, 0xa9, 0x2c, 0x30, 0x34, 0x02, 0xdb, 0x89, 0x70, 0xe2, 0x2e, 0x98, - 0xf4, 0x9f, 0xbc, 0x0c, 0x40, 0x95, 0x6c, 0x6e, 0xbc, 0xab, 0xd6, 0xc6, 0xbb, 0xc2, 0xec, 0xee, - 0x31, 0xcd, 0xe2, 0xd8, 0xce, 0x45, 0x42, 0xa7, 0x10, 0x09, 0x05, 0xb8, 0x77, 0x57, 0xe0, 0xbe, - 0x82, 0xc9, 0xde, 0x1a, 0x26, 0xdf, 0x82, 0xb6, 0x74, 0x40, 0x14, 0x52, 0x9b, 0xc9, 0x09, 0xb6, - 0x95, 0x23, 0x52, 0xd9, 0x91, 0x83, 0x11, 0x1c, 0xcf, 0x66, 0xcb, 0x8b, 0xc0, 0x63, 0x19, 0x09, - 0xb7, 0x52, 0xd9, 0x91, 0x23, 0xf7, 0x8b, 0xa8, 0x22, 0x88, 0x2a, 0x6c, 0x0f, 0x3e, 0x82, 0x66, - 0xea, 0xf5, 0x1f, 0x04, 0xa6, 0xbf, 0x96, 0xa0, 0x9d, 0x27, 0x3a, 0x39, 0x78, 0x32, 0x39, 0xc6, - 0xc1, 0x15, 0x4b, 0x36, 0x65, 0x89, 0xc0, 0x99, 0xcf, 0x5e, 0xd2, 0x99, 0xa7, 0x26, 0x68, 0x58, - 0x99, 0x40, 0x6a, 0x5d, 0xdf, 0xe6, 0x6c, 0x91, 0xa0, 0xaa, 0x62, 0x65, 0x02, 0xf2, 0x31, 0x80, - 0x1b, 0x45, 0x31, 0x53, 0x37, 0xb7, 0x85, 0x34, 0x30, 0x18, 0xab, 0xba, 0x71, 0x9c, 0xd4, 0x8d, - 0xe3, 0x49, 0x52, 0x37, 0x5a, 0x4d, 0xb4, 0xc6, 0x2b, 0xbd, 0x0d, 0x35, 0x79, 0x41, 0x93, 0x63, - 0x44, 0x5e, 0xc5, 0xd2, 0x3d, 0xf3, 0xcf, 0x50, 0x53, 0x95, 0xc5, 0xff, 0x94, 0xbc, 0xef, 0x40, - 0x43, 0xcd, 0xed, 0x3a, 0x3a, 0x56, 0xea, 0xd8, 0x3f, 0x72, 0xcc, 0xef, 0xca, 0xd0, 0xb0, 0x58, - 0x14, 0x06, 0x7e, 0xc4, 0x72, 0x95, 0x4f, 0xe9, 0x95, 0x95, 0x4f, 0x79, 0x63, 0xe5, 0x93, 0xd4, - 0x53, 0x95, 0x5c, 0x3d, 0x35, 0x80, 0x06, 0x67, 0x8e, 0xcb, 0x99, 0x2d, 0x74, 0xed, 0x95, 0xf6, - 0xa5, 0xee, 0x25, 0xe5, 0x32, 0x65, 0x47, 0x98, 0x17, 0x9a, 0x56, 0xda, 0x27, 0x8f, 0xf3, 0x05, - 0x83, 0x2a, 0xc5, 0x76, 0x54, 0xc1, 0xa0, 0xb6, 0xbb, 0xa1, 0x62, 0x78, 0x92, 0x15, 0x5e, 0x75, - 0x8c, 0xe6, 0x3b, 0xf9, 0x01, 0x9b, 0x2b, 0xaf, 0x1f, 0x2d, 0x0f, 0x7f, 0x57, 0x86, 0xed, 0xd5, - 0xbd, 0x6d, 0x40, 0xe0, 0x0e, 0x54, 0x55, 0x3e, 0xd3, 0xf0, 0x15, 0x6b, 0x99, 0xac, 0xb2, 0x42, - 0x74, 0xbf, 0x5a, 0x25, 0x8d, 0x57, 0x43, 0xaf, 0x48, 0x28, 0xef, 0xc2, 0xb6, 0x74, 0x51, 0xc8, - 0x9c, 0xac, 0x46, 0x53, 0x0c, 0xd8, 0xd3, 0xf2, 0xb4, 0x4a, 0x7b, 0x04, 0xfd, 0xc4, 0x34, 0xe3, - 0x86, 0x5a, 0xc1, 0xf6, 0x30, 0xa1, 0x88, 0xdb, 0x50, 0x3b, 0x0f, 0xf8, 0x82, 0x0a, 0x4d, 0x82, - 0xba, 0x57, 0x20, 0x39, 0x64, 0xdb, 0x86, 0xc2, 0x64, 0x22, 0x94, 0xef, 0x10, 0x49, 0x3e, 0xe9, - 0x1b, 0x01, 0x59, 0xb0, 0x61, 0x35, 0x92, 0xb7, 0x81, 0xf9, 0x5b, 0xe8, 0xad, 0x94, 0x85, 0x1b, - 0x1c, 0x99, 0x2d, 0x5f, 0x2e, 0x2c, 0x5f, 0x98, 0xb9, 0xb2, 0x32, 0xf3, 0xef, 0xa0, 0xff, 0x05, - 0xf5, 0x1d, 0x8f, 0xe9, 0xf9, 0xf7, 0xf9, 0x3c, 0x92, 0x09, 0x4e, 0xbf, 0x52, 0xa6, 0x3a, 0xfb, - 0x74, 0xac, 0xa6, 0x96, 0x1c, 0x39, 0xe4, 0x01, 0xd4, 0xb9, 0xb2, 0xd6, 0x00, 0x68, 0xe5, 0xea, - 0x56, 0x2b, 0xd1, 0x99, 0xdf, 0x02, 0x29, 0x4c, 0x2d, 0x1f, 0x28, 0x4b, 0x32, 0x92, 0xe8, 0x57, - 0xa0, 0xd0, 0x51, 0xd5, 0xce, 0x63, 0xd2, 0x4a, 0xb5, 0x64, 0x08, 0x15, 0xc6, 0xb9, 0x5e, 0x02, - 0x0b, 0xc7, 0xec, 0x39, 0x68, 0x49, 0x95, 0xf9, 0x33, 0xe8, 0x9f, 0x85, 0xcc, 0x76, 0xa9, 0x87, - 0x4f, 0x39, 0xb5, 0xc0, 0x7d, 0xa8, 0x4a, 0x27, 0x27, 0x84, 0xd1, 0xc4, 0x81, 0xa8, 0x56, 0x72, - 0xf3, 0x5b, 0x30, 0xd4, 0xbe, 0x0e, 0xaf, 0xdd, 0x48, 0x30, 0xdf, 0x66, 0x07, 0x17, 0xcc, 0xbe, - 0xfc, 0x11, 0x4f, 0x7e, 0x05, 0x77, 0x36, 0xad, 0x90, 0xec, 0xaf, 0x65, 0xcb, 0xde, 0xf4, 0x5c, - 0xe6, 0x0e, 0x5c, 0xa3, 0x61, 0x01, 0x8a, 0x3e, 0x97, 0x12, 0x79, 0x8f, 0x4c, 0x8e, 0x8b, 0x34, - 0x1f, 0xeb, 0x5e, 0xe2, 0x8f, 0xca, 0xcd, 0xfe, 0xf8, 0x5b, 0x09, 0x9a, 0x67, 0x4c, 0xc4, 0x21, - 0x9e, 0xe5, 0x2e, 0x34, 0x67, 0x3c, 0xb8, 0x64, 0x3c, 0x3b, 0x4a, 0x43, 0x09, 0x8e, 0x1c, 0xf2, - 0x18, 0x6a, 0x07, 0x81, 0x7f, 0xee, 0xce, 0xf1, 0x61, 0xab, 0x89, 0x21, 0x1d, 0x3b, 0x56, 0x3a, - 0x45, 0x0c, 0xda, 0x90, 0x0c, 0xa1, 0xa5, 0x3f, 0x13, 0xbc, 0x78, 0x71, 0xf4, 0x2c, 0xa9, 0x78, - 0x73, 0xa2, 0xc1, 0xc7, 0xd0, 0xca, 0x0d, 0xfc, 0x41, 0xa9, 0xea, 0xff, 0x01, 0x70, 0x75, 0xe5, - 0xa3, 0x6d, 0x75, 0x54, 0x3d, 0x52, 0x1e, 0xed, 0x3e, 0x34, 0x65, 0x71, 0xa5, 0xd4, 0x49, 0x92, - 0x2c, 0x65, 0x49, 0xd2, 0x7c, 0x00, 0xfd, 0x23, 0xff, 0x8a, 0x7a, 0xae, 0x43, 0x05, 0xfb, 0x92, - 0x2d, 0xd1, 0x05, 0x6b, 0x3b, 0x30, 0xcf, 0xa0, 0xad, 0x5f, 0xda, 0xaf, 0xb5, 0xc7, 0xb6, 0xde, - 0xe3, 0xf7, 0x07, 0xd1, 0xbb, 0xd0, 0xd3, 0x93, 0x1e, 0xbb, 0x3a, 0x84, 0x64, 0x8d, 0xc1, 0xd9, - 0xb9, 0x7b, 0xad, 0xa7, 0xd6, 0x3d, 0xf3, 0x29, 0x6c, 0xe7, 0x4c, 0xd3, 0xe3, 0x5c, 0xb2, 0x65, - 0x94, 0x7c, 0x81, 0x90, 0xed, 0xc4, 0x03, 0xe5, 0xcc, 0x03, 0x26, 0x74, 0xf5, 0xc8, 0xe7, 0x4c, - 0xdc, 0x70, 0xba, 0x2f, 0xd3, 0x8d, 0x3c, 0x67, 0x7a, 0xf2, 0x87, 0x50, 0x65, 0xf2, 0xa4, 0xf9, - 0xfc, 0x99, 0xf7, 0x80, 0xa5, 0xd4, 0x1b, 0x16, 0x7c, 0x9a, 0x2e, 0x78, 0x1a, 0xab, 0x05, 0x5f, - 0x73, 0x2e, 0xf3, 0xed, 0x74, 0x1b, 0xa7, 0xb1, 0xb8, 0xe9, 0x46, 0x1f, 0x40, 0x5f, 0x1b, 0x3d, - 0x63, 0x1e, 0x13, 0xec, 0x86, 0x23, 0x3d, 0x04, 0x52, 0x30, 0xbb, 0x69, 0xba, 0x7b, 0xd0, 0x98, - 0x4c, 0x8e, 0x53, 0x6d, 0x91, 0x1b, 0xcd, 0x4f, 0xa0, 0x7f, 0x16, 0x3b, 0xc1, 0x29, 0x77, 0xaf, - 0x5c, 0x8f, 0xcd, 0xd5, 0x62, 0x49, 0xf1, 0x5b, 0xca, 0x15, 0xbf, 0x1b, 0xb3, 0x91, 0x39, 0x02, - 0x52, 0x18, 0x9e, 0xde, 0x5b, 0x14, 0x3b, 0x81, 0x0e, 0x61, 0x6c, 0x9b, 0x23, 0x68, 0x4f, 0xa8, - 0x2c, 0x36, 0x1c, 0x65, 0x63, 0x40, 0x5d, 0xa8, 0xbe, 0x36, 0x4b, 0xba, 0xe6, 0x1e, 0xec, 0x1c, - 0x50, 0xfb, 0xc2, 0xf5, 0xe7, 0xcf, 0xdc, 0x48, 0x56, 0x5b, 0x7a, 0xc4, 0x00, 0x1a, 0x8e, 0x16, - 0xe8, 0x21, 0x69, 0xdf, 0x7c, 0x1f, 0xde, 0xc8, 0x7d, 0xe6, 0x39, 0x13, 0x34, 0xf1, 0xc7, 0x0e, - 0x54, 0x23, 0xd9, 0xc3, 0x11, 0x55, 0x4b, 0x75, 0xcc, 0xaf, 0x60, 0x27, 0x9f, 0x80, 0x65, 0xed, - 0x93, 0x1c, 0x1c, 0xab, 0x92, 0x52, 0xae, 0x2a, 0xd1, 0x3e, 0x2b, 0x67, 0xf9, 0x64, 0x1b, 0x2a, - 0xbf, 0xfe, 0x66, 0xa2, 0xc1, 0x2e, 0x9b, 0xe6, 0x1f, 0xe5, 0xf2, 0xc5, 0xf9, 0xd4, 0xf2, 0x85, - 0xd2, 0xa4, 0xf4, 0x5a, 0xa5, 0xc9, 0x3a, 0xde, 0xde, 0x87, 0xfe, 0x89, 0x17, 0xd8, 0x97, 0x87, - 0x7e, 0xce, 0x1b, 0x06, 0xd4, 0x99, 0x9f, 0x77, 0x46, 0xd2, 0x35, 0xdf, 0x81, 0xde, 0x71, 0x60, - 0x53, 0xef, 0x24, 0x88, 0x7d, 0x91, 0x7a, 0x01, 0xbf, 0xbb, 0x69, 0x53, 0xd5, 0x31, 0xdf, 0x87, - 0xae, 0x4e, 0xd1, 0xfe, 0x79, 0x90, 0x30, 0x63, 0x96, 0xcc, 0x4b, 0xc5, 0x42, 0xdf, 0x3c, 0x86, - 0x5e, 0x66, 0xae, 0xe6, 0x7d, 0x07, 0x6a, 0x4a, 0xad, 0xcf, 0xd6, 0x4b, 0x5f, 0xaf, 0xca, 0xd2, - 0xd2, 0xea, 0x0d, 0x87, 0x5a, 0x40, 0xf7, 0x14, 0xbf, 0x7f, 0x1e, 0xfa, 0x57, 0x6a, 0xb2, 0x23, - 0x20, 0xea, 0x8b, 0xe8, 0x94, 0xf9, 0x57, 0x2e, 0x0f, 0x7c, 0x2c, 0xae, 0x4b, 0xba, 0x84, 0x49, - 0x26, 0x4e, 0x07, 0x25, 0x16, 0x56, 0x3f, 0x5c, 0x15, 0x6d, 0xf4, 0x21, 0x64, 0x5f, 0x57, 0x64, - 0xaa, 0xe1, 0x6c, 0x11, 0x08, 0x36, 0xa5, 0x8e, 0x93, 0x44, 0x0b, 0x28, 0xd1, 0xbe, 0xe3, 0xf0, - 0xbd, 0xff, 0x94, 0xa1, 0xfe, 0x99, 0x22, 0x70, 0xf2, 0x29, 0x74, 0x0a, 0xe9, 0x9a, 0xbc, 0x81, - 0x65, 0xdd, 0x6a, 0x71, 0x30, 0xb8, 0xbd, 0x26, 0x56, 0xe7, 0xfa, 0x00, 0xda, 0xf9, 0x64, 0x4c, - 0x30, 0xf1, 0xe2, 0xb7, 0xde, 0x01, 0xce, 0xb4, 0x9e, 0xa9, 0xcf, 0x60, 0x67, 0x53, 0x9a, 0x24, - 0xf7, 0xb2, 0x15, 0xd6, 0x53, 0xf4, 0xe0, 0xcd, 0x9b, 0xb4, 0x49, 0x7a, 0xad, 0x1f, 0x78, 0x8c, - 0xfa, 0x71, 0x98, 0xdf, 0x41, 0xd6, 0x24, 0x8f, 0xa1, 0x53, 0x48, 0x14, 0xea, 0x9c, 0x6b, 0xb9, - 0x23, 0x3f, 0xe4, 0x21, 0x54, 0x31, 0x39, 0x91, 0x4e, 0x21, 0x4b, 0x0e, 0xba, 0x69, 0x57, 0xad, - 0x3d, 0x84, 0x2d, 0xfc, 0x02, 0x98, 0x5b, 0x18, 0x47, 0xa4, 0x99, 0x6b, 0xef, 0xdf, 0x25, 0xa8, - 0x27, 0x5f, 0x85, 0x1f, 0xc3, 0x96, 0xcc, 0x01, 0xe4, 0x56, 0x8e, 0x46, 0x93, 0xfc, 0x31, 0xd8, - 0x59, 0x11, 0xaa, 0x05, 0xc6, 0x50, 0x79, 0xce, 0x04, 0x21, 0x39, 0xa5, 0x4e, 0x06, 0x83, 0x5b, - 0x45, 0x59, 0x6a, 0x7f, 0x1a, 0x17, 0xed, 0x35, 0x97, 0x17, 0xec, 0x53, 0x96, 0xfe, 0x08, 0x6a, - 0x8a, 0x65, 0x95, 0x53, 0xd6, 0xf8, 0x59, 0x5d, 0xfe, 0x3a, 0x1f, 0xef, 0xfd, 0x73, 0x0b, 0xe0, - 0x6c, 0x19, 0x09, 0xb6, 0xf8, 0x8d, 0xcb, 0x5e, 0x92, 0x47, 0xd0, 0x7b, 0xc6, 0xce, 0x69, 0xec, - 0x09, 0x7c, 0xaa, 0x49, 0x36, 0xc9, 0xf9, 0x04, 0x0b, 0xbe, 0x94, 0xac, 0x1f, 0x42, 0xeb, 0x84, - 0x5e, 0xbf, 0xda, 0xee, 0x53, 0xe8, 0x14, 0x38, 0x58, 0x6f, 0x71, 0x95, 0xd5, 0xf5, 0x16, 0xd7, - 0xd9, 0xfa, 0x21, 0xd4, 0x35, 0x33, 0xe7, 0xd7, 0xc0, 0x1c, 0x56, 0x60, 0xec, 0x9f, 0x43, 0x6f, - 0x85, 0x97, 0xf3, 0xf6, 0xf8, 0x39, 0x64, 0x23, 0x6f, 0x3f, 0x95, 0xaf, 0x9d, 0x22, 0x37, 0xe7, - 0x07, 0xea, 0x97, 0xd7, 0x26, 0xf2, 0x7e, 0x5e, 0x7c, 0x27, 0xe1, 0x13, 0xd5, 0x58, 0xa5, 0xcf, - 0x84, 0xbc, 0x07, 0x77, 0x36, 0x69, 0xd2, 0x10, 0xcc, 0x33, 0xe8, 0x5a, 0x08, 0xae, 0xd3, 0xeb, - 0x7b, 0x00, 0x19, 0x89, 0xe6, 0xed, 0x11, 0x1e, 0xab, 0xfc, 0xfa, 0x21, 0x40, 0x46, 0x8d, 0x0a, - 0x55, 0x45, 0x66, 0x55, 0xc3, 0x56, 0xe9, 0xf3, 0x11, 0x34, 0x53, 0x3a, 0xcb, 0xaf, 0x81, 0x13, - 0x14, 0xd9, 0xf1, 0xb3, 0x47, 0xbf, 0x1f, 0xcd, 0x5d, 0x71, 0x11, 0xcf, 0xc6, 0x76, 0xb0, 0xd8, - 0xbd, 0xa0, 0xd1, 0x85, 0x6b, 0x07, 0x3c, 0xdc, 0xbd, 0x92, 0x60, 0xda, 0x2d, 0xfc, 0xb4, 0x9a, - 0xd5, 0xf0, 0xa1, 0xf7, 0xe4, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x5b, 0x0c, 0x01, 0xf3, 0xcc, - 0x1a, 0x00, 0x00, + 0x0c, 0x33, 0x36, 0x68, 0xd1, 0x71, 0x2c, 0x27, 0x65, 0xa7, 0x68, 0x8a, 0x96, 0x19, 0x93, 0x36, + 0x6b, 0x09, 0xc7, 0x39, 0x55, 0xc1, 0x83, 0xdd, 0x21, 0xb8, 0xc5, 0xc5, 0xee, 0x66, 0x76, 0x96, + 0x22, 0xae, 0xf2, 0x16, 0x79, 0x8d, 0xdc, 0xe6, 0x2e, 0x77, 0x29, 0x57, 0xee, 0xf3, 0x0a, 0xb9, + 0xcc, 0x33, 0xa4, 0xa6, 0x67, 0xf6, 0x04, 0x80, 0x96, 0x5c, 0xe5, 0xdc, 0xcd, 0x74, 0xf7, 0x9c, + 0x7a, 0xbe, 0xfe, 0xba, 0x67, 0x17, 0xee, 0x47, 0xce, 0xd5, 0x5e, 0xe8, 0xc5, 0x73, 0xd7, 0xdf, + 0x0b, 0x67, 0x7b, 0x33, 0x6a, 0x5f, 0x31, 0xdf, 0x19, 0x87, 0x3c, 0x10, 0x01, 0x29, 0x87, 0xb3, + 0xc1, 0xc3, 0x79, 0x10, 0xcc, 0x3d, 0xb6, 0x87, 0x92, 0x59, 0x7c, 0xb1, 0x27, 0xdc, 0x05, 0x8b, + 0x04, 0x5d, 0x84, 0xca, 0x68, 0x30, 0x90, 0x33, 0x78, 0xc1, 0xdc, 0xb5, 0xa9, 0xb7, 0xe7, 0x3a, + 0xcc, 0x17, 0xae, 0x58, 0x6a, 0x9d, 0x91, 0xd7, 0xa9, 0x55, 0x94, 0xc6, 0xac, 0x43, 0xf5, 0x68, + 0x11, 0x8a, 0xa5, 0x39, 0x84, 0xda, 0xe7, 0x8c, 0x3a, 0x8c, 0x93, 0xbb, 0x50, 0xbb, 0xc4, 0x96, + 0x51, 0x1a, 0x56, 0x46, 0x4d, 0x4b, 0xf7, 0xcc, 0x3f, 0x00, 0x9c, 0xc9, 0x31, 0x47, 0x9c, 0x07, + 0x9c, 0xdc, 0x83, 0x06, 0xe3, 0x7c, 0x2a, 0x96, 0x21, 0x33, 0x4a, 0xc3, 0xd2, 0xa8, 0x63, 0xd5, + 0x19, 0xe7, 0x93, 0x65, 0xc8, 0xc8, 0xff, 0x81, 0x6c, 0x4e, 0x17, 0xd1, 0xdc, 0x28, 0x0f, 0x4b, + 0x72, 0x06, 0xc6, 0xf9, 0x69, 0x34, 0x4f, 0xc6, 0xd8, 0x81, 0xc3, 0x8c, 0xca, 0xb0, 0x34, 0xaa, + 0xe0, 0x98, 0xc3, 0xc0, 0x61, 0xe6, 0x5f, 0x4a, 0x50, 0x3d, 0xa3, 0xe2, 0x32, 0x22, 0x04, 0xb6, + 0x78, 0x10, 0x08, 0xbd, 0x38, 0xb6, 0xc9, 0x08, 0x7a, 0xb1, 0x4f, 0x63, 0x71, 0x29, 0x4f, 0x65, + 0x53, 0xc1, 0x1c, 0xa3, 0x8c, 0xea, 0x55, 0x31, 0x79, 0x13, 0x3a, 0x5e, 0x60, 0x53, 0x6f, 0x1a, + 0x89, 0x80, 0xd3, 0xb9, 0x5c, 0x47, 0xda, 0xb5, 0x51, 0x78, 0xae, 0x64, 0x64, 0x17, 0xfa, 0x11, + 0xa3, 0xde, 0xf4, 0x05, 0xa7, 0x61, 0x6a, 0xb8, 0xa5, 0x26, 0x94, 0x8a, 0x6f, 0x38, 0x0d, 0xb5, + 0xad, 0xf9, 0xf7, 0x1a, 0xd4, 0x2d, 0xf6, 0xa7, 0x98, 0x45, 0x82, 0x74, 0xa1, 0xec, 0x3a, 0x78, + 0xda, 0xa6, 0x55, 0x76, 0x1d, 0x32, 0x06, 0x62, 0xb1, 0xd0, 0x93, 0x4b, 0xbb, 0x81, 0x7f, 0xe8, + 0xc5, 0x91, 0x60, 0x5c, 0x9f, 0x79, 0x83, 0x86, 0x3c, 0x80, 0x66, 0x10, 0x32, 0x8e, 0x32, 0x74, + 0x40, 0xd3, 0xca, 0x04, 0xf2, 0xe0, 0x21, 0x15, 0x97, 0xc6, 0x16, 0x2a, 0xb0, 0x2d, 0x65, 0x0e, + 0x15, 0xd4, 0xa8, 0x2a, 0x99, 0x6c, 0x13, 0x13, 0x6a, 0x11, 0xb3, 0x39, 0x13, 0x46, 0x6d, 0x58, + 0x1a, 0xb5, 0xf6, 0x61, 0x1c, 0xce, 0xc6, 0xe7, 0x28, 0xb1, 0xb4, 0x86, 0x3c, 0x80, 0x2d, 0xe9, + 0x17, 0xa3, 0x8e, 0x16, 0x0d, 0x69, 0x71, 0x10, 0x8b, 0x4b, 0x0b, 0xa5, 0x64, 0x1f, 0xea, 0xea, + 0x4e, 0x23, 0xa3, 0x31, 0xac, 0x8c, 0x5a, 0xfb, 0x86, 0x34, 0xd0, 0xa7, 0x1c, 0x2b, 0x18, 0x44, + 0x47, 0xbe, 0xe0, 0x4b, 0x2b, 0x31, 0x24, 0x6f, 0x40, 0xdb, 0xf6, 0x5c, 0xe6, 0x8b, 0xa9, 0x08, + 0xae, 0x98, 0x6f, 0x34, 0x71, 0x47, 0x2d, 0x25, 0x9b, 0x48, 0x11, 0xd9, 0x87, 0xd7, 0xf2, 0x26, + 0x53, 0x6a, 0xdb, 0x2c, 0x8a, 0x02, 0x6e, 0x00, 0xda, 0xde, 0xc9, 0xd9, 0x1e, 0x68, 0x95, 0x9c, + 0xd6, 0x71, 0xa3, 0xd0, 0xa3, 0xcb, 0xa9, 0x4f, 0x17, 0xcc, 0x68, 0xa9, 0x69, 0xb5, 0xec, 0x4b, + 0xba, 0x60, 0xe4, 0x21, 0xb4, 0x16, 0x41, 0xec, 0x8b, 0x69, 0x18, 0xb8, 0xbe, 0x30, 0xda, 0x68, + 0x01, 0x28, 0x3a, 0x93, 0x12, 0xf2, 0x3a, 0xa8, 0x9e, 0x02, 0x63, 0x47, 0xf9, 0x15, 0x25, 0x08, + 0xc7, 0x47, 0xd0, 0x55, 0xea, 0x74, 0x3f, 0x5d, 0x34, 0xe9, 0xa0, 0x34, 0xdd, 0xc9, 0x7b, 0xd0, + 0x44, 0x3c, 0xb8, 0xfe, 0x45, 0x60, 0xf4, 0xd0, 0x6f, 0x77, 0x72, 0x6e, 0x91, 0x98, 0x38, 0xf6, + 0x2f, 0x02, 0xab, 0xf1, 0x42, 0xb7, 0xc8, 0xc7, 0x70, 0xbf, 0x70, 0x5e, 0xce, 0x16, 0xd4, 0xf5, + 0x5d, 0x7f, 0x3e, 0x8d, 0x23, 0x16, 0x19, 0xdb, 0x88, 0x70, 0x23, 0x77, 0x6a, 0x2b, 0x31, 0xf8, + 0x3a, 0x62, 0x11, 0xb9, 0x0f, 0x4d, 0x15, 0xa4, 0x53, 0xd7, 0x31, 0xfa, 0xb8, 0xa5, 0x86, 0x12, + 0x1c, 0x3b, 0xe4, 0x2d, 0xe8, 0x85, 0x81, 0xe7, 0xda, 0xcb, 0x69, 0x70, 0xcd, 0x38, 0x77, 0x1d, + 0x66, 0x90, 0x61, 0x69, 0xd4, 0xb0, 0xba, 0x4a, 0xfc, 0x95, 0x96, 0x6e, 0x0a, 0x8d, 0x3b, 0x68, + 0xb8, 0x16, 0x1a, 0x63, 0x00, 0x3b, 0xf0, 0x7d, 0x66, 0x23, 0xfc, 0x76, 0xf0, 0x84, 0x5d, 0x79, + 0xc2, 0xc3, 0x54, 0x6a, 0xe5, 0x2c, 0x06, 0x9f, 0x41, 0x3b, 0x0f, 0x05, 0xb2, 0x0d, 0x95, 0x2b, + 0xb6, 0xd4, 0xf0, 0x97, 0x4d, 0x32, 0x84, 0xea, 0x35, 0xf5, 0x62, 0x86, 0x90, 0xd7, 0x40, 0x54, + 0x43, 0x2c, 0xa5, 0xf8, 0x45, 0xf9, 0x69, 0xc9, 0xfc, 0x77, 0x15, 0xb6, 0x24, 0xf8, 0xc8, 0x07, + 0xd0, 0xf1, 0x18, 0x8d, 0xd8, 0x34, 0x08, 0xe5, 0x02, 0x11, 0x4e, 0xd5, 0xda, 0xdf, 0x96, 0xc3, + 0x4e, 0xa4, 0xe2, 0x2b, 0x25, 0xb7, 0xda, 0x5e, 0xae, 0x27, 0x43, 0xda, 0xf5, 0x05, 0xe3, 0x3e, + 0xf5, 0xa6, 0x18, 0x0c, 0x2a, 0xc0, 0xda, 0x89, 0xf0, 0x99, 0x0c, 0x8a, 0x55, 0x1c, 0x55, 0xd6, + 0x71, 0x34, 0x80, 0x06, 0xfa, 0xce, 0x65, 0x91, 0x0e, 0xf6, 0xb4, 0x4f, 0xf6, 0xa1, 0xb1, 0x60, + 0x82, 0xea, 0x58, 0x93, 0x21, 0x71, 0x37, 0x89, 0x99, 0xf1, 0xa9, 0x56, 0xa8, 0x80, 0x48, 0xed, + 0xd6, 0x22, 0xa2, 0xb6, 0x1e, 0x11, 0x03, 0x68, 0xa4, 0xa0, 0xab, 0xab, 0x1b, 0x4e, 0xfa, 0x92, + 0x66, 0x43, 0xc6, 0xdd, 0xc0, 0x31, 0x1a, 0x08, 0x14, 0xdd, 0x93, 0x24, 0xe9, 0xc7, 0x0b, 0x05, + 0xa1, 0xa6, 0x22, 0x49, 0x3f, 0x5e, 0xac, 0x23, 0x06, 0x56, 0x10, 0xf3, 0x13, 0xa8, 0x52, 0xcf, + 0xa5, 0x11, 0x86, 0x90, 0xbc, 0x59, 0xcd, 0xf7, 0xe3, 0x03, 0x29, 0xb5, 0x94, 0x92, 0xbc, 0x0f, + 0x9d, 0x39, 0x0f, 0xe2, 0x70, 0x8a, 0x5d, 0x16, 0x19, 0x6d, 0x3c, 0xed, 0xaa, 0x75, 0x1b, 0x8d, + 0x0e, 0x94, 0x8d, 0x8c, 0xc0, 0x59, 0x10, 0xfb, 0xce, 0xd4, 0x76, 0x1d, 0x1e, 0x19, 0x1d, 0x74, + 0x1e, 0xa0, 0xe8, 0x50, 0x4a, 0x64, 0x88, 0xa9, 0x10, 0x48, 0x1d, 0xdc, 0x45, 0x9b, 0x0e, 0x4a, + 0xcf, 0x12, 0x2f, 0xff, 0x14, 0xfa, 0x49, 0x62, 0xca, 0x2c, 0x7b, 0x68, 0xb9, 0x9d, 0x28, 0x52, + 0xe3, 0x11, 0x6c, 0xb3, 0x1b, 0x49, 0xa1, 0xae, 0x98, 0x2e, 0xe8, 0xcd, 0x54, 0x08, 0x4f, 0x87, + 0x54, 0x37, 0x91, 0x9f, 0xd2, 0x9b, 0x89, 0xf0, 0x64, 0xfc, 0xab, 0xd5, 0x31, 0xfe, 0xfb, 0x98, + 0x8c, 0x9a, 0x28, 0xc1, 0xf8, 0xdf, 0x85, 0xbe, 0x1f, 0x4c, 0x1d, 0x76, 0x41, 0x63, 0x4f, 0xa8, + 0x75, 0x97, 0x3a, 0x98, 0x7a, 0x7e, 0xf0, 0x4c, 0xc9, 0x71, 0xd9, 0xe5, 0xe0, 0x97, 0xd0, 0x29, + 0x5c, 0xf7, 0x06, 0xd0, 0xef, 0xe4, 0x41, 0xdf, 0xcc, 0x03, 0xfd, 0x9f, 0x5b, 0x00, 0x78, 0xef, + 0x6a, 0xe8, 0x6a, 0xb6, 0xc8, 0x83, 0xa1, 0xbc, 0x01, 0x0c, 0x94, 0x33, 0x5f, 0x68, 0xe0, 0xea, + 0xde, 0xf7, 0x62, 0x36, 0xc9, 0x17, 0xd5, 0x5c, 0xbe, 0x78, 0x07, 0xb6, 0x24, 0x3e, 0x8d, 0x5a, + 0x46, 0xeb, 0xd9, 0x8e, 0x10, 0xc9, 0x0a, 0xc5, 0x68, 0xb5, 0x16, 0x34, 0xf5, 0xf5, 0xa0, 0xc9, + 0xa3, 0xb1, 0x51, 0x44, 0xe3, 0x9b, 0xd0, 0xb1, 0x39, 0xc3, 0xdc, 0x35, 0x95, 0xc5, 0x88, 0x46, + 0x6b, 0x3b, 0x11, 0x4e, 0xdc, 0x05, 0x93, 0xfe, 0x93, 0x17, 0x07, 0xa8, 0x92, 0xcd, 0x8d, 0xf7, + 0xda, 0xda, 0x78, 0xaf, 0x58, 0x09, 0x78, 0x4c, 0x33, 0x3e, 0xb6, 0x73, 0x51, 0xd3, 0x29, 0x44, + 0x4d, 0x21, 0x34, 0xba, 0x2b, 0xa1, 0xb1, 0x82, 0xdf, 0xde, 0x1a, 0x7e, 0xdf, 0x80, 0xb6, 0x74, + 0x40, 0x14, 0x52, 0x9b, 0xc9, 0x09, 0xb6, 0x95, 0x23, 0x52, 0xd9, 0xb1, 0x83, 0xd1, 0x1e, 0xcf, + 0x66, 0xcb, 0xcb, 0xc0, 0x63, 0x19, 0x61, 0xb7, 0x52, 0xd9, 0xb1, 0x23, 0xf7, 0x8b, 0x08, 0x24, + 0x88, 0x40, 0x6c, 0x0f, 0x3e, 0x84, 0x66, 0xea, 0xf5, 0x1f, 0x04, 0xa6, 0xbf, 0x96, 0xa0, 0x9d, + 0x27, 0x45, 0x39, 0x78, 0x32, 0x39, 0xc1, 0xc1, 0x15, 0x4b, 0x36, 0x65, 0x39, 0xc1, 0x99, 0xcf, + 0x5e, 0xd0, 0x99, 0xa7, 0x26, 0x68, 0x58, 0x99, 0x40, 0x6a, 0x5d, 0xdf, 0xe6, 0x6c, 0x91, 0xa0, + 0xaa, 0x62, 0x65, 0x02, 0xf2, 0x11, 0x80, 0x1b, 0x45, 0x31, 0x53, 0x37, 0xb7, 0x85, 0x94, 0x31, + 0x18, 0xab, 0x1a, 0x73, 0x9c, 0xd4, 0x98, 0xe3, 0x49, 0x52, 0x63, 0x5a, 0x4d, 0xb4, 0xc6, 0x2b, + 0xbd, 0x0b, 0x35, 0x79, 0x41, 0x93, 0x13, 0x44, 0x5e, 0xc5, 0xd2, 0x3d, 0xf3, 0xcf, 0x50, 0x53, + 0x55, 0xc8, 0xff, 0x94, 0xe8, 0xef, 0x41, 0x43, 0xcd, 0xed, 0x3a, 0x3a, 0x56, 0xea, 0xd8, 0x3f, + 0x76, 0xcc, 0xef, 0xca, 0xd0, 0xb0, 0x58, 0x14, 0x06, 0x7e, 0xc4, 0x72, 0x55, 0x52, 0xe9, 0xa5, + 0x55, 0x52, 0x79, 0x63, 0x95, 0x94, 0xd4, 0x5e, 0x95, 0x5c, 0xed, 0x35, 0x80, 0x06, 0x67, 0x8e, + 0xcb, 0x99, 0x2d, 0x74, 0x9d, 0x96, 0xf6, 0xa5, 0xee, 0x05, 0xe5, 0x32, 0xbd, 0x47, 0x98, 0x43, + 0x9a, 0x56, 0xda, 0x27, 0x4f, 0xf2, 0xc5, 0x85, 0x2a, 0xdb, 0x76, 0x54, 0x71, 0xa1, 0xb6, 0xbb, + 0xa1, 0xba, 0x78, 0x3f, 0x2b, 0xd2, 0xea, 0x18, 0xcd, 0xf7, 0xf2, 0x03, 0x36, 0x57, 0x69, 0x3f, + 0x5a, 0xce, 0xfe, 0xae, 0x0c, 0xdb, 0xab, 0x7b, 0xdb, 0x80, 0xc0, 0x1d, 0xa8, 0xaa, 0xdc, 0xa7, + 0xe1, 0x2b, 0xd6, 0xb2, 0x5e, 0x65, 0x85, 0xe8, 0x7e, 0xb5, 0x4a, 0x1a, 0x2f, 0x87, 0x5e, 0x91, + 0x50, 0xde, 0x86, 0x6d, 0xe9, 0xa2, 0x90, 0x39, 0x59, 0x3d, 0xa7, 0x18, 0xb0, 0xa7, 0xe5, 0x69, + 0x45, 0xb7, 0x0b, 0xfd, 0xc4, 0x34, 0xe3, 0x86, 0x5a, 0xc1, 0xf6, 0x28, 0xa1, 0x88, 0xbb, 0x50, + 0xbb, 0x08, 0xf8, 0x82, 0x0a, 0x4d, 0x82, 0xba, 0x57, 0x20, 0x39, 0x64, 0xdb, 0x86, 0xc2, 0x64, + 0x22, 0x94, 0x6f, 0x16, 0x49, 0x3e, 0xe9, 0x7b, 0x02, 0x59, 0xb0, 0x61, 0x35, 0x92, 0x77, 0x84, + 0xf9, 0x5b, 0xe8, 0xad, 0x94, 0x90, 0x1b, 0x1c, 0x99, 0x2d, 0x5f, 0x2e, 0x2c, 0x5f, 0x98, 0xb9, + 0xb2, 0x32, 0xf3, 0xef, 0xa0, 0xff, 0x39, 0xf5, 0x1d, 0x8f, 0xe9, 0xf9, 0x0f, 0xf8, 0x3c, 0x92, + 0xc9, 0x50, 0xbf, 0x68, 0xa6, 0x3a, 0xfb, 0x74, 0xac, 0xa6, 0x96, 0x1c, 0x3b, 0xe4, 0x11, 0xd4, + 0xb9, 0xb2, 0xd6, 0x00, 0x68, 0xe5, 0x6a, 0x5c, 0x2b, 0xd1, 0x99, 0xdf, 0x02, 0x29, 0x4c, 0x2d, + 0x1f, 0x33, 0x4b, 0x32, 0x92, 0xe8, 0x57, 0xa0, 0xd0, 0x51, 0xd5, 0xce, 0x63, 0xd2, 0x4a, 0xb5, + 0x64, 0x08, 0x15, 0xc6, 0xb9, 0x5e, 0x02, 0x8b, 0xcc, 0xec, 0xe9, 0x68, 0x49, 0x95, 0xf9, 0x33, + 0xe8, 0x9f, 0x87, 0xcc, 0x76, 0xa9, 0x87, 0xcf, 0x3e, 0xb5, 0xc0, 0x43, 0xa8, 0x4a, 0x27, 0x27, + 0x84, 0xd1, 0xc4, 0x81, 0xa8, 0x56, 0x72, 0xf3, 0x5b, 0x30, 0xd4, 0xbe, 0x8e, 0x6e, 0xdc, 0x48, + 0x30, 0xdf, 0x66, 0x87, 0x97, 0xcc, 0xbe, 0xfa, 0x11, 0x4f, 0x7e, 0x0d, 0xf7, 0x36, 0xad, 0x90, + 0xec, 0xaf, 0x65, 0xcb, 0xde, 0xf4, 0x42, 0xe6, 0x0e, 0x5c, 0xa3, 0x61, 0x01, 0x8a, 0x3e, 0x93, + 0x12, 0x79, 0x8f, 0x4c, 0x8e, 0x8b, 0x34, 0x1f, 0xeb, 0x5e, 0xe2, 0x8f, 0xca, 0xed, 0xfe, 0xf8, + 0x5b, 0x09, 0x9a, 0xe7, 0x4c, 0xc4, 0x21, 0x9e, 0xe5, 0x3e, 0x34, 0x67, 0x3c, 0xb8, 0x62, 0x3c, + 0x3b, 0x4a, 0x43, 0x09, 0x8e, 0x1d, 0xf2, 0x04, 0x6a, 0x87, 0x81, 0x7f, 0xe1, 0xce, 0xf1, 0x11, + 0xac, 0x89, 0x21, 0x1d, 0x3b, 0x56, 0x3a, 0x45, 0x0c, 0xda, 0x90, 0x0c, 0xa1, 0xa5, 0x3f, 0x29, + 0x7c, 0xfd, 0xf5, 0xf1, 0xb3, 0xa4, 0x3a, 0xce, 0x89, 0x06, 0x1f, 0x41, 0x2b, 0x37, 0xf0, 0x07, + 0xa5, 0xaa, 0xff, 0x07, 0xc0, 0xd5, 0x95, 0x8f, 0xb6, 0xd5, 0x51, 0xf5, 0x48, 0x79, 0xb4, 0x87, + 0xd0, 0x94, 0x85, 0x98, 0x52, 0x27, 0x49, 0xb2, 0x94, 0x25, 0x49, 0xf3, 0x11, 0xf4, 0x8f, 0xfd, + 0x6b, 0xea, 0xb9, 0x0e, 0x15, 0xec, 0x0b, 0xb6, 0x44, 0x17, 0xac, 0xed, 0xc0, 0x3c, 0x87, 0xb6, + 0x7e, 0x95, 0xbf, 0xd2, 0x1e, 0xdb, 0x7a, 0x8f, 0xdf, 0x1f, 0x44, 0x6f, 0x43, 0x4f, 0x4f, 0x7a, + 0xe2, 0xea, 0x10, 0x92, 0x35, 0x06, 0x67, 0x17, 0xee, 0x8d, 0x9e, 0x5a, 0xf7, 0xcc, 0xa7, 0xb0, + 0x9d, 0x33, 0x4d, 0x8f, 0x73, 0xc5, 0x96, 0x51, 0xf2, 0xb5, 0x42, 0xb6, 0x13, 0x0f, 0x94, 0x33, + 0x0f, 0x98, 0xd0, 0xd5, 0x23, 0x9f, 0x33, 0x71, 0xcb, 0xe9, 0xbe, 0x48, 0x37, 0xf2, 0x9c, 0xe9, + 0xc9, 0x1f, 0x43, 0x95, 0xc9, 0x93, 0xe6, 0xf3, 0x67, 0xde, 0x03, 0x96, 0x52, 0x6f, 0x58, 0xf0, + 0x69, 0xba, 0xe0, 0x59, 0xac, 0x16, 0x7c, 0xc5, 0xb9, 0xcc, 0x37, 0xd3, 0x6d, 0x9c, 0xc5, 0xe2, + 0xb6, 0x1b, 0x7d, 0x04, 0x7d, 0x6d, 0xf4, 0x8c, 0x79, 0x4c, 0xb0, 0x5b, 0x8e, 0xf4, 0x18, 0x48, + 0xc1, 0xec, 0xb6, 0xe9, 0x1e, 0x40, 0x63, 0x32, 0x39, 0x49, 0xb5, 0x45, 0x6e, 0x34, 0x3f, 0x86, + 0xfe, 0x79, 0xec, 0x04, 0x67, 0xdc, 0xbd, 0x76, 0x3d, 0x36, 0x57, 0x8b, 0x25, 0xc5, 0x6f, 0x29, + 0x57, 0xfc, 0x6e, 0xcc, 0x46, 0xe6, 0x08, 0x48, 0x61, 0x78, 0x7a, 0x6f, 0x51, 0xec, 0x04, 0x3a, + 0x84, 0xb1, 0x6d, 0x8e, 0xa0, 0x3d, 0xa1, 0xb2, 0xd8, 0x70, 0x94, 0x8d, 0x01, 0x75, 0xa1, 0xfa, + 0xda, 0x2c, 0xe9, 0x9a, 0xfb, 0xb0, 0x73, 0x48, 0xed, 0x4b, 0xd7, 0x9f, 0x3f, 0x73, 0x23, 0x59, + 0x6d, 0xe9, 0x11, 0x03, 0x68, 0x38, 0x5a, 0xa0, 0x87, 0xa4, 0x7d, 0xf3, 0x5d, 0x78, 0x2d, 0xf7, + 0x49, 0xe8, 0x5c, 0xd0, 0xc4, 0x1f, 0x3b, 0x50, 0x8d, 0x64, 0x0f, 0x47, 0x54, 0x2d, 0xd5, 0x31, + 0xbf, 0x84, 0x9d, 0x7c, 0x02, 0x96, 0xb5, 0x4f, 0x72, 0x70, 0xac, 0x4a, 0x4a, 0xb9, 0xaa, 0x44, + 0xfb, 0xac, 0x9c, 0xe5, 0x93, 0x6d, 0xa8, 0xfc, 0xfa, 0x9b, 0x89, 0x06, 0xbb, 0x6c, 0x9a, 0x7f, + 0x94, 0xcb, 0x17, 0xe7, 0x53, 0xcb, 0x17, 0x4a, 0x93, 0xd2, 0x2b, 0x95, 0x26, 0xeb, 0x78, 0x7b, + 0x17, 0xfa, 0xa7, 0x5e, 0x60, 0x5f, 0x1d, 0xf9, 0x39, 0x6f, 0x18, 0x50, 0x67, 0x7e, 0xde, 0x19, + 0x49, 0xd7, 0x7c, 0x0b, 0x7a, 0x27, 0x81, 0x4d, 0xbd, 0xd3, 0x20, 0xf6, 0x45, 0xea, 0x05, 0xfc, + 0x46, 0xa7, 0x4d, 0x55, 0xc7, 0x7c, 0x17, 0xba, 0x3a, 0x45, 0xfb, 0x17, 0x41, 0xc2, 0x8c, 0x59, + 0x32, 0x2f, 0x15, 0x0b, 0x7d, 0xf3, 0x04, 0x7a, 0x99, 0xb9, 0x9a, 0xf7, 0x2d, 0xa8, 0x29, 0xb5, + 0x3e, 0x5b, 0x2f, 0x7d, 0xe9, 0x2a, 0x4b, 0x4b, 0xab, 0x37, 0x1c, 0x6a, 0x01, 0xdd, 0x33, 0xfc, + 0x56, 0x7a, 0xe4, 0x5f, 0xab, 0xc9, 0x8e, 0x81, 0xa8, 0xaf, 0xa7, 0x53, 0xe6, 0x5f, 0xbb, 0x3c, + 0xf0, 0xb1, 0xb8, 0x2e, 0xe9, 0x12, 0x26, 0x99, 0x38, 0x1d, 0x94, 0x58, 0x58, 0xfd, 0x70, 0x55, + 0xb4, 0xd1, 0x87, 0x90, 0x7d, 0x89, 0x91, 0xa9, 0x86, 0xb3, 0x45, 0x20, 0xd8, 0x94, 0x3a, 0x4e, + 0x12, 0x2d, 0xa0, 0x44, 0x07, 0x8e, 0xc3, 0xf7, 0xff, 0x53, 0x86, 0xfa, 0xa7, 0x8a, 0xc0, 0xc9, + 0x27, 0xd0, 0x29, 0xa4, 0x6b, 0xf2, 0x1a, 0x96, 0x75, 0xab, 0xc5, 0xc1, 0xe0, 0xee, 0x9a, 0x58, + 0x9d, 0xeb, 0x3d, 0x68, 0xe7, 0x93, 0x31, 0xc1, 0xc4, 0x8b, 0xdf, 0x85, 0x07, 0x38, 0xd3, 0x7a, + 0xa6, 0x3e, 0x87, 0x9d, 0x4d, 0x69, 0x92, 0x3c, 0xc8, 0x56, 0x58, 0x4f, 0xd1, 0x83, 0xd7, 0x6f, + 0xd3, 0x26, 0xe9, 0xb5, 0x7e, 0xe8, 0x31, 0xea, 0xc7, 0x61, 0x7e, 0x07, 0x59, 0x93, 0x3c, 0x81, + 0x4e, 0x21, 0x51, 0xa8, 0x73, 0xae, 0xe5, 0x8e, 0xfc, 0x90, 0xc7, 0x50, 0xc5, 0xe4, 0x44, 0x3a, + 0x85, 0x2c, 0x39, 0xe8, 0xa6, 0x5d, 0xb5, 0xf6, 0x10, 0xb6, 0xf0, 0x6b, 0x41, 0x6e, 0x61, 0x1c, + 0x91, 0x66, 0xae, 0xfd, 0x7f, 0x95, 0xa0, 0x9e, 0x7c, 0x41, 0x7e, 0x02, 0x5b, 0x32, 0x07, 0x90, + 0x3b, 0x39, 0x1a, 0x4d, 0xf2, 0xc7, 0x60, 0x67, 0x45, 0xa8, 0x16, 0x18, 0x43, 0xe5, 0x39, 0x13, + 0x84, 0xe4, 0x94, 0x3a, 0x19, 0x0c, 0xee, 0x14, 0x65, 0xa9, 0xfd, 0x59, 0x5c, 0xb4, 0xd7, 0x5c, + 0x5e, 0xb0, 0x4f, 0x59, 0xfa, 0x43, 0xa8, 0x29, 0x96, 0x55, 0x4e, 0x59, 0xe3, 0x67, 0x75, 0xf9, + 0xeb, 0x7c, 0xbc, 0xff, 0x8f, 0x2d, 0x80, 0xf3, 0x65, 0x24, 0xd8, 0xe2, 0x37, 0x2e, 0x7b, 0x41, + 0x76, 0xa1, 0xa7, 0xbf, 0x89, 0xe0, 0x53, 0x4d, 0xb2, 0x49, 0xce, 0x27, 0x58, 0xf0, 0xa5, 0x64, + 0xfd, 0x18, 0x5a, 0xa7, 0xf4, 0xe6, 0xe5, 0x76, 0x9f, 0x40, 0xa7, 0xc0, 0xc1, 0x7a, 0x8b, 0xab, + 0xac, 0xae, 0xb7, 0xb8, 0xce, 0xd6, 0x8f, 0xa1, 0xae, 0x99, 0x39, 0xbf, 0x06, 0xe6, 0xb0, 0x02, + 0x63, 0xff, 0x1c, 0x7a, 0x2b, 0xbc, 0x9c, 0xb7, 0xc7, 0xcf, 0x21, 0x1b, 0x79, 0xfb, 0xa9, 0x7c, + 0xed, 0x14, 0xb9, 0x39, 0x3f, 0x50, 0xbf, 0xbc, 0x36, 0x91, 0xf7, 0xf3, 0xe2, 0x3b, 0x09, 0x9f, + 0xa8, 0xc6, 0x2a, 0x7d, 0x26, 0xe4, 0x3d, 0xb8, 0xb7, 0x49, 0x93, 0x86, 0x60, 0x9e, 0x41, 0xd7, + 0x42, 0x70, 0x9d, 0x5e, 0xdf, 0x01, 0xc8, 0x48, 0x34, 0x6f, 0x8f, 0xf0, 0x58, 0xe5, 0xd7, 0x0f, + 0x00, 0x32, 0x6a, 0x54, 0xa8, 0x2a, 0x32, 0xab, 0x1a, 0xb6, 0x4a, 0x9f, 0xbb, 0xd0, 0x4c, 0xe9, + 0x2c, 0xbf, 0x06, 0x4e, 0x50, 0x64, 0xc7, 0x4f, 0x77, 0x7f, 0x3f, 0x9a, 0xbb, 0xe2, 0x32, 0x9e, + 0x8d, 0xed, 0x60, 0xb1, 0x77, 0x49, 0xa3, 0x4b, 0xd7, 0x0e, 0x78, 0xb8, 0x77, 0x2d, 0xc1, 0xb4, + 0x57, 0xf8, 0xc1, 0x35, 0xab, 0xe1, 0x43, 0xef, 0xfd, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xbd, + 0xc6, 0x6e, 0xfa, 0xf8, 0x1a, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/sdk/plugin/pb/backend.proto b/sdk/plugin/pb/backend.proto index 65f4ef80dc..ca86c9c139 100644 --- a/sdk/plugin/pb/backend.proto +++ b/sdk/plugin/pb/backend.proto @@ -210,6 +210,9 @@ message Auth { // TokenType is the type of token being requested uint32 token_type = 17; + + // Whether the default policy should be added automatically by core + bool no_default_policy = 18; } message TokenEntry { diff --git a/sdk/plugin/pb/translation.go b/sdk/plugin/pb/translation.go index 3ef85a0821..23c7e718cb 100644 --- a/sdk/plugin/pb/translation.go +++ b/sdk/plugin/pb/translation.go @@ -507,6 +507,7 @@ func LogicalAuthToProtoAuth(a *logical.Auth) (*Auth, error) { Policies: a.Policies, TokenPolicies: a.TokenPolicies, IdentityPolicies: a.IdentityPolicies, + NoDefaultPolicy: a.NoDefaultPolicy, Metadata: a.Metadata, ClientToken: a.ClientToken, Accessor: a.Accessor, @@ -554,6 +555,7 @@ func ProtoAuthToLogicalAuth(a *Auth) (*logical.Auth, error) { Policies: a.Policies, TokenPolicies: a.TokenPolicies, IdentityPolicies: a.IdentityPolicies, + NoDefaultPolicy: a.NoDefaultPolicy, Metadata: a.Metadata, ClientToken: a.ClientToken, Accessor: a.Accessor, diff --git a/vault/external_tests/policy/no_default_test.go b/vault/external_tests/policy/no_default_test.go new file mode 100644 index 0000000000..a296d1364b --- /dev/null +++ b/vault/external_tests/policy/no_default_test.go @@ -0,0 +1,89 @@ +package policy + +// This is TODO once tokenhelper is added to ldaputil +/* + +import ( + "testing" + + "github.com/go-test/deep" + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/api" + "github.com/hashicorp/vault/builtin/credential/ldap" + vaulthttp "github.com/hashicorp/vault/http" + "github.com/hashicorp/vault/sdk/logical" + "github.com/hashicorp/vault/vault" +) + +func TestNoDefaultPolicy(t *testing.T) { + var err error + coreConfig := &vault.CoreConfig{ + DisableMlock: true, + DisableCache: true, + Logger: hclog.NewNullLogger(), + CredentialBackends: map[string]logical.Factory{ + "ldap": ldap.Factory, + }, + } + + cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + + cluster.Start() + defer cluster.Cleanup() + + cores := cluster.Cores + + vault.TestWaitActive(t, cores[0].Core) + + client := cores[0].Client + + err = client.Sys().EnableAuthWithOptions("ldap", &api.EnableAuthOptions{ + Type: "ldap", + }) + if err != nil { + t.Fatal(err) + } + + // Configure LDAP auth backend + secret, err := client.Logical().Write("auth/ldap/config", map[string]interface{}{ + "url": "ldap://ldap.forumsys.com", + "userattr": "uid", + "userdn": "dc=example,dc=com", + "groupdn": "dc=example,dc=com", + "binddn": "cn=read-only-admin,dc=example,dc=com", + "token_no_default_policy": true, + }) + if err != nil { + t.Fatal(err) + } + + // Create a local user in LDAP + secret, err = client.Logical().Write("auth/ldap/users/tesla", map[string]interface{}{ + "policies": "foo", + }) + if err != nil { + t.Fatal(err) + } + + // Login with LDAP and create a token + secret, err = client.Logical().Write("auth/ldap/login/tesla", map[string]interface{}{ + "password": "password", + }) + if err != nil { + t.Fatal(err) + } + token := secret.Auth.ClientToken + + // Lookup the token to get the entity ID + secret, err = client.Auth().Token().Lookup(token) + if err != nil { + t.Fatal(err) + } + + if diff := deep.Equal(secret.Data["policies"], []interface{}{"foo"}); diff != nil { + t.Fatal(diff) + } +} +*/ diff --git a/vault/request_handling.go b/vault/request_handling.go index 99d2949bfb..41aac1a309 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -1034,6 +1034,11 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re var entity *identity.Entity auth = resp.Auth + // Only the token store can toggle this off, and that's via a different + // path since it's not a login request; it's explicitly disallowed + // above + auth.Renewable = true + mEntry := c.router.MatchingMountEntry(ctx, req.Path) if auth.Alias != nil && @@ -1109,7 +1114,7 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re return nil, nil, ErrInternalError } - auth.TokenPolicies = policyutil.SanitizePolicies(auth.Policies, policyutil.AddDefaultPolicy) + auth.TokenPolicies = policyutil.SanitizePolicies(auth.Policies, !auth.NoDefaultPolicy) allPolicies := policyutil.SanitizePolicies(append(auth.TokenPolicies, identityPolicies[ns.ID]...), policyutil.DoNotAddDefaultPolicy) // Prevent internal policies from being assigned to tokens. We check diff --git a/vault/token_store.go b/vault/token_store.go index 31789ca878..1d23707316 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -32,6 +32,7 @@ import ( "github.com/hashicorp/vault/sdk/helper/policyutil" "github.com/hashicorp/vault/sdk/helper/salt" "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/hashicorp/vault/sdk/helper/tokenutil" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/sdk/plugin/pb" "github.com/mitchellh/mapstructure" @@ -47,7 +48,7 @@ const ( accessorPrefix = "accessor/" // parentPrefix is the prefix used to store tokens for their - // secondar parent based index + // secondary parent based index parentPrefix = "parent/" // tokenSubPath is the sub-path used for the token store @@ -103,7 +104,7 @@ var ( ) func (ts *TokenStore) paths() []*framework.Path { - return []*framework.Path{ + p := []*framework.Path{ { Pattern: "roles/?$", @@ -126,76 +127,6 @@ func (ts *TokenStore) paths() []*framework.Path { HelpDescription: tokenListAccessorsHelp, }, - { - Pattern: "roles/" + framework.GenericNameRegex("role_name"), - Fields: map[string]*framework.FieldSchema{ - "role_name": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Name of the role", - }, - - "allowed_policies": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: tokenAllowedPoliciesHelp, - }, - - "disallowed_policies": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: tokenDisallowedPoliciesHelp, - }, - - "orphan": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: false, - Description: tokenOrphanHelp, - }, - - "period": &framework.FieldSchema{ - Type: framework.TypeDurationSecond, - Default: 0, - Description: tokenPeriodHelp, - }, - - "path_suffix": &framework.FieldSchema{ - Type: framework.TypeString, - Default: "", - Description: tokenPathSuffixHelp + pathSuffixSanitize.String(), - }, - - "explicit_max_ttl": &framework.FieldSchema{ - Type: framework.TypeDurationSecond, - Default: 0, - Description: tokenExplicitMaxTTLHelp, - }, - - "renewable": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: true, - Description: tokenRenewableHelp, - }, - - "bound_cidrs": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: `Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.`, - }, - - "token_type": &framework.FieldSchema{ - Type: framework.TypeString, - Default: "service", - Description: "The type of token to generate, service or batch", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: ts.tokenStoreRoleRead, - logical.CreateOperation: ts.tokenStoreRoleCreateUpdate, - logical.UpdateOperation: ts.tokenStoreRoleCreateUpdate, - logical.DeleteOperation: ts.tokenStoreRoleDelete, - }, - - ExistenceCheck: ts.tokenStoreRoleExistenceCheck, - }, - { Pattern: "create-orphan$", @@ -414,6 +345,74 @@ func (ts *TokenStore) paths() []*framework.Path { HelpDescription: strings.TrimSpace(tokenTidyDesc), }, } + + rolesPath := &framework.Path{ + Pattern: "roles/" + framework.GenericNameRegex("role_name"), + Fields: map[string]*framework.FieldSchema{ + "role_name": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Name of the role", + }, + + "allowed_policies": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: tokenAllowedPoliciesHelp, + }, + + "disallowed_policies": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: tokenDisallowedPoliciesHelp, + }, + + "orphan": &framework.FieldSchema{ + Type: framework.TypeBool, + Description: tokenOrphanHelp, + }, + + "period": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: "(DEPRECATED) Use 'token_period' instead. If this and 'token_period' are both specified both will be retained but 'token_period' will take precedence.", + Deprecated: true, + }, + + "path_suffix": &framework.FieldSchema{ + Type: framework.TypeString, + Description: tokenPathSuffixHelp + pathSuffixSanitize.String(), + }, + + "explicit_max_ttl": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: "(DEPRECATED) Use 'token_explicit_max_ttl' instead. If this and 'token_explicit_max_ttl' are both specified both will be retained but 'token_explicit_max_ttl' will take precedence.", + Deprecated: true, + }, + + "renewable": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: true, + Description: tokenRenewableHelp, + }, + + "bound_cidrs": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "(DEPRECATED) Use 'token_bound_cidrs' instead. If this and 'token_bound_cidrs' are both specified both will be retained but 'token_bound_cidrs' will take precedence.", + Deprecated: true, + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: ts.tokenStoreRoleRead, + logical.CreateOperation: ts.tokenStoreRoleCreateUpdate, + logical.UpdateOperation: ts.tokenStoreRoleCreateUpdate, + logical.DeleteOperation: ts.tokenStoreRoleDelete, + }, + + ExistenceCheck: ts.tokenStoreRoleExistenceCheck, + } + + tokenutil.AddTokenFieldsWithAllowList(rolesPath.Fields, []string{"token_bound_cidrs", "token_explicit_max_ttl", "token_period", "token_type", "token_no_default_policy", "token_num_uses"}) + p = append(p, rolesPath) + + return p } // LookupToken returns the properties of the token from the token store. This @@ -540,7 +539,6 @@ func NewTokenStore(ctx context.Context, logger log.Logger, core *Core, config *l } func (ts *TokenStore) Invalidate(ctx context.Context, key string) { - //ts.logger.Debug("invalidating key", "key", key) switch key { case tokenSubPath + salt.DefaultLocation: @@ -581,6 +579,8 @@ func (ts *TokenStore) Salt(ctx context.Context) (*salt.Salt, error) { // tsRoleEntry contains token store role information type tsRoleEntry struct { + tokenutil.TokenParams + // The name of the role. Embedded so it can be used for pathing Name string `json:"name" mapstructure:"name" structs:"name"` @@ -611,9 +611,6 @@ type tsRoleEntry struct { // The set of CIDRs that tokens generated using this role will be bound to BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs"` - - // The type of token this role should issue - TokenType logical.TokenType `json:"token_type" mapstructure:"token_type"` } type accessorEntry struct { @@ -2236,6 +2233,16 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque renewable = false } + // Update te.NumUses which is equal to req.Data["num_uses"] at this point + // 0 means unlimited so 1 is actually less than 0 + switch { + case role.TokenNumUses == 0: + case te.NumUses == 0: + te.NumUses = role.TokenNumUses + case role.TokenNumUses < te.NumUses: + te.NumUses = role.TokenNumUses + } + if role.PathSuffix != "" { te.Path = fmt.Sprintf("%s/%s", te.Path, role.PathSuffix) } @@ -2284,7 +2291,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque // isn't in the disallowed list, add it. This is in line with the idea // that roles, when allowed/disallowed ar set, allow a subset of // policies to be set disjoint from the parent token's policies. - if !data.NoDefaultPolicy && !strutil.StrListContains(role.DisallowedPolicies, "default") { + if !data.NoDefaultPolicy && !role.TokenNoDefaultPolicy && !strutil.StrListContains(role.DisallowedPolicies, "default") { localAddDefault = true } @@ -2406,8 +2413,8 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque te.Parent = "" } - if len(role.BoundCIDRs) > 0 { - te.BoundCIDRs = role.BoundCIDRs + if len(role.TokenBoundCIDRs) > 0 { + te.BoundCIDRs = role.TokenBoundCIDRs } case data.NoParent: @@ -2501,24 +2508,24 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque // in role. Batch tokens will error out if not set via role, but here we // need to explicitly check if role != nil && te.Type != logical.TokenTypeBatch { - if role.ExplicitMaxTTL != 0 { + if role.TokenExplicitMaxTTL != 0 { switch { case explicitMaxTTLToUse == 0: - explicitMaxTTLToUse = role.ExplicitMaxTTL + explicitMaxTTLToUse = role.TokenExplicitMaxTTL default: - if role.ExplicitMaxTTL < explicitMaxTTLToUse { - explicitMaxTTLToUse = role.ExplicitMaxTTL + if role.TokenExplicitMaxTTL < explicitMaxTTLToUse { + explicitMaxTTLToUse = role.TokenExplicitMaxTTL } resp.AddWarning(fmt.Sprintf("Explicit max TTL specified both during creation call and in role; using the lesser value of %d seconds", int64(explicitMaxTTLToUse.Seconds()))) } } - if role.Period != 0 { + if role.TokenPeriod != 0 { switch { case periodToUse == 0: - periodToUse = role.Period + periodToUse = role.TokenPeriod default: - if role.Period < periodToUse { - periodToUse = role.Period + if role.TokenPeriod < periodToUse { + periodToUse = role.TokenPeriod } resp.AddWarning(fmt.Sprintf("Period specified both during creation call and in role; using the lesser value of %d seconds", int64(periodToUse.Seconds()))) } @@ -2872,8 +2879,8 @@ func (ts *TokenStore) authRenew(ctx context.Context, req *logical.Request, d *fr return nil, fmt.Errorf("original token role %q could not be found, not renewing", te.Role) } - req.Auth.Period = role.Period - req.Auth.ExplicitMaxTTL = role.ExplicitMaxTTL + req.Auth.Period = role.TokenPeriod + req.Auth.ExplicitMaxTTL = role.TokenExplicitMaxTTL return &logical.Response{Auth: req.Auth}, nil } @@ -2900,6 +2907,18 @@ func (ts *TokenStore) tokenStoreRole(ctx context.Context, name string) (*tsRoleE result.TokenType = logical.TokenTypeDefaultService } + // Token field upgrades. We preserve the original value for read + // compatibility. + if result.Period > 0 && result.TokenPeriod == 0 { + result.TokenPeriod = result.Period + } + if result.ExplicitMaxTTL > 0 && result.TokenExplicitMaxTTL == 0 { + result.TokenExplicitMaxTTL = result.ExplicitMaxTTL + } + if len(result.BoundCIDRs) > 0 && len(result.TokenBoundCIDRs) == 0 { + result.TokenBoundCIDRs = result.BoundCIDRs + } + return &result, nil } @@ -2945,23 +2964,32 @@ func (ts *TokenStore) tokenStoreRoleRead(ctx context.Context, req *logical.Reque return nil, nil } + // TODO (1.4): Remove "period" and "explicit_max_ttl" if they're zero resp := &logical.Response{ Data: map[string]interface{}{ - "period": int64(role.Period.Seconds()), - "explicit_max_ttl": int64(role.ExplicitMaxTTL.Seconds()), - "disallowed_policies": role.DisallowedPolicies, - "allowed_policies": role.AllowedPolicies, - "name": role.Name, - "orphan": role.Orphan, - "path_suffix": role.PathSuffix, - "renewable": role.Renewable, - "token_type": role.TokenType.String(), + "period": int64(role.Period.Seconds()), + "token_period": int64(role.TokenPeriod.Seconds()), + "explicit_max_ttl": int64(role.ExplicitMaxTTL.Seconds()), + "token_explicit_max_ttl": int64(role.TokenExplicitMaxTTL.Seconds()), + "disallowed_policies": role.DisallowedPolicies, + "allowed_policies": role.AllowedPolicies, + "name": role.Name, + "orphan": role.Orphan, + "path_suffix": role.PathSuffix, + "renewable": role.Renewable, + "token_type": role.TokenType.String(), }, } + if len(role.TokenBoundCIDRs) > 0 { + resp.Data["token_bound_cidrs"] = role.TokenBoundCIDRs + } if len(role.BoundCIDRs) > 0 { resp.Data["bound_cidrs"] = role.BoundCIDRs } + if role.TokenNumUses > 0 { + resp.Data["token_num_uses"] = role.TokenNumUses + } return resp, nil } @@ -2997,140 +3025,160 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(ctx context.Context, req *logic } } - // In this series of blocks, if we do not find a user-provided value and - // it's a creation operation, we call data.Get to get the appropriate - // default - - orphanInt, ok := data.GetOk("orphan") - if ok { - entry.Orphan = orphanInt.(bool) - } else if req.Operation == logical.CreateOperation { - entry.Orphan = data.Get("orphan").(bool) - } - - periodInt, ok := data.GetOk("period") - if ok { - entry.Period = time.Second * time.Duration(periodInt.(int)) - } else if req.Operation == logical.CreateOperation { - entry.Period = time.Second * time.Duration(data.Get("period").(int)) - } - - renewableInt, ok := data.GetOk("renewable") - if ok { - entry.Renewable = renewableInt.(bool) - } else if req.Operation == logical.CreateOperation { - entry.Renewable = data.Get("renewable").(bool) - } - - boundCIDRsRaw, ok := data.GetOk("bound_cidrs") - if ok { - boundCIDRs := boundCIDRsRaw.([]string) - if len(boundCIDRs) == 0 { - entry.BoundCIDRs = nil - } else { - var parsedCIDRs []*sockaddr.SockAddrMarshaler - for _, v := range boundCIDRs { - parsedCIDR, err := sockaddr.NewSockAddr(v) - if err != nil { - return logical.ErrorResponse(errwrap.Wrapf(fmt.Sprintf("invalid value %q when parsing bound cidrs: {{err}}", v), err).Error()), nil - } - parsedCIDRs = append(parsedCIDRs, &sockaddr.SockAddrMarshaler{parsedCIDR}) - } - entry.BoundCIDRs = parsedCIDRs + // First parse fields not duplicated by the token helper + { + orphanInt, ok := data.GetOk("orphan") + if ok { + entry.Orphan = orphanInt.(bool) + } else if req.Operation == logical.CreateOperation { + entry.Orphan = data.Get("orphan").(bool) } + + renewableInt, ok := data.GetOk("renewable") + if ok { + entry.Renewable = renewableInt.(bool) + } else if req.Operation == logical.CreateOperation { + entry.Renewable = data.Get("renewable").(bool) + } + + pathSuffixInt, ok := data.GetOk("path_suffix") + if ok { + pathSuffix := pathSuffixInt.(string) + switch { + case pathSuffix != "": + matched := pathSuffixSanitize.MatchString(pathSuffix) + if !matched { + return logical.ErrorResponse(fmt.Sprintf( + "given role path suffix contains invalid characters; must match %s", + pathSuffixSanitize.String())), nil + } + } + entry.PathSuffix = pathSuffix + } else if req.Operation == logical.CreateOperation { + entry.PathSuffix = data.Get("path_suffix").(string) + } + + if strings.Contains(entry.PathSuffix, "..") { + return logical.ErrorResponse(fmt.Sprintf("error registering path suffix: %s", consts.ErrPathContainsParentReferences)), nil + } + + allowedPoliciesRaw, ok := data.GetOk("allowed_policies") + if ok { + entry.AllowedPolicies = policyutil.SanitizePolicies(allowedPoliciesRaw.([]string), policyutil.DoNotAddDefaultPolicy) + } else if req.Operation == logical.CreateOperation { + entry.AllowedPolicies = policyutil.SanitizePolicies(data.Get("allowed_policies").([]string), policyutil.DoNotAddDefaultPolicy) + } + + disallowedPoliciesRaw, ok := data.GetOk("disallowed_policies") + if ok { + entry.DisallowedPolicies = strutil.RemoveDuplicates(disallowedPoliciesRaw.([]string), true) + } else if req.Operation == logical.CreateOperation { + entry.DisallowedPolicies = strutil.RemoveDuplicates(data.Get("disallowed_policies").([]string), true) + } + } + + // Next parse token fields from the helper + if err := entry.ParseTokenFields(req, data); err != nil { + return logical.ErrorResponse(errwrap.Wrapf("error parsing role fields: {{err}}", err).Error()), nil } var resp *logical.Response - explicitMaxTTLInt, ok := data.GetOk("explicit_max_ttl") - if ok { - entry.ExplicitMaxTTL = time.Second * time.Duration(explicitMaxTTLInt.(int)) - } else if req.Operation == logical.CreateOperation { - entry.ExplicitMaxTTL = time.Second * time.Duration(data.Get("explicit_max_ttl").(int)) + // Now handle backwards compat. Prefer token_ fields over others if both + // are set. We set the original fields here so that on read of token role + // we can return the same values that were set. We clear out the Token* + // values because otherwise when we read the role back we'll read stale + // data since if they're not emptied they'll take precedence. + periodRaw, ok := data.GetOk("token_period") + if !ok { + periodRaw, ok = data.GetOk("period") + if ok { + entry.Period = time.Second * time.Duration(periodRaw.(int)) + entry.TokenPeriod = 0 + } + } else { + _, ok = data.GetOk("period") + if ok { + if resp == nil { + resp = &logical.Response{} + } + resp.AddWarning("Both 'token_period' and deprecated 'period' value supplied, ignoring the deprecated value") + } + entry.Period = 0 } - if entry.ExplicitMaxTTL != 0 { + + boundCIDRsRaw, ok := data.GetOk("token_bound_cidrs") + if !ok { + boundCIDRsRaw, ok = data.GetOk("bound_cidrs") + if ok { + boundCIDRs, err := parseutil.ParseAddrs(boundCIDRsRaw.([]string)) + if err != nil { + return logical.ErrorResponse(errwrap.Wrapf("error parsing bound_cidrs: {{err}}", err).Error()), nil + } + entry.BoundCIDRs = boundCIDRs + entry.TokenBoundCIDRs = nil + } + } else { + _, ok = data.GetOk("bound_cidrs") + if ok { + if resp == nil { + resp = &logical.Response{} + } + resp.AddWarning("Both 'token_bound_cidrs' and deprecated 'bound_cidrs' value supplied, ignoring the deprecated value") + } + entry.BoundCIDRs = nil + } + + finalExplicitMaxTTL := entry.TokenExplicitMaxTTL + explicitMaxTTLRaw, ok := data.GetOk("token_explicit_max_ttl") + if !ok { + explicitMaxTTLRaw, ok = data.GetOk("explicit_max_ttl") + if ok { + entry.ExplicitMaxTTL = time.Second * time.Duration(explicitMaxTTLRaw.(int)) + entry.TokenExplicitMaxTTL = 0 + } + finalExplicitMaxTTL = entry.ExplicitMaxTTL + } else { + _, ok = data.GetOk("explicit_max_ttl") + if ok { + if resp == nil { + resp = &logical.Response{} + } + resp.AddWarning("Both 'token_explicit_max_ttl' and deprecated 'explicit_max_ttl' value supplied, ignoring the deprecated value") + } + entry.ExplicitMaxTTL = 0 + } + if finalExplicitMaxTTL != 0 { sysView := ts.System() - if sysView.MaxLeaseTTL() != time.Duration(0) && entry.ExplicitMaxTTL > sysView.MaxLeaseTTL() { + if sysView.MaxLeaseTTL() != time.Duration(0) && finalExplicitMaxTTL > sysView.MaxLeaseTTL() { if resp == nil { resp = &logical.Response{} } resp.AddWarning(fmt.Sprintf( "Given explicit max TTL of %d is greater than system/mount allowed value of %d seconds; until this is fixed attempting to create tokens against this role will result in an error", - int64(entry.ExplicitMaxTTL.Seconds()), int64(sysView.MaxLeaseTTL().Seconds()))) + int64(finalExplicitMaxTTL.Seconds()), int64(sysView.MaxLeaseTTL().Seconds()))) } } - pathSuffixInt, ok := data.GetOk("path_suffix") + // no legacy version without the token_ prefix to check for + tokenNumUses, ok := data.GetOk("token_num_uses") if ok { - pathSuffix := pathSuffixInt.(string) - switch { - case pathSuffix != "": - matched := pathSuffixSanitize.MatchString(pathSuffix) - if !matched { - return logical.ErrorResponse(fmt.Sprintf( - "given role path suffix contains invalid characters; must match %s", - pathSuffixSanitize.String())), nil - } - } - entry.PathSuffix = pathSuffix - } else if req.Operation == logical.CreateOperation { - entry.PathSuffix = data.Get("path_suffix").(string) + entry.TokenNumUses = tokenNumUses.(int) } - if strings.Contains(entry.PathSuffix, "..") { - return logical.ErrorResponse(fmt.Sprintf("error registering path suffix: %s", consts.ErrPathContainsParentReferences)), nil - } - - allowedPoliciesRaw, ok := data.GetOk("allowed_policies") - if ok { - entry.AllowedPolicies = policyutil.SanitizePolicies(allowedPoliciesRaw.([]string), policyutil.DoNotAddDefaultPolicy) - } else if req.Operation == logical.CreateOperation { - entry.AllowedPolicies = policyutil.SanitizePolicies(data.Get("allowed_policies").([]string), policyutil.DoNotAddDefaultPolicy) - } - - disallowedPoliciesRaw, ok := data.GetOk("disallowed_policies") - if ok { - entry.DisallowedPolicies = strutil.RemoveDuplicates(disallowedPoliciesRaw.([]string), true) - } else if req.Operation == logical.CreateOperation { - entry.DisallowedPolicies = strutil.RemoveDuplicates(data.Get("disallowed_policies").([]string), true) - } - - tokenType := entry.TokenType - if tokenType == logical.TokenTypeDefault { - tokenType = logical.TokenTypeDefaultService - } - tokenTypeRaw, ok := data.GetOk("token_type") - if ok { - tokenTypeStr := tokenTypeRaw.(string) - switch tokenTypeStr { - case "service": - tokenType = logical.TokenTypeService - case "batch": - tokenType = logical.TokenTypeBatch - case "default-service": - tokenType = logical.TokenTypeDefaultService - case "default-batch": - tokenType = logical.TokenTypeDefaultBatch - default: - return logical.ErrorResponse(fmt.Sprintf("invalid 'token_type' value %q", tokenTypeStr)), nil - } - } else if req.Operation == logical.CreateOperation { - tokenType = logical.TokenTypeDefaultService - } - entry.TokenType = tokenType - + // Run validity checks on token type if entry.TokenType == logical.TokenTypeBatch { if !entry.Orphan { return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate non-orphan tokens"), nil } - if entry.Period != 0 { + if entry.Period != 0 || entry.TokenPeriod != 0 { return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate periodic tokens"), nil } if entry.Renewable { return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate renewable tokens"), nil } - if entry.ExplicitMaxTTL != 0 { + if entry.ExplicitMaxTTL != 0 || entry.TokenExplicitMaxTTL != 0 { return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate tokens with an explicit max TTL"), nil } } diff --git a/vault/token_store_test.go b/vault/token_store_test.go index bbfd4566a5..0fabc3a157 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -21,6 +21,8 @@ import ( uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/helper/locksutil" + "github.com/hashicorp/vault/sdk/helper/parseutil" + "github.com/hashicorp/vault/sdk/helper/tokenutil" "github.com/hashicorp/vault/sdk/logical" ) @@ -2679,6 +2681,8 @@ func TestTokenStore_RoleCRUD(t *testing.T) { "allowed_policies": "test1,test2", "path_suffix": "happenin", "bound_cidrs": []string{"0.0.0.0/0"}, + "explicit_max_ttl": "2h", + "token_num_uses": 123, } resp, err = core.HandleRequest(namespace.RootContext(nil), req) @@ -2703,21 +2707,28 @@ func TestTokenStore_RoleCRUD(t *testing.T) { } expected := map[string]interface{}{ - "name": "test", - "orphan": true, - "period": int64(259200), - "allowed_policies": []string{"test1", "test2"}, - "disallowed_policies": []string{}, - "path_suffix": "happenin", - "explicit_max_ttl": int64(0), - "renewable": true, - "token_type": "default-service", + "name": "test", + "orphan": true, + "token_period": int64(259200), + "period": int64(259200), + "allowed_policies": []string{"test1", "test2"}, + "disallowed_policies": []string{}, + "path_suffix": "happenin", + "explicit_max_ttl": int64(7200), + "token_explicit_max_ttl": int64(7200), + "renewable": true, + "token_type": "default-service", + "token_num_uses": 123, } if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" { t.Fatal("unexpected bound cidrs") } delete(resp.Data, "bound_cidrs") + if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" { + t.Fatal("unexpected bound cidrs") + } + delete(resp.Data, "token_bound_cidrs") if diff := deep.Equal(expected, resp.Data); diff != nil { t.Fatal(diff) @@ -2731,6 +2742,8 @@ func TestTokenStore_RoleCRUD(t *testing.T) { "allowed_policies": "test3", "path_suffix": "happenin", "renewable": false, + "explicit_max_ttl": "80h", + "token_num_uses": 0, } resp, err = core.HandleRequest(namespace.RootContext(nil), req) @@ -2754,21 +2767,27 @@ func TestTokenStore_RoleCRUD(t *testing.T) { } expected = map[string]interface{}{ - "name": "test", - "orphan": true, - "period": int64(284400), - "allowed_policies": []string{"test3"}, - "disallowed_policies": []string{}, - "path_suffix": "happenin", - "explicit_max_ttl": int64(0), - "renewable": false, - "token_type": "default-service", + "name": "test", + "orphan": true, + "period": int64(284400), + "token_period": int64(284400), + "allowed_policies": []string{"test3"}, + "disallowed_policies": []string{}, + "path_suffix": "happenin", + "token_explicit_max_ttl": int64(288000), + "explicit_max_ttl": int64(288000), + "renewable": false, + "token_type": "default-service", } if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" { t.Fatal("unexpected bound cidrs") } delete(resp.Data, "bound_cidrs") + if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" { + t.Fatal("unexpected bound cidrs") + } + delete(resp.Data, "token_bound_cidrs") if diff := deep.Equal(expected, resp.Data); diff != nil { t.Fatal(diff) @@ -2797,21 +2816,27 @@ func TestTokenStore_RoleCRUD(t *testing.T) { } expected = map[string]interface{}{ - "name": "test", - "orphan": true, - "explicit_max_ttl": int64(5), - "allowed_policies": []string{"test3"}, - "disallowed_policies": []string{}, - "path_suffix": "happenin", - "period": int64(0), - "renewable": false, - "token_type": "default-service", + "name": "test", + "orphan": true, + "explicit_max_ttl": int64(5), + "token_explicit_max_ttl": int64(5), + "allowed_policies": []string{"test3"}, + "disallowed_policies": []string{}, + "path_suffix": "happenin", + "period": int64(0), + "token_period": int64(0), + "renewable": false, + "token_type": "default-service", } if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" { t.Fatal("unexpected bound cidrs") } delete(resp.Data, "bound_cidrs") + if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "0.0.0.0/0" { + t.Fatal("unexpected bound cidrs") + } + delete(resp.Data, "token_bound_cidrs") if diff := deep.Equal(expected, resp.Data); diff != nil { t.Fatal(diff) @@ -2840,15 +2865,17 @@ func TestTokenStore_RoleCRUD(t *testing.T) { } expected = map[string]interface{}{ - "name": "test", - "orphan": true, - "explicit_max_ttl": int64(5), - "allowed_policies": []string{"test3"}, - "disallowed_policies": []string{}, - "path_suffix": "", - "period": int64(0), - "renewable": false, - "token_type": "default-service", + "name": "test", + "orphan": true, + "token_explicit_max_ttl": int64(5), + "explicit_max_ttl": int64(5), + "allowed_policies": []string{"test3"}, + "disallowed_policies": []string{}, + "path_suffix": "", + "period": int64(0), + "token_period": int64(0), + "renewable": false, + "token_type": "default-service", } if diff := deep.Equal(expected, resp.Data); diff != nil { @@ -3585,6 +3612,248 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { } } +func TestTokenStore_RoleTokenFields(t *testing.T) { + c, _, _ := TestCoreUnsealed(t) + //c, _, root := TestCoreUnsealed(t) + ts := c.tokenStore + rootContext := namespace.RootContext(context.Background()) + + boundCIDRs, err := parseutil.ParseAddrs([]string{"127.0.0.1/32"}) + if err != nil { + t.Fatal(err) + } + + // First test the upgrade case. Create a role with values and ensure they + // are reflected properly on read. + { + roleEntry := &tsRoleEntry{ + Name: "test", + TokenParams: tokenutil.TokenParams{ + TokenType: logical.TokenTypeBatch, + }, + Period: time.Second, + ExplicitMaxTTL: time.Hour, + } + roleEntry.BoundCIDRs = boundCIDRs + ns := namespace.RootNamespace + jsonEntry, err := logical.StorageEntryJSON("test", roleEntry) + if err != nil { + t.Fatal(err) + } + if err := ts.rolesView(ns).Put(rootContext, jsonEntry); err != nil { + t.Fatal(err) + } + // Read it back + roleEntry, err = ts.tokenStoreRole(rootContext, "test") + if err != nil { + t.Fatal(err) + } + expRoleEntry := &tsRoleEntry{ + Name: "test", + TokenParams: tokenutil.TokenParams{ + TokenPeriod: time.Second, + TokenExplicitMaxTTL: time.Hour, + TokenBoundCIDRs: boundCIDRs, + TokenType: logical.TokenTypeBatch, + }, + Period: time.Second, + ExplicitMaxTTL: time.Hour, + BoundCIDRs: boundCIDRs, + } + if diff := deep.Equal(expRoleEntry, roleEntry); diff != nil { + t.Fatal(diff) + } + } + + // Now, read that back through the API and verify we see what we expect + { + req := logical.TestRequest(t, logical.ReadOperation, "roles/test") + resp, err := ts.HandleRequest(rootContext, req) + if err != nil { + t.Fatalf("err: %v", err) + } + + expected := map[string]interface{}{ + "name": "test", + "orphan": false, + "period": int64(1), + "token_period": int64(1), + "allowed_policies": []string(nil), + "disallowed_policies": []string(nil), + "path_suffix": "", + "token_explicit_max_ttl": int64(3600), + "explicit_max_ttl": int64(3600), + "renewable": false, + "token_type": "batch", + } + + if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" { + t.Fatalf("unexpected bound cidrs: %s", resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String()) + } + delete(resp.Data, "bound_cidrs") + if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" { + t.Fatalf("unexpected token bound cidrs: %s", resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String()) + } + delete(resp.Data, "token_bound_cidrs") + + if diff := deep.Equal(expected, resp.Data); diff != nil { + t.Fatal(diff) + } + } + + // Put values in just the old locations, but through the API + { + req := logical.TestRequest(t, logical.UpdateOperation, "roles/test") + req.Data = map[string]interface{}{ + "explicit_max_ttl": 7200, + "token_type": "default-batch", + "period": 5, + "bound_cidrs": boundCIDRs[0].String(), + } + + resp, err := ts.HandleRequest(rootContext, req) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("err: %v\nresp: %#v", err, resp) + } + if resp != nil { + t.Fatalf("expected a nil response") + } + + req = logical.TestRequest(t, logical.ReadOperation, "roles/test") + resp, err = ts.HandleRequest(rootContext, req) + if err != nil { + t.Fatalf("err: %v", err) + } + + expected := map[string]interface{}{ + "name": "test", + "orphan": false, + "period": int64(5), + "token_period": int64(5), + "allowed_policies": []string(nil), + "disallowed_policies": []string(nil), + "path_suffix": "", + "token_explicit_max_ttl": int64(7200), + "explicit_max_ttl": int64(7200), + "renewable": false, + "token_type": "default-batch", + } + + if resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" { + t.Fatalf("unexpected bound cidrs: %s", resp.Data["bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String()) + } + delete(resp.Data, "bound_cidrs") + if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" { + t.Fatalf("unexpected token bound cidrs: %s", resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String()) + } + delete(resp.Data, "token_bound_cidrs") + + if diff := deep.Equal(expected, resp.Data); diff != nil { + t.Fatal(diff) + } + } + // Same thing for just the new locations + { + req := logical.TestRequest(t, logical.UpdateOperation, "roles/test") + req.Data = map[string]interface{}{ + "token_explicit_max_ttl": 5200, + "token_type": "default-service", + "token_period": 7, + "token_bound_cidrs": boundCIDRs[0].String(), + } + + resp, err := ts.HandleRequest(rootContext, req) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("err: %v\nresp: %#v", err, resp) + } + if resp != nil { + t.Fatalf("expected a nil response") + } + + req = logical.TestRequest(t, logical.ReadOperation, "roles/test") + resp, err = ts.HandleRequest(rootContext, req) + if err != nil { + t.Fatalf("err: %v", err) + } + + expected := map[string]interface{}{ + "name": "test", + "orphan": false, + "period": int64(0), + "token_period": int64(7), + "allowed_policies": []string(nil), + "disallowed_policies": []string(nil), + "path_suffix": "", + "token_explicit_max_ttl": int64(5200), + "explicit_max_ttl": int64(0), + "renewable": false, + "token_type": "default-service", + } + + if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" { + t.Fatalf("unexpected token bound cidrs: %s", resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String()) + } + delete(resp.Data, "token_bound_cidrs") + + if diff := deep.Equal(expected, resp.Data); diff != nil { + t.Fatal(diff) + } + } + // Put values in both locations + { + req := logical.TestRequest(t, logical.UpdateOperation, "roles/test") + req.Data = map[string]interface{}{ + "token_explicit_max_ttl": 7200, + "explicit_max_ttl": 5200, + "token_type": "service", + "token_period": 5, + "period": 1, + "token_bound_cidrs": boundCIDRs[0].String(), + "bound_cidrs": boundCIDRs[0].String(), + } + + resp, err := ts.HandleRequest(rootContext, req) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("err: %v\nresp: %#v", err, resp) + } + if resp == nil { + t.Fatalf("expected a non-nil response") + } + if len(resp.Warnings) != 3 { + t.Fatalf("expected 3 warnings, got %#v", resp.Warnings) + } + + req = logical.TestRequest(t, logical.ReadOperation, "roles/test") + resp, err = ts.HandleRequest(rootContext, req) + if err != nil { + t.Fatalf("err: %v", err) + } + + expected := map[string]interface{}{ + "name": "test", + "orphan": false, + "period": int64(0), + "token_period": int64(5), + "allowed_policies": []string(nil), + "disallowed_policies": []string(nil), + "path_suffix": "", + "token_explicit_max_ttl": int64(7200), + "explicit_max_ttl": int64(0), + "renewable": false, + "token_type": "service", + } + + if resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String() != "127.0.0.1" { + t.Fatalf("unexpected token bound cidrs: %s", resp.Data["token_bound_cidrs"].([]*sockaddr.SockAddrMarshaler)[0].String()) + } + delete(resp.Data, "token_bound_cidrs") + + if diff := deep.Equal(expected, resp.Data); diff != nil { + t.Fatal(diff) + } + } +} + func TestTokenStore_Periodic(t *testing.T) { core, _, root := TestCoreUnsealed(t) @@ -3726,6 +3995,120 @@ func TestTokenStore_Periodic(t *testing.T) { } } +func testTokenStore_NumUses_ErrorCheckHelper(t *testing.T, resp *logical.Response, err error) { + if err != nil { + t.Fatal(err) + } + if resp == nil { + t.Fatal("response was nil") + } + if resp.Auth == nil { + t.Fatalf(fmt.Sprintf("response auth was nil, resp is %#v", *resp)) + } + if resp.Auth.ClientToken == "" { + t.Fatalf("bad: %#v", resp) + } +} + +func testTokenStore_NumUses_SelfLookupHelper(t *testing.T, core *Core, clientToken string, expectedNumUses int) { + req := logical.TestRequest(t, logical.ReadOperation, "auth/token/lookup-self") + req.ClientToken = clientToken + resp, err := core.HandleRequest(namespace.RootContext(nil), req) + if err != nil { + t.Fatalf("err: %v", err) + } + // Just used the token, this should decrement the num_uses counter + expectedNumUses = expectedNumUses - 1 + actualNumUses := resp.Data["num_uses"].(int) + + if actualNumUses != expectedNumUses { + t.Fatalf("num_uses mismatch (expected %d, got %d)", expectedNumUses, actualNumUses) + } +} +func TestTokenStore_NumUses(t *testing.T) { + core, _, root := TestCoreUnsealed(t) + roleNumUses := 10 + lesserNumUses := 5 + greaterNumUses := 15 + + // Create a test role with limited token_num_uses + req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/roles/test-limited-uses") + req.ClientToken = root + req.Data = map[string]interface{}{ + "token_num_uses": roleNumUses, + } + resp, err := core.HandleRequest(namespace.RootContext(nil), req) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("err: %v\nresp: %#v", err, resp) + } + if resp != nil { + t.Fatalf("expected a nil response") + } + + // Create a test role with unlimited token_num_uses + req.Path = "auth/token/roles/test-unlimited-uses" + req.Data = map[string]interface{}{} + resp, err = core.HandleRequest(namespace.RootContext(nil), req) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("err: %v\nresp: %#v", err, resp) + } + if resp != nil { + t.Fatalf("expected a nil response") + } + + // Generate some tokens from the test roles + req.Path = "auth/token/create/test-limited-uses" + + // first token, num_uses is expected to come from the limited uses role + resp, err = core.HandleRequest(namespace.RootContext(nil), req) + testTokenStore_NumUses_ErrorCheckHelper(t, resp, err) + noOverrideToken := resp.Auth.ClientToken + + // second token, override num_uses with a lesser value, this should become the value + // applied to the token + req.Data = map[string]interface{}{ + "num_uses": lesserNumUses, + } + resp, err = core.HandleRequest(namespace.RootContext(nil), req) + testTokenStore_NumUses_ErrorCheckHelper(t, resp, err) + lesserOverrideToken := resp.Auth.ClientToken + + // third token, override num_uses with a greater value, the value + // applied to the token should come from the limited uses role + req.Data = map[string]interface{}{ + "num_uses": greaterNumUses, + } + resp, err = core.HandleRequest(namespace.RootContext(nil), req) + testTokenStore_NumUses_ErrorCheckHelper(t, resp, err) + greaterOverrideToken := resp.Auth.ClientToken + + // fourth token, override num_uses with a zero value, a num_uses value of zero + // has an internal meaning of unlimited so num_uses == 1 is actually less than + // num_uses == 0. In this case, the lesser value of the limited-uses role should be applied. + req.Data = map[string]interface{}{ + "num_uses": 0, + } + resp, err = core.HandleRequest(namespace.RootContext(nil), req) + testTokenStore_NumUses_ErrorCheckHelper(t, resp, err) + zeroOverrideToken := resp.Auth.ClientToken + + // fifth token, override num_uses with a value from a role that has unlimited num_uses. num_uses + // should be the specified num_uses parameter at the create endpoint + req.Path = "auth/token/create/test-unlimited-uses" + req.Data = map[string]interface{}{ + "num_uses": lesserNumUses, + } + resp, err = core.HandleRequest(namespace.RootContext(nil), req) + testTokenStore_NumUses_ErrorCheckHelper(t, resp, err) + unlimitedRoleOverrideToken := resp.Auth.ClientToken + + testTokenStore_NumUses_SelfLookupHelper(t, core, noOverrideToken, roleNumUses) + testTokenStore_NumUses_SelfLookupHelper(t, core, lesserOverrideToken, lesserNumUses) + testTokenStore_NumUses_SelfLookupHelper(t, core, greaterOverrideToken, roleNumUses) + testTokenStore_NumUses_SelfLookupHelper(t, core, zeroOverrideToken, roleNumUses) + testTokenStore_NumUses_SelfLookupHelper(t, core, unlimitedRoleOverrideToken, lesserNumUses) +} + func TestTokenStore_Periodic_ExplicitMax(t *testing.T) { core, _, root := TestCoreUnsealed(t) diff --git a/vendor/github.com/hashicorp/vault/sdk/helper/tokenhelper/tokenhelper.go b/vendor/github.com/hashicorp/vault/sdk/helper/tokenhelper/tokenhelper.go new file mode 100644 index 0000000000..09e8dcede9 --- /dev/null +++ b/vendor/github.com/hashicorp/vault/sdk/helper/tokenhelper/tokenhelper.go @@ -0,0 +1,207 @@ +package tokenhelper + +import ( + "errors" + "fmt" + "time" + + sockaddr "github.com/hashicorp/go-sockaddr" + "github.com/hashicorp/vault/sdk/framework" + "github.com/hashicorp/vault/sdk/helper/parseutil" + "github.com/hashicorp/vault/sdk/helper/strutil" + "github.com/hashicorp/vault/sdk/logical" +) + +type TokenParams struct { + // The set of CIDRs that tokens generated using this role will be bound to + TokenBoundCIDRs []*sockaddr.SockAddrMarshaler `json:"token_bound_cidrs"` + + // If set, the token entry will have an explicit maximum TTL set, rather + // than deferring to role/mount values + TokenExplicitMaxTTL time.Duration `json:"token_explicit_max_ttl" mapstructure:"token_explicit_max_ttl"` + + // The max TTL to use for the token + TokenMaxTTL time.Duration `json:"token_max_ttl" mapstructure:"token_max_ttl"` + + // If set, core will not automatically add default to the policy list + TokenNoDefaultPolicy bool `json:"token_no_default_policy" mapstructure:"token_no_default_policy"` + + // If non-zero, tokens created using this role will be able to be renewed + // forever, but will have a fixed renewal period of this value + TokenPeriod time.Duration `json:"token_period" mapstructure:"token_period"` + + // The policies to set + TokenPolicies []string `json:"token_policies" mapstructure:"token_policies"` + + // The type of token this role should issue + TokenType logical.TokenType `json:"token_type" mapstructure:"token_type"` + + // The TTL to user for the token + TokenTTL time.Duration `json:"token_ttl" mapstructure:"token_ttl"` +} + +// AddTokenFields adds fields to an existing role. It panics if it would +// overwrite an existing field. +func AddTokenFields(m map[string]*framework.FieldSchema) { + AddTokenFieldsWithAllowList(m, nil) +} + +func AddTokenFieldsWithAllowList(m map[string]*framework.FieldSchema, allowed []string) { + r := TokenFields() + for k, v := range r { + if len(allowed) > 0 && !strutil.StrListContains(allowed, k) { + continue + } + if _, has := m[k]; has { + panic(fmt.Sprintf("adding role field %s would overwrite existing field", k)) + } + m[k] = v + } +} + +func TokenFields() map[string]*framework.FieldSchema { + return map[string]*framework.FieldSchema{ + "token_bound_cidrs": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: `Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.`, + }, + + "token_explicit_max_ttl": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: tokenExplicitMaxTTLHelp, + }, + + "token_max_ttl": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: "The maximum lifetime of the generated token", + }, + + "token_no_default_policy": &framework.FieldSchema{ + Type: framework.TypeBool, + Description: "If true, the 'default' policy will not automatically be added to generated tokens", + }, + + "token_period": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: tokenPeriodHelp, + }, + + "token_policies": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "Comma-separated list of policies", + }, + + "token_type": &framework.FieldSchema{ + Type: framework.TypeString, + Default: "default-service", + Description: "The type of token to generate, service or batch", + }, + + "token_ttl": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: "The initial ttl of the token to generate", + }, + } +} + +func (t *TokenParams) ParseTokenFields(req *logical.Request, d *framework.FieldData) error { + if boundCIDRsRaw, ok := d.GetOk("token_bound_cidrs"); ok { + boundCIDRs, err := parseutil.ParseAddrs(boundCIDRsRaw.([]string)) + if err != nil { + return err + } + t.TokenBoundCIDRs = boundCIDRs + } + + if explicitMaxTTLRaw, ok := d.GetOk("token_explicit_max_ttl"); ok { + t.TokenExplicitMaxTTL = time.Duration(explicitMaxTTLRaw.(int)) * time.Second + } + + if maxTTLRaw, ok := d.GetOk("token_max_ttl"); ok { + t.TokenMaxTTL = time.Duration(maxTTLRaw.(int)) * time.Second + } + if t.TokenMaxTTL < 0 { + return errors.New("'token_max_ttl' cannot be negative") + } + + if noDefaultRaw, ok := d.GetOk("token_no_default_policy"); ok { + t.TokenNoDefaultPolicy = noDefaultRaw.(bool) + } + + if periodRaw, ok := d.GetOk("token_period"); ok { + t.TokenPeriod = time.Duration(periodRaw.(int)) * time.Second + } + if t.TokenPeriod < 0 { + return errors.New("'token_period' cannot be negative") + } + + if policiesRaw, ok := d.GetOk("token_policies"); ok { + t.TokenPolicies = policiesRaw.([]string) + } + + if tokenTypeRaw, ok := d.GetOk("token_type"); ok { + var tokenType logical.TokenType + tokenTypeStr := tokenTypeRaw.(string) + switch tokenTypeStr { + case "service": + tokenType = logical.TokenTypeService + case "batch": + tokenType = logical.TokenTypeBatch + case "", "default", "default-service": + tokenType = logical.TokenTypeDefaultService + case "default-batch": + tokenType = logical.TokenTypeDefaultBatch + default: + return fmt.Errorf("invalid 'token_type' value %q", tokenTypeStr) + } + t.TokenType = tokenType + } + + if ttlRaw, ok := d.GetOk("token_ttl"); ok { + t.TokenTTL = time.Duration(ttlRaw.(int)) * time.Second + } + if t.TokenTTL < 0 { + return errors.New("'token_ttl' cannot be negative") + } + if t.TokenTTL > 0 && t.TokenMaxTTL > 0 && t.TokenTTL > t.TokenMaxTTL { + return errors.New("'token_ttl' cannot be greater than 'token_max_ttl'") + } + + return nil +} + +func (t *TokenParams) PopulateTokenData(m map[string]interface{}) { + m["token_bound_cidrs"] = t.TokenBoundCIDRs + m["token_explicit_max_ttl"] = t.TokenExplicitMaxTTL.Seconds() + m["token_max_ttl"] = t.TokenMaxTTL.Seconds() + m["token_no_default_policy"] = t.TokenNoDefaultPolicy + m["token_period"] = t.TokenPeriod.Seconds() + m["token_policies"] = t.TokenPolicies + m["token_type"] = t.TokenType.String() + m["token_ttl"] = t.TokenTTL.Seconds() +} + +func (t *TokenParams) PopulateTokenAuth(auth *logical.Auth) { + auth.BoundCIDRs = t.TokenBoundCIDRs + auth.ExplicitMaxTTL = t.TokenExplicitMaxTTL + auth.MaxTTL = t.TokenMaxTTL + auth.NoDefaultPolicy = t.TokenNoDefaultPolicy + auth.Period = t.TokenPeriod + auth.Policies = t.TokenPolicies + auth.TokenType = t.TokenType + auth.TTL = t.TokenTTL +} + +const ( + tokenPeriodHelp = `If set, tokens created via this role +will have no max lifetime; instead, their +renewal period will be fixed to this value. +This takes an integer number of seconds, +or a string duration (e.g. "24h").` + tokenExplicitMaxTTLHelp = `If set, tokens created via this role +carry an explicit maximum TTL. During renewal, +the current maximum TTL values of the role +and the mount are not checked for changes, +and any updates to these values will have +no effect on the token being renewed.` +) diff --git a/vendor/github.com/hashicorp/vault/sdk/logical/auth.go b/vendor/github.com/hashicorp/vault/sdk/logical/auth.go index 89aa916590..2bfb6e0015 100644 --- a/vendor/github.com/hashicorp/vault/sdk/logical/auth.go +++ b/vendor/github.com/hashicorp/vault/sdk/logical/auth.go @@ -38,6 +38,11 @@ type Auth struct { // different namespaces indexed by respective namespace identifiers ExternalNamespacePolicies map[string][]string `json:"external_namespace_policies" mapstructure:"external_namespace_policies" structs:"external_namespace_policies"` + // Indicates that the default policy should not be added by core when + // creating a token. The default policy will still be added if it's + // explicitly defined. + NoDefaultPolicy bool `json:"no_default_policy" mapstructure:"no_default_policy" structs:"no_default_policy"` + // Metadata is used to attach arbitrary string-type metadata to // an authenticated user. This metadata will be outputted into the // audit log. diff --git a/vendor/github.com/hashicorp/vault/sdk/plugin/pb/backend.pb.go b/vendor/github.com/hashicorp/vault/sdk/plugin/pb/backend.pb.go index 2db00c7e9e..28ff0c455e 100644 --- a/vendor/github.com/hashicorp/vault/sdk/plugin/pb/backend.pb.go +++ b/vendor/github.com/hashicorp/vault/sdk/plugin/pb/backend.pb.go @@ -526,7 +526,9 @@ type Auth struct { // TTL is a hard limit and cannot be exceeded, also counts for periodic tokens. ExplicitMaxTTL int64 `sentinel:"" protobuf:"varint,16,opt,name=explicit_max_ttl,json=explicitMaxTtl,proto3" json:"explicit_max_ttl,omitempty"` // TokenType is the type of token being requested - TokenType uint32 `sentinel:"" protobuf:"varint,17,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` + TokenType uint32 `sentinel:"" protobuf:"varint,17,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` + // Whether the default policy should be added automatically by core + NoDefaultPolicy bool `sentinel:"" protobuf:"varint,18,opt,name=no_default_policy,json=noDefaultPolicy,proto3" json:"no_default_policy,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -676,6 +678,13 @@ func (m *Auth) GetTokenType() uint32 { return 0 } +func (m *Auth) GetNoDefaultPolicy() bool { + if m != nil { + return m.NoDefaultPolicy + } + return false +} + type TokenEntry struct { ID string `sentinel:"" protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` Accessor string `sentinel:"" protobuf:"bytes,2,opt,name=accessor,proto3" json:"accessor,omitempty"` @@ -2713,164 +2722,165 @@ func init() { func init() { proto.RegisterFile("sdk/plugin/pb/backend.proto", fileDescriptor_4dbf1dfe0c11846b) } var fileDescriptor_4dbf1dfe0c11846b = []byte{ - // 2499 bytes of a gzipped FileDescriptorProto + // 2519 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xdb, 0x72, 0x1b, 0xc7, 0xd1, 0x2e, 0x00, 0xc4, 0xa9, 0x71, 0x22, 0x46, 0xb4, 0xfe, 0x15, 0x24, 0xff, 0x82, 0xd7, 0x91, - 0x0c, 0x2b, 0x36, 0x68, 0x51, 0x71, 0x2c, 0x27, 0x65, 0xa7, 0x68, 0x8a, 0x96, 0x19, 0x93, 0x36, - 0x6b, 0x09, 0xc5, 0x39, 0x55, 0xc1, 0x83, 0xdd, 0x21, 0xb8, 0xc5, 0xc5, 0xee, 0x66, 0x76, 0x96, - 0x22, 0xae, 0xf2, 0x16, 0x79, 0x8d, 0xdc, 0xa6, 0x72, 0x93, 0xbb, 0x94, 0x2b, 0xf7, 0x79, 0x8d, - 0x3c, 0x43, 0x6a, 0x7a, 0x66, 0x4f, 0x00, 0x68, 0xc9, 0x55, 0xce, 0xdd, 0x4c, 0x77, 0xcf, 0xa9, - 0xe7, 0xeb, 0xaf, 0x7b, 0x76, 0xe1, 0x6e, 0xe4, 0x5c, 0xee, 0x86, 0x5e, 0x3c, 0x77, 0xfd, 0xdd, - 0x70, 0xb6, 0x3b, 0xa3, 0xf6, 0x25, 0xf3, 0x9d, 0x71, 0xc8, 0x03, 0x11, 0x90, 0x72, 0x38, 0x1b, - 0xdc, 0x9f, 0x07, 0xc1, 0xdc, 0x63, 0xbb, 0x28, 0x99, 0xc5, 0xe7, 0xbb, 0xc2, 0x5d, 0xb0, 0x48, - 0xd0, 0x45, 0xa8, 0x8c, 0x06, 0x03, 0x39, 0x83, 0x17, 0xcc, 0x5d, 0x9b, 0x7a, 0xbb, 0xae, 0xc3, - 0x7c, 0xe1, 0x8a, 0xa5, 0xd6, 0x19, 0x79, 0x9d, 0x5a, 0x45, 0x69, 0xcc, 0x3a, 0x54, 0x0f, 0x17, - 0xa1, 0x58, 0x9a, 0x43, 0xa8, 0x7d, 0xc1, 0xa8, 0xc3, 0x38, 0xb9, 0x0d, 0xb5, 0x0b, 0x6c, 0x19, - 0xa5, 0x61, 0x65, 0xd4, 0xb4, 0x74, 0xcf, 0xfc, 0x03, 0xc0, 0xa9, 0x1c, 0x73, 0xc8, 0x79, 0xc0, - 0xc9, 0x1d, 0x68, 0x30, 0xce, 0xa7, 0x62, 0x19, 0x32, 0xa3, 0x34, 0x2c, 0x8d, 0x3a, 0x56, 0x9d, - 0x71, 0x3e, 0x59, 0x86, 0x8c, 0xfc, 0x1f, 0xc8, 0xe6, 0x74, 0x11, 0xcd, 0x8d, 0xf2, 0xb0, 0x24, - 0x67, 0x60, 0x9c, 0x9f, 0x44, 0xf3, 0x64, 0x8c, 0x1d, 0x38, 0xcc, 0xa8, 0x0c, 0x4b, 0xa3, 0x0a, - 0x8e, 0x39, 0x08, 0x1c, 0x66, 0xfe, 0xa5, 0x04, 0xd5, 0x53, 0x2a, 0x2e, 0x22, 0x42, 0x60, 0x8b, - 0x07, 0x81, 0xd0, 0x8b, 0x63, 0x9b, 0x8c, 0xa0, 0x17, 0xfb, 0x34, 0x16, 0x17, 0xf2, 0x54, 0x36, - 0x15, 0xcc, 0x31, 0xca, 0xa8, 0x5e, 0x15, 0x93, 0xb7, 0xa1, 0xe3, 0x05, 0x36, 0xf5, 0xa6, 0x91, - 0x08, 0x38, 0x9d, 0xcb, 0x75, 0xa4, 0x5d, 0x1b, 0x85, 0x67, 0x4a, 0x46, 0x1e, 0x41, 0x3f, 0x62, - 0xd4, 0x9b, 0xbe, 0xe4, 0x34, 0x4c, 0x0d, 0xb7, 0xd4, 0x84, 0x52, 0xf1, 0x0d, 0xa7, 0xa1, 0xb6, - 0x35, 0xff, 0x51, 0x83, 0xba, 0xc5, 0xfe, 0x14, 0xb3, 0x48, 0x90, 0x2e, 0x94, 0x5d, 0x07, 0x4f, - 0xdb, 0xb4, 0xca, 0xae, 0x43, 0xc6, 0x40, 0x2c, 0x16, 0x7a, 0x72, 0x69, 0x37, 0xf0, 0x0f, 0xbc, - 0x38, 0x12, 0x8c, 0xeb, 0x33, 0x6f, 0xd0, 0x90, 0x7b, 0xd0, 0x0c, 0x42, 0xc6, 0x51, 0x86, 0x0e, - 0x68, 0x5a, 0x99, 0x40, 0x1e, 0x3c, 0xa4, 0xe2, 0xc2, 0xd8, 0x42, 0x05, 0xb6, 0xa5, 0xcc, 0xa1, - 0x82, 0x1a, 0x55, 0x25, 0x93, 0x6d, 0x62, 0x42, 0x2d, 0x62, 0x36, 0x67, 0xc2, 0xa8, 0x0d, 0x4b, - 0xa3, 0xd6, 0x1e, 0x8c, 0xc3, 0xd9, 0xf8, 0x0c, 0x25, 0x96, 0xd6, 0x90, 0x7b, 0xb0, 0x25, 0xfd, - 0x62, 0xd4, 0xd1, 0xa2, 0x21, 0x2d, 0xf6, 0x63, 0x71, 0x61, 0xa1, 0x94, 0xec, 0x41, 0x5d, 0xdd, - 0x69, 0x64, 0x34, 0x86, 0x95, 0x51, 0x6b, 0xcf, 0x90, 0x06, 0xfa, 0x94, 0x63, 0x05, 0x83, 0xe8, - 0xd0, 0x17, 0x7c, 0x69, 0x25, 0x86, 0xe4, 0x2d, 0x68, 0xdb, 0x9e, 0xcb, 0x7c, 0x31, 0x15, 0xc1, - 0x25, 0xf3, 0x8d, 0x26, 0xee, 0xa8, 0xa5, 0x64, 0x13, 0x29, 0x22, 0x7b, 0xf0, 0x46, 0xde, 0x64, - 0x4a, 0x6d, 0x9b, 0x45, 0x51, 0xc0, 0x0d, 0x40, 0xdb, 0x5b, 0x39, 0xdb, 0x7d, 0xad, 0x92, 0xd3, - 0x3a, 0x6e, 0x14, 0x7a, 0x74, 0x39, 0xf5, 0xe9, 0x82, 0x19, 0x2d, 0x35, 0xad, 0x96, 0x7d, 0x45, - 0x17, 0x8c, 0xdc, 0x87, 0xd6, 0x22, 0x88, 0x7d, 0x31, 0x0d, 0x03, 0xd7, 0x17, 0x46, 0x1b, 0x2d, - 0x00, 0x45, 0xa7, 0x52, 0x42, 0xde, 0x04, 0xd5, 0x53, 0x60, 0xec, 0x28, 0xbf, 0xa2, 0x04, 0xe1, - 0xf8, 0x00, 0xba, 0x4a, 0x9d, 0xee, 0xa7, 0x8b, 0x26, 0x1d, 0x94, 0xa6, 0x3b, 0xf9, 0x00, 0x9a, - 0x88, 0x07, 0xd7, 0x3f, 0x0f, 0x8c, 0x1e, 0xfa, 0xed, 0x56, 0xce, 0x2d, 0x12, 0x13, 0x47, 0xfe, - 0x79, 0x60, 0x35, 0x5e, 0xea, 0x16, 0xf9, 0x04, 0xee, 0x16, 0xce, 0xcb, 0xd9, 0x82, 0xba, 0xbe, - 0xeb, 0xcf, 0xa7, 0x71, 0xc4, 0x22, 0x63, 0x1b, 0x11, 0x6e, 0xe4, 0x4e, 0x6d, 0x25, 0x06, 0x2f, - 0x22, 0x16, 0x91, 0xbb, 0xd0, 0x54, 0x41, 0x3a, 0x75, 0x1d, 0xa3, 0x8f, 0x5b, 0x6a, 0x28, 0xc1, - 0x91, 0x43, 0xde, 0x81, 0x5e, 0x18, 0x78, 0xae, 0xbd, 0x9c, 0x06, 0x57, 0x8c, 0x73, 0xd7, 0x61, - 0x06, 0x19, 0x96, 0x46, 0x0d, 0xab, 0xab, 0xc4, 0x5f, 0x6b, 0xe9, 0xa6, 0xd0, 0xb8, 0x85, 0x86, - 0x6b, 0xa1, 0x31, 0x06, 0xb0, 0x03, 0xdf, 0x67, 0x36, 0xc2, 0x6f, 0x07, 0x4f, 0xd8, 0x95, 0x27, - 0x3c, 0x48, 0xa5, 0x56, 0xce, 0x62, 0xf0, 0x39, 0xb4, 0xf3, 0x50, 0x20, 0xdb, 0x50, 0xb9, 0x64, - 0x4b, 0x0d, 0x7f, 0xd9, 0x24, 0x43, 0xa8, 0x5e, 0x51, 0x2f, 0x66, 0x08, 0x79, 0x0d, 0x44, 0x35, - 0xc4, 0x52, 0x8a, 0x5f, 0x94, 0x9f, 0x96, 0xcc, 0xbf, 0x57, 0x61, 0x4b, 0x82, 0x8f, 0x7c, 0x08, - 0x1d, 0x8f, 0xd1, 0x88, 0x4d, 0x83, 0x50, 0x2e, 0x10, 0xe1, 0x54, 0xad, 0xbd, 0x6d, 0x39, 0xec, - 0x58, 0x2a, 0xbe, 0x56, 0x72, 0xab, 0xed, 0xe5, 0x7a, 0x32, 0xa4, 0x5d, 0x5f, 0x30, 0xee, 0x53, - 0x6f, 0x8a, 0xc1, 0xa0, 0x02, 0xac, 0x9d, 0x08, 0x9f, 0xc9, 0xa0, 0x58, 0xc5, 0x51, 0x65, 0x1d, - 0x47, 0x03, 0x68, 0xa0, 0xef, 0x5c, 0x16, 0xe9, 0x60, 0x4f, 0xfb, 0x64, 0x0f, 0x1a, 0x0b, 0x26, - 0xa8, 0x8e, 0x35, 0x19, 0x12, 0xb7, 0x93, 0x98, 0x19, 0x9f, 0x68, 0x85, 0x0a, 0x88, 0xd4, 0x6e, - 0x2d, 0x22, 0x6a, 0xeb, 0x11, 0x31, 0x80, 0x46, 0x0a, 0xba, 0xba, 0xba, 0xe1, 0xa4, 0x2f, 0x69, - 0x36, 0x64, 0xdc, 0x0d, 0x1c, 0xa3, 0x81, 0x40, 0xd1, 0x3d, 0x49, 0x92, 0x7e, 0xbc, 0x50, 0x10, - 0x6a, 0x2a, 0x92, 0xf4, 0xe3, 0xc5, 0x3a, 0x62, 0x60, 0x05, 0x31, 0x3f, 0x81, 0x2a, 0xf5, 0x5c, - 0x1a, 0x61, 0x08, 0xc9, 0x9b, 0xd5, 0x7c, 0x3f, 0xde, 0x97, 0x52, 0x4b, 0x29, 0xc9, 0x13, 0xe8, - 0xcc, 0x79, 0x10, 0x87, 0x53, 0xec, 0xb2, 0xc8, 0x68, 0xe3, 0x69, 0x57, 0xad, 0xdb, 0x68, 0xb4, - 0xaf, 0x6c, 0x64, 0x04, 0xce, 0x82, 0xd8, 0x77, 0xa6, 0xb6, 0xeb, 0xf0, 0xc8, 0xe8, 0xa0, 0xf3, - 0x00, 0x45, 0x07, 0x52, 0x22, 0x43, 0x4c, 0x85, 0x40, 0xea, 0xe0, 0x2e, 0xda, 0x74, 0x50, 0x7a, - 0x9a, 0x78, 0xf9, 0xa7, 0xd0, 0x4f, 0x12, 0x53, 0x66, 0xd9, 0x43, 0xcb, 0xed, 0x44, 0x91, 0x1a, - 0x8f, 0x60, 0x9b, 0x5d, 0x4b, 0x0a, 0x75, 0xc5, 0x74, 0x41, 0xaf, 0xa7, 0x42, 0x78, 0x3a, 0xa4, - 0xba, 0x89, 0xfc, 0x84, 0x5e, 0x4f, 0x84, 0x27, 0xe3, 0x5f, 0xad, 0x8e, 0xf1, 0xdf, 0xc7, 0x64, - 0xd4, 0x44, 0x89, 0x8c, 0xff, 0xc1, 0x2f, 0xa1, 0x53, 0xb8, 0xc2, 0x0d, 0x40, 0xde, 0xc9, 0x03, - 0xb9, 0x99, 0x07, 0xef, 0xbf, 0xb6, 0x00, 0xf0, 0x2e, 0xd5, 0xd0, 0xd5, 0x0c, 0x90, 0xbf, 0xe0, - 0xf2, 0x86, 0x0b, 0xa6, 0x9c, 0xf9, 0x42, 0x83, 0x51, 0xf7, 0xbe, 0x17, 0x87, 0x49, 0x0e, 0xa8, - 0xe6, 0x72, 0xc0, 0x7b, 0xb0, 0x25, 0x31, 0x67, 0xd4, 0x32, 0xaa, 0xce, 0x76, 0x84, 0xe8, 0x54, - 0xc8, 0x44, 0xab, 0xb5, 0x40, 0xa8, 0xaf, 0x07, 0x42, 0x1e, 0x61, 0x8d, 0x22, 0xc2, 0xde, 0x86, - 0x8e, 0xcd, 0x19, 0xe6, 0xa3, 0xa9, 0x2c, 0x30, 0x34, 0x02, 0xdb, 0x89, 0x70, 0xe2, 0x2e, 0x98, - 0xf4, 0x9f, 0xbc, 0x0c, 0x40, 0x95, 0x6c, 0x6e, 0xbc, 0xab, 0xd6, 0xc6, 0xbb, 0xc2, 0xec, 0xee, - 0x31, 0xcd, 0xe2, 0xd8, 0xce, 0x45, 0x42, 0xa7, 0x10, 0x09, 0x05, 0xb8, 0x77, 0x57, 0xe0, 0xbe, - 0x82, 0xc9, 0xde, 0x1a, 0x26, 0xdf, 0x82, 0xb6, 0x74, 0x40, 0x14, 0x52, 0x9b, 0xc9, 0x09, 0xb6, - 0x95, 0x23, 0x52, 0xd9, 0x91, 0x83, 0x11, 0x1c, 0xcf, 0x66, 0xcb, 0x8b, 0xc0, 0x63, 0x19, 0x09, - 0xb7, 0x52, 0xd9, 0x91, 0x23, 0xf7, 0x8b, 0xa8, 0x22, 0x88, 0x2a, 0x6c, 0x0f, 0x3e, 0x82, 0x66, - 0xea, 0xf5, 0x1f, 0x04, 0xa6, 0xbf, 0x96, 0xa0, 0x9d, 0x27, 0x3a, 0x39, 0x78, 0x32, 0x39, 0xc6, - 0xc1, 0x15, 0x4b, 0x36, 0x65, 0x89, 0xc0, 0x99, 0xcf, 0x5e, 0xd2, 0x99, 0xa7, 0x26, 0x68, 0x58, - 0x99, 0x40, 0x6a, 0x5d, 0xdf, 0xe6, 0x6c, 0x91, 0xa0, 0xaa, 0x62, 0x65, 0x02, 0xf2, 0x31, 0x80, - 0x1b, 0x45, 0x31, 0x53, 0x37, 0xb7, 0x85, 0x34, 0x30, 0x18, 0xab, 0xba, 0x71, 0x9c, 0xd4, 0x8d, - 0xe3, 0x49, 0x52, 0x37, 0x5a, 0x4d, 0xb4, 0xc6, 0x2b, 0xbd, 0x0d, 0x35, 0x79, 0x41, 0x93, 0x63, - 0x44, 0x5e, 0xc5, 0xd2, 0x3d, 0xf3, 0xcf, 0x50, 0x53, 0x95, 0xc5, 0xff, 0x94, 0xbc, 0xef, 0x40, - 0x43, 0xcd, 0xed, 0x3a, 0x3a, 0x56, 0xea, 0xd8, 0x3f, 0x72, 0xcc, 0xef, 0xca, 0xd0, 0xb0, 0x58, - 0x14, 0x06, 0x7e, 0xc4, 0x72, 0x95, 0x4f, 0xe9, 0x95, 0x95, 0x4f, 0x79, 0x63, 0xe5, 0x93, 0xd4, - 0x53, 0x95, 0x5c, 0x3d, 0x35, 0x80, 0x06, 0x67, 0x8e, 0xcb, 0x99, 0x2d, 0x74, 0xed, 0x95, 0xf6, - 0xa5, 0xee, 0x25, 0xe5, 0x32, 0x65, 0x47, 0x98, 0x17, 0x9a, 0x56, 0xda, 0x27, 0x8f, 0xf3, 0x05, - 0x83, 0x2a, 0xc5, 0x76, 0x54, 0xc1, 0xa0, 0xb6, 0xbb, 0xa1, 0x62, 0x78, 0x92, 0x15, 0x5e, 0x75, - 0x8c, 0xe6, 0x3b, 0xf9, 0x01, 0x9b, 0x2b, 0xaf, 0x1f, 0x2d, 0x0f, 0x7f, 0x57, 0x86, 0xed, 0xd5, - 0xbd, 0x6d, 0x40, 0xe0, 0x0e, 0x54, 0x55, 0x3e, 0xd3, 0xf0, 0x15, 0x6b, 0x99, 0xac, 0xb2, 0x42, - 0x74, 0xbf, 0x5a, 0x25, 0x8d, 0x57, 0x43, 0xaf, 0x48, 0x28, 0xef, 0xc2, 0xb6, 0x74, 0x51, 0xc8, - 0x9c, 0xac, 0x46, 0x53, 0x0c, 0xd8, 0xd3, 0xf2, 0xb4, 0x4a, 0x7b, 0x04, 0xfd, 0xc4, 0x34, 0xe3, - 0x86, 0x5a, 0xc1, 0xf6, 0x30, 0xa1, 0x88, 0xdb, 0x50, 0x3b, 0x0f, 0xf8, 0x82, 0x0a, 0x4d, 0x82, - 0xba, 0x57, 0x20, 0x39, 0x64, 0xdb, 0x86, 0xc2, 0x64, 0x22, 0x94, 0xef, 0x10, 0x49, 0x3e, 0xe9, - 0x1b, 0x01, 0x59, 0xb0, 0x61, 0x35, 0x92, 0xb7, 0x81, 0xf9, 0x5b, 0xe8, 0xad, 0x94, 0x85, 0x1b, - 0x1c, 0x99, 0x2d, 0x5f, 0x2e, 0x2c, 0x5f, 0x98, 0xb9, 0xb2, 0x32, 0xf3, 0xef, 0xa0, 0xff, 0x05, - 0xf5, 0x1d, 0x8f, 0xe9, 0xf9, 0xf7, 0xf9, 0x3c, 0x92, 0x09, 0x4e, 0xbf, 0x52, 0xa6, 0x3a, 0xfb, - 0x74, 0xac, 0xa6, 0x96, 0x1c, 0x39, 0xe4, 0x01, 0xd4, 0xb9, 0xb2, 0xd6, 0x00, 0x68, 0xe5, 0xea, - 0x56, 0x2b, 0xd1, 0x99, 0xdf, 0x02, 0x29, 0x4c, 0x2d, 0x1f, 0x28, 0x4b, 0x32, 0x92, 0xe8, 0x57, - 0xa0, 0xd0, 0x51, 0xd5, 0xce, 0x63, 0xd2, 0x4a, 0xb5, 0x64, 0x08, 0x15, 0xc6, 0xb9, 0x5e, 0x02, - 0x0b, 0xc7, 0xec, 0x39, 0x68, 0x49, 0x95, 0xf9, 0x33, 0xe8, 0x9f, 0x85, 0xcc, 0x76, 0xa9, 0x87, - 0x4f, 0x39, 0xb5, 0xc0, 0x7d, 0xa8, 0x4a, 0x27, 0x27, 0x84, 0xd1, 0xc4, 0x81, 0xa8, 0x56, 0x72, - 0xf3, 0x5b, 0x30, 0xd4, 0xbe, 0x0e, 0xaf, 0xdd, 0x48, 0x30, 0xdf, 0x66, 0x07, 0x17, 0xcc, 0xbe, - 0xfc, 0x11, 0x4f, 0x7e, 0x05, 0x77, 0x36, 0xad, 0x90, 0xec, 0xaf, 0x65, 0xcb, 0xde, 0xf4, 0x5c, - 0xe6, 0x0e, 0x5c, 0xa3, 0x61, 0x01, 0x8a, 0x3e, 0x97, 0x12, 0x79, 0x8f, 0x4c, 0x8e, 0x8b, 0x34, - 0x1f, 0xeb, 0x5e, 0xe2, 0x8f, 0xca, 0xcd, 0xfe, 0xf8, 0x5b, 0x09, 0x9a, 0x67, 0x4c, 0xc4, 0x21, - 0x9e, 0xe5, 0x2e, 0x34, 0x67, 0x3c, 0xb8, 0x64, 0x3c, 0x3b, 0x4a, 0x43, 0x09, 0x8e, 0x1c, 0xf2, - 0x18, 0x6a, 0x07, 0x81, 0x7f, 0xee, 0xce, 0xf1, 0x61, 0xab, 0x89, 0x21, 0x1d, 0x3b, 0x56, 0x3a, - 0x45, 0x0c, 0xda, 0x90, 0x0c, 0xa1, 0xa5, 0x3f, 0x13, 0xbc, 0x78, 0x71, 0xf4, 0x2c, 0xa9, 0x78, - 0x73, 0xa2, 0xc1, 0xc7, 0xd0, 0xca, 0x0d, 0xfc, 0x41, 0xa9, 0xea, 0xff, 0x01, 0x70, 0x75, 0xe5, - 0xa3, 0x6d, 0x75, 0x54, 0x3d, 0x52, 0x1e, 0xed, 0x3e, 0x34, 0x65, 0x71, 0xa5, 0xd4, 0x49, 0x92, - 0x2c, 0x65, 0x49, 0xd2, 0x7c, 0x00, 0xfd, 0x23, 0xff, 0x8a, 0x7a, 0xae, 0x43, 0x05, 0xfb, 0x92, - 0x2d, 0xd1, 0x05, 0x6b, 0x3b, 0x30, 0xcf, 0xa0, 0xad, 0x5f, 0xda, 0xaf, 0xb5, 0xc7, 0xb6, 0xde, - 0xe3, 0xf7, 0x07, 0xd1, 0xbb, 0xd0, 0xd3, 0x93, 0x1e, 0xbb, 0x3a, 0x84, 0x64, 0x8d, 0xc1, 0xd9, - 0xb9, 0x7b, 0xad, 0xa7, 0xd6, 0x3d, 0xf3, 0x29, 0x6c, 0xe7, 0x4c, 0xd3, 0xe3, 0x5c, 0xb2, 0x65, - 0x94, 0x7c, 0x81, 0x90, 0xed, 0xc4, 0x03, 0xe5, 0xcc, 0x03, 0x26, 0x74, 0xf5, 0xc8, 0xe7, 0x4c, - 0xdc, 0x70, 0xba, 0x2f, 0xd3, 0x8d, 0x3c, 0x67, 0x7a, 0xf2, 0x87, 0x50, 0x65, 0xf2, 0xa4, 0xf9, - 0xfc, 0x99, 0xf7, 0x80, 0xa5, 0xd4, 0x1b, 0x16, 0x7c, 0x9a, 0x2e, 0x78, 0x1a, 0xab, 0x05, 0x5f, - 0x73, 0x2e, 0xf3, 0xed, 0x74, 0x1b, 0xa7, 0xb1, 0xb8, 0xe9, 0x46, 0x1f, 0x40, 0x5f, 0x1b, 0x3d, - 0x63, 0x1e, 0x13, 0xec, 0x86, 0x23, 0x3d, 0x04, 0x52, 0x30, 0xbb, 0x69, 0xba, 0x7b, 0xd0, 0x98, - 0x4c, 0x8e, 0x53, 0x6d, 0x91, 0x1b, 0xcd, 0x4f, 0xa0, 0x7f, 0x16, 0x3b, 0xc1, 0x29, 0x77, 0xaf, - 0x5c, 0x8f, 0xcd, 0xd5, 0x62, 0x49, 0xf1, 0x5b, 0xca, 0x15, 0xbf, 0x1b, 0xb3, 0x91, 0x39, 0x02, - 0x52, 0x18, 0x9e, 0xde, 0x5b, 0x14, 0x3b, 0x81, 0x0e, 0x61, 0x6c, 0x9b, 0x23, 0x68, 0x4f, 0xa8, - 0x2c, 0x36, 0x1c, 0x65, 0x63, 0x40, 0x5d, 0xa8, 0xbe, 0x36, 0x4b, 0xba, 0xe6, 0x1e, 0xec, 0x1c, - 0x50, 0xfb, 0xc2, 0xf5, 0xe7, 0xcf, 0xdc, 0x48, 0x56, 0x5b, 0x7a, 0xc4, 0x00, 0x1a, 0x8e, 0x16, - 0xe8, 0x21, 0x69, 0xdf, 0x7c, 0x1f, 0xde, 0xc8, 0x7d, 0xe6, 0x39, 0x13, 0x34, 0xf1, 0xc7, 0x0e, - 0x54, 0x23, 0xd9, 0xc3, 0x11, 0x55, 0x4b, 0x75, 0xcc, 0xaf, 0x60, 0x27, 0x9f, 0x80, 0x65, 0xed, - 0x93, 0x1c, 0x1c, 0xab, 0x92, 0x52, 0xae, 0x2a, 0xd1, 0x3e, 0x2b, 0x67, 0xf9, 0x64, 0x1b, 0x2a, - 0xbf, 0xfe, 0x66, 0xa2, 0xc1, 0x2e, 0x9b, 0xe6, 0x1f, 0xe5, 0xf2, 0xc5, 0xf9, 0xd4, 0xf2, 0x85, - 0xd2, 0xa4, 0xf4, 0x5a, 0xa5, 0xc9, 0x3a, 0xde, 0xde, 0x87, 0xfe, 0x89, 0x17, 0xd8, 0x97, 0x87, - 0x7e, 0xce, 0x1b, 0x06, 0xd4, 0x99, 0x9f, 0x77, 0x46, 0xd2, 0x35, 0xdf, 0x81, 0xde, 0x71, 0x60, - 0x53, 0xef, 0x24, 0x88, 0x7d, 0x91, 0x7a, 0x01, 0xbf, 0xbb, 0x69, 0x53, 0xd5, 0x31, 0xdf, 0x87, - 0xae, 0x4e, 0xd1, 0xfe, 0x79, 0x90, 0x30, 0x63, 0x96, 0xcc, 0x4b, 0xc5, 0x42, 0xdf, 0x3c, 0x86, - 0x5e, 0x66, 0xae, 0xe6, 0x7d, 0x07, 0x6a, 0x4a, 0xad, 0xcf, 0xd6, 0x4b, 0x5f, 0xaf, 0xca, 0xd2, - 0xd2, 0xea, 0x0d, 0x87, 0x5a, 0x40, 0xf7, 0x14, 0xbf, 0x7f, 0x1e, 0xfa, 0x57, 0x6a, 0xb2, 0x23, - 0x20, 0xea, 0x8b, 0xe8, 0x94, 0xf9, 0x57, 0x2e, 0x0f, 0x7c, 0x2c, 0xae, 0x4b, 0xba, 0x84, 0x49, - 0x26, 0x4e, 0x07, 0x25, 0x16, 0x56, 0x3f, 0x5c, 0x15, 0x6d, 0xf4, 0x21, 0x64, 0x5f, 0x57, 0x64, - 0xaa, 0xe1, 0x6c, 0x11, 0x08, 0x36, 0xa5, 0x8e, 0x93, 0x44, 0x0b, 0x28, 0xd1, 0xbe, 0xe3, 0xf0, - 0xbd, 0xff, 0x94, 0xa1, 0xfe, 0x99, 0x22, 0x70, 0xf2, 0x29, 0x74, 0x0a, 0xe9, 0x9a, 0xbc, 0x81, - 0x65, 0xdd, 0x6a, 0x71, 0x30, 0xb8, 0xbd, 0x26, 0x56, 0xe7, 0xfa, 0x00, 0xda, 0xf9, 0x64, 0x4c, - 0x30, 0xf1, 0xe2, 0xb7, 0xde, 0x01, 0xce, 0xb4, 0x9e, 0xa9, 0xcf, 0x60, 0x67, 0x53, 0x9a, 0x24, - 0xf7, 0xb2, 0x15, 0xd6, 0x53, 0xf4, 0xe0, 0xcd, 0x9b, 0xb4, 0x49, 0x7a, 0xad, 0x1f, 0x78, 0x8c, - 0xfa, 0x71, 0x98, 0xdf, 0x41, 0xd6, 0x24, 0x8f, 0xa1, 0x53, 0x48, 0x14, 0xea, 0x9c, 0x6b, 0xb9, - 0x23, 0x3f, 0xe4, 0x21, 0x54, 0x31, 0x39, 0x91, 0x4e, 0x21, 0x4b, 0x0e, 0xba, 0x69, 0x57, 0xad, - 0x3d, 0x84, 0x2d, 0xfc, 0x02, 0x98, 0x5b, 0x18, 0x47, 0xa4, 0x99, 0x6b, 0xef, 0xdf, 0x25, 0xa8, - 0x27, 0x5f, 0x85, 0x1f, 0xc3, 0x96, 0xcc, 0x01, 0xe4, 0x56, 0x8e, 0x46, 0x93, 0xfc, 0x31, 0xd8, - 0x59, 0x11, 0xaa, 0x05, 0xc6, 0x50, 0x79, 0xce, 0x04, 0x21, 0x39, 0xa5, 0x4e, 0x06, 0x83, 0x5b, - 0x45, 0x59, 0x6a, 0x7f, 0x1a, 0x17, 0xed, 0x35, 0x97, 0x17, 0xec, 0x53, 0x96, 0xfe, 0x08, 0x6a, - 0x8a, 0x65, 0x95, 0x53, 0xd6, 0xf8, 0x59, 0x5d, 0xfe, 0x3a, 0x1f, 0xef, 0xfd, 0x73, 0x0b, 0xe0, - 0x6c, 0x19, 0x09, 0xb6, 0xf8, 0x8d, 0xcb, 0x5e, 0x92, 0x47, 0xd0, 0x7b, 0xc6, 0xce, 0x69, 0xec, - 0x09, 0x7c, 0xaa, 0x49, 0x36, 0xc9, 0xf9, 0x04, 0x0b, 0xbe, 0x94, 0xac, 0x1f, 0x42, 0xeb, 0x84, - 0x5e, 0xbf, 0xda, 0xee, 0x53, 0xe8, 0x14, 0x38, 0x58, 0x6f, 0x71, 0x95, 0xd5, 0xf5, 0x16, 0xd7, - 0xd9, 0xfa, 0x21, 0xd4, 0x35, 0x33, 0xe7, 0xd7, 0xc0, 0x1c, 0x56, 0x60, 0xec, 0x9f, 0x43, 0x6f, - 0x85, 0x97, 0xf3, 0xf6, 0xf8, 0x39, 0x64, 0x23, 0x6f, 0x3f, 0x95, 0xaf, 0x9d, 0x22, 0x37, 0xe7, - 0x07, 0xea, 0x97, 0xd7, 0x26, 0xf2, 0x7e, 0x5e, 0x7c, 0x27, 0xe1, 0x13, 0xd5, 0x58, 0xa5, 0xcf, - 0x84, 0xbc, 0x07, 0x77, 0x36, 0x69, 0xd2, 0x10, 0xcc, 0x33, 0xe8, 0x5a, 0x08, 0xae, 0xd3, 0xeb, - 0x7b, 0x00, 0x19, 0x89, 0xe6, 0xed, 0x11, 0x1e, 0xab, 0xfc, 0xfa, 0x21, 0x40, 0x46, 0x8d, 0x0a, - 0x55, 0x45, 0x66, 0x55, 0xc3, 0x56, 0xe9, 0xf3, 0x11, 0x34, 0x53, 0x3a, 0xcb, 0xaf, 0x81, 0x13, - 0x14, 0xd9, 0xf1, 0xb3, 0x47, 0xbf, 0x1f, 0xcd, 0x5d, 0x71, 0x11, 0xcf, 0xc6, 0x76, 0xb0, 0xd8, - 0xbd, 0xa0, 0xd1, 0x85, 0x6b, 0x07, 0x3c, 0xdc, 0xbd, 0x92, 0x60, 0xda, 0x2d, 0xfc, 0xb4, 0x9a, - 0xd5, 0xf0, 0xa1, 0xf7, 0xe4, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x5b, 0x0c, 0x01, 0xf3, 0xcc, - 0x1a, 0x00, 0x00, + 0x0c, 0x33, 0x36, 0x68, 0xd1, 0x71, 0x2c, 0x27, 0x65, 0xa7, 0x68, 0x8a, 0x96, 0x19, 0x93, 0x36, + 0x6b, 0x09, 0xc7, 0x39, 0x55, 0xc1, 0x83, 0xdd, 0x21, 0xb8, 0xc5, 0xc5, 0xee, 0x66, 0x76, 0x96, + 0x22, 0xae, 0xf2, 0x16, 0x79, 0x8d, 0xdc, 0xe6, 0x2e, 0x77, 0x29, 0x57, 0xee, 0xf3, 0x0a, 0xb9, + 0xcc, 0x33, 0xa4, 0xa6, 0x67, 0xf6, 0x04, 0x80, 0x96, 0x5c, 0xe5, 0xdc, 0xcd, 0x74, 0xf7, 0x9c, + 0x7a, 0xbe, 0xfe, 0xba, 0x67, 0x17, 0xee, 0x47, 0xce, 0xd5, 0x5e, 0xe8, 0xc5, 0x73, 0xd7, 0xdf, + 0x0b, 0x67, 0x7b, 0x33, 0x6a, 0x5f, 0x31, 0xdf, 0x19, 0x87, 0x3c, 0x10, 0x01, 0x29, 0x87, 0xb3, + 0xc1, 0xc3, 0x79, 0x10, 0xcc, 0x3d, 0xb6, 0x87, 0x92, 0x59, 0x7c, 0xb1, 0x27, 0xdc, 0x05, 0x8b, + 0x04, 0x5d, 0x84, 0xca, 0x68, 0x30, 0x90, 0x33, 0x78, 0xc1, 0xdc, 0xb5, 0xa9, 0xb7, 0xe7, 0x3a, + 0xcc, 0x17, 0xae, 0x58, 0x6a, 0x9d, 0x91, 0xd7, 0xa9, 0x55, 0x94, 0xc6, 0xac, 0x43, 0xf5, 0x68, + 0x11, 0x8a, 0xa5, 0x39, 0x84, 0xda, 0xe7, 0x8c, 0x3a, 0x8c, 0x93, 0xbb, 0x50, 0xbb, 0xc4, 0x96, + 0x51, 0x1a, 0x56, 0x46, 0x4d, 0x4b, 0xf7, 0xcc, 0x3f, 0x00, 0x9c, 0xc9, 0x31, 0x47, 0x9c, 0x07, + 0x9c, 0xdc, 0x83, 0x06, 0xe3, 0x7c, 0x2a, 0x96, 0x21, 0x33, 0x4a, 0xc3, 0xd2, 0xa8, 0x63, 0xd5, + 0x19, 0xe7, 0x93, 0x65, 0xc8, 0xc8, 0xff, 0x81, 0x6c, 0x4e, 0x17, 0xd1, 0xdc, 0x28, 0x0f, 0x4b, + 0x72, 0x06, 0xc6, 0xf9, 0x69, 0x34, 0x4f, 0xc6, 0xd8, 0x81, 0xc3, 0x8c, 0xca, 0xb0, 0x34, 0xaa, + 0xe0, 0x98, 0xc3, 0xc0, 0x61, 0xe6, 0x5f, 0x4a, 0x50, 0x3d, 0xa3, 0xe2, 0x32, 0x22, 0x04, 0xb6, + 0x78, 0x10, 0x08, 0xbd, 0x38, 0xb6, 0xc9, 0x08, 0x7a, 0xb1, 0x4f, 0x63, 0x71, 0x29, 0x4f, 0x65, + 0x53, 0xc1, 0x1c, 0xa3, 0x8c, 0xea, 0x55, 0x31, 0x79, 0x13, 0x3a, 0x5e, 0x60, 0x53, 0x6f, 0x1a, + 0x89, 0x80, 0xd3, 0xb9, 0x5c, 0x47, 0xda, 0xb5, 0x51, 0x78, 0xae, 0x64, 0x64, 0x17, 0xfa, 0x11, + 0xa3, 0xde, 0xf4, 0x05, 0xa7, 0x61, 0x6a, 0xb8, 0xa5, 0x26, 0x94, 0x8a, 0x6f, 0x38, 0x0d, 0xb5, + 0xad, 0xf9, 0xf7, 0x1a, 0xd4, 0x2d, 0xf6, 0xa7, 0x98, 0x45, 0x82, 0x74, 0xa1, 0xec, 0x3a, 0x78, + 0xda, 0xa6, 0x55, 0x76, 0x1d, 0x32, 0x06, 0x62, 0xb1, 0xd0, 0x93, 0x4b, 0xbb, 0x81, 0x7f, 0xe8, + 0xc5, 0x91, 0x60, 0x5c, 0x9f, 0x79, 0x83, 0x86, 0x3c, 0x80, 0x66, 0x10, 0x32, 0x8e, 0x32, 0x74, + 0x40, 0xd3, 0xca, 0x04, 0xf2, 0xe0, 0x21, 0x15, 0x97, 0xc6, 0x16, 0x2a, 0xb0, 0x2d, 0x65, 0x0e, + 0x15, 0xd4, 0xa8, 0x2a, 0x99, 0x6c, 0x13, 0x13, 0x6a, 0x11, 0xb3, 0x39, 0x13, 0x46, 0x6d, 0x58, + 0x1a, 0xb5, 0xf6, 0x61, 0x1c, 0xce, 0xc6, 0xe7, 0x28, 0xb1, 0xb4, 0x86, 0x3c, 0x80, 0x2d, 0xe9, + 0x17, 0xa3, 0x8e, 0x16, 0x0d, 0x69, 0x71, 0x10, 0x8b, 0x4b, 0x0b, 0xa5, 0x64, 0x1f, 0xea, 0xea, + 0x4e, 0x23, 0xa3, 0x31, 0xac, 0x8c, 0x5a, 0xfb, 0x86, 0x34, 0xd0, 0xa7, 0x1c, 0x2b, 0x18, 0x44, + 0x47, 0xbe, 0xe0, 0x4b, 0x2b, 0x31, 0x24, 0x6f, 0x40, 0xdb, 0xf6, 0x5c, 0xe6, 0x8b, 0xa9, 0x08, + 0xae, 0x98, 0x6f, 0x34, 0x71, 0x47, 0x2d, 0x25, 0x9b, 0x48, 0x11, 0xd9, 0x87, 0xd7, 0xf2, 0x26, + 0x53, 0x6a, 0xdb, 0x2c, 0x8a, 0x02, 0x6e, 0x00, 0xda, 0xde, 0xc9, 0xd9, 0x1e, 0x68, 0x95, 0x9c, + 0xd6, 0x71, 0xa3, 0xd0, 0xa3, 0xcb, 0xa9, 0x4f, 0x17, 0xcc, 0x68, 0xa9, 0x69, 0xb5, 0xec, 0x4b, + 0xba, 0x60, 0xe4, 0x21, 0xb4, 0x16, 0x41, 0xec, 0x8b, 0x69, 0x18, 0xb8, 0xbe, 0x30, 0xda, 0x68, + 0x01, 0x28, 0x3a, 0x93, 0x12, 0xf2, 0x3a, 0xa8, 0x9e, 0x02, 0x63, 0x47, 0xf9, 0x15, 0x25, 0x08, + 0xc7, 0x47, 0xd0, 0x55, 0xea, 0x74, 0x3f, 0x5d, 0x34, 0xe9, 0xa0, 0x34, 0xdd, 0xc9, 0x7b, 0xd0, + 0x44, 0x3c, 0xb8, 0xfe, 0x45, 0x60, 0xf4, 0xd0, 0x6f, 0x77, 0x72, 0x6e, 0x91, 0x98, 0x38, 0xf6, + 0x2f, 0x02, 0xab, 0xf1, 0x42, 0xb7, 0xc8, 0xc7, 0x70, 0xbf, 0x70, 0x5e, 0xce, 0x16, 0xd4, 0xf5, + 0x5d, 0x7f, 0x3e, 0x8d, 0x23, 0x16, 0x19, 0xdb, 0x88, 0x70, 0x23, 0x77, 0x6a, 0x2b, 0x31, 0xf8, + 0x3a, 0x62, 0x11, 0xb9, 0x0f, 0x4d, 0x15, 0xa4, 0x53, 0xd7, 0x31, 0xfa, 0xb8, 0xa5, 0x86, 0x12, + 0x1c, 0x3b, 0xe4, 0x2d, 0xe8, 0x85, 0x81, 0xe7, 0xda, 0xcb, 0x69, 0x70, 0xcd, 0x38, 0x77, 0x1d, + 0x66, 0x90, 0x61, 0x69, 0xd4, 0xb0, 0xba, 0x4a, 0xfc, 0x95, 0x96, 0x6e, 0x0a, 0x8d, 0x3b, 0x68, + 0xb8, 0x16, 0x1a, 0x63, 0x00, 0x3b, 0xf0, 0x7d, 0x66, 0x23, 0xfc, 0x76, 0xf0, 0x84, 0x5d, 0x79, + 0xc2, 0xc3, 0x54, 0x6a, 0xe5, 0x2c, 0x06, 0x9f, 0x41, 0x3b, 0x0f, 0x05, 0xb2, 0x0d, 0x95, 0x2b, + 0xb6, 0xd4, 0xf0, 0x97, 0x4d, 0x32, 0x84, 0xea, 0x35, 0xf5, 0x62, 0x86, 0x90, 0xd7, 0x40, 0x54, + 0x43, 0x2c, 0xa5, 0xf8, 0x45, 0xf9, 0x69, 0xc9, 0xfc, 0x77, 0x15, 0xb6, 0x24, 0xf8, 0xc8, 0x07, + 0xd0, 0xf1, 0x18, 0x8d, 0xd8, 0x34, 0x08, 0xe5, 0x02, 0x11, 0x4e, 0xd5, 0xda, 0xdf, 0x96, 0xc3, + 0x4e, 0xa4, 0xe2, 0x2b, 0x25, 0xb7, 0xda, 0x5e, 0xae, 0x27, 0x43, 0xda, 0xf5, 0x05, 0xe3, 0x3e, + 0xf5, 0xa6, 0x18, 0x0c, 0x2a, 0xc0, 0xda, 0x89, 0xf0, 0x99, 0x0c, 0x8a, 0x55, 0x1c, 0x55, 0xd6, + 0x71, 0x34, 0x80, 0x06, 0xfa, 0xce, 0x65, 0x91, 0x0e, 0xf6, 0xb4, 0x4f, 0xf6, 0xa1, 0xb1, 0x60, + 0x82, 0xea, 0x58, 0x93, 0x21, 0x71, 0x37, 0x89, 0x99, 0xf1, 0xa9, 0x56, 0xa8, 0x80, 0x48, 0xed, + 0xd6, 0x22, 0xa2, 0xb6, 0x1e, 0x11, 0x03, 0x68, 0xa4, 0xa0, 0xab, 0xab, 0x1b, 0x4e, 0xfa, 0x92, + 0x66, 0x43, 0xc6, 0xdd, 0xc0, 0x31, 0x1a, 0x08, 0x14, 0xdd, 0x93, 0x24, 0xe9, 0xc7, 0x0b, 0x05, + 0xa1, 0xa6, 0x22, 0x49, 0x3f, 0x5e, 0xac, 0x23, 0x06, 0x56, 0x10, 0xf3, 0x13, 0xa8, 0x52, 0xcf, + 0xa5, 0x11, 0x86, 0x90, 0xbc, 0x59, 0xcd, 0xf7, 0xe3, 0x03, 0x29, 0xb5, 0x94, 0x92, 0xbc, 0x0f, + 0x9d, 0x39, 0x0f, 0xe2, 0x70, 0x8a, 0x5d, 0x16, 0x19, 0x6d, 0x3c, 0xed, 0xaa, 0x75, 0x1b, 0x8d, + 0x0e, 0x94, 0x8d, 0x8c, 0xc0, 0x59, 0x10, 0xfb, 0xce, 0xd4, 0x76, 0x1d, 0x1e, 0x19, 0x1d, 0x74, + 0x1e, 0xa0, 0xe8, 0x50, 0x4a, 0x64, 0x88, 0xa9, 0x10, 0x48, 0x1d, 0xdc, 0x45, 0x9b, 0x0e, 0x4a, + 0xcf, 0x12, 0x2f, 0xff, 0x14, 0xfa, 0x49, 0x62, 0xca, 0x2c, 0x7b, 0x68, 0xb9, 0x9d, 0x28, 0x52, + 0xe3, 0x11, 0x6c, 0xb3, 0x1b, 0x49, 0xa1, 0xae, 0x98, 0x2e, 0xe8, 0xcd, 0x54, 0x08, 0x4f, 0x87, + 0x54, 0x37, 0x91, 0x9f, 0xd2, 0x9b, 0x89, 0xf0, 0x64, 0xfc, 0xab, 0xd5, 0x31, 0xfe, 0xfb, 0x98, + 0x8c, 0x9a, 0x28, 0xc1, 0xf8, 0xdf, 0x85, 0xbe, 0x1f, 0x4c, 0x1d, 0x76, 0x41, 0x63, 0x4f, 0xa8, + 0x75, 0x97, 0x3a, 0x98, 0x7a, 0x7e, 0xf0, 0x4c, 0xc9, 0x71, 0xd9, 0xe5, 0xe0, 0x97, 0xd0, 0x29, + 0x5c, 0xf7, 0x06, 0xd0, 0xef, 0xe4, 0x41, 0xdf, 0xcc, 0x03, 0xfd, 0x9f, 0x5b, 0x00, 0x78, 0xef, + 0x6a, 0xe8, 0x6a, 0xb6, 0xc8, 0x83, 0xa1, 0xbc, 0x01, 0x0c, 0x94, 0x33, 0x5f, 0x68, 0xe0, 0xea, + 0xde, 0xf7, 0x62, 0x36, 0xc9, 0x17, 0xd5, 0x5c, 0xbe, 0x78, 0x07, 0xb6, 0x24, 0x3e, 0x8d, 0x5a, + 0x46, 0xeb, 0xd9, 0x8e, 0x10, 0xc9, 0x0a, 0xc5, 0x68, 0xb5, 0x16, 0x34, 0xf5, 0xf5, 0xa0, 0xc9, + 0xa3, 0xb1, 0x51, 0x44, 0xe3, 0x9b, 0xd0, 0xb1, 0x39, 0xc3, 0xdc, 0x35, 0x95, 0xc5, 0x88, 0x46, + 0x6b, 0x3b, 0x11, 0x4e, 0xdc, 0x05, 0x93, 0xfe, 0x93, 0x17, 0x07, 0xa8, 0x92, 0xcd, 0x8d, 0xf7, + 0xda, 0xda, 0x78, 0xaf, 0x58, 0x09, 0x78, 0x4c, 0x33, 0x3e, 0xb6, 0x73, 0x51, 0xd3, 0x29, 0x44, + 0x4d, 0x21, 0x34, 0xba, 0x2b, 0xa1, 0xb1, 0x82, 0xdf, 0xde, 0x1a, 0x7e, 0xdf, 0x80, 0xb6, 0x74, + 0x40, 0x14, 0x52, 0x9b, 0xc9, 0x09, 0xb6, 0x95, 0x23, 0x52, 0xd9, 0xb1, 0x83, 0xd1, 0x1e, 0xcf, + 0x66, 0xcb, 0xcb, 0xc0, 0x63, 0x19, 0x61, 0xb7, 0x52, 0xd9, 0xb1, 0x23, 0xf7, 0x8b, 0x08, 0x24, + 0x88, 0x40, 0x6c, 0x0f, 0x3e, 0x84, 0x66, 0xea, 0xf5, 0x1f, 0x04, 0xa6, 0xbf, 0x96, 0xa0, 0x9d, + 0x27, 0x45, 0x39, 0x78, 0x32, 0x39, 0xc1, 0xc1, 0x15, 0x4b, 0x36, 0x65, 0x39, 0xc1, 0x99, 0xcf, + 0x5e, 0xd0, 0x99, 0xa7, 0x26, 0x68, 0x58, 0x99, 0x40, 0x6a, 0x5d, 0xdf, 0xe6, 0x6c, 0x91, 0xa0, + 0xaa, 0x62, 0x65, 0x02, 0xf2, 0x11, 0x80, 0x1b, 0x45, 0x31, 0x53, 0x37, 0xb7, 0x85, 0x94, 0x31, + 0x18, 0xab, 0x1a, 0x73, 0x9c, 0xd4, 0x98, 0xe3, 0x49, 0x52, 0x63, 0x5a, 0x4d, 0xb4, 0xc6, 0x2b, + 0xbd, 0x0b, 0x35, 0x79, 0x41, 0x93, 0x13, 0x44, 0x5e, 0xc5, 0xd2, 0x3d, 0xf3, 0xcf, 0x50, 0x53, + 0x55, 0xc8, 0xff, 0x94, 0xe8, 0xef, 0x41, 0x43, 0xcd, 0xed, 0x3a, 0x3a, 0x56, 0xea, 0xd8, 0x3f, + 0x76, 0xcc, 0xef, 0xca, 0xd0, 0xb0, 0x58, 0x14, 0x06, 0x7e, 0xc4, 0x72, 0x55, 0x52, 0xe9, 0xa5, + 0x55, 0x52, 0x79, 0x63, 0x95, 0x94, 0xd4, 0x5e, 0x95, 0x5c, 0xed, 0x35, 0x80, 0x06, 0x67, 0x8e, + 0xcb, 0x99, 0x2d, 0x74, 0x9d, 0x96, 0xf6, 0xa5, 0xee, 0x05, 0xe5, 0x32, 0xbd, 0x47, 0x98, 0x43, + 0x9a, 0x56, 0xda, 0x27, 0x4f, 0xf2, 0xc5, 0x85, 0x2a, 0xdb, 0x76, 0x54, 0x71, 0xa1, 0xb6, 0xbb, + 0xa1, 0xba, 0x78, 0x3f, 0x2b, 0xd2, 0xea, 0x18, 0xcd, 0xf7, 0xf2, 0x03, 0x36, 0x57, 0x69, 0x3f, + 0x5a, 0xce, 0xfe, 0xae, 0x0c, 0xdb, 0xab, 0x7b, 0xdb, 0x80, 0xc0, 0x1d, 0xa8, 0xaa, 0xdc, 0xa7, + 0xe1, 0x2b, 0xd6, 0xb2, 0x5e, 0x65, 0x85, 0xe8, 0x7e, 0xb5, 0x4a, 0x1a, 0x2f, 0x87, 0x5e, 0x91, + 0x50, 0xde, 0x86, 0x6d, 0xe9, 0xa2, 0x90, 0x39, 0x59, 0x3d, 0xa7, 0x18, 0xb0, 0xa7, 0xe5, 0x69, + 0x45, 0xb7, 0x0b, 0xfd, 0xc4, 0x34, 0xe3, 0x86, 0x5a, 0xc1, 0xf6, 0x28, 0xa1, 0x88, 0xbb, 0x50, + 0xbb, 0x08, 0xf8, 0x82, 0x0a, 0x4d, 0x82, 0xba, 0x57, 0x20, 0x39, 0x64, 0xdb, 0x86, 0xc2, 0x64, + 0x22, 0x94, 0x6f, 0x16, 0x49, 0x3e, 0xe9, 0x7b, 0x02, 0x59, 0xb0, 0x61, 0x35, 0x92, 0x77, 0x84, + 0xf9, 0x5b, 0xe8, 0xad, 0x94, 0x90, 0x1b, 0x1c, 0x99, 0x2d, 0x5f, 0x2e, 0x2c, 0x5f, 0x98, 0xb9, + 0xb2, 0x32, 0xf3, 0xef, 0xa0, 0xff, 0x39, 0xf5, 0x1d, 0x8f, 0xe9, 0xf9, 0x0f, 0xf8, 0x3c, 0x92, + 0xc9, 0x50, 0xbf, 0x68, 0xa6, 0x3a, 0xfb, 0x74, 0xac, 0xa6, 0x96, 0x1c, 0x3b, 0xe4, 0x11, 0xd4, + 0xb9, 0xb2, 0xd6, 0x00, 0x68, 0xe5, 0x6a, 0x5c, 0x2b, 0xd1, 0x99, 0xdf, 0x02, 0x29, 0x4c, 0x2d, + 0x1f, 0x33, 0x4b, 0x32, 0x92, 0xe8, 0x57, 0xa0, 0xd0, 0x51, 0xd5, 0xce, 0x63, 0xd2, 0x4a, 0xb5, + 0x64, 0x08, 0x15, 0xc6, 0xb9, 0x5e, 0x02, 0x8b, 0xcc, 0xec, 0xe9, 0x68, 0x49, 0x95, 0xf9, 0x33, + 0xe8, 0x9f, 0x87, 0xcc, 0x76, 0xa9, 0x87, 0xcf, 0x3e, 0xb5, 0xc0, 0x43, 0xa8, 0x4a, 0x27, 0x27, + 0x84, 0xd1, 0xc4, 0x81, 0xa8, 0x56, 0x72, 0xf3, 0x5b, 0x30, 0xd4, 0xbe, 0x8e, 0x6e, 0xdc, 0x48, + 0x30, 0xdf, 0x66, 0x87, 0x97, 0xcc, 0xbe, 0xfa, 0x11, 0x4f, 0x7e, 0x0d, 0xf7, 0x36, 0xad, 0x90, + 0xec, 0xaf, 0x65, 0xcb, 0xde, 0xf4, 0x42, 0xe6, 0x0e, 0x5c, 0xa3, 0x61, 0x01, 0x8a, 0x3e, 0x93, + 0x12, 0x79, 0x8f, 0x4c, 0x8e, 0x8b, 0x34, 0x1f, 0xeb, 0x5e, 0xe2, 0x8f, 0xca, 0xed, 0xfe, 0xf8, + 0x5b, 0x09, 0x9a, 0xe7, 0x4c, 0xc4, 0x21, 0x9e, 0xe5, 0x3e, 0x34, 0x67, 0x3c, 0xb8, 0x62, 0x3c, + 0x3b, 0x4a, 0x43, 0x09, 0x8e, 0x1d, 0xf2, 0x04, 0x6a, 0x87, 0x81, 0x7f, 0xe1, 0xce, 0xf1, 0x11, + 0xac, 0x89, 0x21, 0x1d, 0x3b, 0x56, 0x3a, 0x45, 0x0c, 0xda, 0x90, 0x0c, 0xa1, 0xa5, 0x3f, 0x29, + 0x7c, 0xfd, 0xf5, 0xf1, 0xb3, 0xa4, 0x3a, 0xce, 0x89, 0x06, 0x1f, 0x41, 0x2b, 0x37, 0xf0, 0x07, + 0xa5, 0xaa, 0xff, 0x07, 0xc0, 0xd5, 0x95, 0x8f, 0xb6, 0xd5, 0x51, 0xf5, 0x48, 0x79, 0xb4, 0x87, + 0xd0, 0x94, 0x85, 0x98, 0x52, 0x27, 0x49, 0xb2, 0x94, 0x25, 0x49, 0xf3, 0x11, 0xf4, 0x8f, 0xfd, + 0x6b, 0xea, 0xb9, 0x0e, 0x15, 0xec, 0x0b, 0xb6, 0x44, 0x17, 0xac, 0xed, 0xc0, 0x3c, 0x87, 0xb6, + 0x7e, 0x95, 0xbf, 0xd2, 0x1e, 0xdb, 0x7a, 0x8f, 0xdf, 0x1f, 0x44, 0x6f, 0x43, 0x4f, 0x4f, 0x7a, + 0xe2, 0xea, 0x10, 0x92, 0x35, 0x06, 0x67, 0x17, 0xee, 0x8d, 0x9e, 0x5a, 0xf7, 0xcc, 0xa7, 0xb0, + 0x9d, 0x33, 0x4d, 0x8f, 0x73, 0xc5, 0x96, 0x51, 0xf2, 0xb5, 0x42, 0xb6, 0x13, 0x0f, 0x94, 0x33, + 0x0f, 0x98, 0xd0, 0xd5, 0x23, 0x9f, 0x33, 0x71, 0xcb, 0xe9, 0xbe, 0x48, 0x37, 0xf2, 0x9c, 0xe9, + 0xc9, 0x1f, 0x43, 0x95, 0xc9, 0x93, 0xe6, 0xf3, 0x67, 0xde, 0x03, 0x96, 0x52, 0x6f, 0x58, 0xf0, + 0x69, 0xba, 0xe0, 0x59, 0xac, 0x16, 0x7c, 0xc5, 0xb9, 0xcc, 0x37, 0xd3, 0x6d, 0x9c, 0xc5, 0xe2, + 0xb6, 0x1b, 0x7d, 0x04, 0x7d, 0x6d, 0xf4, 0x8c, 0x79, 0x4c, 0xb0, 0x5b, 0x8e, 0xf4, 0x18, 0x48, + 0xc1, 0xec, 0xb6, 0xe9, 0x1e, 0x40, 0x63, 0x32, 0x39, 0x49, 0xb5, 0x45, 0x6e, 0x34, 0x3f, 0x86, + 0xfe, 0x79, 0xec, 0x04, 0x67, 0xdc, 0xbd, 0x76, 0x3d, 0x36, 0x57, 0x8b, 0x25, 0xc5, 0x6f, 0x29, + 0x57, 0xfc, 0x6e, 0xcc, 0x46, 0xe6, 0x08, 0x48, 0x61, 0x78, 0x7a, 0x6f, 0x51, 0xec, 0x04, 0x3a, + 0x84, 0xb1, 0x6d, 0x8e, 0xa0, 0x3d, 0xa1, 0xb2, 0xd8, 0x70, 0x94, 0x8d, 0x01, 0x75, 0xa1, 0xfa, + 0xda, 0x2c, 0xe9, 0x9a, 0xfb, 0xb0, 0x73, 0x48, 0xed, 0x4b, 0xd7, 0x9f, 0x3f, 0x73, 0x23, 0x59, + 0x6d, 0xe9, 0x11, 0x03, 0x68, 0x38, 0x5a, 0xa0, 0x87, 0xa4, 0x7d, 0xf3, 0x5d, 0x78, 0x2d, 0xf7, + 0x49, 0xe8, 0x5c, 0xd0, 0xc4, 0x1f, 0x3b, 0x50, 0x8d, 0x64, 0x0f, 0x47, 0x54, 0x2d, 0xd5, 0x31, + 0xbf, 0x84, 0x9d, 0x7c, 0x02, 0x96, 0xb5, 0x4f, 0x72, 0x70, 0xac, 0x4a, 0x4a, 0xb9, 0xaa, 0x44, + 0xfb, 0xac, 0x9c, 0xe5, 0x93, 0x6d, 0xa8, 0xfc, 0xfa, 0x9b, 0x89, 0x06, 0xbb, 0x6c, 0x9a, 0x7f, + 0x94, 0xcb, 0x17, 0xe7, 0x53, 0xcb, 0x17, 0x4a, 0x93, 0xd2, 0x2b, 0x95, 0x26, 0xeb, 0x78, 0x7b, + 0x17, 0xfa, 0xa7, 0x5e, 0x60, 0x5f, 0x1d, 0xf9, 0x39, 0x6f, 0x18, 0x50, 0x67, 0x7e, 0xde, 0x19, + 0x49, 0xd7, 0x7c, 0x0b, 0x7a, 0x27, 0x81, 0x4d, 0xbd, 0xd3, 0x20, 0xf6, 0x45, 0xea, 0x05, 0xfc, + 0x46, 0xa7, 0x4d, 0x55, 0xc7, 0x7c, 0x17, 0xba, 0x3a, 0x45, 0xfb, 0x17, 0x41, 0xc2, 0x8c, 0x59, + 0x32, 0x2f, 0x15, 0x0b, 0x7d, 0xf3, 0x04, 0x7a, 0x99, 0xb9, 0x9a, 0xf7, 0x2d, 0xa8, 0x29, 0xb5, + 0x3e, 0x5b, 0x2f, 0x7d, 0xe9, 0x2a, 0x4b, 0x4b, 0xab, 0x37, 0x1c, 0x6a, 0x01, 0xdd, 0x33, 0xfc, + 0x56, 0x7a, 0xe4, 0x5f, 0xab, 0xc9, 0x8e, 0x81, 0xa8, 0xaf, 0xa7, 0x53, 0xe6, 0x5f, 0xbb, 0x3c, + 0xf0, 0xb1, 0xb8, 0x2e, 0xe9, 0x12, 0x26, 0x99, 0x38, 0x1d, 0x94, 0x58, 0x58, 0xfd, 0x70, 0x55, + 0xb4, 0xd1, 0x87, 0x90, 0x7d, 0x89, 0x91, 0xa9, 0x86, 0xb3, 0x45, 0x20, 0xd8, 0x94, 0x3a, 0x4e, + 0x12, 0x2d, 0xa0, 0x44, 0x07, 0x8e, 0xc3, 0xf7, 0xff, 0x53, 0x86, 0xfa, 0xa7, 0x8a, 0xc0, 0xc9, + 0x27, 0xd0, 0x29, 0xa4, 0x6b, 0xf2, 0x1a, 0x96, 0x75, 0xab, 0xc5, 0xc1, 0xe0, 0xee, 0x9a, 0x58, + 0x9d, 0xeb, 0x3d, 0x68, 0xe7, 0x93, 0x31, 0xc1, 0xc4, 0x8b, 0xdf, 0x85, 0x07, 0x38, 0xd3, 0x7a, + 0xa6, 0x3e, 0x87, 0x9d, 0x4d, 0x69, 0x92, 0x3c, 0xc8, 0x56, 0x58, 0x4f, 0xd1, 0x83, 0xd7, 0x6f, + 0xd3, 0x26, 0xe9, 0xb5, 0x7e, 0xe8, 0x31, 0xea, 0xc7, 0x61, 0x7e, 0x07, 0x59, 0x93, 0x3c, 0x81, + 0x4e, 0x21, 0x51, 0xa8, 0x73, 0xae, 0xe5, 0x8e, 0xfc, 0x90, 0xc7, 0x50, 0xc5, 0xe4, 0x44, 0x3a, + 0x85, 0x2c, 0x39, 0xe8, 0xa6, 0x5d, 0xb5, 0xf6, 0x10, 0xb6, 0xf0, 0x6b, 0x41, 0x6e, 0x61, 0x1c, + 0x91, 0x66, 0xae, 0xfd, 0x7f, 0x95, 0xa0, 0x9e, 0x7c, 0x41, 0x7e, 0x02, 0x5b, 0x32, 0x07, 0x90, + 0x3b, 0x39, 0x1a, 0x4d, 0xf2, 0xc7, 0x60, 0x67, 0x45, 0xa8, 0x16, 0x18, 0x43, 0xe5, 0x39, 0x13, + 0x84, 0xe4, 0x94, 0x3a, 0x19, 0x0c, 0xee, 0x14, 0x65, 0xa9, 0xfd, 0x59, 0x5c, 0xb4, 0xd7, 0x5c, + 0x5e, 0xb0, 0x4f, 0x59, 0xfa, 0x43, 0xa8, 0x29, 0x96, 0x55, 0x4e, 0x59, 0xe3, 0x67, 0x75, 0xf9, + 0xeb, 0x7c, 0xbc, 0xff, 0x8f, 0x2d, 0x80, 0xf3, 0x65, 0x24, 0xd8, 0xe2, 0x37, 0x2e, 0x7b, 0x41, + 0x76, 0xa1, 0xa7, 0xbf, 0x89, 0xe0, 0x53, 0x4d, 0xb2, 0x49, 0xce, 0x27, 0x58, 0xf0, 0xa5, 0x64, + 0xfd, 0x18, 0x5a, 0xa7, 0xf4, 0xe6, 0xe5, 0x76, 0x9f, 0x40, 0xa7, 0xc0, 0xc1, 0x7a, 0x8b, 0xab, + 0xac, 0xae, 0xb7, 0xb8, 0xce, 0xd6, 0x8f, 0xa1, 0xae, 0x99, 0x39, 0xbf, 0x06, 0xe6, 0xb0, 0x02, + 0x63, 0xff, 0x1c, 0x7a, 0x2b, 0xbc, 0x9c, 0xb7, 0xc7, 0xcf, 0x21, 0x1b, 0x79, 0xfb, 0xa9, 0x7c, + 0xed, 0x14, 0xb9, 0x39, 0x3f, 0x50, 0xbf, 0xbc, 0x36, 0x91, 0xf7, 0xf3, 0xe2, 0x3b, 0x09, 0x9f, + 0xa8, 0xc6, 0x2a, 0x7d, 0x26, 0xe4, 0x3d, 0xb8, 0xb7, 0x49, 0x93, 0x86, 0x60, 0x9e, 0x41, 0xd7, + 0x42, 0x70, 0x9d, 0x5e, 0xdf, 0x01, 0xc8, 0x48, 0x34, 0x6f, 0x8f, 0xf0, 0x58, 0xe5, 0xd7, 0x0f, + 0x00, 0x32, 0x6a, 0x54, 0xa8, 0x2a, 0x32, 0xab, 0x1a, 0xb6, 0x4a, 0x9f, 0xbb, 0xd0, 0x4c, 0xe9, + 0x2c, 0xbf, 0x06, 0x4e, 0x50, 0x64, 0xc7, 0x4f, 0x77, 0x7f, 0x3f, 0x9a, 0xbb, 0xe2, 0x32, 0x9e, + 0x8d, 0xed, 0x60, 0xb1, 0x77, 0x49, 0xa3, 0x4b, 0xd7, 0x0e, 0x78, 0xb8, 0x77, 0x2d, 0xc1, 0xb4, + 0x57, 0xf8, 0xc1, 0x35, 0xab, 0xe1, 0x43, 0xef, 0xfd, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xbd, + 0xc6, 0x6e, 0xfa, 0xf8, 0x1a, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/vendor/github.com/hashicorp/vault/sdk/plugin/pb/backend.proto b/vendor/github.com/hashicorp/vault/sdk/plugin/pb/backend.proto index 65f4ef80dc..ca86c9c139 100644 --- a/vendor/github.com/hashicorp/vault/sdk/plugin/pb/backend.proto +++ b/vendor/github.com/hashicorp/vault/sdk/plugin/pb/backend.proto @@ -210,6 +210,9 @@ message Auth { // TokenType is the type of token being requested uint32 token_type = 17; + + // Whether the default policy should be added automatically by core + bool no_default_policy = 18; } message TokenEntry { diff --git a/vendor/github.com/hashicorp/vault/sdk/plugin/pb/translation.go b/vendor/github.com/hashicorp/vault/sdk/plugin/pb/translation.go index 3ef85a0821..23c7e718cb 100644 --- a/vendor/github.com/hashicorp/vault/sdk/plugin/pb/translation.go +++ b/vendor/github.com/hashicorp/vault/sdk/plugin/pb/translation.go @@ -507,6 +507,7 @@ func LogicalAuthToProtoAuth(a *logical.Auth) (*Auth, error) { Policies: a.Policies, TokenPolicies: a.TokenPolicies, IdentityPolicies: a.IdentityPolicies, + NoDefaultPolicy: a.NoDefaultPolicy, Metadata: a.Metadata, ClientToken: a.ClientToken, Accessor: a.Accessor, @@ -554,6 +555,7 @@ func ProtoAuthToLogicalAuth(a *Auth) (*logical.Auth, error) { Policies: a.Policies, TokenPolicies: a.TokenPolicies, IdentityPolicies: a.IdentityPolicies, + NoDefaultPolicy: a.NoDefaultPolicy, Metadata: a.Metadata, ClientToken: a.ClientToken, Accessor: a.Accessor, diff --git a/vendor/modules.txt b/vendor/modules.txt index 9b3f1816f8..5fb41b6565 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -386,6 +386,7 @@ github.com/hashicorp/vault/sdk/helper/pathmanager github.com/hashicorp/vault/sdk/database/helper/connutil github.com/hashicorp/vault/sdk/helper/license github.com/hashicorp/vault/sdk/helper/pluginutil +github.com/hashicorp/vault/sdk/helper/tokenhelper github.com/hashicorp/vault/sdk/plugin/pb github.com/hashicorp/vault/sdk/helper/kdf github.com/hashicorp/vault/sdk/plugin/mock From 906f4cec4895f89e4ec2ab46d63eeb8f4149d881 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 14 Jun 2019 12:53:00 -0400 Subject: [PATCH 14/20] Prevent entity alias creation when entity is in different NS than mount (#943) (#6886) --- vault/identity_store_aliases.go | 398 ++++++++++++++++---------- vault/identity_store_aliases_test.go | 4 +- vault/identity_store_entities.go | 1 + vault/identity_store_group_aliases.go | 15 +- vault/identity_store_groups.go | 1 + vault/identity_store_upgrade.go | 4 +- vault/identity_store_util.go | 36 ++- 7 files changed, 290 insertions(+), 169 deletions(-) diff --git a/vault/identity_store_aliases.go b/vault/identity_store_aliases.go index 97cd9a5cce..f51f2d2e67 100644 --- a/vault/identity_store_aliases.go +++ b/vault/identity_store_aliases.go @@ -46,7 +46,7 @@ This field is deprecated, use canonical_id.`, }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: i.handleAliasUpdateCommon(), + logical.UpdateOperation: i.handleAliasCreateUpdate(), }, HelpSynopsis: strings.TrimSpace(aliasHelp["alias"][0]), @@ -79,7 +79,7 @@ This field is deprecated, use canonical_id.`, }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: i.handleAliasUpdateCommon(), + logical.UpdateOperation: i.handleAliasCreateUpdate(), logical.ReadOperation: i.pathAliasIDRead(), logical.DeleteOperation: i.pathAliasIDDelete(), }, @@ -99,31 +99,25 @@ This field is deprecated, use canonical_id.`, } } -// handleAliasUpdateCommon is used to update an alias -func (i *IdentityStore) handleAliasUpdateCommon() framework.OperationFunc { +// handleAliasCreateUpdate is used to create or update an alias +func (i *IdentityStore) handleAliasCreateUpdate() framework.OperationFunc { return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { var err error - var alias *identity.Alias - var entity *identity.Entity - var previousEntity *identity.Entity - i.lock.Lock() - defer i.lock.Unlock() - - // Check for update or create - aliasID := d.Get("id").(string) - if aliasID != "" { - alias, err = i.MemDBAliasByID(aliasID, true, false) - if err != nil { - return nil, err - } - if alias == nil { - return logical.ErrorResponse("invalid alias id"), nil - } - } else { - alias = &identity.Alias{} + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err } + // Get alias name, if any + name := d.Get("name").(string) + + // Get mount accessor, if any + mountAccessor := d.Get("mount_accessor").(string) + + // Get ID, if any + id := d.Get("id").(string) + // Get entity id canonicalID := d.Get("canonical_id").(string) if canonicalID == "" { @@ -131,155 +125,252 @@ func (i *IdentityStore) handleAliasUpdateCommon() framework.OperationFunc { canonicalID = d.Get("entity_id").(string) } - // Get alias name - if aliasName := d.Get("name").(string); aliasName == "" { - if alias.Name == "" { - return logical.ErrorResponse("missing alias name"), nil - } - } else { - alias.Name = aliasName - } + i.lock.Lock() + defer i.lock.Unlock() - // Get mount accessor - if mountAccessor := d.Get("mount_accessor").(string); mountAccessor == "" { - if alias.MountAccessor == "" { - return logical.ErrorResponse("missing mount_accessor"), nil - } - } else { - alias.MountAccessor = mountAccessor - } - - mountValidationResp := i.core.router.validateMountByAccessor(alias.MountAccessor) - if mountValidationResp == nil { - return logical.ErrorResponse(fmt.Sprintf("invalid mount accessor %q", alias.MountAccessor)), nil - } - if mountValidationResp.MountLocal { - return logical.ErrorResponse(fmt.Sprintf("mount_accessor %q is of a local mount", alias.MountAccessor)), nil - } - - // Verify that the combination of alias name and mount is not - // already tied to a different alias - aliasByFactors, err := i.MemDBAliasByFactors(mountValidationResp.MountAccessor, alias.Name, false, false) - if err != nil { - return nil, err - } - if aliasByFactors != nil { - // If it's a create we won't have an alias ID so this will correctly - // bail. If it's an update alias will be the same as aliasbyfactors so - // we don't need to transfer any info over - if aliasByFactors.ID != alias.ID { - return logical.ErrorResponse("combination of mount and alias name is already in use"), nil - } - - // Fetch the entity to which the alias is tied. We don't need to append - // here, so the only further checking is whether the canonical ID is - // different - entity, err = i.MemDBEntityByAliasID(alias.ID, true) - if err != nil { - return nil, err - } - if entity == nil { - return nil, fmt.Errorf("existing alias is not associated with an entity") - } - } else if alias.ID != "" { - // This is an update, not a create; if we have an associated entity - // already, load it - entity, err = i.MemDBEntityByAliasID(alias.ID, true) - if err != nil { - return nil, err - } - } - - resp := &logical.Response{} - - // If we found an existing alias we won't hit this condition because - // canonicalID being empty will result in nil being returned in the block - // above, so in this case we know that creating a new entity is the right - // thing. - if canonicalID == "" { - entity = &identity.Entity{ - Aliases: []*identity.Alias{ - alias, - }, - } - } else { - // If we can look up by the given canonical ID, see if this is a - // transfer; otherwise if we found no previous entity but we find one - // here, use it. - canonicalEntity, err := i.MemDBEntityByID(canonicalID, true) - if err != nil { - return nil, err - } - if canonicalEntity == nil { - return logical.ErrorResponse("invalid canonical ID"), nil - } - if entity == nil { - // If entity is nil, we didn't find a previous alias from factors, - // so append to this entity - entity = canonicalEntity - entity.Aliases = append(entity.Aliases, alias) - } else if entity.ID != canonicalEntity.ID { - // In this case we found an entity from alias factors or given - // alias ID but it's not the same, so it's a migration - previousEntity = entity - entity = canonicalEntity - - for aliasIndex, item := range previousEntity.Aliases { - if item.ID == alias.ID { - previousEntity.Aliases = append(previousEntity.Aliases[:aliasIndex], previousEntity.Aliases[aliasIndex+1:]...) - break - } + // This block is run if they provided an ID + { + // If they provide an ID it must be an update. Find the alias, perform + // due diligence, call the update function. + if id != "" { + alias, err := i.MemDBAliasByID(id, true, false) + if err != nil { + return nil, err + } + if alias == nil { + return logical.ErrorResponse("invalid alias ID provided"), nil + } + if alias.NamespaceID != ns.ID { + return logical.ErrorResponse("cannot modify aliases across namespaces"), logical.ErrPermissionDenied } - entity.Aliases = append(entity.Aliases, alias) - resp.AddWarning(fmt.Sprintf("alias is being transferred from entity %q to %q", previousEntity.ID, entity.ID)) + switch { + case mountAccessor == "" && name == "": + // Just a canonical ID update, maybe + if canonicalID == "" { + // Nothing to do, so be idempotent + return nil, nil + } + + name = alias.Name + mountAccessor = alias.MountAccessor + + case mountAccessor == "": + // No change to mount accessor + mountAccessor = alias.MountAccessor + + case name == "": + // No change to mount name + name = alias.Name + + default: + // Both provided + } + + return i.handleAliasUpdate(ctx, req, canonicalID, name, mountAccessor, alias) } } - // ID creation and other validations; This is more useful for new entities - // and may not perform anything for the existing entities. Placing the - // check here to make the flow common for both new and existing entities. - err = i.sanitizeEntity(ctx, entity) + // If they didn't provide an ID, we must have both accessor and name provided + if mountAccessor == "" || name == "" { + return logical.ErrorResponse("'id' or 'mount_accessor' and 'name' must be provided"), nil + } + + // Look up the alias by factors; if it's found it's an update + mountEntry := i.core.router.MatchingMountByAccessor(mountAccessor) + if mountEntry == nil { + return logical.ErrorResponse(fmt.Sprintf("invalid mount accessor %q", mountAccessor)), nil + } + if mountEntry.Local { + return logical.ErrorResponse(fmt.Sprintf("mount accessor %q is of a local mount", mountAccessor)), nil + } + if mountEntry.NamespaceID != ns.ID { + return logical.ErrorResponse("matching mount is in a different namespace than request"), logical.ErrPermissionDenied + } + alias, err := i.MemDBAliasByFactors(mountAccessor, name, false, false) if err != nil { return nil, err } - - // Explicitly set to empty as in the past we incorrectly saved it - alias.MountPath = "" - alias.MountType = "" - - // Set the canonical ID in the alias index. This should be done after - // sanitizing entity. - alias.CanonicalID = entity.ID - - // ID creation and other validations - err = i.sanitizeAlias(ctx, alias) - if err != nil { - return nil, err - } - - for index, item := range entity.Aliases { - if item.ID == alias.ID { - entity.Aliases[index] = alias + if alias != nil { + if alias.NamespaceID != ns.ID { + return logical.ErrorResponse("cannot modify aliases across namespaces"), logical.ErrPermissionDenied } + + return i.handleAliasUpdate(ctx, req, alias.CanonicalID, name, mountAccessor, alias) } - // Index entity and its aliases in MemDB and persist entity along with - // aliases in storage. If the alias is being transferred over from - // one entity to another, previous entity needs to get refreshed in MemDB - // and persisted in storage as well. - if err := i.upsertEntity(ctx, entity, previousEntity, true); err != nil { + // At this point we know it's a new creation request + return i.handleAliasCreate(ctx, req, canonicalID, name, mountAccessor) + } +} + +func (i *IdentityStore) handleAliasCreate(ctx context.Context, req *logical.Request, canonicalID, name, mountAccessor string) (*logical.Response, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + + alias := &identity.Alias{ + MountAccessor: mountAccessor, + Name: name, + } + entity := &identity.Entity{} + + // If a canonical ID is provided pull up the entity and make sure we're in + // the right NS + if canonicalID != "" { + entity, err = i.MemDBEntityByID(canonicalID, true) + if err != nil { return nil, err } + if entity == nil { + return logical.ErrorResponse("invalid canonical ID"), nil + } + if entity.NamespaceID != ns.ID { + return logical.ErrorResponse("entity found with 'canonical_id' not in request namespace"), logical.ErrPermissionDenied + } + } - // Return ID of both alias and entity - resp.Data = map[string]interface{}{ + entity.Aliases = append(entity.Aliases, alias) + + // ID creation and other validations; This is more useful for new entities + // and may not perform anything for the existing entities. Placing the + // check here to make the flow common for both new and existing entities. + err = i.sanitizeEntity(ctx, entity) + if err != nil { + return nil, err + } + + // Set the canonical ID in the alias index. This should be done after + // sanitizing entity in case it's a new entity that didn't have an ID. + alias.CanonicalID = entity.ID + + // ID creation and other validations + err = i.sanitizeAlias(ctx, alias) + if err != nil { + return nil, err + } + + // Index entity and its aliases in MemDB and persist entity along with + // aliases in storage. + if err := i.upsertEntity(ctx, entity, nil, true); err != nil { + return nil, err + } + + // Return ID of both alias and entity + return &logical.Response{ + Data: map[string]interface{}{ "id": alias.ID, "canonical_id": entity.ID, + }, + }, nil +} + +func (i *IdentityStore) handleAliasUpdate(ctx context.Context, req *logical.Request, canonicalID, name, mountAccessor string, alias *identity.Alias) (*logical.Response, error) { + if name == alias.Name && + mountAccessor == alias.MountAccessor && + (canonicalID == alias.CanonicalID || canonicalID == "") { + // Nothing to do; return nil to be idempotent + return nil, nil + } + + alias.LastUpdateTime = ptypes.TimestampNow() + + // If we're changing one or the other or both of these, make sure that + // there isn't a matching alias already, and make sure it's in the same + // namespace. + if name != alias.Name || mountAccessor != alias.MountAccessor { + // Check here to see if such an alias already exists, if so bail + mountEntry := i.core.router.MatchingMountByAccessor(mountAccessor) + if mountEntry == nil { + return logical.ErrorResponse(fmt.Sprintf("invalid mount accessor %q", mountAccessor)), nil + } + if mountEntry.Local { + return logical.ErrorResponse(fmt.Sprintf("mount_accessor %q is of a local mount", mountAccessor)), nil + } + if mountEntry.NamespaceID != alias.NamespaceID { + return logical.ErrorResponse("given mount accessor is not in the same namespace as the existing alias"), logical.ErrPermissionDenied } - return resp, nil + existingAlias, err := i.MemDBAliasByFactors(mountAccessor, name, false, false) + if err != nil { + return nil, err + } + // Bail unless it's just a case change + if existingAlias != nil && !strings.EqualFold(existingAlias.Name, name) { + return logical.ErrorResponse("alias with combination of mount accessor and name already exists"), nil + } + + // Update the values in the alias + alias.Name = name + alias.MountAccessor = mountAccessor } + + // Get our current entity, which may be the same as the new one if the + // canonical ID hasn't changed + currentEntity, err := i.MemDBEntityByAliasID(alias.ID, true) + if err != nil { + return nil, err + } + if currentEntity == nil { + return logical.ErrorResponse("given alias is not associated with an entity"), nil + } + if currentEntity.NamespaceID != alias.NamespaceID { + return logical.ErrorResponse("alias associated with an entity in a different namespace"), logical.ErrPermissionDenied + } + + newEntity := currentEntity + if canonicalID != "" && canonicalID != alias.CanonicalID { + newEntity, err = i.MemDBEntityByID(canonicalID, true) + if err != nil { + return nil, err + } + if newEntity == nil { + return logical.ErrorResponse("given 'canonical_id' is not associated with an entity"), nil + } + if newEntity.NamespaceID != alias.NamespaceID { + return logical.ErrorResponse("given 'canonical_id' associated with entity in a different namespace from the alias"), logical.ErrPermissionDenied + } + + // Update the canonical ID value and move it from the current enitity to the new one + alias.CanonicalID = newEntity.ID + newEntity.Aliases = append(newEntity.Aliases, alias) + for aliasIndex, item := range currentEntity.Aliases { + if item.ID == alias.ID { + currentEntity.Aliases = append(currentEntity.Aliases[:aliasIndex], currentEntity.Aliases[aliasIndex+1:]...) + break + } + } + } else { + // If it's not moving we still need to update it in the existing + // entity's aliases + for aliasIndex, item := range currentEntity.Aliases { + if item.ID == alias.ID { + currentEntity.Aliases[aliasIndex] = alias + break + } + } + // newEntity will be pointing to the same entity; set currentEntity nil + // so the upsertCall gets nil for the previous entity as we're only + // changing one. + currentEntity = nil + } + + // Index entity and its aliases in MemDB and persist entity along with + // aliases in storage. If the alias is being transferred over from + // one entity to another, previous entity needs to get refreshed in MemDB + // and persisted in storage as well. + if err := i.upsertEntity(ctx, newEntity, currentEntity, true); err != nil { + return nil, err + } + + // Return ID of both alias and entity + return &logical.Response{ + Data: map[string]interface{}{ + "id": alias.ID, + "canonical_id": newEntity.ID, + }, + }, nil } // pathAliasIDRead returns the properties of an alias for a given @@ -310,7 +401,7 @@ func (i *IdentityStore) handleAliasReadCommon(ctx context.Context, alias *identi return nil, err } if ns.ID != alias.NamespaceID { - return nil, nil + return logical.ErrorResponse("alias and request are in different namespaces"), logical.ErrPermissionDenied } respData := map[string]interface{}{} @@ -320,6 +411,7 @@ func (i *IdentityStore) handleAliasReadCommon(ctx context.Context, alias *identi respData["metadata"] = alias.Metadata respData["name"] = alias.Name respData["merged_from_canonical_ids"] = alias.MergedFromCanonicalIDs + respData["namespace_id"] = alias.NamespaceID if mountValidationResp := i.core.router.validateMountByAccessor(alias.MountAccessor); mountValidationResp != nil { respData["mount_path"] = mountValidationResp.MountPath @@ -366,7 +458,7 @@ func (i *IdentityStore) pathAliasIDDelete() framework.OperationFunc { return nil, err } if ns.ID != alias.NamespaceID { - return nil, logical.ErrUnsupportedPath + return logical.ErrorResponse("request and alias are in different namespaces"), logical.ErrPermissionDenied } // Fetch the associated entity diff --git a/vault/identity_store_aliases_test.go b/vault/identity_store_aliases_test.go index 5dbb6d9269..72b7ef8e9a 100644 --- a/vault/identity_store_aliases_test.go +++ b/vault/identity_store_aliases_test.go @@ -196,8 +196,8 @@ func TestIdentityStore_AliasSameAliasNames(t *testing.T) { if err != nil { t.Fatal(err) } - if resp == nil || !resp.IsError() { - t.Fatalf("expected an error due to alias name not being unique") + if resp != nil { + t.Fatalf("expected no response since this modification should be idempotent") } } diff --git a/vault/identity_store_entities.go b/vault/identity_store_entities.go index aa02c1ddb6..1aeb292fba 100644 --- a/vault/identity_store_entities.go +++ b/vault/identity_store_entities.go @@ -337,6 +337,7 @@ func (i *IdentityStore) handleEntityReadCommon(ctx context.Context, entity *iden respData["merged_entity_ids"] = entity.MergedEntityIDs respData["policies"] = entity.Policies respData["disabled"] = entity.Disabled + respData["namespace_id"] = entity.NamespaceID // Convert protobuf timestamp into RFC3339 format respData["creation_time"] = ptypes.TimestampString(entity.CreationTime) diff --git a/vault/identity_store_group_aliases.go b/vault/identity_store_group_aliases.go index 169973347f..475d9a328a 100644 --- a/vault/identity_store_group_aliases.go +++ b/vault/identity_store_group_aliases.go @@ -152,16 +152,16 @@ func (i *IdentityStore) handleGroupAliasUpdateCommon(ctx context.Context, req *l return logical.ErrorResponse("missing mount_accessor"), nil } - mountValidationResp := i.core.router.validateMountByAccessor(mountAccessor) - if mountValidationResp == nil { + mountEntry := i.core.router.MatchingMountByAccessor(mountAccessor) + if mountEntry == nil { return logical.ErrorResponse(fmt.Sprintf("invalid mount accessor %q", mountAccessor)), nil } - if mountValidationResp.MountLocal { + if mountEntry.Local { return logical.ErrorResponse(fmt.Sprintf("mount_accessor %q is of a local mount", mountAccessor)), nil } - groupAliasByFactors, err := i.MemDBAliasByFactors(mountValidationResp.MountAccessor, groupAliasName, false, true) + groupAliasByFactors, err := i.MemDBAliasByFactors(mountEntry.Accessor, groupAliasName, false, true) if err != nil { return nil, err } @@ -208,8 +208,13 @@ func (i *IdentityStore) handleGroupAliasUpdateCommon(ctx context.Context, req *l group.Alias = groupAlias } + // Check if the group we found belongs to a different namespace than the mount accessor + if group.NamespaceID != mountEntry.NamespaceID { + return logical.ErrorResponse("mount accessor and group are located in different namespaces"), nil + } + group.Alias.Name = groupAliasName - group.Alias.MountAccessor = mountValidationResp.MountAccessor + group.Alias.MountAccessor = mountEntry.Accessor // Explicitly correct for previous versions that persisted this group.Alias.MountType = "" diff --git a/vault/identity_store_groups.go b/vault/identity_store_groups.go index d3a8fa8c36..8fa550dead 100644 --- a/vault/identity_store_groups.go +++ b/vault/identity_store_groups.go @@ -335,6 +335,7 @@ func (i *IdentityStore) handleGroupReadCommon(ctx context.Context, group *identi respData["last_update_time"] = ptypes.TimestampString(group.LastUpdateTime) respData["modify_index"] = group.ModifyIndex respData["type"] = group.Type + respData["namespace_id"] = group.NamespaceID aliasMap := map[string]interface{}{} if group.Alias != nil { diff --git a/vault/identity_store_upgrade.go b/vault/identity_store_upgrade.go index 978cfa54dd..2c28925d7b 100644 --- a/vault/identity_store_upgrade.go +++ b/vault/identity_store_upgrade.go @@ -115,7 +115,7 @@ vault metadata=key1=value1 metadata=key2=value2 }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: i.handleAliasUpdateCommon(), + logical.UpdateOperation: i.handleAliasCreateUpdate(), }, HelpSynopsis: strings.TrimSpace(aliasHelp["alias"][0]), @@ -147,7 +147,7 @@ vault metadata=key1=value1 metadata=key2=value2 }, }, Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: i.handleAliasUpdateCommon(), + logical.UpdateOperation: i.handleAliasCreateUpdate(), logical.ReadOperation: i.pathAliasIDRead(), logical.DeleteOperation: i.pathAliasIDDelete(), }, diff --git a/vault/identity_store_util.go b/vault/identity_store_util.go index eb52e96936..78a6f28400 100644 --- a/vault/identity_store_util.go +++ b/vault/identity_store_util.go @@ -332,11 +332,19 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e var err error if txn == nil { - return fmt.Errorf("txn is nil") + return errors.New("txn is nil") } if entity == nil { - return fmt.Errorf("entity is nil") + return errors.New("entity is nil") + } + + if entity.NamespaceID == "" { + entity.NamespaceID = namespace.RootNamespaceID + } + + if previousEntity != nil && previousEntity.NamespaceID != entity.NamespaceID { + return errors.New("entity and previous entity are not in the same namespace") } aliasFactors := make([]string, len(entity.Aliases)) @@ -348,12 +356,24 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e return err } + if alias.NamespaceID == "" { + alias.NamespaceID = namespace.RootNamespaceID + } + switch { case aliasByFactors == nil: - // Not found, no merging needed + // Not found, no merging needed, just check namespace + if alias.NamespaceID != entity.NamespaceID { + return errors.New("alias and entity are not in the same namespace") + } + case aliasByFactors.CanonicalID == entity.ID: // Lookup found the same entity, so it's already attached to the // right place + if aliasByFactors.NamespaceID != entity.NamespaceID { + return errors.New("alias from factors and entity are not in the same namespace") + } + case previousEntity != nil && aliasByFactors.CanonicalID == previousEntity.ID: // previousEntity isn't upserted yet so may still contain the old // alias reference in memdb if it was just changed; validate @@ -372,8 +392,10 @@ func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, e } // Otherwise it's still tied to previousEntity and fall through - // into merging + // into merging. We don't need a namespace check here as existing + // checks when creating the aliases should ensure that all line up. fallthrough + default: i.logger.Warn("alias is already tied to a different entity; these entities are being merged", "alias_id", alias.ID, "other_entity_id", aliasByFactors.CanonicalID, "entity_aliases", entity.Aliases, "alias_by_factors", aliasByFactors) @@ -943,7 +965,7 @@ func (i *IdentityStore) sanitizeAlias(ctx context.Context, alias *identity.Alias return err } if ns.ID != alias.NamespaceID { - return fmt.Errorf("alias belongs to a different namespace") + return errors.New("alias belongs to a different namespace") } // Set the creation and last update times @@ -983,7 +1005,7 @@ func (i *IdentityStore) sanitizeEntity(ctx context.Context, entity *identity.Ent entity.NamespaceID = ns.ID } if ns.ID != entity.NamespaceID { - return fmt.Errorf("entity does not belong to this namespace") + return errors.New("entity does not belong to this namespace") } // Create a name if there isn't one already @@ -1047,7 +1069,7 @@ func (i *IdentityStore) sanitizeAndUpsertGroup(ctx context.Context, group *ident return err } if ns.ID != group.NamespaceID { - return fmt.Errorf("group does not belong to this namespace") + return errors.New("group does not belong to this namespace") } // Create a name if there isn't one already From c6c79cab840a02b457c02973a9435a14e8f18ee7 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 14 Jun 2019 12:54:08 -0400 Subject: [PATCH 15/20] changelog++ --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98cdc474f6..4a59827128 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,9 @@ IMPROVEMENTS: BUG FIXES: + * identity: Fix a case where modifying aliases of an entity could end up + moving the entity into the wrong namespace + ## 1.1.3 (June 5th, 2019) IMPROVEMENTS: From 47d4e5b1f688b5082a3563f88b45d7ea48d0c39d Mon Sep 17 00:00:00 2001 From: ncabatoff Date: Fri, 14 Jun 2019 12:59:24 -0400 Subject: [PATCH 16/20] Fix deadlock in TestPostgreSQLBackend (#6884) Make lock2's retryInterval smaller so it grabs the lock as soon as lock1's renewer fails to renew in time. Fix the logic to test if lock1's leader channel gets closed: we don't need a goroutine, and the logic was broken in that if we timed out we'd never write to the blocking channel we then try to read from. Moreover the timeout was wrong. --- physical/postgresql/postgresql_test.go | 143 ++++++++++++------------- 1 file changed, 66 insertions(+), 77 deletions(-) diff --git a/physical/postgresql/postgresql_test.go b/physical/postgresql/postgresql_test.go index 54d7d0e60c..af83865fa3 100644 --- a/physical/postgresql/postgresql_test.go +++ b/physical/postgresql/postgresql_test.go @@ -107,103 +107,92 @@ func testPostgresSQLLockTTL(t *testing.T, ha physical.HABackend) { // Set much smaller lock times to speed up the test. lockTTL := 3 renewInterval := time.Second * 1 - watchInterval := time.Second * 1 + retryInterval := time.Second * 1 + longRenewInterval := time.Duration(lockTTL*2) * time.Second + lockkey := "postgresttl" + + var leaderCh <-chan struct{} // Get the lock - origLock, err := ha.LockWith("dynamodbttl", "bar") + origLock, err := ha.LockWith(lockkey, "bar") if err != nil { t.Fatalf("err: %v", err) } - // set the first lock renew period to double the expected TTL. - lock := origLock.(*PostgreSQLLock) - lock.renewInterval = time.Duration(lockTTL*2) * time.Second - lock.ttlSeconds = lockTTL - // lock.retryInterval = watchInterval + { + // set the first lock renew period to double the expected TTL. + lock := origLock.(*PostgreSQLLock) + lock.renewInterval = longRenewInterval + lock.ttlSeconds = lockTTL - // Attempt to lock - leaderCh, err := lock.Lock(nil) - if err != nil { - t.Fatalf("err: %v", err) - } - if leaderCh == nil { - t.Fatalf("failed to get leader ch") - } + // Attempt to lock + leaderCh, err = lock.Lock(nil) + if err != nil { + t.Fatalf("err: %v", err) + } + if leaderCh == nil { + t.Fatalf("failed to get leader ch") + } - // Check the value - held, val, err := lock.Value() - if err != nil { - t.Fatalf("err: %v", err) - } - if !held { - t.Fatalf("should be held") - } - if val != "bar" { - t.Fatalf("bad value: %v", err) + // Check the value + held, val, err := lock.Value() + if err != nil { + t.Fatalf("err: %v", err) + } + if !held { + t.Fatalf("should be held") + } + if val != "bar" { + t.Fatalf("bad value: %v", val) + } } // Second acquisition should succeed because the first lock should // not renew within the 3 sec TTL. - origLock2, err := ha.LockWith("dynamodbttl", "baz") + origLock2, err := ha.LockWith(lockkey, "baz") if err != nil { t.Fatalf("err: %v", err) } + { + lock2 := origLock2.(*PostgreSQLLock) + lock2.renewInterval = renewInterval + lock2.ttlSeconds = lockTTL + lock2.retryInterval = retryInterval - lock2 := origLock2.(*PostgreSQLLock) - lock2.renewInterval = renewInterval - lock2.ttlSeconds = lockTTL - // lock2.retryInterval = watchInterval + // Cancel attempt in 6 sec so as not to block unit tests forever + stopCh := make(chan struct{}) + time.AfterFunc(time.Duration(lockTTL*2)*time.Second, func() { + close(stopCh) + }) - // Cancel attempt in 6 sec so as not to block unit tests forever - stopCh := make(chan struct{}) - time.AfterFunc(time.Duration(lockTTL*2)*time.Second, func() { - close(stopCh) - }) + // Attempt to lock should work + leaderCh2, err := lock2.Lock(stopCh) + if err != nil { + t.Fatalf("err: %v", err) + } + if leaderCh2 == nil { + t.Fatalf("should get leader ch") + } + defer lock2.Unlock() - // Attempt to lock should work - leaderCh2, err := lock2.Lock(stopCh) - if err != nil { - t.Fatalf("err: %v", err) - } - if leaderCh2 == nil { - t.Fatalf("should get leader ch") - } - - // Check the value - held, val, err = lock2.Value() - if err != nil { - t.Fatalf("err: %v", err) - } - if !held { - t.Fatalf("should be held") - } - if val != "baz" { - t.Fatalf("bad value: %v", err) + // Check the value + held, val, err := lock2.Value() + if err != nil { + t.Fatalf("err: %v", err) + } + if !held { + t.Fatalf("should be held") + } + if val != "baz" { + t.Fatalf("bad value: %v", val) + } } // The first lock should have lost the leader channel - leaderChClosed := false - blocking := make(chan struct{}) - // Attempt to read from the leader or the blocking channel, which ever one - // happens first. - go func() { - select { - case <-time.After(watchInterval * 3): - return - case <-leaderCh: - leaderChClosed = true - close(blocking) - case <-blocking: - return - } - }() - - <-blocking - if !leaderChClosed { + select { + case <-time.After(longRenewInterval * 2): t.Fatalf("original lock did not have its leader channel closed.") + case <-leaderCh: } - - // Cleanup - lock2.Unlock() } // Verify that once Unlock is called, we don't keep trying to renew the original @@ -237,7 +226,7 @@ func testPostgresSQLLockRenewal(t *testing.T, ha physical.HABackend) { t.Fatalf("should be held") } if val != "bar" { - t.Fatalf("bad value: %v", err) + t.Fatalf("bad value: %v", val) } // Release the lock, which will delete the stored item @@ -280,7 +269,7 @@ func testPostgresSQLLockRenewal(t *testing.T, ha physical.HABackend) { t.Fatalf("should be held") } if val != "baz" { - t.Fatalf("bad value: %v", err) + t.Fatalf("bad value: %v", val) } // Cleanup From c1ef613224e7134b0a02161051653dc7a9d3d79c Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 14 Jun 2019 14:29:16 -0400 Subject: [PATCH 17/20] Update vendoring and api/sdk --- api/go.mod | 2 +- go.mod | 4 +-- vendor/github.com/hashicorp/vault/api/go.mod | 2 +- .../tokenhelper.go => tokenutil/tokenutil.go} | 28 ++++++++++++++++++- vendor/modules.txt | 6 ++-- 5 files changed, 34 insertions(+), 8 deletions(-) rename vendor/github.com/hashicorp/vault/sdk/helper/{tokenhelper/tokenhelper.go => tokenutil/tokenutil.go} (85%) diff --git a/api/go.mod b/api/go.mod index 6f8d42cb2e..0c3d7cf9b0 100644 --- a/api/go.mod +++ b/api/go.mod @@ -11,7 +11,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.5.3 github.com/hashicorp/go-rootcerts v1.0.0 github.com/hashicorp/hcl v1.0.0 - github.com/hashicorp/vault/sdk v0.1.8 + github.com/hashicorp/vault/sdk v0.1.12-0.20190614165924-47d4e5b1f688 github.com/mitchellh/mapstructure v1.1.2 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 diff --git a/go.mod b/go.mod index b1de25ed4f..7bb1adafa2 100644 --- a/go.mod +++ b/go.mod @@ -80,8 +80,8 @@ require ( github.com/hashicorp/vault-plugin-secrets-gcp v0.5.2 github.com/hashicorp/vault-plugin-secrets-gcpkms v0.5.1 github.com/hashicorp/vault-plugin-secrets-kv v0.5.2-0.20190416155133-fd495225dea0 - github.com/hashicorp/vault/api v1.0.1 - github.com/hashicorp/vault/sdk v0.1.11 + github.com/hashicorp/vault/api v1.0.3-0.20190614165924-47d4e5b1f688 + github.com/hashicorp/vault/sdk v0.1.12-0.20190614165924-47d4e5b1f688 github.com/influxdata/influxdb v0.0.0-20190411212539-d24b7ba8c4c4 github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect github.com/jackc/pgx v3.3.0+incompatible // indirect diff --git a/vendor/github.com/hashicorp/vault/api/go.mod b/vendor/github.com/hashicorp/vault/api/go.mod index 6f8d42cb2e..0c3d7cf9b0 100644 --- a/vendor/github.com/hashicorp/vault/api/go.mod +++ b/vendor/github.com/hashicorp/vault/api/go.mod @@ -11,7 +11,7 @@ require ( github.com/hashicorp/go-retryablehttp v0.5.3 github.com/hashicorp/go-rootcerts v1.0.0 github.com/hashicorp/hcl v1.0.0 - github.com/hashicorp/vault/sdk v0.1.8 + github.com/hashicorp/vault/sdk v0.1.12-0.20190614165924-47d4e5b1f688 github.com/mitchellh/mapstructure v1.1.2 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 diff --git a/vendor/github.com/hashicorp/vault/sdk/helper/tokenhelper/tokenhelper.go b/vendor/github.com/hashicorp/vault/sdk/helper/tokenutil/tokenutil.go similarity index 85% rename from vendor/github.com/hashicorp/vault/sdk/helper/tokenhelper/tokenhelper.go rename to vendor/github.com/hashicorp/vault/sdk/helper/tokenutil/tokenutil.go index 09e8dcede9..5945fd47e0 100644 --- a/vendor/github.com/hashicorp/vault/sdk/helper/tokenhelper/tokenhelper.go +++ b/vendor/github.com/hashicorp/vault/sdk/helper/tokenutil/tokenutil.go @@ -1,4 +1,4 @@ -package tokenhelper +package tokenutil import ( "errors" @@ -12,6 +12,8 @@ import ( "github.com/hashicorp/vault/sdk/logical" ) +// TokenParams contains a set of common parameters that auth plugins can use +// for setting token behavior type TokenParams struct { // The set of CIDRs that tokens generated using this role will be bound to TokenBoundCIDRs []*sockaddr.SockAddrMarshaler `json:"token_bound_cidrs"` @@ -26,6 +28,9 @@ type TokenParams struct { // If set, core will not automatically add default to the policy list TokenNoDefaultPolicy bool `json:"token_no_default_policy" mapstructure:"token_no_default_policy"` + // The maximum number of times a token issued from this role may be used. + TokenNumUses int `json:"token_num_uses" mapstructure:"token_num_uses"` + // If non-zero, tokens created using this role will be able to be renewed // forever, but will have a fixed renewal period of this value TokenPeriod time.Duration `json:"token_period" mapstructure:"token_period"` @@ -46,6 +51,9 @@ func AddTokenFields(m map[string]*framework.FieldSchema) { AddTokenFieldsWithAllowList(m, nil) } +// AddTokenFields adds fields to an existing role. It panics if it would +// overwrite an existing field. Allowed can be use to restrict the set, e.g. if +// there would be conflicts. func AddTokenFieldsWithAllowList(m map[string]*framework.FieldSchema, allowed []string) { r := TokenFields() for k, v := range r { @@ -59,6 +67,7 @@ func AddTokenFieldsWithAllowList(m map[string]*framework.FieldSchema, allowed [] } } +// TokenFields provides a set of field schemas for the parameters func TokenFields() map[string]*framework.FieldSchema { return map[string]*framework.FieldSchema{ "token_bound_cidrs": &framework.FieldSchema{ @@ -101,9 +110,15 @@ func TokenFields() map[string]*framework.FieldSchema { Type: framework.TypeDurationSecond, Description: "The initial ttl of the token to generate", }, + + "token_num_uses": &framework.FieldSchema{ + Type: framework.TypeInt, + Description: "The maximum number of times a token may be used, a value of zero means unlimited", + }, } } +// ParseTokenFields provides common field parsing functionality into a TokenFields struct func (t *TokenParams) ParseTokenFields(req *logical.Request, d *framework.FieldData) error { if boundCIDRsRaw, ok := d.GetOk("token_bound_cidrs"); ok { boundCIDRs, err := parseutil.ParseAddrs(boundCIDRsRaw.([]string)) @@ -167,9 +182,17 @@ func (t *TokenParams) ParseTokenFields(req *logical.Request, d *framework.FieldD return errors.New("'token_ttl' cannot be greater than 'token_max_ttl'") } + if tokenNumUses, ok := d.GetOk("token_num_uses"); ok { + t.TokenNumUses = tokenNumUses.(int) + } + if t.TokenNumUses < 0 { + return errors.New("'token_num_uses' cannot be negative") + } + return nil } +// PopulateTokenData adds information from TokenParams into the map func (t *TokenParams) PopulateTokenData(m map[string]interface{}) { m["token_bound_cidrs"] = t.TokenBoundCIDRs m["token_explicit_max_ttl"] = t.TokenExplicitMaxTTL.Seconds() @@ -179,8 +202,10 @@ func (t *TokenParams) PopulateTokenData(m map[string]interface{}) { m["token_policies"] = t.TokenPolicies m["token_type"] = t.TokenType.String() m["token_ttl"] = t.TokenTTL.Seconds() + m["token_num_uses"] = t.TokenNumUses } +// PopulateTokenAuth populates Auth with parameters func (t *TokenParams) PopulateTokenAuth(auth *logical.Auth) { auth.BoundCIDRs = t.TokenBoundCIDRs auth.ExplicitMaxTTL = t.TokenExplicitMaxTTL @@ -190,6 +215,7 @@ func (t *TokenParams) PopulateTokenAuth(auth *logical.Auth) { auth.Policies = t.TokenPolicies auth.TokenType = t.TokenType auth.TTL = t.TokenTTL + auth.NumUses = t.TokenNumUses } const ( diff --git a/vendor/modules.txt b/vendor/modules.txt index 5fb41b6565..06a634ad66 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -346,9 +346,9 @@ github.com/hashicorp/vault-plugin-secrets-gcp/plugin/util github.com/hashicorp/vault-plugin-secrets-gcpkms # github.com/hashicorp/vault-plugin-secrets-kv v0.5.2-0.20190416155133-fd495225dea0 github.com/hashicorp/vault-plugin-secrets-kv -# github.com/hashicorp/vault/api v1.0.1 => ./api +# github.com/hashicorp/vault/api v1.0.3-0.20190614165924-47d4e5b1f688 => ./api github.com/hashicorp/vault/api -# github.com/hashicorp/vault/sdk v0.1.11 => ./sdk +# github.com/hashicorp/vault/sdk v0.1.12-0.20190614165924-47d4e5b1f688 => ./sdk github.com/hashicorp/vault/sdk/helper/salt github.com/hashicorp/vault/sdk/helper/strutil github.com/hashicorp/vault/sdk/helper/wrapping @@ -386,7 +386,7 @@ github.com/hashicorp/vault/sdk/helper/pathmanager github.com/hashicorp/vault/sdk/database/helper/connutil github.com/hashicorp/vault/sdk/helper/license github.com/hashicorp/vault/sdk/helper/pluginutil -github.com/hashicorp/vault/sdk/helper/tokenhelper +github.com/hashicorp/vault/sdk/helper/tokenutil github.com/hashicorp/vault/sdk/plugin/pb github.com/hashicorp/vault/sdk/helper/kdf github.com/hashicorp/vault/sdk/plugin/mock From 2fab20e082e64b2c565d4a73c59f21e7cd3f3fc0 Mon Sep 17 00:00:00 2001 From: Joshua Ogle Date: Fri, 14 Jun 2019 15:46:18 -0600 Subject: [PATCH 18/20] UI: Remove upgrade modal (#6871) * Remove upgrade-link component * Hide enterprrise links from OSS * Don't show sidebar if they can't use the options --- ui/app/components/edition-badge.js | 18 --- ui/app/styles/components/empty-state.scss | 2 +- ui/app/styles/components/upgrade-overlay.scss | 63 ---------- ui/app/styles/core.scss | 1 - ui/app/templates/components/edition-badge.hbs | 4 - ui/app/templates/partials/status/cluster.hbs | 109 ++++++++---------- ui/app/templates/vault.hbs | 7 +- ui/app/templates/vault/cluster/access.hbs | 16 +-- ui/app/templates/vault/cluster/policies.hbs | 74 +++++------- .../vault/cluster/policies/index.hbs | 2 + ui/app/templates/vault/cluster/policy.hbs | 42 +++---- ui/lib/core/addon/components/upgrade-link.js | 44 ------- ui/lib/core/addon/components/upgrade-page.js | 2 +- .../templates/components/empty-state.hbs | 2 +- .../components/replication-mode-summary.hbs | 10 +- .../templates/components/upgrade-link.hbs | 79 ------------- .../templates/components/upgrade-page.hbs | 29 ++--- ui/lib/core/app/components/upgrade-link.js | 1 - .../components/replication-summary.hbs | 21 ++-- ui/public/edition-enterprise.svg | 17 --- ui/public/edition-oss.svg | 17 --- ui/public/vault-hex.svg | 8 -- .../components/edition-badge-test.js | 22 ---- .../components/upgrade-link-test.js | 48 -------- .../components/upgrade-page-test.js | 32 +++-- 25 files changed, 162 insertions(+), 508 deletions(-) delete mode 100644 ui/app/components/edition-badge.js delete mode 100644 ui/app/styles/components/upgrade-overlay.scss delete mode 100644 ui/app/templates/components/edition-badge.hbs delete mode 100644 ui/lib/core/addon/components/upgrade-link.js delete mode 100644 ui/lib/core/addon/templates/components/upgrade-link.hbs delete mode 100644 ui/lib/core/app/components/upgrade-link.js delete mode 100644 ui/public/edition-enterprise.svg delete mode 100644 ui/public/edition-oss.svg delete mode 100644 ui/public/vault-hex.svg delete mode 100644 ui/tests/integration/components/edition-badge-test.js delete mode 100644 ui/tests/integration/components/upgrade-link-test.js diff --git a/ui/app/components/edition-badge.js b/ui/app/components/edition-badge.js deleted file mode 100644 index 025f657be6..0000000000 --- a/ui/app/components/edition-badge.js +++ /dev/null @@ -1,18 +0,0 @@ -import { computed } from '@ember/object'; -import Component from '@ember/component'; - -export default Component.extend({ - tagName: 'span', - classNames: 'tag is-outlined edition-badge', - attributeBindings: ['edition:aria-label'], - icon: computed('edition', function() { - const edition = this.get('edition'); - const entEditions = ['Enterprise', 'Premium', 'Pro']; - - if (entEditions.includes(edition)) { - return 'edition-enterprise'; - } else { - return 'edition-oss'; - } - }), -}); diff --git a/ui/app/styles/components/empty-state.scss b/ui/app/styles/components/empty-state.scss index 2cb489aae0..b023403cfa 100644 --- a/ui/app/styles/components/empty-state.scss +++ b/ui/app/styles/components/empty-state.scss @@ -2,7 +2,7 @@ align-items: center; color: $grey; display: flex; - background: $ui-gray-050; + background: $ui-gray-010; justify-content: center; padding: $spacing-xxl $spacing-s; box-shadow: 0 -2px 0 -1px $ui-gray-300; diff --git a/ui/app/styles/components/upgrade-overlay.scss b/ui/app/styles/components/upgrade-overlay.scss deleted file mode 100644 index f5770064ac..0000000000 --- a/ui/app/styles/components/upgrade-overlay.scss +++ /dev/null @@ -1,63 +0,0 @@ -.upgrade-overlay { - font-size: 1rem; - opacity: 0; - text-align: left; - transition: opacity $speed-slow; - will-change: opacity; - z-index: 300; - - &.is-animated { - opacity: 1; - } - - .modal-background { - background-image: url('/ui/vault-hex.svg'), linear-gradient(90deg, #191a1c, #1b212d); - opacity: 0.97; - } - - .modal-content { - overflow: auto; - overflow-x: hidden; - transform: translateY(20%) scale(0.9); - transition: transform $speed-slow; - will-change: transform; - } - - &.is-animated { - .modal-content { - transform: translateY(0) scale(1); - } - } - - .upgrade-overlay-title { - border-bottom: 1px solid $grey; - padding-bottom: $size-10; - - .icon { - width: 32px; - - #edition-enterprise-hexagon { - fill: $white; - } - } - } - - .columns { - margin-bottom: $size-4; - margin-top: $size-4; - } - - .column { - display: flex; - - .box { - border-radius: $radius; - box-shadow: inset 0 0 0 1px $grey; - width: 100%; - } - } - - li { - list-style: inside disc; - } -} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index 0eb83f62b4..654221e12b 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -81,7 +81,6 @@ @import './components/toolbar'; @import './components/tool-tip'; @import './components/unseal-warning'; -@import './components/upgrade-overlay'; @import './components/ui-wizard'; @import './components/vault-loading'; diff --git a/ui/app/templates/components/edition-badge.hbs b/ui/app/templates/components/edition-badge.hbs deleted file mode 100644 index 28bf3e6b50..0000000000 --- a/ui/app/templates/components/edition-badge.hbs +++ /dev/null @@ -1,4 +0,0 @@ -{{#if icon}} - -{{/if}} -{{edition}} diff --git a/ui/app/templates/partials/status/cluster.hbs b/ui/app/templates/partials/status/cluster.hbs index e3bf8b5fe3..abed797612 100644 --- a/ui/app/templates/partials/status/cluster.hbs +++ b/ui/app/templates/partials/status/cluster.hbs @@ -1,76 +1,67 @@