From 3f1ab4c21995cfe1292e12dbb581293db73cce01 Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Mon, 11 Apr 2022 14:36:28 -0600 Subject: [PATCH] UI/merge main (#14997) * Vault documentation: changing references from learn to tutorial (#14844) * changed learn to tutorial references * changed learn to tutorial * Update website/content/docs/plugins/plugin-portal.mdx Co-authored-by: Yoko Hyakuna * Update website/content/docs/platform/aws/run.mdx Co-authored-by: Yoko Hyakuna Co-authored-by: Yoko Hyakuna * UI/Only show form values if have read access (#14794) * only show value in edit form if has read capabilities * revert messing with form * delete secret data from secret * add check for selected version * remove added line * add changelog * modified text (#14854) * fixed a link issue (#14850) * docs: add known issue to 1.10 release notes (#14859) * Vault 3999 Change permissions for directory/archive created by debug command (#14846) * adding debug changes from ent * adding changelog * Vault 3992 ToB Config and Plugins Permissions (#14817) * updating changes from ent PR * adding changelog * fixing err * fixing semgrep error * updated references from learn to tutorial (#14866) * updated references from learn to tutorial (#14867) * changed reference from learn to tutorial (#14868) * Fix handling of default zero SignatureBits value with Any key type in PKI Secrets Engine (#14875) * Correctly handle minimums, default SignatureBits When using KeyType = "any" on a role (whether explicitly or implicitly via a sign-verbatim like operation), we need to update the value of SignatureBits from its new value 0 to a per-key-type default value. This will allow sign operations on these paths to function correctly, having the correctly inferred default signature bit length. Additionally, this allows the computed default value for key type to be used for minimum size validation in the RSA/ECDSA paths. We additionally enforce the 2048-minimum in this case as well. Signed-off-by: Alexander Scheel * Fix defaults and validation of "any" KeyType When certutil is given the placeholder any keytype, it attempts to validate and update the default zero value. However, in lacking a default value for SignatureBits, it cannot update the value from the zero value, thus causing validation to fail. Add more awareness to the placeholder "any" value to certutil. Signed-off-by: Alexander Scheel * Add role-based regression tests for key bits This adds regression tests for Key Type, Key Bits, and Signature Bits parameters on the role. We test several values, including the "any" value to ensure it correctly restricts key sizes. Signed-off-by: Alexander Scheel * Add sign-verbatim test for key type This ensures that we test sign-verbatim against a variety of key types. Signed-off-by: Alexander Scheel * Add changelog entry Signed-off-by: Alexander Scheel Co-authored-by: Steven Clark * Subtle docs change for allow_store_key (#14889) * Subtle docs change for allow_store_key * errant space * Adds Vault version prerelease and metadata to logical.PluginEnvironment (#14851) * docs: fix formatting on plugin upgrade page (#14874) * docs: fix formatting on plugin upgrade page * fix more formatting issues * Update CC docs (#14714) * Update CC docs * Add sample response * Address review feedback * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Meggie * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Update website/content/api-docs/system/internal-counters.mdx Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> * Minor edits * Update partial month API Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> Co-authored-by: Meggie * Docs improvements for Managed Keys (#14756) * Add more color around managed keys in their concepts page, and create additional links between the various docs pages related to them. * Typos * sdk/useragent: plugin version string consistent with Vault version string (#14912) * clean up (#14911) * website: fix usages of img tag (#14910) * fix usages of img tag and integrate dev-portal workflows * Adjust Makefile * remove mount_accessor from the docs (#14927) * Add extra test coverage to PKI (#14767) * Add PKI test for delete role - Create a role, validate that defaults are what we expect and delete the role, verifying it is gone on subsequent read attempts. * Add PKI test for crl/rotate command - Missing a unit test that validates the crl/rotate command works. The test validates the rotate command was successful by checking if we have a different/new update time on the CRL. * Rework PKI TestBackend_PathFetchValidRaw test to not write directly to storage - Rework the existing test to not write directly to storage as we might change that in the future. - Add tests that validate the ca_chain behaviour of not returning the root authority cert * PR Feedback * Additional PR feedback * Use WriteWithContext in auth helpers (#14775) * Add ability to pass certificate PEM bytes to vault/api (#14753) * Respect increment value in grace period calculations (api/LifetimeWatcher) (#14836) * Mount flag syntax to mitigate confusion from KV-v2 path discrepancies (#14807) * Add explanation to help text and flag usage text * KV get with new mount flag * Clearer naming * KV Put, Patch, Metadata Get + corresponding tests * KV Delete, Destroy, Rollback, Undelete, MetadataDelete, MetadataPatch, MetadataPut * Update KV-v2 docs to use mount flag syntax * Add changelog * Run make fmt * Clarify deprecation message in help string * Address style comments * Update vault-plugin-auth-gcp to newest pseudo-version (#14923) * docs: added hello-vault-spring repo link to developer-qs.mdx. (#14928) * Update developer-qs.mdx docs: added link to Java / Spring Boot sample app repo in developer quick start. * removed space. * trigger ci Co-authored-by: taoism4504 * OIDC Login Bug (#14916) * fixes issue logging in with oidc from listed auth path tab * adds changelog entry * adds more tests for oidc auth workflow * updates oidc auth method test to use non-standard path * Fix handling of SignatureBits for ECDSA issuers (#14943) When adding SignatureBits control logic, we incorrectly allowed specification of SignatureBits in the case of an ECDSA issuer. As noted in the original request, NIST and Mozilla (and others) are fairly prescriptive in the choice of signatures (matching the size of the NIST P-curve), and we shouldn't usually use a smaller (or worse, larger and truncate!) hash. Ignore the configuration of signature bits and always use autodetection for ECDSA like ed25519. Signed-off-by: Alexander Scheel * Bug Fix and Glimmerize secret-edit component (#14941) * inital glimmerize * wip * wip * wip * fix maybeQueryRecord * fix * fix * fix test * cleanup * add changelog * clean up * Agent error log level is mismatched (#14424) * [VAULT-1618] Agent error log level is mismatched `logLevelToStringPtr` translates `go-hclog`'s `ERROR` to `"ERROR"` for Consul Template's runner, but that expects `ERR` and is quite strict about it. This will address https://github.com/hashicorp/vault-k8s/issues/223 after it is set as the default image in `vault-k8s`. I didn't find a simple way to test this other than starting up a full server and agent and letting them run, which is unfortunately fairly slow. I confirmed that this addresses the original issue by modifying the helm chart with the values in this commit and patching the log level to `err`. * VAULT-1618 Add changelog/14424.txt * VAULT-1618 Update changelog/14424.txt based on @kalafut suggestion Co-authored-by: Jim Kalafut * VAULT-1618 Move cancel and server stop into defer in tests * VAULT-1618 Triggering CircleCI tests * VAULT-1618 Replace ioutil with os functions for agent template tests Co-authored-by: Jim Kalafut * UI/Add months to activity serializer (#14942) * add mock monthly data to mirage handler * add months to serializer for activity response * change selectors Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> * clean up serializer * please stop being flakey <3 Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> * Revert the WithContext changes to vault tests (#14947) * adding env var (#14958) * Fix dead link for JWT supported algorithms (#14953) * Don't clone OutputCurlString value (#14968) * Don't clone OutputCurlString value, add flag to docs * Add changelog * Ensure initialMmapSize is 0 on Windows (#14977) * ensure initialMmapSize is 0 on windows * add changelog * Vault 3992 documentation changes (#14918) * doc changes * adding config changes * adding chnages to plugins * using include * making doc changes * adding newline * aws auth displayName (#14954) * set displayName to include RoleSessionName * Add Windows error (#14982) * Warnings indicating ignored and replaced parameters (#14962) * Warnings indicating ignored and replaced parameters * Avoid additional var creation * Add warnings only if the response is non-nil * Return the response even when error is non-nil * Fix tests * Rearrange comments * Print warning in the log * Fix another test * Add CL * Fix edit capabilities call in auth method (#14966) * Fix edit capabilities call in auth method - Capabilities call was not getting triggered correctly as apiPath method was missing the correct context. * Added changelog * make linting fix Co-authored-by: Loann Le <84412881+taoism4504@users.noreply.github.com> Co-authored-by: Yoko Hyakuna Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Co-authored-by: Austin Gebauer <34121980+austingebauer@users.noreply.github.com> Co-authored-by: akshya96 <87045294+akshya96@users.noreply.github.com> Co-authored-by: Alexander Scheel Co-authored-by: Steven Clark Co-authored-by: Scott Miller Co-authored-by: John-Michael Faircloth Co-authored-by: Vishal Nayak Co-authored-by: Meggie Co-authored-by: Bryce Kalow Co-authored-by: Josh Black Co-authored-by: Anton Averchenkov <84287187+averche@users.noreply.github.com> Co-authored-by: VAL Co-authored-by: Robert <17119716+robmonte@users.noreply.github.com> Co-authored-by: mryan-hashi <88851444+mryan-hashi@users.noreply.github.com> Co-authored-by: taoism4504 Co-authored-by: Jordan Reimer Co-authored-by: Christopher Swenson Co-authored-by: Jim Kalafut Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> Co-authored-by: Tom Proctor Co-authored-by: Jose Estrada Co-authored-by: Arnav Palnitkar --- api/auth/approle/approle.go | 8 +- api/auth/aws/aws.go | 6 +- api/auth/azure/azure.go | 6 +- api/auth/gcp/gcp.go | 6 +- api/auth/kubernetes/kubernetes.go | 6 +- api/auth/ldap/ldap.go | 6 +- api/auth/userpass/userpass.go | 6 +- api/client.go | 52 +- api/client_test.go | 29 +- api/lifetime_watcher.go | 25 +- api/renewer_test.go | 105 +-- builtin/credential/aws/backend_e2e_test.go | 4 +- builtin/credential/aws/path_login.go | 5 + builtin/credential/cert/backend_test.go | 20 +- builtin/logical/database/backend_test.go | 2 +- builtin/logical/pki/backend_test.go | 682 +++++++++++++---- builtin/logical/pki/ca_test.go | 48 +- builtin/logical/pki/cert_util.go | 131 +++- builtin/logical/pki/crl_test.go | 50 +- builtin/logical/ssh/backend_test.go | 42 +- builtin/logical/transit/path_config_test.go | 8 +- builtin/logical/transit/path_keys_test.go | 23 +- changelog/14424.txt | 3 + changelog/14753.txt | 3 + changelog/14775.txt | 3 + changelog/14794.txt | 3 + changelog/14807.txt | 3 + changelog/14817.txt | 3 + changelog/14836.txt | 3 + changelog/14846.txt | 3 + changelog/14875.txt | 3 + changelog/14916.txt | 3 + changelog/14941.txt | 3 + changelog/14943.txt | 3 + changelog/14954.txt | 3 + changelog/14962.txt | 6 + changelog/14966.txt | 3 + changelog/14968.txt | 3 + changelog/14977.txt | 3 + command/agent/alicloud_end_to_end_test.go | 2 +- command/agent/approle_end_to_end_test.go | 32 +- command/agent/auth/approle/approle.go | 4 +- command/agent/auth/auth.go | 4 +- command/agent/auth/auth_test.go | 2 +- ...auto_auth_preload_token_end_to_end_test.go | 10 +- command/agent/aws_end_to_end_test.go | 2 +- command/agent/cache/cache_test.go | 152 ++-- command/agent/cache_end_to_end_test.go | 24 +- command/agent/cert_end_to_end_test.go | 20 +- command/agent/cf_end_to_end_test.go | 4 +- command/agent/jwt_end_to_end_test.go | 4 +- command/agent/template/template.go | 2 +- command/agent/template/template_test.go | 78 +- command/agent_test.go | 31 +- command/approle_concurrency_integ_test.go | 6 +- command/audit_disable_test.go | 7 +- command/audit_enable_test.go | 3 +- command/audit_list_test.go | 3 +- command/auth_disable_test.go | 3 +- command/auth_enable_test.go | 3 +- command/auth_move_test.go | 3 +- command/auth_tune_test.go | 7 +- command/base_predict_test.go | 27 +- command/command_test.go | 2 +- command/debug.go | 24 +- command/debug_test.go | 138 +++- command/delete_test.go | 5 +- command/kv.go | 14 +- command/kv_delete.go | 81 +- command/kv_destroy.go | 62 +- command/kv_get.go | 77 +- command/kv_metadata.go | 12 +- command/kv_metadata_delete.go | 68 +- command/kv_metadata_get.go | 68 +- command/kv_metadata_patch.go | 69 +- command/kv_metadata_patch_test.go | 13 +- command/kv_metadata_put.go | 69 +- command/kv_metadata_put_test.go | 17 +- command/kv_patch.go | 74 +- command/kv_put.go | 78 +- command/kv_rollback.go | 90 ++- command/kv_test.go | 257 ++++--- command/kv_undelete.go | 61 +- command/lease_lookup_test.go | 7 +- command/lease_renew_test.go | 7 +- command/lease_revoke_test.go | 7 +- command/list_test.go | 3 +- command/login_test.go | 19 +- command/operator_diagnose_test.go | 3 +- command/operator_generate_root_test.go | 13 +- command/operator_init_test.go | 17 +- command/operator_rekey_test.go | 23 +- command/operator_seal_test.go | 3 +- command/operator_unseal_test.go | 5 +- command/path_map_upgrade_api_test.go | 13 +- command/plugin_deregister_test.go | 5 +- command/plugin_register_test.go | 3 +- command/plugin_reload_test.go | 3 +- command/plugin_test.go | 3 +- command/policy_delete_test.go | 5 +- command/policy_read_test.go | 3 +- command/policy_write_test.go | 5 +- command/read_test.go | 3 +- command/rotate_test.go | 3 +- command/secrets_disable_test.go | 5 +- command/secrets_enable_test.go | 3 +- command/secrets_move_test.go | 3 +- command/secrets_tune_test.go | 19 +- command/server.go | 3 + command/server/config.go | 57 +- command/server/config_test_helpers.go | 3 + .../server/server_seal_transit_acc_test.go | 2 +- command/token_capabilities_test.go | 9 +- command/token_create_test.go | 7 +- command/token_renew_test.go | 7 +- command/token_revoke_test.go | 7 +- command/unwrap_test.go | 3 +- command/write_test.go | 11 +- go.mod | 2 +- go.sum | 4 +- helper/forwarding/types.pb.go | 2 +- helper/identity/mfa/types.pb.go | 2 +- helper/identity/types.pb.go | 2 +- helper/osutil/fileinfo.go | 66 ++ helper/osutil/fileinfo_test.go | 84 +++ helper/osutil/fileinfo_unix.go | 53 ++ helper/osutil/fileinfo_unix_test.go | 100 +++ helper/osutil/fileinfo_windows.go | 11 + helper/storagepacker/types.pb.go | 2 +- http/auth_token_test.go | 25 +- http/forwarding_test.go | 11 +- http/handler_test.go | 2 +- http/logical_test.go | 3 +- http/plugin_test.go | 14 +- http/sys_config_state_test.go | 2 + http/sys_hostinfo_test.go | 5 +- http/sys_mounts_test.go | 7 +- http/sys_wrapping_test.go | 61 +- http/unwrapping_raw_body_test.go | 7 +- physical/raft/types.pb.go | 2 +- physical/raft/vars_32bit.go | 2 +- physical/raft/vars_64bit.go | 2 +- sdk/database/dbplugin/database.pb.go | 2 +- sdk/database/dbplugin/v5/proto/database.pb.go | 2 +- sdk/framework/backend.go | 33 +- sdk/framework/backend_test.go | 48 ++ sdk/helper/certutil/helpers.go | 56 +- sdk/helper/consts/consts.go | 2 + sdk/helper/pluginutil/multiplexing.pb.go | 2 +- sdk/helper/useragent/useragent.go | 19 +- sdk/helper/useragent/useragent_test.go | 81 +- sdk/logical/identity.pb.go | 2 +- sdk/logical/plugin.pb.go | 41 +- sdk/logical/plugin.proto | 6 + sdk/plugin/grpc_system_test.go | 4 +- sdk/plugin/pb/backend.pb.go | 2 +- ui/app/components/auth-form.js | 9 +- ui/app/components/auth-jwt.js | 10 +- ui/app/components/secret-create-or-update.js | 3 + ui/app/components/secret-edit-toolbar.js | 2 - ui/app/components/secret-edit.js | 191 +++-- ui/app/models/auth-method.js | 4 +- ui/app/models/clients/activity.js | 6 +- ui/app/serializers/clients/activity.js | 65 +- .../components/secret-edit-toolbar.hbs | 1 - ui/app/templates/components/secret-edit.hbs | 33 +- .../templates/mode/secondaries/index.hbs | 4 +- ui/mirage/handlers/clients.js | 646 +++++++++++++++- ui/tests/acceptance/client-current-test.js | 3 +- ui/tests/acceptance/client-history-test.js | 5 +- .../acceptance/enterprise-replication-test.js | 2 +- .../acceptance/logout-auth-method-test.js | 43 -- ui/tests/acceptance/oidc-auth-method-test.js | 86 +++ ui/tests/helpers/clients.js | 28 +- vault/activity/activity_log.pb.go | 12 +- vault/auth.go | 2 +- vault/core.go | 33 + vault/diagnose/file_checks_unix.go | 16 +- vault/dynamic_system_view.go | 5 +- vault/expiration_integ_test.go | 19 +- .../api/renewer_integration_test.go | 19 +- vault/external_tests/api/secret_test.go | 155 ++-- .../approle/wrapped_secretid_test.go | 11 +- .../expiration/expiration_test.go | 13 +- vault/external_tests/identity/aliases_test.go | 25 +- .../external_tests/identity/entities_test.go | 43 +- .../identity/group_aliases_test.go | 7 +- vault/external_tests/identity/groups_test.go | 47 +- .../external_tests/identity/identity_test.go | 87 ++- .../identity/login_mfa_duo_test.go | 31 +- .../identity/login_mfa_okta_test.go | 43 +- .../identity/login_mfa_totp_test.go | 40 +- .../identity/oidc_provider_test.go | 8 +- vault/external_tests/kv/kv_patch_test.go | 6 +- vault/external_tests/kv/kvv2_upgrade_test.go | 2 +- .../metrics/core_metrics_int_test.go | 4 +- vault/external_tests/mfa/login_mfa_test.go | 51 +- .../misc/recover_from_panic_test.go | 5 +- vault/external_tests/misc/recovery_test.go | 13 +- .../policy/acl_templating_test.go | 25 +- vault/external_tests/policy/policy_test.go | 31 +- vault/external_tests/quotas/quotas_test.go | 69 +- .../raft/raft_autopilot_test.go | 34 +- vault/external_tests/raft/raft_test.go | 56 +- vault/external_tests/raftha/raft_ha_test.go | 11 +- .../response/allowed_response_headers_test.go | 2 +- .../external_tests/router/router_ext_test.go | 3 +- .../external_tests/token/batch_token_test.go | 93 ++- vault/external_tests/token/token_test.go | 103 ++- vault/logical_system.go | 4 + vault/logical_system_integ_test.go | 33 +- vault/mount.go | 2 +- vault/plugin_catalog.go | 9 + vault/request_forwarding_service.pb.go | 2 +- vault/tokens/token.pb.go | 2 +- website/.gitignore | 2 + website/Makefile | 80 +- website/content/api-docs/auth/jwt.mdx | 2 +- .../api-docs/secret/identity/mfa/duo.mdx | 9 +- .../api-docs/secret/identity/mfa/okta.mdx | 9 +- .../api-docs/secret/identity/mfa/pingid.mdx | 9 +- website/content/api-docs/secret/pki.mdx | 4 + .../api-docs/system/internal-counters.mdx | 712 +++++++++++++++--- .../content/api-docs/system/managed-keys.mdx | 4 +- .../api-docs/system/storage/raftautopilot.mdx | 2 +- .../docs/agent/autoauth/methods/aws.mdx | 9 +- .../agent/autoauth/methods/kubernetes.mdx | 4 +- website/content/docs/agent/caching/index.mdx | 6 +- website/content/docs/auth/approle.mdx | 18 +- website/content/docs/commands/index.mdx | 5 + .../content/docs/commands/operator/usage.mdx | 4 +- website/content/docs/concepts/ha.mdx | 3 +- website/content/docs/concepts/identity.mdx | 29 +- website/content/docs/configuration/index.mdx | 17 + .../docs/configuration/seal/awskms.mdx | 4 +- .../docs/configuration/seal/azurekeyvault.mdx | 6 +- .../docs/configuration/seal/pkcs11.mdx | 4 +- .../docs/configuration/seal/transit.mdx | 8 +- .../docs/configuration/storage/index.mdx | 2 +- .../docs/configuration/storage/raft.mdx | 5 +- .../docs/enterprise/control-groups.mdx | 5 +- .../docs/enterprise/entropy-augmentation.mdx | 6 +- .../content/docs/enterprise/managed-keys.mdx | 23 +- .../content/docs/enterprise/namespaces.mdx | 4 +- .../docs/enterprise/sentinel/index.mdx | 4 +- .../content/docs/get-started/developer-qs.mdx | 2 +- website/content/docs/install.mdx | 4 +- website/content/docs/partnerships.mdx | 39 +- website/content/docs/platform/aws/run.mdx | 2 +- .../content/docs/platform/k8s/csi/index.mdx | 4 +- .../k8s/helm/examples/development.mdx | 6 +- .../docs/platform/k8s/helm/examples/index.mdx | 4 +- .../content/docs/platform/k8s/helm/index.mdx | 9 +- .../docs/platform/k8s/helm/openshift.mdx | 20 +- .../content/docs/platform/k8s/helm/run.mdx | 17 +- website/content/docs/plugins/index.mdx | 2 + .../docs/plugins/plugin-architecture.mdx | 8 +- .../content/docs/plugins/plugin-portal.mdx | 7 +- website/content/docs/release-notes/1.10.0.mdx | 15 + website/content/docs/secrets/ad.mdx | 5 +- website/content/docs/secrets/azure.mdx | 70 +- website/content/docs/secrets/cubbyhole.mdx | 4 +- .../content/docs/secrets/databases/db2.mdx | 9 +- .../docs/secrets/identity/oidc-provider.mdx | 92 +-- .../docs/secrets/key-management/index.mdx | 5 +- website/content/docs/secrets/kv/kv-v1.mdx | 4 +- website/content/docs/secrets/kv/kv-v2.mdx | 94 +-- website/content/docs/secrets/pki.mdx | 8 + .../secrets/ssh/one-time-ssh-passwords.mdx | 6 +- .../content/docs/secrets/transform/index.mdx | 4 +- website/content/docs/secrets/transit.mdx | 4 +- website/content/docs/upgrading/index.mdx | 18 +- website/content/docs/upgrading/plugins.mdx | 40 +- website/content/docs/what-is-vault.mdx | 5 +- .../plugin-file-permissions-check.mdx | 5 + website/next.config.js | 2 +- website/package.json | 4 +- website/{redirects.next.js => redirects.js} | 0 website/scripts/website-build.sh | 31 + website/scripts/website-start.sh | 25 + 280 files changed, 5631 insertions(+), 2358 deletions(-) create mode 100644 changelog/14424.txt create mode 100644 changelog/14753.txt create mode 100644 changelog/14775.txt create mode 100644 changelog/14794.txt create mode 100644 changelog/14807.txt create mode 100644 changelog/14817.txt create mode 100644 changelog/14836.txt create mode 100644 changelog/14846.txt create mode 100644 changelog/14875.txt create mode 100644 changelog/14916.txt create mode 100644 changelog/14941.txt create mode 100644 changelog/14943.txt create mode 100644 changelog/14954.txt create mode 100644 changelog/14962.txt create mode 100644 changelog/14966.txt create mode 100644 changelog/14968.txt create mode 100644 changelog/14977.txt create mode 100644 helper/osutil/fileinfo.go create mode 100644 helper/osutil/fileinfo_test.go create mode 100644 helper/osutil/fileinfo_unix.go create mode 100644 helper/osutil/fileinfo_unix_test.go create mode 100644 helper/osutil/fileinfo_windows.go delete mode 100644 ui/tests/acceptance/logout-auth-method-test.js create mode 100644 ui/tests/acceptance/oidc-auth-method-test.js create mode 100644 website/content/partials/plugin-file-permissions-check.mdx rename website/{redirects.next.js => redirects.js} (100%) create mode 100755 website/scripts/website-build.sh create mode 100755 website/scripts/website-start.sh diff --git a/api/auth/approle/approle.go b/api/auth/approle/approle.go index 61c380cd4fa6..b8cf01228441 100644 --- a/api/auth/approle/approle.go +++ b/api/auth/approle/approle.go @@ -100,6 +100,10 @@ func NewAppRoleAuth(roleID string, secretID *SecretID, opts ...LoginOption) (*Ap } func (a *AppRoleAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := map[string]interface{}{ "role_id": a.roleID, } @@ -125,7 +129,7 @@ func (a *AppRoleAuth) Login(ctx context.Context, client *api.Client) (*api.Secre // if the caller indicated that the value was actually a wrapping token, unwrap it first if a.unwrap { - unwrappedToken, err := client.Logical().Unwrap(secretIDValue) + unwrappedToken, err := client.Logical().UnwrapWithContext(ctx, secretIDValue) if err != nil { return nil, fmt.Errorf("unable to unwrap response wrapping token: %w", err) } @@ -135,7 +139,7 @@ func (a *AppRoleAuth) Login(ctx context.Context, client *api.Client) (*api.Secre } path := fmt.Sprintf("auth/%s/login", a.mountPath) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with app role auth: %w", err) } diff --git a/api/auth/aws/aws.go b/api/auth/aws/aws.go index 9e229b871101..cef19beb8692 100644 --- a/api/auth/aws/aws.go +++ b/api/auth/aws/aws.go @@ -84,6 +84,10 @@ func NewAWSAuth(opts ...LoginOption) (*AWSAuth, error) { // variables. To specify a path to a credentials file on disk instead, set // the environment variable AWS_SHARED_CREDENTIALS_FILE. func (a *AWSAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := make(map[string]interface{}) switch a.authType { case ec2Type: @@ -182,7 +186,7 @@ func (a *AWSAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, e } path := fmt.Sprintf("auth/%s/login", a.mountPath) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with AWS auth: %w", err) } diff --git a/api/auth/azure/azure.go b/api/auth/azure/azure.go index 825003889a74..370ec573dd1b 100644 --- a/api/auth/azure/azure.go +++ b/api/auth/azure/azure.go @@ -90,6 +90,10 @@ func NewAzureAuth(roleName string, opts ...LoginOption) (*AzureAuth, error) { // Login sets up the required request body for the Azure auth method's /login // endpoint, and performs a write to it. func (a *AzureAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + jwtResp, err := a.getJWT() if err != nil { return nil, fmt.Errorf("unable to get access token: %w", err) @@ -110,7 +114,7 @@ func (a *AzureAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, } path := fmt.Sprintf("auth/%s/login", a.mountPath) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with Azure auth: %w", err) } diff --git a/api/auth/gcp/gcp.go b/api/auth/gcp/gcp.go index efa1d0b406c3..a5dd93646128 100644 --- a/api/auth/gcp/gcp.go +++ b/api/auth/gcp/gcp.go @@ -67,6 +67,10 @@ func NewGCPAuth(roleName string, opts ...LoginOption) (*GCPAuth, error) { // endpoint, and performs a write to it. This method defaults to the "gce" // auth type unless NewGCPAuth is called with WithIAMAuth(). func (a *GCPAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := map[string]interface{}{ "role": a.roleName, } @@ -86,7 +90,7 @@ func (a *GCPAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, e } path := fmt.Sprintf("auth/%s/login", a.mountPath) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with GCP auth: %w", err) } diff --git a/api/auth/kubernetes/kubernetes.go b/api/auth/kubernetes/kubernetes.go index 99541708f84b..c2fef86a5fd0 100644 --- a/api/auth/kubernetes/kubernetes.go +++ b/api/auth/kubernetes/kubernetes.go @@ -68,13 +68,17 @@ func NewKubernetesAuth(roleName string, opts ...LoginOption) (*KubernetesAuth, e } func (a *KubernetesAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := map[string]interface{}{ "jwt": a.serviceAccountToken, "role": a.roleName, } path := fmt.Sprintf("auth/%s/login", a.mountPath) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with Kubernetes auth: %w", err) } diff --git a/api/auth/ldap/ldap.go b/api/auth/ldap/ldap.go index 0653484d3e9d..9f37abc664f7 100644 --- a/api/auth/ldap/ldap.go +++ b/api/auth/ldap/ldap.go @@ -84,6 +84,10 @@ func NewLDAPAuth(username string, password *Password, opts ...LoginOption) (*LDA } func (a *LDAPAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := make(map[string]interface{}) if a.passwordFile != "" { @@ -103,7 +107,7 @@ func (a *LDAPAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, } path := fmt.Sprintf("auth/%s/login/%s", a.mountPath, a.username) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with LDAP auth: %w", err) } diff --git a/api/auth/userpass/userpass.go b/api/auth/userpass/userpass.go index d33e787f9cc4..124cd7a68f8e 100644 --- a/api/auth/userpass/userpass.go +++ b/api/auth/userpass/userpass.go @@ -88,6 +88,10 @@ func NewUserpassAuth(username string, password *Password, opts ...LoginOption) ( } func (a *UserpassAuth) Login(ctx context.Context, client *api.Client) (*api.Secret, error) { + if ctx == nil { + ctx = context.Background() + } + loginData := make(map[string]interface{}) if a.passwordFile != "" { @@ -107,7 +111,7 @@ func (a *UserpassAuth) Login(ctx context.Context, client *api.Client) (*api.Secr } path := fmt.Sprintf("auth/%s/login/%s", a.mountPath, a.username) - resp, err := client.Logical().Write(path, loginData) + resp, err := client.Logical().WriteWithContext(ctx, path, loginData) if err != nil { return nil, fmt.Errorf("unable to log in with userpass auth: %w", err) } diff --git a/api/client.go b/api/client.go index 99813a21b19c..c9cea7ca662d 100644 --- a/api/client.go +++ b/api/client.go @@ -36,6 +36,7 @@ const ( EnvVaultAddress = "VAULT_ADDR" EnvVaultAgentAddr = "VAULT_AGENT_ADDR" EnvVaultCACert = "VAULT_CACERT" + EnvVaultCACertBytes = "VAULT_CACERT_BYTES" EnvVaultCAPath = "VAULT_CAPATH" EnvVaultClientCert = "VAULT_CLIENT_CERT" EnvVaultClientKey = "VAULT_CLIENT_KEY" @@ -172,9 +173,14 @@ type Config struct { // used to communicate with Vault. type TLSConfig struct { // CACert is the path to a PEM-encoded CA cert file to use to verify the - // Vault server SSL certificate. + // Vault server SSL certificate. It takes precedence over CACertBytes + // and CAPath. CACert string + // CACertBytes is a PEM-encoded certificate or bundle. It takes precedence + // over CAPath. + CACertBytes []byte + // CAPath is the path to a directory of PEM-encoded CA cert files to verify // the Vault server SSL certificate. CAPath string @@ -266,12 +272,13 @@ func (c *Config) configureTLS(t *TLSConfig) error { return fmt.Errorf("both client cert and client key must be provided") } - if t.CACert != "" || t.CAPath != "" { + if t.CACert != "" || len(t.CACertBytes) != 0 || t.CAPath != "" { c.curlCACert = t.CACert c.curlCAPath = t.CAPath rootConfig := &rootcerts.Config{ - CAFile: t.CACert, - CAPath: t.CAPath, + CAFile: t.CACert, + CACertificate: t.CACertBytes, + CAPath: t.CAPath, } if err := rootcerts.ConfigureTLS(clientTLSConfig, rootConfig); err != nil { return err @@ -313,6 +320,7 @@ func (c *Config) ReadEnvironment() error { var envAddress string var envAgentAddress string var envCACert string + var envCACertBytes []byte var envCAPath string var envClientCert string var envClientKey string @@ -343,6 +351,9 @@ func (c *Config) ReadEnvironment() error { if v := os.Getenv(EnvVaultCACert); v != "" { envCACert = v } + if v := os.Getenv(EnvVaultCACertBytes); v != "" { + envCACertBytes = []byte(v) + } if v := os.Getenv(EnvVaultCAPath); v != "" { envCAPath = v } @@ -398,6 +409,7 @@ func (c *Config) ReadEnvironment() error { // Configure the HTTP clients TLS configuration. t := &TLSConfig{ CACert: envCACert, + CACertBytes: envCACertBytes, CAPath: envCAPath, ClientCert: envClientCert, ClientKey: envClientKey, @@ -576,7 +588,6 @@ func (c *Client) CloneConfig() *Config { newConfig.CheckRetry = c.config.CheckRetry newConfig.Logger = c.config.Logger newConfig.Limiter = c.config.Limiter - newConfig.OutputCurlString = c.config.OutputCurlString newConfig.SRVLookup = c.config.SRVLookup newConfig.CloneHeaders = c.config.CloneHeaders newConfig.CloneToken = c.config.CloneToken @@ -990,22 +1001,21 @@ func (c *Client) clone(cloneHeaders bool) (*Client, error) { defer config.modifyLock.RUnlock() newConfig := &Config{ - Address: config.Address, - HttpClient: config.HttpClient, - MinRetryWait: config.MinRetryWait, - MaxRetryWait: config.MaxRetryWait, - MaxRetries: config.MaxRetries, - Timeout: config.Timeout, - Backoff: config.Backoff, - CheckRetry: config.CheckRetry, - Logger: config.Logger, - Limiter: config.Limiter, - OutputCurlString: config.OutputCurlString, - AgentAddress: config.AgentAddress, - SRVLookup: config.SRVLookup, - CloneHeaders: config.CloneHeaders, - CloneToken: config.CloneToken, - ReadYourWrites: config.ReadYourWrites, + Address: config.Address, + HttpClient: config.HttpClient, + MinRetryWait: config.MinRetryWait, + MaxRetryWait: config.MaxRetryWait, + MaxRetries: config.MaxRetries, + Timeout: config.Timeout, + Backoff: config.Backoff, + CheckRetry: config.CheckRetry, + Logger: config.Logger, + Limiter: config.Limiter, + AgentAddress: config.AgentAddress, + SRVLookup: config.SRVLookup, + CloneHeaders: config.CloneHeaders, + CloneToken: config.CloneToken, + ReadYourWrites: config.ReadYourWrites, } client, err := NewClient(newConfig) if err != nil { diff --git a/api/client_test.go b/api/client_test.go index a8434b28c5bb..73a306453645 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -262,24 +262,37 @@ func TestDefaulRetryPolicy(t *testing.T) { func TestClientEnvSettings(t *testing.T) { cwd, _ := os.Getwd() + + caCertBytes, err := os.ReadFile(cwd + "/test-fixtures/keys/cert.pem") + if err != nil { + t.Fatalf("error reading %q cert file: %v", cwd+"/test-fixtures/keys/cert.pem", err) + } + oldCACert := os.Getenv(EnvVaultCACert) + oldCACertBytes := os.Getenv(EnvVaultCACertBytes) oldCAPath := os.Getenv(EnvVaultCAPath) oldClientCert := os.Getenv(EnvVaultClientCert) oldClientKey := os.Getenv(EnvVaultClientKey) oldSkipVerify := os.Getenv(EnvVaultSkipVerify) oldMaxRetries := os.Getenv(EnvVaultMaxRetries) + os.Setenv(EnvVaultCACert, cwd+"/test-fixtures/keys/cert.pem") + os.Setenv(EnvVaultCACertBytes, string(caCertBytes)) os.Setenv(EnvVaultCAPath, cwd+"/test-fixtures/keys") os.Setenv(EnvVaultClientCert, cwd+"/test-fixtures/keys/cert.pem") os.Setenv(EnvVaultClientKey, cwd+"/test-fixtures/keys/key.pem") os.Setenv(EnvVaultSkipVerify, "true") os.Setenv(EnvVaultMaxRetries, "5") - defer os.Setenv(EnvVaultCACert, oldCACert) - defer os.Setenv(EnvVaultCAPath, oldCAPath) - defer os.Setenv(EnvVaultClientCert, oldClientCert) - defer os.Setenv(EnvVaultClientKey, oldClientKey) - defer os.Setenv(EnvVaultSkipVerify, oldSkipVerify) - defer os.Setenv(EnvVaultMaxRetries, oldMaxRetries) + + defer func() { + os.Setenv(EnvVaultCACert, oldCACert) + os.Setenv(EnvVaultCACertBytes, oldCACertBytes) + os.Setenv(EnvVaultCAPath, oldCAPath) + os.Setenv(EnvVaultClientCert, oldClientCert) + os.Setenv(EnvVaultClientKey, oldClientKey) + os.Setenv(EnvVaultSkipVerify, oldSkipVerify) + os.Setenv(EnvVaultMaxRetries, oldMaxRetries) + }() config := DefaultConfig() if err := config.ReadEnvironment(); err != nil { @@ -513,8 +526,8 @@ func TestClone(t *testing.T) { if parent.MaxRetries() != clone.MaxRetries() { t.Fatalf("maxRetries don't match: %v vs %v", parent.MaxRetries(), clone.MaxRetries()) } - if parent.OutputCurlString() != clone.OutputCurlString() { - t.Fatalf("outputCurlString doesn't match: %v vs %v", parent.OutputCurlString(), clone.OutputCurlString()) + if parent.OutputCurlString() == clone.OutputCurlString() { + t.Fatalf("outputCurlString was copied over when it shouldn't have been: %v and %v", parent.OutputCurlString(), clone.OutputCurlString()) } if parent.SRVLookup() != clone.SRVLookup() { t.Fatalf("SRVLookup doesn't match: %v vs %v", parent.SRVLookup(), clone.SRVLookup()) diff --git a/api/lifetime_watcher.go b/api/lifetime_watcher.go index f775dfb15a7d..f06263526f35 100644 --- a/api/lifetime_watcher.go +++ b/api/lifetime_watcher.go @@ -113,7 +113,9 @@ type LifetimeWatcherInput struct { // The new TTL, in seconds, that should be set on the lease. The TTL set // here may or may not be honored by the vault server, based on Vault - // configuration or any associated max TTL values. + // configuration or any associated max TTL values. If specified, the + // minimum of this value and the remaining lease duration will be used + // for grace period calculations. Increment int // RenewBehavior controls what happens when a renewal errors or the @@ -257,7 +259,7 @@ func (r *LifetimeWatcher) doRenewWithOptions(tokenMode bool, nonRenewable bool, initialTime := time.Now() priorDuration := time.Duration(initLeaseDuration) * time.Second - r.calculateGrace(priorDuration) + r.calculateGrace(priorDuration, time.Duration(r.increment)*time.Second) var errorBackoff backoff.BackOff for { @@ -345,7 +347,7 @@ func (r *LifetimeWatcher) doRenewWithOptions(tokenMode bool, nonRenewable bool, // extending. Once it stops extending, we've hit the max and need to // rely on the grace duration. if remainingLeaseDuration > priorDuration { - r.calculateGrace(remainingLeaseDuration) + r.calculateGrace(remainingLeaseDuration, time.Duration(r.increment)*time.Second) } priorDuration = remainingLeaseDuration @@ -373,16 +375,21 @@ func (r *LifetimeWatcher) doRenewWithOptions(tokenMode bool, nonRenewable bool, } } -// calculateGrace calculates the grace period based on a reasonable set of -// assumptions given the total lease time; it also adds some jitter to not have -// clients be in sync. -func (r *LifetimeWatcher) calculateGrace(leaseDuration time.Duration) { - if leaseDuration <= 0 { +// calculateGrace calculates the grace period based on the minimum of the +// remaining lease duration and the token increment value; it also adds some +// jitter to not have clients be in sync. +func (r *LifetimeWatcher) calculateGrace(leaseDuration, increment time.Duration) { + minDuration := leaseDuration + if minDuration > increment && increment > 0 { + minDuration = increment + } + + if minDuration <= 0 { r.grace = 0 return } - leaseNanos := float64(leaseDuration.Nanoseconds()) + leaseNanos := float64(minDuration.Nanoseconds()) jitterMax := 0.1 * leaseNanos // For a given lease duration, we want to allow 80-90% of that to elapse, diff --git a/api/renewer_test.go b/api/renewer_test.go index 3fa9ed8e2bcf..2a5d5745ba64 100644 --- a/api/renewer_test.go +++ b/api/renewer_test.go @@ -24,28 +24,28 @@ func TestRenewer_NewRenewer(t *testing.T) { err bool }{ { - "nil", - nil, - nil, - true, + name: "nil", + i: nil, + e: nil, + err: true, }, { - "missing_secret", - &RenewerInput{ + name: "missing_secret", + i: &RenewerInput{ Secret: nil, }, - nil, - true, + e: nil, + err: true, }, { - "default_grace", - &RenewerInput{ + name: "default_grace", + i: &RenewerInput{ Secret: &Secret{}, }, - &Renewer{ + e: &Renewer{ secret: &Secret{}, }, - false, + err: false, }, } @@ -98,67 +98,78 @@ func TestLifetimeWatcher(t *testing.T) { expectRenewal bool }{ { - time.Second, - "no_error", - 60, - 60, - func(_ string, _ int) (*Secret, error) { + maxTestTime: time.Second, + name: "no_error", + leaseDurationSeconds: 60, + incrementSeconds: 60, + renew: func(_ string, _ int) (*Secret, error) { return renewedSecret, nil }, - nil, - true, + expectError: nil, + expectRenewal: true, }, { - 5 * time.Second, - "one_error", - 15, - 15, - func(_ string, _ int) (*Secret, error) { + maxTestTime: time.Second, + name: "short_increment_duration", + leaseDurationSeconds: 60, + incrementSeconds: 10, + renew: func(_ string, _ int) (*Secret, error) { + return renewedSecret, nil + }, + expectError: nil, + expectRenewal: true, + }, + { + maxTestTime: 5 * time.Second, + name: "one_error", + leaseDurationSeconds: 15, + incrementSeconds: 15, + renew: func(_ string, _ int) (*Secret, error) { if caseOneErrorCount == 0 { caseOneErrorCount++ return nil, fmt.Errorf("renew failure") } return renewedSecret, nil }, - nil, - true, + expectError: nil, + expectRenewal: true, }, { - 15 * time.Second, - "many_errors", - 15, - 15, - func(_ string, _ int) (*Secret, error) { + maxTestTime: 15 * time.Second, + name: "many_errors", + leaseDurationSeconds: 15, + incrementSeconds: 15, + renew: func(_ string, _ int) (*Secret, error) { if caseManyErrorsCount == 3 { return renewedSecret, nil } caseManyErrorsCount++ return nil, fmt.Errorf("renew failure") }, - nil, - true, + expectError: nil, + expectRenewal: true, }, { - 15 * time.Second, - "only_errors", - 15, - 15, - func(_ string, _ int) (*Secret, error) { + maxTestTime: 15 * time.Second, + name: "only_errors", + leaseDurationSeconds: 15, + incrementSeconds: 15, + renew: func(_ string, _ int) (*Secret, error) { return nil, fmt.Errorf("renew failure") }, - nil, - false, + expectError: nil, + expectRenewal: false, }, { - 15 * time.Second, - "negative_lease_duration", - -15, - 15, - func(_ string, _ int) (*Secret, error) { + maxTestTime: 15 * time.Second, + name: "negative_lease_duration", + leaseDurationSeconds: -15, + incrementSeconds: 15, + renew: func(_ string, _ int) (*Secret, error) { return renewedSecret, nil }, - nil, - true, + expectError: nil, + expectRenewal: true, }, } diff --git a/builtin/credential/aws/backend_e2e_test.go b/builtin/credential/aws/backend_e2e_test.go index 0e8186a44362..ac2bb22f129a 100644 --- a/builtin/credential/aws/backend_e2e_test.go +++ b/builtin/credential/aws/backend_e2e_test.go @@ -63,10 +63,10 @@ func TestBackend_E2E_Initialize(t *testing.T) { "policies": "default", "bound_subnet_id": "subnet-abcdef", } - if _, err := core.Client.Logical().WriteWithContext(context.Background(), "auth/aws/role/test-role", data); err != nil { + if _, err := core.Client.Logical().Write("auth/aws/role/test-role", data); err != nil { t.Fatal(err) } - role, err := core.Client.Logical().ReadWithContext(context.Background(), "auth/aws/role/test-role") + role, err := core.Client.Logical().Read("auth/aws/role/test-role") if err != nil { t.Fatal(err) } diff --git a/builtin/credential/aws/path_login.go b/builtin/credential/aws/path_login.go index 50cdd37d2568..fe70de0d06e8 100644 --- a/builtin/credential/aws/path_login.go +++ b/builtin/credential/aws/path_login.go @@ -1407,6 +1407,11 @@ func (b *backend) pathLoginUpdateIam(ctx context.Context, req *logical.Request, Name: identityAlias, }, } + + if entity.Type == "assumed-role" { + auth.DisplayName = strings.Join([]string{entity.FriendlyName, entity.SessionInfo}, "/") + } + roleEntry.PopulateTokenAuth(auth) if err := identityConfigEntry.IAMAuthMetadataHandler.PopulateDesiredMetadata(auth, map[string]string{ "client_arn": callerID.Arn, diff --git a/builtin/credential/cert/backend_test.go b/builtin/credential/cert/backend_test.go index 8a58c4575f6f..db400dab780a 100644 --- a/builtin/credential/cert/backend_test.go +++ b/builtin/credential/cert/backend_test.go @@ -272,7 +272,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { var err error // Mount /pki as a root CA - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -285,7 +285,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { // Set the cluster's certificate as the root CA in /pki pemBundleRootCA := string(cluster.CACertPEM) + string(cluster.CAKeyPEM) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/config/ca", map[string]interface{}{ + _, err = client.Logical().Write("pki/config/ca", map[string]interface{}{ "pem_bundle": pemBundleRootCA, }) if err != nil { @@ -293,7 +293,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { } // Mount /pki2 to operate as an intermediate CA - err = client.Sys().MountWithContext(context.Background(), "pki2", &api.MountInput{ + err = client.Sys().Mount("pki2", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -305,14 +305,14 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { } // Create a CSR for the intermediate CA - secret, err := client.Logical().WriteWithContext(context.Background(), "pki2/intermediate/generate/internal", nil) + secret, err := client.Logical().Write("pki2/intermediate/generate/internal", nil) if err != nil { t.Fatal(err) } intermediateCSR := secret.Data["csr"].(string) // Sign the intermediate CSR using /pki - secret, err = client.Logical().WriteWithContext(context.Background(), "pki/root/sign-intermediate", map[string]interface{}{ + secret, err = client.Logical().Write("pki/root/sign-intermediate", map[string]interface{}{ "permitted_dns_domains": ".myvault.com", "csr": intermediateCSR, }) @@ -322,7 +322,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { intermediateCertPEM := secret.Data["certificate"].(string) // Configure the intermediate cert as the CA in /pki2 - _, err = client.Logical().WriteWithContext(context.Background(), "pki2/intermediate/set-signed", map[string]interface{}{ + _, err = client.Logical().Write("pki2/intermediate/set-signed", map[string]interface{}{ "certificate": intermediateCertPEM, }) if err != nil { @@ -330,7 +330,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { } // Create a role on the intermediate CA mount - _, err = client.Logical().WriteWithContext(context.Background(), "pki2/roles/myvault-dot-com", map[string]interface{}{ + _, err = client.Logical().Write("pki2/roles/myvault-dot-com", map[string]interface{}{ "allowed_domains": "myvault.com", "allow_subdomains": "true", "max_ttl": "5m", @@ -340,7 +340,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { } // Issue a leaf cert using the intermediate CA - secret, err = client.Logical().WriteWithContext(context.Background(), "pki2/issue/myvault-dot-com", map[string]interface{}{ + secret, err = client.Logical().Write("pki2/issue/myvault-dot-com", map[string]interface{}{ "common_name": "cert.myvault.com", "format": "pem", "ip_sans": "127.0.0.1", @@ -360,7 +360,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { } // Set the intermediate CA cert as a trusted certificate in the backend - _, err = client.Logical().WriteWithContext(context.Background(), "auth/cert/certs/myvault-dot-com", map[string]interface{}{ + _, err = client.Logical().Write("auth/cert/certs/myvault-dot-com", map[string]interface{}{ "display_name": "myvault.com", "policies": "default", "certificate": intermediateCertPEM, @@ -447,7 +447,7 @@ func TestBackend_PermittedDNSDomainsIntermediateCA(t *testing.T) { // Create a new api client with the desired TLS configuration newClient := getAPIClient(cores[0].Listeners[0].Address.Port, cores[0].TLSConfig) - secret, err = newClient.Logical().WriteWithContext(context.Background(), "auth/cert/login", map[string]interface{}{ + secret, err = newClient.Logical().Write("auth/cert/login", map[string]interface{}{ "name": "myvault-dot-com", }) if err != nil { diff --git a/builtin/logical/database/backend_test.go b/builtin/logical/database/backend_test.go index 9a08460820e8..5dff16ea756c 100644 --- a/builtin/logical/database/backend_test.go +++ b/builtin/logical/database/backend_test.go @@ -689,7 +689,7 @@ func TestBackend_connectionCrud(t *testing.T) { if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } - if len(resp.Warnings) != 1 { + if len(resp.Warnings) == 0 { t.Fatalf("expected warning about password in url %s, resp:%#v\n", connURL, resp) } diff --git a/builtin/logical/pki/backend_test.go b/builtin/logical/pki/backend_test.go index f04d9fad99cf..bbb445eba8e0 100644 --- a/builtin/logical/pki/backend_test.go +++ b/builtin/logical/pki/backend_test.go @@ -67,7 +67,7 @@ func TestPKI_RequireCN(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -78,7 +78,7 @@ func TestPKI_RequireCN(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err := client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", }) if err != nil { @@ -89,7 +89,7 @@ func TestPKI_RequireCN(t *testing.T) { } // Create a role which does require CN (default) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/example", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/example", map[string]interface{}{ "allowed_domains": "foobar.com,zipzap.com,abc.com,xyz.com", "allow_bare_domains": true, "allow_subdomains": true, @@ -101,7 +101,7 @@ func TestPKI_RequireCN(t *testing.T) { // Issue a cert with require_cn set to true and with common name supplied. // It should succeed. - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/example", map[string]interface{}{ + resp, err = client.Logical().Write("pki/issue/example", map[string]interface{}{ "common_name": "foobar.com", }) if err != nil { @@ -110,13 +110,13 @@ func TestPKI_RequireCN(t *testing.T) { // Issue a cert with require_cn set to true and with out supplying the // common name. It should error out. - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/example", map[string]interface{}{}) + resp, err = client.Logical().Write("pki/issue/example", map[string]interface{}{}) if err == nil { t.Fatalf("expected an error due to missing common_name") } // Modify the role to make the common name optional - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/example", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/example", map[string]interface{}{ "allowed_domains": "foobar.com,zipzap.com,abc.com,xyz.com", "allow_bare_domains": true, "allow_subdomains": true, @@ -129,7 +129,7 @@ func TestPKI_RequireCN(t *testing.T) { // Issue a cert with require_cn set to false and without supplying the // common name. It should succeed. - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/example", map[string]interface{}{}) + resp, err = client.Logical().Write("pki/issue/example", map[string]interface{}{}) if err != nil { t.Fatal(err) } @@ -140,7 +140,7 @@ func TestPKI_RequireCN(t *testing.T) { // Issue a cert with require_cn set to false and with a common name. It // should succeed. - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/example", map[string]interface{}{}) + resp, err = client.Logical().Write("pki/issue/example", map[string]interface{}{}) if err != nil { t.Fatal(err) } @@ -164,7 +164,7 @@ func TestPKI_DeviceCert(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -175,7 +175,7 @@ func TestPKI_DeviceCert(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err := client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", "not_after": "9999-12-31T23:59:59Z", }) @@ -202,7 +202,7 @@ func TestPKI_DeviceCert(t *testing.T) { } // Create a role which does require CN (default) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/example", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/example", map[string]interface{}{ "allowed_domains": "foobar.com,zipzap.com,abc.com,xyz.com", "allow_bare_domains": true, "allow_subdomains": true, @@ -214,7 +214,7 @@ func TestPKI_DeviceCert(t *testing.T) { // Issue a cert with require_cn set to true and with common name supplied. // It should succeed. - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/example", map[string]interface{}{ + resp, err = client.Logical().Write("pki/issue/example", map[string]interface{}{ "common_name": "foobar.com", }) if err != nil { @@ -250,7 +250,7 @@ func TestBackend_InvalidParameter(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -261,7 +261,7 @@ func TestBackend_InvalidParameter(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", "not_after": "9999-12-31T23:59:59Z", "ttl": "25h", @@ -270,7 +270,7 @@ func TestBackend_InvalidParameter(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", "not_after": "9999-12-31T23:59:59", }) @@ -702,6 +702,46 @@ func generateURLSteps(t *testing.T, caCert, caKey string, intdata, reqdata map[s return ret } +func generateCSR(t *testing.T, csrTemplate *x509.CertificateRequest, keyType string, keyBits int) (interface{}, []byte, string) { + var priv interface{} + var err error + switch keyType { + case "rsa": + priv, err = rsa.GenerateKey(rand.Reader, keyBits) + case "ec": + switch keyBits { + case 224: + priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + case 256: + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case 384: + priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + case 521: + priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) + default: + t.Fatalf("Got unknown ec< key bits: %v", keyBits) + } + case "ed25519": + _, priv, err = ed25519.GenerateKey(rand.Reader) + } + + if err != nil { + t.Fatalf("Got error generating private key for CSR: %v", err) + } + + csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, priv) + if err != nil { + t.Fatalf("Got error generating CSR: %v", err) + } + + csrPem := strings.TrimSpace(string(pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE REQUEST", + Bytes: csr, + }))) + + return priv, csr, csrPem +} + func generateCSRSteps(t *testing.T, caCert, caKey string, intdata, reqdata map[string]interface{}) []logicaltest.TestStep { csrTemplate := x509.CertificateRequest{ Subject: pkix.Name{ @@ -726,12 +766,7 @@ func generateCSRSteps(t *testing.T, caCert, caKey string, intdata, reqdata map[s }, } - priv, _ := rsa.GenerateKey(rand.Reader, 2048) - csr, _ := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, priv) - csrPem := strings.TrimSpace(string(pem.EncodeToMemory(&pem.Block{ - Type: "CERTIFICATE REQUEST", - Bytes: csr, - }))) + _, _, csrPem := generateCSR(t, &csrTemplate, "rsa", 2048) ret := []logicaltest.TestStep{ { @@ -1260,7 +1295,7 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep { for name, allowedInt := range cnMap { roleVals.KeyType = "rsa" roleVals.KeyBits = 2048 - if mathRand.Int()%2 == 1 { + if mathRand.Int()%3 == 1 { roleVals.KeyType = "ec" roleVals.KeyBits = 224 } @@ -1714,30 +1749,96 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep { } func TestBackend_PathFetchValidRaw(t *testing.T) { - // create the backend config := logical.TestBackendConfig() storage := &logical.InmemStorage{} config.StorageView = storage b := Backend(config) err := b.Setup(context.Background(), config) - if err != nil { - t.Fatal(err) + require.NoError(t, err) + + resp, err := b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.UpdateOperation, + Path: "root/generate/internal", + Storage: storage, + Data: map[string]interface{}{ + "common_name": "test.com", + "ttl": "6h", + }, + MountPoint: "pki/", + }) + require.NoError(t, err) + if resp != nil && resp.IsError() { + t.Fatalf("failed to generate root, %#v", resp) } + rootCaAsPem := resp.Data["certificate"].(string) - expectedSerial := "17:67:16:b0:b9:45:58:c0:3a:29:e3:cb:d6:98:33:7a:a6:3b:66:c1" - expectedCert := []byte("test certificate") - entry := &logical.StorageEntry{ - Key: fmt.Sprintf("certs/%s", normalizeSerial(expectedSerial)), - Value: expectedCert, + // The ca_chain call at least for now does not return the root CA authority + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.ReadOperation, + Path: "ca_chain", + Storage: storage, + Data: map[string]interface{}{}, + MountPoint: "pki/", + }) + require.NoError(t, err) + if resp != nil && resp.IsError() { + t.Fatalf("failed read ca_chain, %#v", resp) } - err = storage.Put(context.Background(), entry) - if err != nil { - t.Fatal(err) + require.Equal(t, []byte{}, resp.Data[logical.HTTPRawBody], "ca_chain response should have been empty") + + // The ca/pem should return us the actual CA... + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.ReadOperation, + Path: "ca/pem", + Storage: storage, + Data: map[string]interface{}{}, + MountPoint: "pki/", + }) + require.NoError(t, err) + if resp != nil && resp.IsError() { + t.Fatalf("failed read ca/pem, %#v", resp) + } + // check the raw cert matches the response body + if bytes.Compare(resp.Data[logical.HTTPRawBody].([]byte), []byte(rootCaAsPem)) != 0 { + t.Fatalf("failed to get raw cert") } + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.UpdateOperation, + Path: "roles/example", + Storage: storage, + Data: map[string]interface{}{ + "allowed_domains": "example.com", + "allow_subdomains": "true", + "max_ttl": "1h", + "no_store": "false", + }, + MountPoint: "pki/", + }) + require.NoError(t, err, "error setting up pki role: %v", err) + + // Now issue a short-lived certificate from our pki-external. + resp, err = b.HandleRequest(context.Background(), &logical.Request{ + Operation: logical.UpdateOperation, + Path: "issue/example", + Storage: storage, + Data: map[string]interface{}{ + "common_name": "test.example.com", + "ttl": "5m", + }, + MountPoint: "pki/", + }) + require.NoError(t, err, "error issuing certificate: %v", err) + require.NotNil(t, resp, "got nil response from issuing request") + + issueCrtAsPem := resp.Data["certificate"].(string) + issuedCrt := parseCert(t, issueCrtAsPem) + expectedSerial := certutil.GetHexFormatted(issuedCrt.SerialNumber.Bytes(), ":") + expectedCert := []byte(issueCrtAsPem) + // get der cert - resp, err := b.HandleRequest(context.Background(), &logical.Request{ + resp, err = b.HandleRequest(context.Background(), &logical.Request{ Operation: logical.ReadOperation, Path: fmt.Sprintf("cert/%s/raw", expectedSerial), Storage: storage, @@ -1750,8 +1851,10 @@ func TestBackend_PathFetchValidRaw(t *testing.T) { } // check the raw cert matches the response body - if bytes.Compare(resp.Data[logical.HTTPRawBody].([]byte), expectedCert) != 0 { - t.Fatalf("failed to get raw cert") + rawBody := resp.Data[logical.HTTPRawBody].([]byte) + bodyAsPem := []byte(strings.TrimSpace(string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rawBody})))) + if bytes.Compare(bodyAsPem, expectedCert) != 0 { + t.Fatalf("failed to get raw cert for serial number: %s", expectedSerial) } if resp.Data[logical.HTTPContentType] != "application/pkix-cert" { t.Fatalf("failed to get raw cert content-type") @@ -1770,13 +1873,8 @@ func TestBackend_PathFetchValidRaw(t *testing.T) { t.Fatal(err) } - pemBlock := &pem.Block{ - Type: "CERTIFICATE", - Bytes: expectedCert, - } - pemCert := []byte(strings.TrimSpace(string(pem.EncodeToMemory(pemBlock)))) // check the pem cert matches the response body - if bytes.Compare(resp.Data[logical.HTTPRawBody].([]byte), pemCert) != 0 { + if bytes.Compare(resp.Data[logical.HTTPRawBody].([]byte), expectedCert) != 0 { t.Fatalf("failed to get pem cert") } if resp.Data[logical.HTTPContentType] != "application/pem-certificate-chain" { @@ -1918,6 +2016,23 @@ func TestBackend_PathFetchCertList(t *testing.T) { } func TestBackend_SignVerbatim(t *testing.T) { + testCases := []struct { + testName string + keyType string + }{ + {testName: "RSA", keyType: "rsa"}, + {testName: "ED25519", keyType: "ed25519"}, + {testName: "EC", keyType: "ec"}, + {testName: "Any", keyType: "any"}, + } + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + runTestSignVerbatim(t, tc.keyType) + }) + } +} + +func runTestSignVerbatim(t *testing.T, keyType string) { // create the backend config := logical.TestBackendConfig() storage := &logical.InmemStorage{} @@ -1995,8 +2110,9 @@ func TestBackend_SignVerbatim(t *testing.T) { // create a role entry; we use this to check that sign-verbatim when used with a role is still honoring TTLs roleData := map[string]interface{}{ - "ttl": "4h", - "max_ttl": "8h", + "ttl": "4h", + "max_ttl": "8h", + "key_type": keyType, } resp, err = b.HandleRequest(context.Background(), &logical.Request{ Operation: logical.UpdateOperation, @@ -2109,6 +2225,7 @@ func TestBackend_SignVerbatim(t *testing.T) { "ttl": "4h", "max_ttl": "8h", "generate_lease": true, + "key_type": keyType, } resp, err = b.HandleRequest(context.Background(), &logical.Request{ Operation: logical.UpdateOperation, @@ -2161,7 +2278,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -2172,7 +2289,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err := client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", }) if err != nil { @@ -2181,7 +2298,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { if resp == nil { t.Fatal("expected ca info") } - resp, err = client.Logical().ReadWithContext(context.Background(), "pki/cert/ca_chain") + resp, err = client.Logical().Read("pki/cert/ca_chain") if err != nil { t.Fatalf("error reading ca_chain: %v", err) } @@ -2189,7 +2306,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { r1Data := resp.Data // Try again, make sure it's a 204 and same CA - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", }) if err != nil { @@ -2201,7 +2318,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { if resp.Data != nil || len(resp.Warnings) == 0 { t.Fatalf("bad response: %#v", *resp) } - resp, err = client.Logical().ReadWithContext(context.Background(), "pki/cert/ca_chain") + resp, err = client.Logical().Read("pki/cert/ca_chain") if err != nil { t.Fatalf("error reading ca_chain: %v", err) } @@ -2210,7 +2327,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { t.Fatal("got different ca certs") } - resp, err = client.Logical().DeleteWithContext(context.Background(), "pki/root") + resp, err = client.Logical().Delete("pki/root") if err != nil { t.Fatal(err) } @@ -2218,7 +2335,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { t.Fatal("expected nil response") } // Make sure it behaves the same - resp, err = client.Logical().DeleteWithContext(context.Background(), "pki/root") + resp, err = client.Logical().Delete("pki/root") if err != nil { t.Fatal(err) } @@ -2226,12 +2343,12 @@ func TestBackend_Root_Idempotency(t *testing.T) { t.Fatal("expected nil response") } - _, err = client.Logical().ReadWithContext(context.Background(), "pki/cert/ca_chain") + _, err = client.Logical().Read("pki/cert/ca_chain") if err == nil { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "common_name": "myvault.com", }) if err != nil { @@ -2241,7 +2358,7 @@ func TestBackend_Root_Idempotency(t *testing.T) { t.Fatal("expected ca info") } - _, err = client.Logical().ReadWithContext(context.Background(), "pki/cert/ca_chain") + _, err = client.Logical().Read("pki/cert/ca_chain") if err != nil { t.Fatal(err) } @@ -2261,7 +2378,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "root", &api.MountInput{ + err = client.Sys().Mount("root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -2271,7 +2388,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { if err != nil { t.Fatal(err) } - err = client.Sys().MountWithContext(context.Background(), "int", &api.MountInput{ + err = client.Sys().Mount("int", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "4h", @@ -2283,7 +2400,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { } // Direct issuing from root - _, err = client.Logical().WriteWithContext(context.Background(), "root/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("root/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -2291,7 +2408,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "root/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("root/roles/test", map[string]interface{}{ "allow_bare_domains": true, "allow_subdomains": true, }) @@ -2299,7 +2416,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "int/intermediate/generate/internal", map[string]interface{}{ + resp, err := client.Logical().Write("int/intermediate/generate/internal", map[string]interface{}{ "common_name": "myint.com", }) if err != nil { @@ -2308,7 +2425,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { csr := resp.Data["csr"] - _, err = client.Logical().WriteWithContext(context.Background(), "root/sign/test", map[string]interface{}{ + _, err = client.Logical().Write("root/sign/test", map[string]interface{}{ "common_name": "myint.com", "csr": csr, "ttl": "60h", @@ -2317,7 +2434,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { t.Fatal("expected error") } - _, err = client.Logical().WriteWithContext(context.Background(), "root/sign-verbatim/test", map[string]interface{}{ + _, err = client.Logical().Write("root/sign-verbatim/test", map[string]interface{}{ "common_name": "myint.com", "other_sans": "1.3.6.1.4.1.311.20.2.3;utf8:caadmin@example.com", "csr": csr, @@ -2327,7 +2444,7 @@ func TestBackend_SignIntermediate_AllowedPastCA(t *testing.T) { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/root/sign-intermediate", map[string]interface{}{ + resp, err = client.Logical().Write("root/root/sign-intermediate", map[string]interface{}{ "common_name": "myint.com", "other_sans": "1.3.6.1.4.1.311.20.2.3;utf8:caadmin@example.com", "csr": csr, @@ -2655,7 +2772,7 @@ func TestBackend_OID_SANs(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "root", &api.MountInput{ + err = client.Sys().Mount("root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -2671,7 +2788,7 @@ func TestBackend_OID_SANs(t *testing.T) { var block *pem.Block var cert *x509.Certificate - _, err = client.Logical().WriteWithContext(context.Background(), "root/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("root/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -2679,7 +2796,7 @@ func TestBackend_OID_SANs(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "root/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("root/roles/test", map[string]interface{}{ "allowed_domains": []string{"foobar.com", "zipzap.com"}, "allow_bare_domains": true, "allow_subdomains": true, @@ -2693,7 +2810,7 @@ func TestBackend_OID_SANs(t *testing.T) { // Get a baseline before adding OID SANs. In the next sections we'll verify // that the SANs are all added even as the OID SAN inclusion forces other // adding logic (custom rather than built-in Golang logic) - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foobar.com,foo.foobar.com,bar.foobar.com", @@ -2719,7 +2836,7 @@ func TestBackend_OID_SANs(t *testing.T) { } // First test some bad stuff that shouldn't work - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2731,7 +2848,7 @@ func TestBackend_OID_SANs(t *testing.T) { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2743,7 +2860,7 @@ func TestBackend_OID_SANs(t *testing.T) { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2755,7 +2872,7 @@ func TestBackend_OID_SANs(t *testing.T) { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2767,7 +2884,7 @@ func TestBackend_OID_SANs(t *testing.T) { t.Fatal("expected error") } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2780,7 +2897,7 @@ func TestBackend_OID_SANs(t *testing.T) { } // Valid for first possibility - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2810,7 +2927,7 @@ func TestBackend_OID_SANs(t *testing.T) { } // Valid for second possibility - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2846,7 +2963,7 @@ func TestBackend_OID_SANs(t *testing.T) { fmt.Sprintf("%s;%s:%s", oid1, type1, val1), fmt.Sprintf("%s;%s:%s", oid2, type2, val2), } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -2898,7 +3015,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "root", &api.MountInput{ + err = client.Sys().Mount("root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -2914,7 +3031,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { var block *pem.Block var cert *x509.Certificate - _, err = client.Logical().WriteWithContext(context.Background(), "root/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("root/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -2923,7 +3040,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { } // First test that Serial Numbers are not allowed - _, err = client.Logical().WriteWithContext(context.Background(), "root/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("root/roles/test", map[string]interface{}{ "allow_any_name": true, "enforce_hostnames": false, }) @@ -2931,7 +3048,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar", "ttl": "1h", }) @@ -2939,7 +3056,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar", "ttl": "1h", "serial_number": "foobar", @@ -2949,7 +3066,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { } // Update the role to allow serial numbers - _, err = client.Logical().WriteWithContext(context.Background(), "root/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("root/roles/test", map[string]interface{}{ "allow_any_name": true, "enforce_hostnames": false, "allowed_serial_numbers": "f00*,b4r*", @@ -2958,7 +3075,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar", "ttl": "1h", // Not a valid serial number @@ -2969,7 +3086,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { } // Valid for first possibility - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar", "serial_number": "f00bar", }) @@ -2990,7 +3107,7 @@ func TestBackend_AllowedSerialNumbers(t *testing.T) { } // Valid for second possibility - resp, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar", "serial_number": "b4rf00", }) @@ -3025,7 +3142,7 @@ func TestBackend_URI_SANs(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "root", &api.MountInput{ + err = client.Sys().Mount("root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3036,7 +3153,7 @@ func TestBackend_URI_SANs(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "root/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("root/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -3044,7 +3161,7 @@ func TestBackend_URI_SANs(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "root/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("root/roles/test", map[string]interface{}{ "allowed_domains": []string{"foobar.com", "zipzap.com"}, "allow_bare_domains": true, "allow_subdomains": true, @@ -3056,7 +3173,7 @@ func TestBackend_URI_SANs(t *testing.T) { } // First test some bad stuff that shouldn't work - _, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + _, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -3068,7 +3185,7 @@ func TestBackend_URI_SANs(t *testing.T) { } // Test valid single entry - _, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + _, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -3080,7 +3197,7 @@ func TestBackend_URI_SANs(t *testing.T) { } // Test globed entry - _, err = client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + _, err = client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -3092,7 +3209,7 @@ func TestBackend_URI_SANs(t *testing.T) { } // Test multiple entries - resp, err := client.Logical().WriteWithContext(context.Background(), "root/issue/test", map[string]interface{}{ + resp, err := client.Logical().Write("root/issue/test", map[string]interface{}{ "common_name": "foobar.com", "ip_sans": "1.2.3.4", "alt_names": "foo.foobar.com,bar.foobar.com", @@ -3141,7 +3258,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { client := cluster.Cores[0].Client // Write test policy for userpass auth method. - err := client.Sys().PutPolicyWithContext(context.Background(), "test", ` + err := client.Sys().PutPolicy("test", ` path "pki/*" { capabilities = ["update"] }`) @@ -3155,7 +3272,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { } // Configure test role for userpass. - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/userpassname", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/userpassname", map[string]interface{}{ "password": "test", "policies": "test", }); err != nil { @@ -3163,7 +3280,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { } // Login userpass for test role and keep client token. - secret, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/login/userpassname", map[string]interface{}{ + secret, err := client.Logical().Write("auth/userpass/login/userpassname", map[string]interface{}{ "password": "test", }) if err != nil || secret == nil { @@ -3172,14 +3289,14 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { userpassToken := secret.Auth.ClientToken // Get auth accessor for identity template. - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } userpassAccessor := auths["userpass/"].Accessor // Mount PKI. - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3191,7 +3308,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { } // Generate internal CA. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -3200,7 +3317,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { } // Write role PKI. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/test", map[string]interface{}{ "allowed_uri_sans": []string{ "spiffe://domain/{{identity.entity.aliases." + userpassAccessor + ".name}}", "spiffe://domain/{{identity.entity.aliases." + userpassAccessor + ".name}}/*", "spiffe://domain/foo", @@ -3214,27 +3331,27 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { // Issue certificate with identity templating client.SetToken(userpassToken) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/userpassname, spiffe://domain/foo"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/userpassname, spiffe://domain/foo"}) if err != nil { t.Fatal(err) } // Issue certificate with identity templating and glob client.SetToken(userpassToken) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/userpassname/bar"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/userpassname/bar"}) if err != nil { t.Fatal(err) } // Issue certificate with non-matching identity template parameter client.SetToken(userpassToken) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/unknownuser"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/unknownuser"}) if err == nil { t.Fatal(err) } // Set allowed_uri_sans_template to false. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/test", map[string]interface{}{ "allowed_uri_sans_template": false, }) if err != nil { @@ -3242,7 +3359,7 @@ func TestBackend_AllowedURISANsTemplate(t *testing.T) { } // Issue certificate with userpassToken. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/users/userpassname"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"uri_sans": "spiffe://domain/users/userpassname"}) if err == nil { t.Fatal("expected error") } @@ -3265,7 +3382,7 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { client := cluster.Cores[0].Client // Write test policy for userpass auth method. - err := client.Sys().PutPolicyWithContext(context.Background(), "test", ` + err := client.Sys().PutPolicy("test", ` path "pki/*" { capabilities = ["update"] }`) @@ -3279,7 +3396,7 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { } // Configure test role for userpass. - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/userpassname", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/userpassname", map[string]interface{}{ "password": "test", "policies": "test", }); err != nil { @@ -3293,14 +3410,14 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { } // Get auth accessor for identity template. - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } userpassAccessor := auths["userpass/"].Accessor // Mount PKI. - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3312,7 +3429,7 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { } // Generate internal CA. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + _, err = client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -3321,7 +3438,7 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { } // Write role PKI. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/test", map[string]interface{}{ "allowed_domains": []string{ "foobar.com", "zipzap.com", "{{identity.entity.aliases." + userpassAccessor + ".name}}", "foo.{{identity.entity.aliases." + userpassAccessor + ".name}}.example.com", @@ -3341,31 +3458,31 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { if err != nil || secret == nil { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"common_name": "userpassname"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"common_name": "userpassname"}) if err != nil { t.Fatal(err) } // Issue certificate for foobar.com to verify allowed_domain_templae doesnt break plain domains. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"common_name": "foobar.com"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"common_name": "foobar.com"}) if err != nil { t.Fatal(err) } // Issue certificate for unknown userpassname. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"common_name": "unknownuserpassname"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"common_name": "unknownuserpassname"}) if err == nil { t.Fatal("expected error") } // Issue certificate for foo.userpassname.domain. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"common_name": "foo.userpassname.example.com"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"common_name": "foo.userpassname.example.com"}) if err != nil { t.Fatal("expected error") } // Set allowed_domains_template to false. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/test", map[string]interface{}{ "allowed_domains_template": false, }) if err != nil { @@ -3373,12 +3490,132 @@ func TestBackend_AllowedDomainsTemplate(t *testing.T) { } // Issue certificate with userpassToken. - _, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{"common_name": "userpassname"}) + _, err = client.Logical().Write("pki/issue/test", map[string]interface{}{"common_name": "userpassname"}) if err == nil { t.Fatal("expected error") } } +func TestReadWriteDeleteRoles(t *testing.T) { + ctx := context.Background() + coreConfig := &vault.CoreConfig{ + CredentialBackends: map[string]logical.Factory{ + "userpass": userpass.Factory, + }, + LogicalBackends: map[string]logical.Factory{ + "pki": Factory, + }, + } + cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + cluster.Start() + defer cluster.Cleanup() + client := cluster.Cores[0].Client + + // Mount PKI. + err := client.Sys().MountWithContext(ctx, "pki", &api.MountInput{ + Type: "pki", + Config: api.MountConfigInput{ + DefaultLeaseTTL: "16h", + MaxLeaseTTL: "60h", + }, + }) + if err != nil { + t.Fatal(err) + } + + resp, err := client.Logical().ReadWithContext(ctx, "pki/roles/test") + if err != nil { + t.Fatal(err) + } + + if resp != nil { + t.Fatalf("response should have been emtpy but was:\n%#v", resp) + } + + // Write role PKI. + _, err = client.Logical().WriteWithContext(ctx, "pki/roles/test", map[string]interface{}{}) + if err != nil { + t.Fatal(err) + } + + // Read the role. + resp, err = client.Logical().ReadWithContext(ctx, "pki/roles/test") + if err != nil { + t.Fatal(err) + } + + if resp.Data == nil { + t.Fatal("default data within response was nil when it should have contained data") + } + + // Validate that we have not changed any defaults unknowingly + expectedData := map[string]interface{}{ + "key_type": "rsa", + "use_csr_sans": true, + "client_flag": true, + "allowed_serial_numbers": []interface{}{}, + "generate_lease": false, + "signature_bits": json.Number("256"), + "allowed_domains": []interface{}{}, + "allowed_uri_sans_template": false, + "enforce_hostnames": true, + "policy_identifiers": []interface{}{}, + "require_cn": true, + "allowed_domains_template": false, + "allow_token_displayname": false, + "country": []interface{}{}, + "not_after": "", + "postal_code": []interface{}{}, + "use_csr_common_name": true, + "allow_localhost": true, + "allow_subdomains": false, + "allow_wildcard_certificates": true, + "allowed_other_sans": []interface{}{}, + "allowed_uri_sans": []interface{}{}, + "basic_constraints_valid_for_non_ca": false, + "key_usage": []interface{}{"DigitalSignature", "KeyAgreement", "KeyEncipherment"}, + "not_before_duration": json.Number("30"), + "allow_glob_domains": false, + "ttl": json.Number("0"), + "ou": []interface{}{}, + "email_protection_flag": false, + "locality": []interface{}{}, + "server_flag": true, + "allow_bare_domains": false, + "allow_ip_sans": true, + "ext_key_usage_oids": []interface{}{}, + "allow_any_name": false, + "ext_key_usage": []interface{}{}, + "key_bits": json.Number("2048"), + "max_ttl": json.Number("0"), + "no_store": false, + "organization": []interface{}{}, + "province": []interface{}{}, + "street_address": []interface{}{}, + "code_signing_flag": false, + } + + if diff := deep.Equal(expectedData, resp.Data); len(diff) > 0 { + t.Fatalf("pki role default values have changed, diff: %v", diff) + } + + _, err = client.Logical().DeleteWithContext(ctx, "pki/roles/test") + if err != nil { + t.Fatal(err) + } + + resp, err = client.Logical().ReadWithContext(ctx, "pki/roles/test") + if err != nil { + t.Fatal(err) + } + + if resp != nil { + t.Fatalf("response should have been empty but was:\n%#v", resp) + } +} + func setCerts() { cak, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { @@ -3515,7 +3752,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { var err error // Mount /pki as a root CA - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3528,7 +3765,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { // Set the cluster's certificate as the root CA in /pki pemBundleRootCA := string(cluster.CACertPEM) + string(cluster.CAKeyPEM) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/config/ca", map[string]interface{}{ + _, err = client.Logical().Write("pki/config/ca", map[string]interface{}{ "pem_bundle": pemBundleRootCA, }) if err != nil { @@ -3536,7 +3773,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { } // Mount /pki2 to operate as an intermediate CA - err = client.Sys().MountWithContext(context.Background(), "pki2", &api.MountInput{ + err = client.Sys().Mount("pki2", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3548,14 +3785,14 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { } // Create a CSR for the intermediate CA - secret, err := client.Logical().WriteWithContext(context.Background(), "pki2/intermediate/generate/internal", nil) + secret, err := client.Logical().Write("pki2/intermediate/generate/internal", nil) if err != nil { t.Fatal(err) } intermediateCSR := secret.Data["csr"].(string) // Sign the intermediate CSR using /pki - secret, err = client.Logical().WriteWithContext(context.Background(), "pki/root/sign-intermediate", map[string]interface{}{ + secret, err = client.Logical().Write("pki/root/sign-intermediate", map[string]interface{}{ "permitted_dns_domains": ".myvault.com", "csr": intermediateCSR, "ttl": "10s", @@ -3567,7 +3804,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { intermediateCASerialColon := strings.ReplaceAll(strings.ToLower(intermediateCertSerial), ":", "-") // Get the intermediate cert after signing - secret, err = client.Logical().ReadWithContext(context.Background(), "pki/cert/"+intermediateCASerialColon) + secret, err = client.Logical().Read("pki/cert/" + intermediateCASerialColon) if err != nil { t.Fatal(err) } @@ -3576,7 +3813,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { } // Issue a revoke on on /pki - _, err = client.Logical().WriteWithContext(context.Background(), "pki/revoke", map[string]interface{}{ + _, err = client.Logical().Write("pki/revoke", map[string]interface{}{ "serial_number": intermediateCertSerial, }) if err != nil { @@ -3588,7 +3825,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { time.Sleep(3 * time.Second) // Issue a tidy on /pki - _, err = client.Logical().WriteWithContext(context.Background(), "pki/tidy", map[string]interface{}{ + _, err = client.Logical().Write("pki/tidy", map[string]interface{}{ "tidy_cert_store": true, "tidy_revoked_certs": true, "safety_buffer": "1s", @@ -3636,7 +3873,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { time.Sleep(10 * time.Second) // Issue a tidy on /pki - _, err = client.Logical().WriteWithContext(context.Background(), "pki/tidy", map[string]interface{}{ + _, err = client.Logical().Write("pki/tidy", map[string]interface{}{ "tidy_cert_store": true, "tidy_revoked_certs": true, "safety_buffer": "1s", @@ -3650,7 +3887,7 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) { // Issue a tidy-status on /pki { - tidyStatus, err := client.Logical().ReadWithContext(context.Background(), "pki/tidy-status") + tidyStatus, err := client.Logical().Read("pki/tidy-status") if err != nil { t.Fatal(err) } @@ -3791,7 +4028,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { var err error // Generate a root CA at /pki-root - err = client.Sys().MountWithContext(context.Background(), "pki-root", &api.MountInput{ + err = client.Sys().Mount("pki-root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3802,7 +4039,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "pki-root/root/generate/exported", map[string]interface{}{ + resp, err := client.Logical().Write("pki-root/root/generate/exported", map[string]interface{}{ "common_name": "root myvault.com", "key_type": keyType, }) @@ -3816,7 +4053,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { rootCert := rootData["certificate"].(string) // Validate that root's /cert/ca-chain now contains the certificate. - resp, err = client.Logical().ReadWithContext(context.Background(), "pki-root/cert/ca_chain") + resp, err = client.Logical().Read("pki-root/cert/ca_chain") if err != nil { t.Fatal(err) } @@ -3830,7 +4067,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { } // Now generate an intermediate at /pki-intermediate, signed by the root. - err = client.Sys().MountWithContext(context.Background(), "pki-intermediate", &api.MountInput{ + err = client.Sys().Mount("pki-intermediate", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3841,7 +4078,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "pki-intermediate/intermediate/generate/exported", map[string]interface{}{ + resp, err = client.Logical().Write("pki-intermediate/intermediate/generate/exported", map[string]interface{}{ "common_name": "intermediate myvault.com", "key_type": keyType, }) @@ -3854,7 +4091,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { intermediateData := resp.Data intermediateKey := intermediateData["private_key"].(string) - resp, err = client.Logical().WriteWithContext(context.Background(), "pki-root/root/sign-intermediate", map[string]interface{}{ + resp, err = client.Logical().Write("pki-root/root/sign-intermediate", map[string]interface{}{ "csr": intermediateData["csr"], "format": "pem_bundle", }) @@ -3871,7 +4108,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { intermediaryCaCert := parseCert(t, intermediateCert) requireSignedBy(t, intermediaryCaCert, rootCaCert.PublicKey) - resp, err = client.Logical().WriteWithContext(context.Background(), "pki-intermediate/intermediate/set-signed", map[string]interface{}{ + resp, err = client.Logical().Write("pki-intermediate/intermediate/set-signed", map[string]interface{}{ "certificate": intermediateCert + "\n" + rootCert + "\n", }) if err != nil { @@ -3880,7 +4117,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { // Validate that intermediate's ca_chain field now includes the full // chain. - resp, err = client.Logical().ReadWithContext(context.Background(), "pki-intermediate/cert/ca_chain") + resp, err = client.Logical().Read("pki-intermediate/cert/ca_chain") if err != nil { t.Fatal(err) } @@ -3898,7 +4135,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { // Finally, import this signing cert chain into a new mount to ensure // "external" CAs behave as expected. - err = client.Sys().MountWithContext(context.Background(), "pki-external", &api.MountInput{ + err = client.Sys().Mount("pki-external", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -3909,7 +4146,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "pki-external/config/ca", map[string]interface{}{ + resp, err = client.Logical().Write("pki-external/config/ca", map[string]interface{}{ "pem_bundle": intermediateKey + "\n" + intermediateCert + "\n" + rootCert + "\n", }) if err != nil { @@ -3917,7 +4154,7 @@ func runFullCAChainTest(t *testing.T, keyType string) { } // Validate the external chain information was loaded correctly. - resp, err = client.Logical().ReadWithContext(context.Background(), "pki-external/cert/ca_chain") + resp, err = client.Logical().Read("pki-external/cert/ca_chain") if err != nil { t.Fatal(err) } @@ -3997,7 +4234,7 @@ func RoleIssuanceRegressionHelper(t *testing.T, client *api.Client, index int, t for _, AllowLocalhost := range test.AllowLocalhost.ToValues() { for _, AllowWildcardCertificates := range test.AllowWildcardCertificates.ToValues() { role := fmt.Sprintf("issuance-regression-%d-bare-%v-glob-%v-subdomains-%v-localhost-%v-wildcard-%v", index, AllowBareDomains, AllowGlobDomains, AllowSubdomains, AllowLocalhost, AllowWildcardCertificates) - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/roles/"+role, map[string]interface{}{ + resp, err := client.Logical().Write("pki/roles/"+role, map[string]interface{}{ "allowed_domains": test.AllowedDomains, "allow_bare_domains": AllowBareDomains, "allow_glob_domains": AllowGlobDomains, @@ -4014,7 +4251,7 @@ func RoleIssuanceRegressionHelper(t *testing.T, client *api.Client, index int, t t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/issue/"+role, map[string]interface{}{ + resp, err = client.Logical().Write("pki/issue/"+role, map[string]interface{}{ "common_name": test.CommonName, }) @@ -4205,7 +4442,7 @@ func TestBackend_Roles_IssuanceRegression(t *testing.T) { var err error // Generate a root CA at /pki to use for our tests - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "12h", @@ -4216,11 +4453,12 @@ func TestBackend_Roles_IssuanceRegression(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/root/generate/exported", map[string]interface{}{ + // We need a RSA key so all signature sizes are valid with it. + resp, err := client.Logical().Write("pki/root/generate/exported", map[string]interface{}{ "common_name": "myvault.com", "ttl": "128h", - "key_type": "ec", - "key_bits": 256, + "key_type": "rsa", + "key_bits": 2048, }) if err != nil { t.Fatal(err) @@ -4237,6 +4475,168 @@ func TestBackend_Roles_IssuanceRegression(t *testing.T) { t.Log(fmt.Sprintf("Issuance regression expanded matrix test scenarios: %d", tested)) } +type KeySizeRegression struct { + // Values reused for both Role and CA configuration. + RoleKeyType string + RoleKeyBits []int + + // Signature Bits presently is only specified on the role. + RoleSignatureBits []int + + // These are tuples; must be of the same length. + TestKeyTypes []string + TestKeyBits []int + + // All of the above key types/sizes must pass or fail together. + ExpectError bool +} + +func (k KeySizeRegression) KeyTypeValues() []string { + if k.RoleKeyType == "any" { + return []string{"rsa", "ec", "ed25519"} + } + + return []string{k.RoleKeyType} +} + +func RoleKeySizeRegressionHelper(t *testing.T, client *api.Client, index int, test KeySizeRegression) int { + tested := 0 + + for _, caKeyType := range test.KeyTypeValues() { + for _, caKeyBits := range test.RoleKeyBits { + // Generate a new CA key. + resp, err := client.Logical().Write("pki/root/generate/exported", map[string]interface{}{ + "common_name": "myvault.com", + "ttl": "128h", + "key_type": caKeyType, + "key_bits": caKeyBits, + }) + if err != nil { + t.Fatal(err) + } + if resp == nil { + t.Fatal("expected ca info") + } + + for _, roleKeyBits := range test.RoleKeyBits { + for _, roleSignatureBits := range test.RoleSignatureBits { + role := fmt.Sprintf("key-size-regression-%d-keytype-%v-keybits-%d-signature-bits-%d", index, test.RoleKeyType, roleKeyBits, roleSignatureBits) + resp, err := client.Logical().Write("pki/roles/"+role, map[string]interface{}{ + "key_type": test.RoleKeyType, + "key_bits": roleKeyBits, + "signature_bits": roleSignatureBits, + }) + if err != nil { + t.Fatal(err) + } + + for index, keyType := range test.TestKeyTypes { + keyBits := test.TestKeyBits[index] + + _, _, csrPem := generateCSR(t, &x509.CertificateRequest{ + Subject: pkix.Name{ + CommonName: "localhost", + }, + }, keyType, keyBits) + + resp, err = client.Logical().Write("pki/sign/"+role, map[string]interface{}{ + "common_name": "localhost", + "csr": csrPem, + }) + + haveErr := err != nil || resp == nil + + if haveErr != test.ExpectError { + t.Fatalf("key size regression test [%d] failed: haveErr: %v, expectErr: %v, err: %v, resp: %v, test case: %v, caKeyType: %v, caKeyBits: %v, role: %v, keyType: %v, keyBits: %v", index, haveErr, test.ExpectError, err, resp, test, caKeyType, caKeyBits, role, keyType, keyBits) + } + + tested += 1 + } + } + } + + _, err = client.Logical().Delete("pki/root") + if err != nil { + t.Fatal(err) + } + } + } + + return tested +} + +func TestBackend_Roles_KeySizeRegression(t *testing.T) { + // Regression testing of role's issuance policy. + testCases := []KeySizeRegression{ + // RSA with default parameters should fail to issue smaller RSA keys + // and any size ECDSA/Ed25519 keys. + /* 0 */ {"rsa", []int{0, 2048}, []int{0, 256, 384, 512}, []string{"rsa", "rsa", "ec", "ec", "ec", "ec", "ed25519"}, []int{512, 1024, 224, 256, 384, 521, 0}, true}, + // But it should work to issue larger RSA keys. + /* 1 */ {"rsa", []int{0, 2048}, []int{0, 256, 384, 512}, []string{"rsa", "rsa", "rsa"}, []int{2048, 3072, 4906}, false}, + + // EC with default parameters should fail to issue smaller EC keys + // and any size RSA/Ed25519 keys. + /* 2 */ {"ec", []int{0}, []int{0}, []string{"rsa", "rsa", "rsa", "ec", "ed25519"}, []int{1024, 2048, 4096, 224, 0}, true}, + // But it should work to issue larger EC keys. Note that we should be + // independent of signature bits as that's computed from the issuer + // type (for EC based issuers). + /* 3 */ {"ec", []int{224}, []int{0, 256, 384, 521}, []string{"ec", "ec", "ec", "ec"}, []int{224, 256, 384, 521}, false}, + /* 4 */ {"ec", []int{0, 256}, []int{0, 256, 384, 521}, []string{"ec", "ec", "ec"}, []int{256, 384, 521}, false}, + /* 5 */ {"ec", []int{384}, []int{0, 256, 384, 521}, []string{"ec", "ec"}, []int{384, 521}, false}, + /* 6 */ {"ec", []int{521}, []int{0, 256, 384, 512}, []string{"ec"}, []int{521}, false}, + + // Ed25519 should reject RSA and EC keys. + /* 7 */ {"ed25519", []int{0}, []int{0}, []string{"rsa", "rsa", "rsa", "ec", "ec"}, []int{1024, 2048, 4096, 256, 521}, true}, + // But it should work to issue Ed25519 keys. + /* 8 */ {"ed25519", []int{0}, []int{0}, []string{"ed25519"}, []int{0}, false}, + + // Any key type should reject insecure RSA key sizes. + /* 9 */ {"any", []int{0}, []int{0, 256, 384, 512}, []string{"rsa", "rsa"}, []int{512, 1024}, true}, + // But work for everything else. + /* 10 */ {"any", []int{0}, []int{0, 256, 384, 512}, []string{"rsa", "rsa", "rsa", "ec", "ec", "ec", "ec", "ed25519"}, []int{2048, 3072, 4906, 224, 256, 384, 521, 0}, false}, + + // RSA with larger than default key size should reject smaller ones. + /* 11 */ {"rsa", []int{3072}, []int{0, 256, 384, 512}, []string{"rsa", "rsa", "rsa"}, []int{512, 1024, 2048}, true}, + } + + if len(testCases) != 12 { + t.Fatalf("misnumbered test case entries will make it hard to find bugs: %v", len(testCases)) + } + + coreConfig := &vault.CoreConfig{ + LogicalBackends: map[string]logical.Factory{ + "pki": Factory, + }, + } + cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + }) + cluster.Start() + defer cluster.Cleanup() + + client := cluster.Cores[0].Client + var err error + + // Generate a root CA at /pki to use for our tests + err = client.Sys().Mount("pki", &api.MountInput{ + Type: "pki", + Config: api.MountConfigInput{ + DefaultLeaseTTL: "12h", + MaxLeaseTTL: "128h", + }, + }) + if err != nil { + t.Fatal(err) + } + + tested := 0 + for index, test := range testCases { + tested += RoleKeySizeRegressionHelper(t, client, index, test) + } + + t.Log(fmt.Sprintf("Key size regression expanded matrix test scenarios: %d", tested)) +} + var ( initTest sync.Once rsaCAKey string diff --git a/builtin/logical/pki/ca_test.go b/builtin/logical/pki/ca_test.go index 6ec791f6fd58..c1ba77cbde41 100644 --- a/builtin/logical/pki/ca_test.go +++ b/builtin/logical/pki/ca_test.go @@ -155,7 +155,7 @@ func TestBackend_CA_Steps(t *testing.T) { // Setup backends var rsaRoot, rsaInt, ecRoot, ecInt, edRoot, edInt *backend { - if err := client.Sys().MountWithContext(context.Background(), "rsaroot", &api.MountInput{ + if err := client.Sys().Mount("rsaroot", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -166,7 +166,7 @@ func TestBackend_CA_Steps(t *testing.T) { } rsaRoot = b - if err := client.Sys().MountWithContext(context.Background(), "rsaint", &api.MountInput{ + if err := client.Sys().Mount("rsaint", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -177,7 +177,7 @@ func TestBackend_CA_Steps(t *testing.T) { } rsaInt = b - if err := client.Sys().MountWithContext(context.Background(), "ecroot", &api.MountInput{ + if err := client.Sys().Mount("ecroot", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -188,7 +188,7 @@ func TestBackend_CA_Steps(t *testing.T) { } ecRoot = b - if err := client.Sys().MountWithContext(context.Background(), "ecint", &api.MountInput{ + if err := client.Sys().Mount("ecint", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -199,7 +199,7 @@ func TestBackend_CA_Steps(t *testing.T) { } ecInt = b - if err := client.Sys().MountWithContext(context.Background(), "ed25519root", &api.MountInput{ + if err := client.Sys().Mount("ed25519root", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -210,7 +210,7 @@ func TestBackend_CA_Steps(t *testing.T) { } edRoot = b - if err := client.Sys().MountWithContext(context.Background(), "ed25519int", &api.MountInput{ + if err := client.Sys().Mount("ed25519int", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -259,7 +259,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, { // Attempt import but only provide one the cert { - _, err := client.Logical().WriteWithContext(context.Background(), rootName+"config/ca", map[string]interface{}{ + _, err := client.Logical().Write(rootName+"config/ca", map[string]interface{}{ "pem_bundle": caCert, }) if err == nil { @@ -269,7 +269,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Same but with only the key { - _, err := client.Logical().WriteWithContext(context.Background(), rootName+"config/ca", map[string]interface{}{ + _, err := client.Logical().Write(rootName+"config/ca", map[string]interface{}{ "pem_bundle": caKey, }) if err == nil { @@ -279,7 +279,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Import CA bundle { - _, err := client.Logical().WriteWithContext(context.Background(), rootName+"config/ca", map[string]interface{}{ + _, err := client.Logical().Write(rootName+"config/ca", map[string]interface{}{ "pem_bundle": strings.Join([]string{caKey, caCert}, "\n"), }) if err != nil { @@ -292,7 +292,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // cert/ca path { - resp, err := client.Logical().ReadWithContext(context.Background(), rootName+"cert/ca") + resp, err := client.Logical().Read(rootName + "cert/ca") if err != nil { t.Fatal(err) } @@ -359,7 +359,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, { // Set CRL config { - _, err := client.Logical().WriteWithContext(context.Background(), rootName+"config/crl", map[string]interface{}{ + _, err := client.Logical().Write(rootName+"config/crl", map[string]interface{}{ "expiry": "16h", }) if err != nil { @@ -369,7 +369,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Verify it { - resp, err := client.Logical().ReadWithContext(context.Background(), rootName+"config/crl") + resp, err := client.Logical().Read(rootName + "config/crl") if err != nil { t.Fatal(err) } @@ -390,7 +390,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, { // First, delete the existing CA info { - _, err := client.Logical().DeleteWithContext(context.Background(), rootName+"root") + _, err := client.Logical().Delete(rootName + "root") if err != nil { t.Fatal(err) } @@ -399,7 +399,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, var rootPEM, rootKey, rootPEMBundle string // Test exported root generation { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"root/generate/exported", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"root/generate/exported", map[string]interface{}{ "common_name": "Root Cert", "ttl": "180h", }) @@ -421,7 +421,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, var intPEM, intCSR, intKey string // Test exported intermediate CSR generation { - resp, err := client.Logical().WriteWithContext(context.Background(), intName+"intermediate/generate/exported", map[string]interface{}{ + resp, err := client.Logical().Write(intName+"intermediate/generate/exported", map[string]interface{}{ "common_name": "intermediate.cert.com", "ttl": "180h", }) @@ -441,7 +441,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Test signing { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"root/sign-intermediate", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"root/sign-intermediate", map[string]interface{}{ "common_name": "intermediate.cert.com", "ttl": "10s", "csr": intCSR, @@ -458,7 +458,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Test setting signed { - resp, err := client.Logical().WriteWithContext(context.Background(), intName+"intermediate/set-signed", map[string]interface{}{ + resp, err := client.Logical().Write(intName+"intermediate/set-signed", map[string]interface{}{ "certificate": intPEM, }) if err != nil { @@ -471,7 +471,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Verify we can find it via the root { - resp, err := client.Logical().ReadWithContext(context.Background(), rootName+"cert/"+intSerialNumber) + resp, err := client.Logical().Read(rootName + "cert/" + intSerialNumber) if err != nil { t.Fatal(err) } @@ -485,7 +485,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Revoke the intermediate { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"revoke", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"revoke", map[string]interface{}{ "serial_number": intSerialNumber, }) if err != nil { @@ -501,7 +501,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, t.Helper() // Verify it is now revoked { - resp, err := client.Logical().ReadWithContext(context.Background(), rootName+"cert/"+intSerialNumber) + resp, err := client.Logical().Read(rootName + "cert/" + intSerialNumber) if err != nil { t.Fatal(err) } @@ -559,7 +559,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, } verifyTidyStatus := func(expectedCertStoreDeleteCount int, expectedRevokedCertDeletedCount int) { - tidyStatus, err := client.Logical().ReadWithContext(context.Background(), rootName+"tidy-status") + tidyStatus, err := client.Logical().Read(rootName + "tidy-status") if err != nil { t.Fatal(err) } @@ -594,7 +594,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, { // Run with a high safety buffer, nothing should happen { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"tidy", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"tidy", map[string]interface{}{ "safety_buffer": "3h", "tidy_cert_store": true, "tidy_revoked_certs": true, @@ -617,7 +617,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Run with both values set false, nothing should happen { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"tidy", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"tidy", map[string]interface{}{ "safety_buffer": "1s", "tidy_cert_store": false, "tidy_revoked_certs": false, @@ -640,7 +640,7 @@ func runSteps(t *testing.T, rootB, intB *backend, client *api.Client, rootName, // Run with a short safety buffer and both set to true, both should be cleared { - resp, err := client.Logical().WriteWithContext(context.Background(), rootName+"tidy", map[string]interface{}{ + resp, err := client.Logical().Write(rootName+"tidy", map[string]interface{}{ "safety_buffer": "1s", "tidy_cert_store": true, "tidy_revoked_certs": true, diff --git a/builtin/logical/pki/cert_util.go b/builtin/logical/pki/cert_util.go index 301f4534d617..11ac905e069e 100644 --- a/builtin/logical/pki/cert_util.go +++ b/builtin/logical/pki/cert_util.go @@ -673,6 +673,11 @@ func signCert(b *backend, return nil, errutil.UserError{Err: fmt.Sprintf("certificate request could not be parsed: %v", err)} } + // This switch validates that the CSR key type matches the role and sets + // the value in the actualKeyType/actualKeyBits values. + actualKeyType := "" + actualKeyBits := 0 + switch data.role.KeyType { case "rsa": // Verify that the key matches the role type @@ -681,24 +686,14 @@ func signCert(b *backend, "role requires keys of type %s", data.role.KeyType)} } + pubKey, ok := csr.PublicKey.(*rsa.PublicKey) if !ok { return nil, errutil.UserError{Err: "could not parse CSR's public key"} } - // Verify that the key is at least 2048 bits - if pubKey.N.BitLen() < 2048 { - return nil, errutil.UserError{Err: "RSA keys < 2048 bits are unsafe and not supported"} - } - - // Verify that the bit size is at least the size specified in the role - if pubKey.N.BitLen() < data.role.KeyBits { - return nil, errutil.UserError{Err: fmt.Sprintf( - "role requires a minimum of a %d-bit key, but CSR's key is %d bits", - data.role.KeyBits, - pubKey.N.BitLen())} - } - + actualKeyType = "rsa" + actualKeyBits = pubKey.N.BitLen() case "ec": // Verify that the key matches the role type if csr.PublicKeyAlgorithm != x509.ECDSA { @@ -711,14 +706,8 @@ func signCert(b *backend, return nil, errutil.UserError{Err: "could not parse CSR's public key"} } - // Verify that the bit size is at least the size specified in the role - if pubKey.Params().BitSize < data.role.KeyBits { - return nil, errutil.UserError{Err: fmt.Sprintf( - "role requires a minimum of a %d-bit key, but CSR's key is %d bits", - data.role.KeyBits, - pubKey.Params().BitSize)} - } - + actualKeyType = "ec" + actualKeyBits = pubKey.Params().BitSize case "ed25519": // Verify that the key matches the role type if csr.PublicKeyAlgorithm != x509.Ed25519 { @@ -726,29 +715,105 @@ func signCert(b *backend, "role requires keys of type %s", data.role.KeyType)} } + _, ok := csr.PublicKey.(ed25519.PublicKey) if !ok { return nil, errutil.UserError{Err: "could not parse CSR's public key"} } + actualKeyType = "ed25519" + actualKeyBits = 0 case "any": - // We only care about running RSA < 2048 bit checks, so if not RSA - // break out - if csr.PublicKeyAlgorithm != x509.RSA { - break + // We need to compute the actual key type and key bits, to correctly + // validate minimums and SignatureBits below. + switch csr.PublicKeyAlgorithm { + case x509.RSA: + pubKey, ok := csr.PublicKey.(*rsa.PublicKey) + if !ok { + return nil, errutil.UserError{Err: "could not parse CSR's public key"} + } + if pubKey.N.BitLen() < 2048 { + return nil, errutil.UserError{Err: "RSA keys < 2048 bits are unsafe and not supported"} + } + + actualKeyType = "rsa" + actualKeyBits = pubKey.N.BitLen() + case x509.ECDSA: + pubKey, ok := csr.PublicKey.(*ecdsa.PublicKey) + if !ok { + return nil, errutil.UserError{Err: "could not parse CSR's public key"} + } + + actualKeyType = "ec" + actualKeyBits = pubKey.Params().BitSize + case x509.Ed25519: + _, ok := csr.PublicKey.(ed25519.PublicKey) + if !ok { + return nil, errutil.UserError{Err: "could not parse CSR's public key"} + } + + actualKeyType = "ed25519" + actualKeyBits = 0 + default: + return nil, errutil.UserError{Err: "Unknown key type in CSR: " + csr.PublicKeyAlgorithm.String()} } + default: + return nil, errutil.InternalError{Err: fmt.Sprintf("unsupported key type value: %s", data.role.KeyType)} + } - // Run RSA < 2048 bit checks - pubKey, ok := csr.PublicKey.(*rsa.PublicKey) - if !ok { - return nil, errutil.UserError{Err: "could not parse CSR's public key"} + // Before validating key lengths, update our KeyBits/SignatureBits based + // on the actual CSR key type. + if data.role.KeyType == "any" { + // We update the value of KeyBits and SignatureBits here (from the + // role), using the specified key type. This allows us to convert + // the default value (0) for SignatureBits and KeyBits to a + // meaningful value. In the event KeyBits takes a zero value, we also + // update that to a new value. + // + // This is mandatory because on some roles, with KeyType any, we'll + // set a default SignatureBits to 0, but this will need to be updated + // in order to behave correctly during signing. + roleBitsWasZero := data.role.KeyBits == 0 + if data.role.KeyBits, data.role.SignatureBits, err = certutil.ValidateDefaultOrValueKeyTypeSignatureLength(actualKeyType, data.role.KeyBits, data.role.SignatureBits); err != nil { + return nil, errutil.InternalError{Err: fmt.Sprintf("unknown internal error updating default values: %v", err)} } - if pubKey.N.BitLen() < 2048 { - return nil, errutil.UserError{Err: "RSA keys < 2048 bits are unsafe and not supported"} + + // We're using the KeyBits field as a minimum value, and P-224 is safe + // and a previously allowed value. However, the above call defaults + // to P-256 as that's a saner default than P-224 (w.r.t. generation). + // So, override our fake Role value if it was previously zero. + if actualKeyType == "ec" && roleBitsWasZero { + data.role.KeyBits = 224 } + } - default: - return nil, errutil.InternalError{Err: fmt.Sprintf("unsupported key type value: %s", data.role.KeyType)} + // At this point, data.role.KeyBits and data.role.SignatureBits should both + // be non-zero, for RSA and ECDSA keys. Validate the actualKeyBits based on + // the role's values. If the KeyType was any, and KeyBits was set to 0, + // KeyBits should be updated to 2048 unless some other value was chosen + // explicitly. + // + // This validation needs to occur regardless of the role's key type, so + // that we always validate both RSA and ECDSA key sizes. + if actualKeyType == "rsa" { + if actualKeyBits < data.role.KeyBits { + return nil, errutil.UserError{Err: fmt.Sprintf( + "role requires a minimum of a %d-bit key, but CSR's key is %d bits", + data.role.KeyBits, actualKeyBits)} + } + + if actualKeyBits < 2048 { + return nil, errutil.UserError{Err: fmt.Sprintf( + "Vault requires a minimum of a 2048-bit key, but CSR's key is %d bits", + actualKeyBits)} + } + } else if actualKeyType == "ec" { + if actualKeyBits < data.role.KeyBits { + return nil, errutil.UserError{Err: fmt.Sprintf( + "role requires a minimum of a %d-bit key, but CSR's key is %d bits", + data.role.KeyBits, + actualKeyBits)} + } } creation, err := generateCreationBundle(b, data, caSign, csr) diff --git a/builtin/logical/pki/crl_test.go b/builtin/logical/pki/crl_test.go index 867c8fcc49e9..4f52050bba7b 100644 --- a/builtin/logical/pki/crl_test.go +++ b/builtin/logical/pki/crl_test.go @@ -3,12 +3,15 @@ package pki import ( "context" "crypto/x509" + "crypto/x509/pkix" "testing" + "time" "github.com/hashicorp/vault/api" vaulthttp "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/sdk/logical" "github.com/hashicorp/vault/vault" + "github.com/stretchr/testify/require" ) func TestBackend_CRL_EnableDisable(t *testing.T) { @@ -25,7 +28,7 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { client := cluster.Cores[0].Client var err error - err = client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err = client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -33,7 +36,7 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { }, }) - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/root/generate/internal", map[string]interface{}{ + resp, err := client.Logical().Write("pki/root/generate/internal", map[string]interface{}{ "ttl": "40h", "common_name": "myvault.com", }) @@ -42,7 +45,7 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { } caSerial := resp.Data["serial_number"] - _, err = client.Logical().WriteWithContext(context.Background(), "pki/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("pki/roles/test", map[string]interface{}{ "allow_bare_domains": true, "allow_subdomains": true, "allowed_domains": "foobar.com", @@ -54,7 +57,7 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { serials := make(map[int]string) for i := 0; i < 6; i++ { - resp, err := client.Logical().WriteWithContext(context.Background(), "pki/issue/test", map[string]interface{}{ + resp, err := client.Logical().Write("pki/issue/test", map[string]interface{}{ "common_name": "test.foobar.com", }) if err != nil { @@ -64,30 +67,22 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { } test := func(num int) { - resp, err := client.Logical().ReadWithContext(context.Background(), "pki/cert/crl") - if err != nil { - t.Fatal(err) - } - crlPem := resp.Data["certificate"].(string) - certList, err := x509.ParseCRL([]byte(crlPem)) - if err != nil { - t.Fatal(err) - } - lenList := len(certList.TBSCertList.RevokedCertificates) + certList := getCrlCertificateList(t, client) + lenList := len(certList.RevokedCertificates) if lenList != num { - t.Fatalf("expected %d, found %d", num, lenList) + t.Fatalf("expected %d revoked certificates, found %d", num, lenList) } } revoke := func(num int) { - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/revoke", map[string]interface{}{ + resp, err = client.Logical().Write("pki/revoke", map[string]interface{}{ "serial_number": serials[num], }) if err != nil { t.Fatal(err) } - resp, err = client.Logical().WriteWithContext(context.Background(), "pki/revoke", map[string]interface{}{ + resp, err = client.Logical().Write("pki/revoke", map[string]interface{}{ "serial_number": caSerial, }) if err == nil { @@ -96,7 +91,7 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { } toggle := func(disabled bool) { - _, err = client.Logical().WriteWithContext(context.Background(), "pki/config/crl", map[string]interface{}{ + _, err = client.Logical().Write("pki/config/crl", map[string]interface{}{ "disable": disabled, }) if err != nil { @@ -122,4 +117,23 @@ func TestBackend_CRL_EnableDisable(t *testing.T) { test(0) toggle(false) test(6) + + // The rotate command should reset the update time of the CRL. + crlCreationTime1 := getCrlCertificateList(t, client).ThisUpdate + time.Sleep(1 * time.Second) + _, err = client.Logical().Read("pki/crl/rotate") + require.NoError(t, err) + + crlCreationTime2 := getCrlCertificateList(t, client).ThisUpdate + require.NotEqual(t, crlCreationTime1, crlCreationTime2) +} + +func getCrlCertificateList(t *testing.T, client *api.Client) pkix.TBSCertificateList { + resp, err := client.Logical().ReadWithContext(context.Background(), "pki/cert/crl") + require.NoError(t, err) + + crlPem := resp.Data["certificate"].(string) + certList, err := x509.ParseCRL([]byte(crlPem)) + require.NoError(t, err) + return certList.TBSCertList } diff --git a/builtin/logical/ssh/backend_test.go b/builtin/logical/ssh/backend_test.go index 2137468c9a5e..adee82aa1b83 100644 --- a/builtin/logical/ssh/backend_test.go +++ b/builtin/logical/ssh/backend_test.go @@ -1464,14 +1464,14 @@ func TestBackend_DefExtTemplatingEnabled(t *testing.T) { client := cluster.Cores[0].Client // Get auth accessor for identity template. - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } userpassAccessor := auths["userpass/"].Accessor // Write SSH role. - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/roles/test", map[string]interface{}{ + _, err = client.Logical().Write("ssh/roles/test", map[string]interface{}{ "key_type": "ca", "allowed_extensions": "login@zipzap.com", "allow_user_certificates": true, @@ -1490,7 +1490,7 @@ func TestBackend_DefExtTemplatingEnabled(t *testing.T) { // Issue SSH certificate with default extensions templating enabled, and no user-provided extensions client.SetToken(userpassToken) - resp, err := client.Logical().WriteWithContext(context.Background(), "ssh/sign/test", map[string]interface{}{ + resp, err := client.Logical().Write("ssh/sign/test", map[string]interface{}{ "public_key": publicKey4096, }) if err != nil { @@ -1518,7 +1518,7 @@ func TestBackend_DefExtTemplatingEnabled(t *testing.T) { userProvidedExtensionPermissions := map[string]string{ "login@zipzap.com": "some_other_user_name", } - resp, err = client.Logical().WriteWithContext(context.Background(), "ssh/sign/test", map[string]interface{}{ + resp, err = client.Logical().Write("ssh/sign/test", map[string]interface{}{ "public_key": publicKey4096, "extensions": userProvidedExtensionPermissions, }) @@ -1542,7 +1542,7 @@ func TestBackend_DefExtTemplatingEnabled(t *testing.T) { invalidUserProvidedExtensionPermissions := map[string]string{ "login@foobar.com": "{{identity.entity.metadata}}", } - resp, err = client.Logical().WriteWithContext(context.Background(), "ssh/sign/test", map[string]interface{}{ + resp, err = client.Logical().Write("ssh/sign/test", map[string]interface{}{ "public_key": publicKey4096, "extensions": invalidUserProvidedExtensionPermissions, }) @@ -1557,7 +1557,7 @@ func TestBackend_EmptyAllowedExtensionFailsClosed(t *testing.T) { client := cluster.Cores[0].Client // Get auth accessor for identity template. - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } @@ -1565,7 +1565,7 @@ func TestBackend_EmptyAllowedExtensionFailsClosed(t *testing.T) { // Write SSH role to test with no allowed extension. We also provide a templated default extension, // to verify that it's not actually being evaluated - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/roles/test_allow_all_extensions", map[string]interface{}{ + _, err = client.Logical().Write("ssh/roles/test_allow_all_extensions", map[string]interface{}{ "key_type": "ca", "allow_user_certificates": true, "allowed_users": "tuber", @@ -1585,7 +1585,7 @@ func TestBackend_EmptyAllowedExtensionFailsClosed(t *testing.T) { userProvidedAnyExtensionPermissions := map[string]string{ "login@foobar.com": "not_userpassname", } - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/sign/test_allow_all_extensions", map[string]interface{}{ + _, err = client.Logical().Write("ssh/sign/test_allow_all_extensions", map[string]interface{}{ "public_key": publicKey4096, "extensions": userProvidedAnyExtensionPermissions, }) @@ -1604,7 +1604,7 @@ func TestBackend_DefExtTemplatingDisabled(t *testing.T) { client := cluster.Cores[0].Client // Get auth accessor for identity template. - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } @@ -1612,7 +1612,7 @@ func TestBackend_DefExtTemplatingDisabled(t *testing.T) { // Write SSH role to test with any extension. We also provide a templated default extension, // to verify that it's not actually being evaluated - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/roles/test_allow_all_extensions", map[string]interface{}{ + _, err = client.Logical().Write("ssh/roles/test_allow_all_extensions", map[string]interface{}{ "key_type": "ca", "allow_user_certificates": true, "allowed_users": "tuber", @@ -1635,7 +1635,7 @@ func TestBackend_DefExtTemplatingDisabled(t *testing.T) { "login@foobar.com": "{{identity.entity.aliases." + userpassAccessor + ".name}}", "login@zipzap.com": "some_other_user_name", } - resp, err := client.Logical().WriteWithContext(context.Background(), "ssh/sign/test_allow_all_extensions", map[string]interface{}{ + resp, err := client.Logical().Write("ssh/sign/test_allow_all_extensions", map[string]interface{}{ "public_key": publicKey4096, "extensions": defaultExtensionPermissions, }) @@ -1661,7 +1661,7 @@ func TestBackend_DefExtTemplatingDisabled(t *testing.T) { "login@foobar.com": "not_userpassname", "login@zipzap.com": "some_other_user_name", } - resp, err = client.Logical().WriteWithContext(context.Background(), "ssh/sign/test_allow_all_extensions", map[string]interface{}{ + resp, err = client.Logical().Write("ssh/sign/test_allow_all_extensions", map[string]interface{}{ "public_key": publicKey4096, "extensions": userProvidedAnyExtensionPermissions, }) @@ -1698,7 +1698,7 @@ func getSshCaTestCluster(t *testing.T, userIdentity string) (*vault.TestCluster, client := cluster.Cores[0].Client // Write test policy for userpass auth method. - err := client.Sys().PutPolicyWithContext(context.Background(), "test", ` + err := client.Sys().PutPolicy("test", ` path "ssh/*" { capabilities = ["update"] }`) @@ -1712,7 +1712,7 @@ func getSshCaTestCluster(t *testing.T, userIdentity string) (*vault.TestCluster, } // Configure test role for userpass. - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/"+userIdentity, map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/"+userIdentity, map[string]interface{}{ "password": "test", "policies": "test", }); err != nil { @@ -1720,7 +1720,7 @@ func getSshCaTestCluster(t *testing.T, userIdentity string) (*vault.TestCluster, } // Login userpass for test role and keep client token. - secret, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/login/"+userIdentity, map[string]interface{}{ + secret, err := client.Logical().Write("auth/userpass/login/"+userIdentity, map[string]interface{}{ "password": "test", }) if err != nil || secret == nil { @@ -1729,7 +1729,7 @@ func getSshCaTestCluster(t *testing.T, userIdentity string) (*vault.TestCluster, userpassToken := secret.Auth.ClientToken // Mount SSH. - err = client.Sys().MountWithContext(context.Background(), "ssh", &api.MountInput{ + err = client.Sys().Mount("ssh", &api.MountInput{ Type: "ssh", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -1741,7 +1741,7 @@ func getSshCaTestCluster(t *testing.T, userIdentity string) (*vault.TestCluster, } // Configure SSH CA. - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/config/ca", map[string]interface{}{ + _, err = client.Logical().Write("ssh/config/ca", map[string]interface{}{ "public_key": testCAPublicKey, "private_key": testCAPrivateKey, }) @@ -1759,21 +1759,21 @@ func testAllowedUsersTemplate(t *testing.T, testAllowedUsersTemplate string, client := cluster.Cores[0].Client // set metadata "ssh_username" to userpass username - tokenLookupResponse, err := client.Logical().WriteWithContext(context.Background(), "/auth/token/lookup", map[string]interface{}{ + tokenLookupResponse, err := client.Logical().Write("/auth/token/lookup", map[string]interface{}{ "token": userpassToken, }) if err != nil { t.Fatal(err) } entityID := tokenLookupResponse.Data["entity_id"].(string) - _, err = client.Logical().WriteWithContext(context.Background(), "/identity/entity/id/"+entityID, map[string]interface{}{ + _, err = client.Logical().Write("/identity/entity/id/"+entityID, map[string]interface{}{ "metadata": testEntityMetadata, }) if err != nil { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "ssh/roles/my-role", map[string]interface{}{ + _, err = client.Logical().Write("ssh/roles/my-role", map[string]interface{}{ "key_type": testCaKeyType, "allow_user_certificates": true, "allowed_users": testAllowedUsersTemplate, @@ -1785,7 +1785,7 @@ func testAllowedUsersTemplate(t *testing.T, testAllowedUsersTemplate string, // sign SSH key as userpass user client.SetToken(userpassToken) - signResponse, err := client.Logical().WriteWithContext(context.Background(), "ssh/sign/my-role", map[string]interface{}{ + signResponse, err := client.Logical().Write("ssh/sign/my-role", map[string]interface{}{ "public_key": testCAPublicKey, "valid_principals": expectedValidPrincipal, }) diff --git a/builtin/logical/transit/path_config_test.go b/builtin/logical/transit/path_config_test.go index 87f665104ca6..f6dee45090dc 100644 --- a/builtin/logical/transit/path_config_test.go +++ b/builtin/logical/transit/path_config_test.go @@ -348,7 +348,7 @@ func TestTransit_UpdateKeyConfigWithAutorotation(t *testing.T) { cores := cluster.Cores vault.TestWaitActive(t, cores[0].Core) client := cores[0].Client - err := client.Sys().MountWithContext(context.Background(), "transit", &api.MountInput{ + err := client.Sys().Mount("transit", &api.MountInput{ Type: "transit", }) if err != nil { @@ -363,13 +363,13 @@ func TestTransit_UpdateKeyConfigWithAutorotation(t *testing.T) { } keyName := hex.EncodeToString(keyNameBytes) - _, err = client.Logical().WriteWithContext(context.Background(), fmt.Sprintf("transit/keys/%s", keyName), map[string]interface{}{ + _, err = client.Logical().Write(fmt.Sprintf("transit/keys/%s", keyName), map[string]interface{}{ "auto_rotate_period": test.initialAutoRotatePeriod, }) if err != nil { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), fmt.Sprintf("transit/keys/%s/config", keyName), map[string]interface{}{ + resp, err := client.Logical().Write(fmt.Sprintf("transit/keys/%s/config", keyName), map[string]interface{}{ "auto_rotate_period": test.newAutoRotatePeriod, }) switch { @@ -380,7 +380,7 @@ func TestTransit_UpdateKeyConfigWithAutorotation(t *testing.T) { } if !test.shouldError { - resp, err = client.Logical().ReadWithContext(context.Background(), fmt.Sprintf("transit/keys/%s", keyName)) + resp, err = client.Logical().Read(fmt.Sprintf("transit/keys/%s", keyName)) if err != nil { t.Fatal(err) } diff --git a/builtin/logical/transit/path_keys_test.go b/builtin/logical/transit/path_keys_test.go index 3d91e6608e62..04c1d8da092d 100644 --- a/builtin/logical/transit/path_keys_test.go +++ b/builtin/logical/transit/path_keys_test.go @@ -1,7 +1,6 @@ package transit_test import ( - "context" "encoding/hex" "encoding/json" "fmt" @@ -40,7 +39,7 @@ func TestTransit_Issue_2958(t *testing.T) { client := cores[0].Client - err := client.Sys().EnableAuditWithOptionsWithContext(context.Background(), "file", &api.EnableAuditOptions{ + err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{ Type: "file", Options: map[string]string{ "file_path": "/dev/null", @@ -50,45 +49,45 @@ func TestTransit_Issue_2958(t *testing.T) { t.Fatal(err) } - err = client.Sys().MountWithContext(context.Background(), "transit", &api.MountInput{ + err = client.Sys().Mount("transit", &api.MountInput{ Type: "transit", }) if err != nil { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "transit/keys/foo", map[string]interface{}{ + _, err = client.Logical().Write("transit/keys/foo", map[string]interface{}{ "type": "ecdsa-p256", }) if err != nil { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "transit/keys/foobar", map[string]interface{}{ + _, err = client.Logical().Write("transit/keys/foobar", map[string]interface{}{ "type": "ecdsa-p384", }) if err != nil { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "transit/keys/bar", map[string]interface{}{ + _, err = client.Logical().Write("transit/keys/bar", map[string]interface{}{ "type": "ed25519", }) if err != nil { t.Fatal(err) } - _, err = client.Logical().ReadWithContext(context.Background(), "transit/keys/foo") + _, err = client.Logical().Read("transit/keys/foo") if err != nil { t.Fatal(err) } - _, err = client.Logical().ReadWithContext(context.Background(), "transit/keys/foobar") + _, err = client.Logical().Read("transit/keys/foobar") if err != nil { t.Fatal(err) } - _, err = client.Logical().ReadWithContext(context.Background(), "transit/keys/bar") + _, err = client.Logical().Read("transit/keys/bar") if err != nil { t.Fatal(err) } @@ -145,7 +144,7 @@ func TestTransit_CreateKeyWithAutorotation(t *testing.T) { cores := cluster.Cores vault.TestWaitActive(t, cores[0].Core) client := cores[0].Client - err := client.Sys().MountWithContext(context.Background(), "transit", &api.MountInput{ + err := client.Sys().Mount("transit", &api.MountInput{ Type: "transit", }) if err != nil { @@ -160,7 +159,7 @@ func TestTransit_CreateKeyWithAutorotation(t *testing.T) { } keyName := hex.EncodeToString(keyNameBytes) - _, err = client.Logical().WriteWithContext(context.Background(), fmt.Sprintf("transit/keys/%s", keyName), map[string]interface{}{ + _, err = client.Logical().Write(fmt.Sprintf("transit/keys/%s", keyName), map[string]interface{}{ "auto_rotate_period": test.autoRotatePeriod, }) switch { @@ -171,7 +170,7 @@ func TestTransit_CreateKeyWithAutorotation(t *testing.T) { } if !test.shouldError { - resp, err := client.Logical().ReadWithContext(context.Background(), fmt.Sprintf("transit/keys/%s", keyName)) + resp, err := client.Logical().Read(fmt.Sprintf("transit/keys/%s", keyName)) if err != nil { t.Fatal(err) } diff --git a/changelog/14424.txt b/changelog/14424.txt new file mode 100644 index 000000000000..cc54058f91c4 --- /dev/null +++ b/changelog/14424.txt @@ -0,0 +1,3 @@ +```release-note:bug +agent: Fix log level mismatch between ERR and ERROR +``` diff --git a/changelog/14753.txt b/changelog/14753.txt new file mode 100644 index 000000000000..af7fbb5344d5 --- /dev/null +++ b/changelog/14753.txt @@ -0,0 +1,3 @@ +```release-note:improvement +api: Add ability to pass certificate as PEM bytes to api.Client. +``` diff --git a/changelog/14775.txt b/changelog/14775.txt new file mode 100644 index 000000000000..03beb827a25b --- /dev/null +++ b/changelog/14775.txt @@ -0,0 +1,3 @@ +```release-note:improvement +api: Use the context passed to the api/auth Login helpers. +``` diff --git a/changelog/14794.txt b/changelog/14794.txt new file mode 100644 index 000000000000..fb454922201b --- /dev/null +++ b/changelog/14794.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fix KV secret showing in the edit form after a user creates a new version but doesn't have read capabilities +``` \ No newline at end of file diff --git a/changelog/14807.txt b/changelog/14807.txt new file mode 100644 index 000000000000..3c338a21155d --- /dev/null +++ b/changelog/14807.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli: Alternative flag-based syntax for KV to mitigate confusion from automatically appended /data +``` \ No newline at end of file diff --git a/changelog/14817.txt b/changelog/14817.txt new file mode 100644 index 000000000000..9b8e39ac4c48 --- /dev/null +++ b/changelog/14817.txt @@ -0,0 +1,3 @@ +```release-note:improvement +core : check uid and permissions of config dir, config file, plugin dir and plugin binaries +``` \ No newline at end of file diff --git a/changelog/14836.txt b/changelog/14836.txt new file mode 100644 index 000000000000..6ee8d54a2929 --- /dev/null +++ b/changelog/14836.txt @@ -0,0 +1,3 @@ +```release-note:bug +api: Respect increment value in grace period calculations in LifetimeWatcher +``` diff --git a/changelog/14846.txt b/changelog/14846.txt new file mode 100644 index 000000000000..10621ff4fba1 --- /dev/null +++ b/changelog/14846.txt @@ -0,0 +1,3 @@ +```release-note:bug +core: fixing excessive unix file permissions on dir, files and archive created by vault debug command +``` \ No newline at end of file diff --git a/changelog/14875.txt b/changelog/14875.txt new file mode 100644 index 000000000000..ef4622d2e874 --- /dev/null +++ b/changelog/14875.txt @@ -0,0 +1,3 @@ +```release-note:bug +secrets/pki: Fix handling of "any" key type with default zero signature bits value. +``` diff --git a/changelog/14916.txt b/changelog/14916.txt new file mode 100644 index 000000000000..d61065a8de48 --- /dev/null +++ b/changelog/14916.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixes issue logging in with OIDC from a listed auth mounts tab +``` \ No newline at end of file diff --git a/changelog/14941.txt b/changelog/14941.txt new file mode 100644 index 000000000000..f82b63366854 --- /dev/null +++ b/changelog/14941.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fix issue with KV not recomputing model when you changed versions. +``` diff --git a/changelog/14943.txt b/changelog/14943.txt new file mode 100644 index 000000000000..1501c563d728 --- /dev/null +++ b/changelog/14943.txt @@ -0,0 +1,3 @@ +```release-note:bug +secrets/pki: Fixed bug where larger SHA-2 hashes were truncated with shorter ECDSA CA certificates +``` diff --git a/changelog/14954.txt b/changelog/14954.txt new file mode 100644 index 000000000000..fc8be705694e --- /dev/null +++ b/changelog/14954.txt @@ -0,0 +1,3 @@ +```release-note:change +auth/aws: Add RoleSession to DisplayName when using assumeRole for authentication +``` \ No newline at end of file diff --git a/changelog/14962.txt b/changelog/14962.txt new file mode 100644 index 000000000000..b8bea4582b17 --- /dev/null +++ b/changelog/14962.txt @@ -0,0 +1,6 @@ +```release-note:improvement +api: If the parameters supplied over the API payload are ignored due to not +being what the endpoints were expecting, or if the parameters supplied get +replaced by the values in the endpoint's path itself, warnings will be added to +the non-empty responses listing all the ignored and replaced parameters. +``` diff --git a/changelog/14966.txt b/changelog/14966.txt new file mode 100644 index 000000000000..889294400a2c --- /dev/null +++ b/changelog/14966.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixes edit auth method capabilities issue +``` \ No newline at end of file diff --git a/changelog/14968.txt b/changelog/14968.txt new file mode 100644 index 000000000000..a9a5e76fae26 --- /dev/null +++ b/changelog/14968.txt @@ -0,0 +1,3 @@ +```release-note:bug +api: Fixes bug where OutputCurlString field was unintentionally being copied over during client cloning +``` diff --git a/changelog/14977.txt b/changelog/14977.txt new file mode 100644 index 000000000000..937c5d779cc3 --- /dev/null +++ b/changelog/14977.txt @@ -0,0 +1,3 @@ +```release-note:bug +raft: Ensure initialMmapSize is set to 0 on Windows +``` diff --git a/command/agent/alicloud_end_to_end_test.go b/command/agent/alicloud_end_to_end_test.go index e4660f99e9b5..1684ecae4ad7 100644 --- a/command/agent/alicloud_end_to_end_test.go +++ b/command/agent/alicloud_end_to_end_test.go @@ -60,7 +60,7 @@ func TestAliCloudEndToEnd(t *testing.T) { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/alicloud/role/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/alicloud/role/test", map[string]interface{}{ "arn": os.Getenv(envVarAlicloudRoleArn), }); err != nil { t.Fatal(err) diff --git a/command/agent/approle_end_to_end_test.go b/command/agent/approle_end_to_end_test.go index 382073110efa..35186cd8e606 100644 --- a/command/agent/approle_end_to_end_test.go +++ b/command/agent/approle_end_to_end_test.go @@ -93,7 +93,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1", addConstraints(!bindSecretID, map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/test1", addConstraints(!bindSecretID, map[string]interface{}{ "bind_secret_id": bindSecretID, "token_ttl": "6s", "token_max_ttl": "10s", @@ -109,7 +109,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo secretID1 := "" secretID2 := "" if bindSecretID { - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err := client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } @@ -117,13 +117,13 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo } else { logger.Trace("skipped write to auth/approle/role/test1/secret-id") } - resp, err := client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test1/role-id") + resp, err := client.Logical().Read("auth/approle/role/test1/role-id") if err != nil { t.Fatal(err) } roleID1 := resp.Data["role_id"].(string) - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test2", addConstraints(!bindSecretID, map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/test2", addConstraints(!bindSecretID, map[string]interface{}{ "bind_secret_id": bindSecretID, "token_ttl": "6s", "token_max_ttl": "10s", @@ -132,7 +132,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo t.Fatal(err) } if bindSecretID { - resp, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test2/secret-id", nil) + resp, err = client.Logical().Write("auth/approle/role/test2/secret-id", nil) if err != nil { t.Fatal(err) } @@ -140,7 +140,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo } else { logger.Trace("skipped write to auth/approle/role/test2/secret-id") } - resp, err = client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test2/role-id") + resp, err = client.Logical().Read("auth/approle/role/test2/role-id") if err != nil { t.Fatal(err) } @@ -321,7 +321,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo } } client.SetToken(string(val)) - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -345,7 +345,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo if time.Now().After(timeout) { break } - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -385,7 +385,7 @@ func testAppRoleEndToEnd(t *testing.T, removeSecretIDFile bool, bindSecretID boo if time.Now().After(timeout) { break } - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -455,7 +455,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1", addConstraints(!bindSecretID, map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/test1", addConstraints(!bindSecretID, map[string]interface{}{ "bind_secret_id": bindSecretID, "token_ttl": "6s", "token_max_ttl": "10s", @@ -474,7 +474,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, secret := "" secretID1 := "" if bindSecretID { - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err := client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } @@ -482,7 +482,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, } else { logger.Trace("skipped write to auth/approle/role/test1/secret-id") } - resp, err := client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test1/role-id") + resp, err := client.Logical().Read("auth/approle/role/test1/role-id") if err != nil { t.Fatal(err) } @@ -664,7 +664,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, } client.SetToken(string(val)) - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -690,7 +690,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, if time.Now().After(timeout) { break } - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -708,7 +708,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, logger.Trace("origToken set into client", "origToken", origToken) if bindSecretID { - resp, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err = client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } @@ -732,7 +732,7 @@ func testAppRoleWithWrapping(t *testing.T, bindSecretID bool, secretIDLess bool, if time.Now().After(timeout) { break } - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } diff --git a/command/agent/auth/approle/approle.go b/command/agent/auth/approle/approle.go index 8a1a9b3a60d3..e58299ad7b2e 100644 --- a/command/agent/auth/approle/approle.go +++ b/command/agent/auth/approle/approle.go @@ -138,7 +138,7 @@ func (a *approleMethod) Authenticate(ctx context.Context, client *api.Client) (s } clonedClient.SetToken(stringSecretID) // Validate the creation path - resp, err := clonedClient.Logical().Read("sys/wrapping/lookup") + resp, err := clonedClient.Logical().ReadWithContext(ctx, "sys/wrapping/lookup") if err != nil { return "", nil, nil, fmt.Errorf("error looking up wrapped secret ID: %w", err) } @@ -161,7 +161,7 @@ func (a *approleMethod) Authenticate(ctx context.Context, client *api.Client) (s return "", nil, nil, errors.New("unable to validate wrapping token creation path") } // Now get the secret ID - resp, err = clonedClient.Logical().Unwrap("") + resp, err = clonedClient.Logical().UnwrapWithContext(ctx, "") if err != nil { return "", nil, nil, fmt.Errorf("error unwrapping secret ID: %w", err) } diff --git a/command/agent/auth/auth.go b/command/agent/auth/auth.go index 889eedd85bd8..c00028608803 100644 --- a/command/agent/auth/auth.go +++ b/command/agent/auth/auth.go @@ -172,7 +172,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error { ah.logger.Debug("lookup-self with preloaded token") clientToUse.SetToken(ah.token) - secret, err = clientToUse.Logical().Read("auth/token/lookup-self") + secret, err = clientToUse.Auth().Token().LookupSelfWithContext(ctx) if err != nil { ah.logger.Error("could not look up token", "err", err, "backoff", backoff) backoffOrQuit(ctx, backoff) @@ -220,7 +220,7 @@ func (ah *AuthHandler) Run(ctx context.Context, am AuthMethod) error { // This should only happen if there's no preloaded token (regular auto-auth login) // or if a preloaded token has expired and is now switching to auto-auth. if secret.Auth == nil { - secret, err = clientToUse.Logical().Write(path, data) + secret, err = clientToUse.Logical().WriteWithContext(ctx, path, data) // Check errors/sanity if err != nil { ah.logger.Error("error authenticating", "error", err, "backoff", backoff) diff --git a/command/agent/auth/auth_test.go b/command/agent/auth/auth_test.go index b2d2c26ad86c..05c24fe1fa39 100644 --- a/command/agent/auth/auth_test.go +++ b/command/agent/auth/auth_test.go @@ -33,7 +33,7 @@ func newUserpassTestMethod(t *testing.T, client *api.Client) AuthMethod { } func (u *userpassTestMethod) Authenticate(_ context.Context, client *api.Client) (string, http.Header, map[string]interface{}, error) { - _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/foo", map[string]interface{}{ + _, err := client.Logical().Write("auth/userpass/users/foo", map[string]interface{}{ "password": "bar", }) if err != nil { diff --git a/command/agent/auto_auth_preload_token_end_to_end_test.go b/command/agent/auto_auth_preload_token_end_to_end_test.go index 8ecabd1158b8..3f8d972a32cf 100644 --- a/command/agent/auto_auth_preload_token_end_to_end_test.go +++ b/command/agent/auto_auth_preload_token_end_to_end_test.go @@ -48,7 +48,7 @@ func TestTokenPreload_UsingAutoAuth(t *testing.T) { } // Setup Approle - _, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1", map[string]interface{}{ + _, err := client.Logical().Write("auth/approle/role/test1", map[string]interface{}{ "bind_secret_id": "true", "token_ttl": "3s", "token_max_ttl": "10s", @@ -58,13 +58,13 @@ func TestTokenPreload_UsingAutoAuth(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err := client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } secretID1 := resp.Data["secret_id"].(string) - resp, err = client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test1/role-id") + resp, err = client.Logical().Read("auth/approle/role/test1/role-id") if err != nil { t.Fatal(err) } @@ -108,7 +108,7 @@ func TestTokenPreload_UsingAutoAuth(t *testing.T) { } // Setup Preload Token - tokenRespRaw, err := client.Logical().WriteWithContext(context.Background(), "auth/token/create", map[string]interface{}{ + tokenRespRaw, err := client.Logical().Write("auth/token/create", map[string]interface{}{ "ttl": "10s", "explicit-max-ttl": "15s", "policies": []string{""}, @@ -222,7 +222,7 @@ func TestTokenPreload_UsingAutoAuth(t *testing.T) { wrappedToken := map[string]interface{}{ "token": authToken.Token, } - unwrapResp, err := client.Logical().WriteWithContext(context.Background(), "sys/wrapping/unwrap", wrappedToken) + unwrapResp, err := client.Logical().Write("sys/wrapping/unwrap", wrappedToken) if err != nil { t.Fatalf("error unwrapping token: %s", err) } diff --git a/command/agent/aws_end_to_end_test.go b/command/agent/aws_end_to_end_test.go index 71fcd427100c..ca7b419648c7 100644 --- a/command/agent/aws_end_to_end_test.go +++ b/command/agent/aws_end_to_end_test.go @@ -70,7 +70,7 @@ func TestAWSEndToEnd(t *testing.T) { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/aws/role/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/aws/role/test", map[string]interface{}{ "auth_type": "iam", "policies": "default", // Retain thru the account number of the given arn and wildcard the rest. diff --git a/command/agent/cache/cache_test.go b/command/agent/cache/cache_test.go index 0b16b663f9ca..bee5fc0e87f7 100644 --- a/command/agent/cache/cache_test.go +++ b/command/agent/cache/cache_test.go @@ -92,7 +92,7 @@ func setupClusterAndAgentCommon(ctx context.Context, t *testing.T, coreConfig *v } // Add an admin policy - if err := activeClient.Sys().PutPolicyWithContext(context.Background(), "admin", policyAdmin); err != nil { + if err := activeClient.Sys().PutPolicy("admin", policyAdmin); err != nil { t.Fatal(err) } @@ -105,7 +105,7 @@ func setupClusterAndAgentCommon(ctx context.Context, t *testing.T, coreConfig *v t.Fatal(err) } - _, err = activeClient.Logical().WriteWithContext(context.Background(), "auth/userpass/users/foo", map[string]interface{}{ + _, err = activeClient.Logical().Write("auth/userpass/users/foo", map[string]interface{}{ "password": "bar", "policies": []string{"admin"}, }) @@ -174,7 +174,7 @@ func setupClusterAndAgentCommon(ctx context.Context, t *testing.T, coreConfig *v // Login via userpass method to derive a managed token. Set that token as the // testClient's token - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/userpass/login/foo", map[string]interface{}{ + resp, err := testClient.Logical().Write("auth/userpass/login/foo", map[string]interface{}{ "password": "bar", }) if err != nil { @@ -264,7 +264,7 @@ func TestCache_AutoAuthTokenStripping(t *testing.T) { // Empty the token in the client. Auto-auth token should be put to use. testClient.SetToken("") - secret, err := testClient.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := testClient.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -272,7 +272,7 @@ func TestCache_AutoAuthTokenStripping(t *testing.T) { t.Fatalf("failed to strip off auto-auth token on lookup-self") } - secret, err = testClient.Auth().Token().LookupWithContext(context.Background(), "") + secret, err = testClient.Auth().Token().Lookup("") if err != nil { t.Fatal(err) } @@ -329,7 +329,7 @@ func TestCache_AutoAuthClientTokenProxyStripping(t *testing.T) { // Empty the token in the client. Auto-auth token should be put to use. testClient.SetToken(dummyToken) - _, err = testClient.Auth().Token().LookupSelfWithContext(context.Background()) + _, err = testClient.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -351,7 +351,7 @@ func TestCache_ConcurrentRequests(t *testing.T) { cleanup, _, testClient, _ := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig) defer cleanup() - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -364,13 +364,13 @@ func TestCache_ConcurrentRequests(t *testing.T) { go func(i int) { defer wg.Done() key := fmt.Sprintf("kv/foo/%d_%d", i, rand.Int()) - _, err := testClient.Logical().WriteWithContext(context.Background(), key, map[string]interface{}{ + _, err := testClient.Logical().Write(key, map[string]interface{}{ "key": key, }) if err != nil { t.Fatal(err) } - secret, err := testClient.Logical().ReadWithContext(context.Background(), key) + secret, err := testClient.Logical().Read(key) if err != nil { t.Fatal(err) } @@ -402,7 +402,7 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -410,7 +410,7 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -419,14 +419,14 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -435,14 +435,14 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -451,7 +451,7 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -469,7 +469,7 @@ func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) { // including the child tokens and leases of the child tokens should be // untouched. testClient.SetToken(token2) - err = testClient.Auth().Token().RevokeOrphanWithContext(context.Background(), token2) + err = testClient.Auth().Token().RevokeOrphan(token2) if err != nil { t.Fatal(err) } @@ -503,7 +503,7 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -511,7 +511,7 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -520,14 +520,14 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -536,14 +536,14 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -552,7 +552,7 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -569,7 +569,7 @@ func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) { // token, evict entries for all the child tokens and their respective // leases. testClient.SetToken(token3) - err = testClient.Auth().Token().RevokeSelfWithContext(context.Background(), "") + err = testClient.Auth().Token().RevokeSelf("") if err != nil { t.Fatal(err) } @@ -603,7 +603,7 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -611,7 +611,7 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -620,14 +620,14 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -636,14 +636,14 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -652,7 +652,7 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -669,7 +669,7 @@ func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) { // belonging to this token, evict entries for all the child tokens and // their respective leases. testClient.SetToken(token2) - err = testClient.Auth().Token().RevokeSelfWithContext(context.Background(), "") + err = testClient.Auth().Token().RevokeSelf("") if err != nil { t.Fatal(err) } @@ -701,7 +701,7 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -709,7 +709,7 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -718,14 +718,14 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -734,14 +734,14 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -750,7 +750,7 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -767,7 +767,7 @@ func TestCache_TokenRevocations_TopLevelToken(t *testing.T) { // to this token, evict entries for all the child tokens and their // respective leases. testClient.SetToken(token1) - err = testClient.Auth().Token().RevokeSelfWithContext(context.Background(), "") + err = testClient.Auth().Token().RevokeSelf("") if err != nil { t.Fatal(err) } @@ -797,7 +797,7 @@ func TestCache_TokenRevocations_Shutdown(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -805,7 +805,7 @@ func TestCache_TokenRevocations_Shutdown(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -814,14 +814,14 @@ func TestCache_TokenRevocations_Shutdown(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -830,14 +830,14 @@ func TestCache_TokenRevocations_Shutdown(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -846,7 +846,7 @@ func TestCache_TokenRevocations_Shutdown(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -886,7 +886,7 @@ func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) { sampleSpace[token1] = "token" // Mount the kv backend - err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -894,7 +894,7 @@ func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) { } // Create a secret in the backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -903,14 +903,14 @@ func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) { } // Read the secret and create a lease - leaseResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease1 := leaseResp.LeaseID sampleSpace[lease1] = "lease" - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -919,14 +919,14 @@ func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) { testClient.SetToken(token2) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } lease2 := leaseResp.LeaseID sampleSpace[lease2] = "lease" - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -935,7 +935,7 @@ func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) { testClient.SetToken(token3) - leaseResp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + leaseResp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -972,13 +972,13 @@ func TestCache_NonCacheable(t *testing.T) { defer cleanup() // Query mounts first - origMounts, err := testClient.Sys().ListMountsWithContext(context.Background()) + origMounts, err := testClient.Sys().ListMounts() if err != nil { t.Fatal(err) } // Mount a kv backend - if err := testClient.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + if err := testClient.Sys().Mount("kv", &api.MountInput{ Type: "kv", Options: map[string]string{ "version": "2", @@ -988,7 +988,7 @@ func TestCache_NonCacheable(t *testing.T) { } // Query mounts again - newMounts, err := testClient.Sys().ListMountsWithContext(context.Background()) + newMounts, err := testClient.Sys().ListMounts() if err != nil { t.Fatal(err) } @@ -1020,7 +1020,7 @@ func TestCache_Caching_AuthResponse(t *testing.T) { cleanup, _, testClient, _ := setupClusterAndAgent(namespace.RootContext(nil), t, nil) defer cleanup() - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err := testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -1028,7 +1028,7 @@ func TestCache_Caching_AuthResponse(t *testing.T) { testClient.SetToken(token) authTokeCreateReq := func(t *testing.T, policies map[string]interface{}) *api.Secret { - resp, err := testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", policies) + resp, err := testClient.Logical().Write("auth/token/create", policies) if err != nil { t.Fatal(err) } @@ -1079,7 +1079,7 @@ func TestCache_Caching_LeaseResponse(t *testing.T) { cleanup, client, testClient, _ := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig) defer cleanup() - err := client.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := client.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -1089,14 +1089,14 @@ func TestCache_Caching_LeaseResponse(t *testing.T) { // Test proxy by issuing two different requests { // Write data to the lease-kv backend - _, err := testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err := testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) if err != nil { t.Fatal(err) } - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foobar", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foobar", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -1104,12 +1104,12 @@ func TestCache_Caching_LeaseResponse(t *testing.T) { t.Fatal(err) } - firstResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + firstResp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } - secondResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foobar") + secondResp, err := testClient.Logical().Read("kv/foobar") if err != nil { t.Fatal(err) } @@ -1122,7 +1122,7 @@ func TestCache_Caching_LeaseResponse(t *testing.T) { // Test caching behavior by issue the same request twice { - _, err := testClient.Logical().WriteWithContext(context.Background(), "kv/baz", map[string]interface{}{ + _, err := testClient.Logical().Write("kv/baz", map[string]interface{}{ "value": "foo", "ttl": "1h", }) @@ -1130,12 +1130,12 @@ func TestCache_Caching_LeaseResponse(t *testing.T) { t.Fatal(err) } - proxiedResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/baz") + proxiedResp, err := testClient.Logical().Read("kv/baz") if err != nil { t.Fatal(err) } - cachedResp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/baz") + cachedResp, err := testClient.Logical().Read("kv/baz") if err != nil { t.Fatal(err) } @@ -1181,7 +1181,7 @@ func testCachingCacheClearCommon(t *testing.T, clearType string) { cleanup, client, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig) defer cleanup() - err := client.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err := client.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -1189,7 +1189,7 @@ func testCachingCacheClearCommon(t *testing.T, clearType string) { } // Write data to the lease-kv backend - _, err = testClient.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = testClient.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -1198,7 +1198,7 @@ func testCachingCacheClearCommon(t *testing.T, clearType string) { } // Proxy this request, agent should cache the response - resp, err := testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + resp, err := testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -1228,7 +1228,7 @@ func testCachingCacheClearCommon(t *testing.T, clearType string) { case "token": data["value"] = testClient.Token() case "token_accessor": - lookupResp, err := client.Auth().Token().LookupWithContext(context.Background(), testClient.Token()) + lookupResp, err := client.Auth().Token().Lookup(testClient.Token()) if err != nil { t.Fatal(err) } @@ -1285,7 +1285,7 @@ func TestCache_AuthTokenCreateOrphan(t *testing.T) { Policies: []string{"default"}, NoParent: true, } - resp, err := testClient.Auth().Token().CreateWithContext(context.Background(), reqOpts) + resp, err := testClient.Auth().Token().Create(reqOpts) if err != nil { t.Fatal(err) } @@ -1312,7 +1312,7 @@ func TestCache_AuthTokenCreateOrphan(t *testing.T) { // Use the test client but set the token to one that's not managed by agent testClient.SetToken(clusterClient.Token()) - resp, err := testClient.Auth().Token().CreateWithContext(context.Background(), reqOpts) + resp, err := testClient.Auth().Token().Create(reqOpts) if err != nil { t.Fatal(err) } @@ -1336,7 +1336,7 @@ func TestCache_AuthTokenCreateOrphan(t *testing.T) { reqOpts := &api.TokenCreateRequest{ Policies: []string{"default"}, } - resp, err := testClient.Auth().Token().CreateOrphanWithContext(context.Background(), reqOpts) + resp, err := testClient.Auth().Token().CreateOrphan(reqOpts) if err != nil { t.Fatal(err) } @@ -1362,7 +1362,7 @@ func TestCache_AuthTokenCreateOrphan(t *testing.T) { // Use the test client but set the token to one that's not managed by agent testClient.SetToken(clusterClient.Token()) - resp, err := testClient.Auth().Token().CreateOrphanWithContext(context.Background(), reqOpts) + resp, err := testClient.Auth().Token().CreateOrphan(reqOpts) if err != nil { t.Fatal(err) } diff --git a/command/agent/cache_end_to_end_test.go b/command/agent/cache_end_to_end_test.go index 69d3e3fc0bd7..4ad056a850cc 100644 --- a/command/agent/cache_end_to_end_test.go +++ b/command/agent/cache_end_to_end_test.go @@ -71,7 +71,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { defer os.Setenv(api.EnvVaultCACert, os.Getenv(api.EnvVaultCACert)) os.Setenv(api.EnvVaultCACert, fmt.Sprintf("%s/ca_cert.pem", cluster.TempDir)) - err = client.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + err = client.Sys().Mount("kv", &api.MountInput{ Type: "kv", }) if err != nil { @@ -79,7 +79,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { } // Create a secret in the backend - _, err = client.Logical().WriteWithContext(context.Background(), "kv/foo", map[string]interface{}{ + _, err = client.Logical().Write("kv/foo", map[string]interface{}{ "value": "bar", "ttl": "1h", }) @@ -88,7 +88,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { } // Add an kv-admin policy - if err := client.Sys().PutPolicyWithContext(context.Background(), "test-autoauth", policyAutoAuthAppRole); err != nil { + if err := client.Sys().PutPolicy("test-autoauth", policyAutoAuthAppRole); err != nil { t.Fatal(err) } @@ -100,7 +100,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1", map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/test1", map[string]interface{}{ "bind_secret_id": "true", "token_ttl": "3s", "token_max_ttl": "10s", @@ -110,13 +110,13 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err := client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } secretID1 := resp.Data["secret_id"].(string) - resp, err = client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test1/role-id") + resp, err = client.Logical().Read("auth/approle/role/test1/role-id") if err != nil { t.Fatal(err) } @@ -343,7 +343,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { // Empty the token in the client to ensure that auto-auth token is used testClient.SetToken("") - resp, err = testClient.Logical().ReadWithContext(context.Background(), "auth/token/lookup-self") + resp, err = testClient.Logical().Read("auth/token/lookup-self") if err != nil { t.Fatal(err) } @@ -354,14 +354,14 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { // This block tests lease creation caching using the auto-auth token. { - resp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + resp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } origReqID := resp.RequestID - resp, err = testClient.Logical().ReadWithContext(context.Background(), "kv/foo") + resp, err = testClient.Logical().Read("kv/foo") if err != nil { t.Fatal(err) } @@ -379,7 +379,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { // This block tests auth token creation caching (child, non-orphan tokens) // using the auto-auth token. { - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -388,7 +388,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { // Sleep for a bit to allow renewer logic to kick in time.Sleep(20 * time.Millisecond) - resp, err = testClient.Logical().WriteWithContext(context.Background(), "auth/token/create", nil) + resp, err = testClient.Logical().Write("auth/token/create", nil) if err != nil { t.Fatal(err) } @@ -405,7 +405,7 @@ func TestCache_UsingAutoAuthToken(t *testing.T) { // Empty the token in the client to ensure that auto-auth token is used testClient.SetToken(client.Token()) - resp, err = testClient.Logical().ReadWithContext(context.Background(), "auth/token/lookup-self") + resp, err = testClient.Logical().Read("auth/token/lookup-self") if err != nil { t.Fatal(err) } diff --git a/command/agent/cert_end_to_end_test.go b/command/agent/cert_end_to_end_test.go index 127274b5df30..bacb188021cd 100644 --- a/command/agent/cert_end_to_end_test.go +++ b/command/agent/cert_end_to_end_test.go @@ -90,7 +90,7 @@ func testCertEndToEnd(t *testing.T, withCertRoleName, ahWrapping bool) { certificatePEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cluster.CACert.Raw}) certRoleName := "test" - _, err = client.Logical().WriteWithContext(context.Background(), fmt.Sprintf("auth/cert/certs/%s", certRoleName), map[string]interface{}{ + _, err = client.Logical().Write(fmt.Sprintf("auth/cert/certs/%s", certRoleName), map[string]interface{}{ "certificate": string(certificatePEM), "policies": "default", }) @@ -327,7 +327,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { // ///////////// // Mount /pki as a root CA - err := client.Sys().MountWithContext(context.Background(), "pki", &api.MountInput{ + err := client.Sys().Mount("pki", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -340,7 +340,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { // Set the cluster's certificate as the root CA in /pki pemBundleRootCA := string(cluster.CACertPEM) + string(cluster.CAKeyPEM) - _, err = client.Logical().WriteWithContext(context.Background(), "pki/config/ca", map[string]interface{}{ + _, err = client.Logical().Write("pki/config/ca", map[string]interface{}{ "pem_bundle": pemBundleRootCA, }) if err != nil { @@ -348,7 +348,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { } // Mount /pki2 to operate as an intermediate CA - err = client.Sys().MountWithContext(context.Background(), "pki2", &api.MountInput{ + err = client.Sys().Mount("pki2", &api.MountInput{ Type: "pki", Config: api.MountConfigInput{ DefaultLeaseTTL: "16h", @@ -360,14 +360,14 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { } // Create a CSR for the intermediate CA - secret, err := client.Logical().WriteWithContext(context.Background(), "pki2/intermediate/generate/internal", nil) + secret, err := client.Logical().Write("pki2/intermediate/generate/internal", nil) if err != nil { t.Fatal(err) } intermediateCSR := secret.Data["csr"].(string) // Sign the intermediate CSR using /pki - secret, err = client.Logical().WriteWithContext(context.Background(), "pki/root/sign-intermediate", map[string]interface{}{ + secret, err = client.Logical().Write("pki/root/sign-intermediate", map[string]interface{}{ "permitted_dns_domains": ".myvault.com", "csr": intermediateCSR, }) @@ -377,7 +377,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { intermediateCertPEM := secret.Data["certificate"].(string) // Configure the intermediate cert as the CA in /pki2 - _, err = client.Logical().WriteWithContext(context.Background(), "pki2/intermediate/set-signed", map[string]interface{}{ + _, err = client.Logical().Write("pki2/intermediate/set-signed", map[string]interface{}{ "certificate": intermediateCertPEM, }) if err != nil { @@ -385,7 +385,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { } // Create a role on the intermediate CA mount - _, err = client.Logical().WriteWithContext(context.Background(), "pki2/roles/myvault-dot-com", map[string]interface{}{ + _, err = client.Logical().Write("pki2/roles/myvault-dot-com", map[string]interface{}{ "allowed_domains": "myvault.com", "allow_subdomains": "true", "max_ttl": "5m", @@ -395,7 +395,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { } // Issue a leaf cert using the intermediate CA - secret, err = client.Logical().WriteWithContext(context.Background(), "pki2/issue/myvault-dot-com", map[string]interface{}{ + secret, err = client.Logical().Write("pki2/issue/myvault-dot-com", map[string]interface{}{ "common_name": "cert.myvault.com", "format": "pem", "ip_sans": "127.0.0.1", @@ -457,7 +457,7 @@ func TestCertEndToEnd_CertsInConfig(t *testing.T) { } // Set the intermediate CA cert as a trusted certificate in the backend - _, err = client.Logical().WriteWithContext(context.Background(), "auth/cert/certs/myvault-dot-com", map[string]interface{}{ + _, err = client.Logical().Write("auth/cert/certs/myvault-dot-com", map[string]interface{}{ "display_name": "myvault.com", "policies": "default", "certificate": intermediateCertPEM, diff --git a/command/agent/cf_end_to_end_test.go b/command/agent/cf_end_to_end_test.go index 17963fda729f..6bc1fa8b6a07 100644 --- a/command/agent/cf_end_to_end_test.go +++ b/command/agent/cf_end_to_end_test.go @@ -68,7 +68,7 @@ func TestCFEndToEnd(t *testing.T) { defer mockCFAPI.Close() // Configure a CA certificate like a Vault operator would in setting up CF. - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/cf/config", map[string]interface{}{ + if _, err := client.Logical().Write("auth/cf/config", map[string]interface{}{ "identity_ca_certificates": testCFCerts.CACertificate, "cf_api_addr": mockCFAPI.URL, "cf_username": cfAPI.AuthUsername, @@ -78,7 +78,7 @@ func TestCFEndToEnd(t *testing.T) { } // Configure a role to be used for logging in, another thing a Vault operator would do. - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/cf/roles/test-role", map[string]interface{}{ + if _, err := client.Logical().Write("auth/cf/roles/test-role", map[string]interface{}{ "bound_instance_ids": cfAPI.FoundServiceGUID, "bound_organization_ids": cfAPI.FoundOrgGUID, "bound_space_ids": cfAPI.FoundSpaceGUID, diff --git a/command/agent/jwt_end_to_end_test.go b/command/agent/jwt_end_to_end_test.go index 96ea28d67a52..c2d74d9f37dc 100644 --- a/command/agent/jwt_end_to_end_test.go +++ b/command/agent/jwt_end_to_end_test.go @@ -53,7 +53,7 @@ func testJWTEndToEnd(t *testing.T, ahWrapping bool) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/config", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/config", map[string]interface{}{ "bound_issuer": "https://team-vault.auth0.com/", "jwt_validation_pubkeys": TestECDSAPubKey, "jwt_supported_algs": "ES256", @@ -62,7 +62,7 @@ func testJWTEndToEnd(t *testing.T, ahWrapping bool) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/role/test", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/role/test", map[string]interface{}{ "role_type": "jwt", "bound_subject": "r3qXcK2bix9eFECzsU3Sbmh0K16fatW6@clients", "bound_audiences": "https://vault.plugin.auth.jwt.test", diff --git a/command/agent/template/template.go b/command/agent/template/template.go index 3ad546787c81..cf21944ab0a8 100644 --- a/command/agent/template/template.go +++ b/command/agent/template/template.go @@ -341,7 +341,7 @@ func logLevelToStringPtr(level hclog.Level) *string { case hclog.Warn: levelStr = "WARN" case hclog.Error: - levelStr = "ERROR" + levelStr = "ERR" default: levelStr = "INFO" } diff --git a/command/agent/template/template_test.go b/command/agent/template/template_test.go index 9f0464730e11..77f166affc08 100644 --- a/command/agent/template/template_test.go +++ b/command/agent/template/template_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "net/http" "net/http/httptest" "os" @@ -188,7 +187,7 @@ func TestCacheConfigNoListener(t *testing.T) { assert.NotNil(t, ctConfig.Vault.Transport.CustomDialer) } -func TestServerRun(t *testing.T) { +func createHttpTestServer() *httptest.Server { // create http test server mux := http.NewServeMux() mux.HandleFunc("/v1/kv/myapp/config", func(w http.ResponseWriter, r *http.Request) { @@ -203,9 +202,14 @@ func TestServerRun(t *testing.T) { fmt.Fprintln(w, `{"errors":["1 error occurred:\n\t* permission denied\n\n"]}`) }) - ts := httptest.NewServer(mux) + return httptest.NewServer(mux) +} + +func TestServerRun(t *testing.T) { + ts := createHttpTestServer() defer ts.Close() - tmpDir, err := ioutil.TempDir("", "agent-tests") + + tmpDir, err := os.MkdirTemp("", "agent-tests") defer os.RemoveAll(tmpDir) if err != nil { t.Fatal(err) @@ -379,7 +383,7 @@ func TestServerRun(t *testing.T) { if template.Destination == nil { t.Fatal("nil template destination") } - content, err := ioutil.ReadFile(*template.Destination) + content, err := os.ReadFile(*template.Destination) if err != nil { t.Fatal(err) } @@ -400,6 +404,70 @@ func TestServerRun(t *testing.T) { } } +// TestNewServerLogLevels tests that the server can be started with any log +// level. +func TestNewServerLogLevels(t *testing.T) { + ts := createHttpTestServer() + defer ts.Close() + + tmpDir, err := os.MkdirTemp("", "agent-tests") + defer os.RemoveAll(tmpDir) + if err != nil { + t.Fatal(err) + } + + levels := []hclog.Level{hclog.NoLevel, hclog.Trace, hclog.Debug, hclog.Info, hclog.Warn, hclog.Error} + for _, level := range levels { + name := fmt.Sprintf("log_%s", level) + t.Run(name, func(t *testing.T) { + server := NewServer(&ServerConfig{ + Logger: logging.NewVaultLogger(level), + LogWriter: hclog.DefaultOutput, + LogLevel: level, + ExitAfterAuth: true, + AgentConfig: &config.Config{ + Vault: &config.Vault{ + Address: ts.URL, + }, + }, + }) + if server == nil { + t.Fatal("nil server returned") + } + defer server.Stop() + + templateTokenCh := make(chan string, 1) + + templateTest := &ctconfig.TemplateConfig{ + Contents: pointerutil.StringPtr(templateContents), + } + dstFile := fmt.Sprintf("%s/%s", tmpDir, name) + templateTest.Destination = pointerutil.StringPtr(dstFile) + templatesToRender := []*ctconfig.TemplateConfig{templateTest} + + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + + errCh := make(chan error) + go func() { + errCh <- server.Run(ctx, templateTokenCh, templatesToRender) + }() + + // send a dummy value to trigger auth so the server will exit + templateTokenCh <- "test" + + select { + case <-ctx.Done(): + t.Fatal("timeout reached before templates were rendered") + case err := <-errCh: + if err != nil { + t.Fatalf("did not expect error, got: %v", err) + } + } + }) + } +} + var jsonResponse = ` { "request_id": "8af096e9-518c-7351-eff5-5ba20554b21f", diff --git a/command/agent_test.go b/command/agent_test.go index 2f191aa951a7..4b62020e1dfb 100644 --- a/command/agent_test.go +++ b/command/agent_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "encoding/json" "fmt" "io/ioutil" @@ -76,7 +75,7 @@ func TestAgent_Cache_UnixListener(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/config", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/config", map[string]interface{}{ "bound_issuer": "https://team-vault.auth0.com/", "jwt_validation_pubkeys": agent.TestECDSAPubKey, }) @@ -84,7 +83,7 @@ func TestAgent_Cache_UnixListener(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/role/test", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/role/test", map[string]interface{}{ "role_type": "jwt", "bound_subject": "r3qXcK2bix9eFECzsU3Sbmh0K16fatW6@clients", "bound_audiences": "https://vault.plugin.auth.jwt.test", @@ -219,7 +218,7 @@ cache { time.Sleep(1 * time.Second) // Invoke lookup self through the agent - secret, err := testClient.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := testClient.Auth().Token().LookupSelf() if err != nil { t.Fatal(err) } @@ -264,7 +263,7 @@ func testAgentExitAfterAuth(t *testing.T, viaFlag bool) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/config", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/config", map[string]interface{}{ "bound_issuer": "https://team-vault.auth0.com/", "jwt_validation_pubkeys": agent.TestECDSAPubKey, "jwt_supported_algs": "ES256", @@ -273,7 +272,7 @@ func testAgentExitAfterAuth(t *testing.T, viaFlag bool) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/jwt/role/test", map[string]interface{}{ + _, err = client.Logical().Write("auth/jwt/role/test", map[string]interface{}{ "role_type": "jwt", "bound_subject": "r3qXcK2bix9eFECzsU3Sbmh0K16fatW6@clients", "bound_audiences": "https://vault.plugin.auth.jwt.test", @@ -1307,7 +1306,7 @@ func TestAgent_Template_Retry(t *testing.T) { methodConf, cleanup := prepAgentApproleKV(t, serverClient) defer cleanup() - err := serverClient.Sys().TuneMountWithContext(context.Background(), "secret", api.MountConfigInput{ + err := serverClient.Sys().TuneMount("secret", api.MountConfigInput{ Options: map[string]string{ "version": "2", }, @@ -1316,7 +1315,7 @@ func TestAgent_Template_Retry(t *testing.T) { t.Fatal(err) } - _, err = serverClient.Logical().WriteWithContext(context.Background(), "secret/data/otherapp", map[string]interface{}{ + _, err = serverClient.Logical().Write("secret/data/otherapp", map[string]interface{}{ "data": map[string]interface{}{ "username": "barstuff", "password": "zap", @@ -1500,7 +1499,7 @@ path "/secret/*" { } ` // Add an kv-admin policy - if err := client.Sys().PutPolicyWithContext(context.Background(), "test-autoauth", policyAutoAuthAppRole); err != nil { + if err := client.Sys().PutPolicy("test-autoauth", policyAutoAuthAppRole); err != nil { t.Fatal(err) } @@ -1512,7 +1511,7 @@ path "/secret/*" { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1", map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/test1", map[string]interface{}{ "bind_secret_id": "true", "token_ttl": "1h", "token_max_ttl": "2h", @@ -1522,14 +1521,14 @@ path "/secret/*" { t.Fatal(err) } - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/test1/secret-id", nil) + resp, err := client.Logical().Write("auth/approle/role/test1/secret-id", nil) if err != nil { t.Fatal(err) } secretID := resp.Data["secret_id"].(string) secretIDFile := makeTempFile(t, "secret_id.txt", secretID+"\n") - resp, err = client.Logical().ReadWithContext(context.Background(), "auth/approle/role/test1/role-id") + resp, err = client.Logical().Read("auth/approle/role/test1/role-id") if err != nil { t.Fatal(err) } @@ -1591,7 +1590,7 @@ func TestAgent_Cache_Retry(t *testing.T) { defer os.Setenv(api.EnvVaultAddress, os.Getenv(api.EnvVaultAddress)) os.Unsetenv(api.EnvVaultAddress) - _, err := serverClient.Logical().WriteWithContext(context.Background(), "secret/foo", map[string]interface{}{ + _, err := serverClient.Logical().Write("secret/foo", map[string]interface{}{ "bar": "baz", }) if err != nil { @@ -1689,7 +1688,7 @@ vault { if err != nil { t.Fatal(err) } - secret, err := client.Logical().ReadWithContext(context.Background(), "secret/foo") + secret, err := client.Logical().Read("secret/foo") switch { case (err != nil || secret == nil) && tc.expectError: case (err == nil || secret != nil) && !tc.expectError: @@ -1743,7 +1742,7 @@ func TestAgent_TemplateConfig_ExitOnRetryFailure(t *testing.T) { autoAuthConfig, cleanup := prepAgentApproleKV(t, serverClient) defer cleanup() - err := serverClient.Sys().TuneMountWithContext(context.Background(), "secret", api.MountConfigInput{ + err := serverClient.Sys().TuneMount("secret", api.MountConfigInput{ Options: map[string]string{ "version": "2", }, @@ -1752,7 +1751,7 @@ func TestAgent_TemplateConfig_ExitOnRetryFailure(t *testing.T) { t.Fatal(err) } - _, err = serverClient.Logical().WriteWithContext(context.Background(), "secret/data/otherapp", map[string]interface{}{ + _, err = serverClient.Logical().Write("secret/data/otherapp", map[string]interface{}{ "data": map[string]interface{}{ "username": "barstuff", "password": "zap", diff --git a/command/approle_concurrency_integ_test.go b/command/approle_concurrency_integ_test.go index 2b465e586bf2..5dbcce064c8d 100644 --- a/command/approle_concurrency_integ_test.go +++ b/command/approle_concurrency_integ_test.go @@ -45,7 +45,7 @@ func TestAppRole_Integ_ConcurrentLogins(t *testing.T) { t.Fatal(err) } - _, err = client.Logical().WriteWithContext(context.Background(), "auth/approle/role/role1", map[string]interface{}{ + _, err = client.Logical().Write("auth/approle/role/role1", map[string]interface{}{ "bind_secret_id": "true", "period": "300", }) @@ -53,13 +53,13 @@ func TestAppRole_Integ_ConcurrentLogins(t *testing.T) { t.Fatal(err) } - secret, err := client.Logical().WriteWithContext(context.Background(), "auth/approle/role/role1/secret-id", nil) + secret, err := client.Logical().Write("auth/approle/role/role1/secret-id", nil) if err != nil { t.Fatal(err) } secretID := secret.Data["secret_id"].(string) - secret, err = client.Logical().ReadWithContext(context.Background(), "auth/approle/role/role1/role-id") + secret, err = client.Logical().Read("auth/approle/role/role1/role-id") if err != nil { t.Fatal(err) } diff --git a/command/audit_disable_test.go b/command/audit_disable_test.go index 17043326c481..0a7e8e4dcd99 100644 --- a/command/audit_disable_test.go +++ b/command/audit_disable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -64,7 +63,7 @@ func TestAuditDisableCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().EnableAuditWithOptionsWithContext(context.Background(), "file", &api.EnableAuditOptions{ + if err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{ Type: "file", Options: map[string]string{ "file_path": "discard", @@ -94,7 +93,7 @@ func TestAuditDisableCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().EnableAuditWithOptionsWithContext(context.Background(), "integration_audit_disable", &api.EnableAuditOptions{ + if err := client.Sys().EnableAuditWithOptions("integration_audit_disable", &api.EnableAuditOptions{ Type: "file", Options: map[string]string{ "file_path": "discard", @@ -119,7 +118,7 @@ func TestAuditDisableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } diff --git a/command/audit_enable_test.go b/command/audit_enable_test.go index b0526d5679ed..1f55703c27bf 100644 --- a/command/audit_enable_test.go +++ b/command/audit_enable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "io/ioutil" "os" "strings" @@ -109,7 +108,7 @@ func TestAuditEnableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - audits, err := client.Sys().ListAuditWithContext(context.Background()) + audits, err := client.Sys().ListAudit() if err != nil { t.Fatal(err) } diff --git a/command/audit_list_test.go b/command/audit_list_test.go index 3660f5275c6a..9cbb0af5eee3 100644 --- a/command/audit_list_test.go +++ b/command/audit_list_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -58,7 +57,7 @@ func TestAuditListCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().EnableAuditWithOptionsWithContext(context.Background(), "file", &api.EnableAuditOptions{ + if err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{ Type: "file", Options: map[string]string{ "file_path": "discard", diff --git a/command/auth_disable_test.go b/command/auth_disable_test.go index 3b1ba9cc9dd9..51419b86637a 100644 --- a/command/auth_disable_test.go +++ b/command/auth_disable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -96,7 +95,7 @@ func TestAuthDisableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } diff --git a/command/auth_enable_test.go b/command/auth_enable_test.go index a1574a4d22dc..0cc125fc9756 100644 --- a/command/auth_enable_test.go +++ b/command/auth_enable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "io/ioutil" "strings" "testing" @@ -106,7 +105,7 @@ func TestAuthEnableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } diff --git a/command/auth_move_test.go b/command/auth_move_test.go index 5143e086c5ea..035938efe5aa 100644 --- a/command/auth_move_test.go +++ b/command/auth_move_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -105,7 +104,7 @@ func TestAuthMoveCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListAuthWithContext(context.Background()) + mounts, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } diff --git a/command/auth_tune_test.go b/command/auth_tune_test.go index b890877ad332..227330ea774e 100644 --- a/command/auth_tune_test.go +++ b/command/auth_tune_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -110,7 +109,7 @@ func TestAuthTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } @@ -176,7 +175,7 @@ func TestAuthTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } @@ -219,7 +218,7 @@ func TestAuthTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - auths, err := client.Sys().ListAuthWithContext(context.Background()) + auths, err := client.Sys().ListAuth() if err != nil { t.Fatal(err) } diff --git a/command/base_predict_test.go b/command/base_predict_test.go index 65b08b99a04c..12f364106f7a 100644 --- a/command/base_predict_test.go +++ b/command/base_predict_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "reflect" "testing" @@ -17,25 +16,25 @@ func TestPredictVaultPaths(t *testing.T) { defer closer() data := map[string]interface{}{"a": "b"} - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/bar", data); err != nil { + if _, err := client.Logical().Write("secret/bar", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/foo", data); err != nil { + if _, err := client.Logical().Write("secret/foo", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/zip/zap", data); err != nil { + if _, err := client.Logical().Write("secret/zip/zap", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/zip/zonk", data); err != nil { + if _, err := client.Logical().Write("secret/zip/zonk", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/zip/twoot", data); err != nil { + if _, err := client.Logical().Write("secret/zip/twoot", data); err != nil { t.Fatal(err) } - if err := client.Sys().MountWithContext(context.Background(), "level1a/level2a/level3a", &api.MountInput{Type: "kv"}); err != nil { + if err := client.Sys().Mount("level1a/level2a/level3a", &api.MountInput{Type: "kv"}); err != nil { t.Fatal(err) } - if err := client.Sys().MountWithContext(context.Background(), "level1a/level2a/level3b", &api.MountInput{Type: "kv"}); err != nil { + if err := client.Sys().Mount("level1a/level2a/level3b", &api.MountInput{Type: "kv"}); err != nil { t.Fatal(err) } @@ -231,7 +230,7 @@ func TestPredict_Audits(t *testing.T) { badClient, badCloser := testVaultServerBad(t) defer badCloser() - if err := client.Sys().EnableAuditWithOptionsWithContext(context.Background(), "file", &api.EnableAuditOptions{ + if err := client.Sys().EnableAuditWithOptions("file", &api.EnableAuditOptions{ Type: "file", Options: map[string]string{ "file_path": "discard", @@ -496,13 +495,13 @@ func TestPredict_Paths(t *testing.T) { defer closer() data := map[string]interface{}{"a": "b"} - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/bar", data); err != nil { + if _, err := client.Logical().Write("secret/bar", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/foo", data); err != nil { + if _, err := client.Logical().Write("secret/foo", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/zip/zap", data); err != nil { + if _, err := client.Logical().Write("secret/zip/zap", data); err != nil { t.Fatal(err) } @@ -572,10 +571,10 @@ func TestPredict_ListPaths(t *testing.T) { defer badCloser() data := map[string]interface{}{"a": "b"} - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/bar", data); err != nil { + if _, err := client.Logical().Write("secret/bar", data); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/foo", data); err != nil { + if _, err := client.Logical().Write("secret/foo", data); err != nil { t.Fatal(err) } diff --git a/command/command_test.go b/command/command_test.go index 76ab22575156..8de036e40ffb 100644 --- a/command/command_test.go +++ b/command/command_test.go @@ -236,7 +236,7 @@ func testVaultServerBad(tb testing.TB) (*api.Client, func()) { func testTokenAndAccessor(tb testing.TB, client *api.Client) (string, string) { tb.Helper() - secret, err := client.Auth().Token().CreateWithContext(context.Background(), &api.TokenCreateRequest{ + secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{ Policies: []string{"default"}, TTL: "30m", }) diff --git a/command/debug.go b/command/debug.go index f981b18478f6..e0fd8cd3144c 100644 --- a/command/debug.go +++ b/command/debug.go @@ -8,9 +8,11 @@ import ( "net/url" "os" "path/filepath" + "runtime" "strconv" "strings" "sync" + "syscall" "time" "github.com/hashicorp/go-hclog" @@ -356,7 +358,7 @@ func (c *DebugCommand) generateIndex() error { } // Write out file - if err := ioutil.WriteFile(filepath.Join(c.flagOutput, "index.json"), bytes, 0o644); err != nil { + if err := ioutil.WriteFile(filepath.Join(c.flagOutput, "index.json"), bytes, 0o600); err != nil { return fmt.Errorf("error generating index file; %s", err) } @@ -453,7 +455,7 @@ func (c *DebugCommand) preflight(rawArgs []string) (string, error) { _, err = os.Stat(c.flagOutput) switch { case os.IsNotExist(err): - err := os.MkdirAll(c.flagOutput, 0o755) + err := os.MkdirAll(c.flagOutput, 0o700) if err != nil { return "", fmt.Errorf("unable to create output directory: %s", err) } @@ -741,7 +743,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { // Create a sub-directory for pprof data currentDir := currentTimestamp.Format(fileFriendlyTimeFormat) dirName := filepath.Join(c.flagOutput, currentDir) - if err := os.MkdirAll(dirName, 0o755); err != nil { + if err := os.MkdirAll(dirName, 0o700); err != nil { c.UI.Error(fmt.Sprintf("Error creating sub-directory for time interval: %s", err)) continue } @@ -758,7 +760,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { return } - err = ioutil.WriteFile(filepath.Join(dirName, target+".prof"), data, 0o644) + err = ioutil.WriteFile(filepath.Join(dirName, target+".prof"), data, 0o600) if err != nil { c.captureError("pprof."+target, err) } @@ -776,7 +778,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { return } - err = ioutil.WriteFile(filepath.Join(dirName, "goroutines.txt"), data, 0o644) + err = ioutil.WriteFile(filepath.Join(dirName, "goroutines.txt"), data, 0o600) if err != nil { c.captureError("pprof.goroutines-text", err) } @@ -800,7 +802,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { return } - err = ioutil.WriteFile(filepath.Join(dirName, "profile.prof"), data, 0o644) + err = ioutil.WriteFile(filepath.Join(dirName, "profile.prof"), data, 0o600) if err != nil { c.captureError("pprof.profile", err) } @@ -816,7 +818,7 @@ func (c *DebugCommand) collectPprof(ctx context.Context) { return } - err = ioutil.WriteFile(filepath.Join(dirName, "trace.out"), data, 0o644) + err = ioutil.WriteFile(filepath.Join(dirName, "trace.out"), data, 0o600) if err != nil { c.captureError("pprof.trace", err) } @@ -952,7 +954,7 @@ func (c *DebugCommand) persistCollection(collection []map[string]interface{}, ou if err != nil { return err } - if err := ioutil.WriteFile(filepath.Join(c.flagOutput, outFile), bytes, 0o644); err != nil { + if err := ioutil.WriteFile(filepath.Join(c.flagOutput, outFile), bytes, 0o600); err != nil { return err } @@ -960,6 +962,10 @@ func (c *DebugCommand) persistCollection(collection []map[string]interface{}, ou } func (c *DebugCommand) compress(dst string) error { + if runtime.GOOS != "windows" { + defer syscall.Umask(syscall.Umask(0o077)) + } + tgz := archiver.NewTarGz() if err := tgz.Archive([]string{c.flagOutput}, dst); err != nil { return fmt.Errorf("failed to compress data: %s", err) @@ -1044,7 +1050,7 @@ func (c *DebugCommand) captureError(target string, err error) { } func (c *DebugCommand) writeLogs(ctx context.Context) { - out, err := os.Create(filepath.Join(c.flagOutput, "vault.log")) + out, err := os.OpenFile(filepath.Join(c.flagOutput, "vault.log"), os.O_CREATE, 0o600) if err != nil { c.captureError("log", err) return diff --git a/command/debug_test.go b/command/debug_test.go index 3c6ca4567c07..046474af8660 100644 --- a/command/debug_test.go +++ b/command/debug_test.go @@ -2,13 +2,14 @@ package command import ( "archive/tar" - "context" "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" + "runtime" "strings" + "syscall" "testing" "time" @@ -599,7 +600,7 @@ func TestDebugCommand_OutputExists(t *testing.T) { t.Fatal(err) } } else { - err = os.Mkdir(outputPath, 0o755) + err = os.Mkdir(outputPath, 0o700) if err != nil { t.Fatal(err) } @@ -641,7 +642,7 @@ func TestDebugCommand_PartialPermissions(t *testing.T) { defer closer() // Create a new token with default policy - resp, err := client.Logical().WriteWithContext(context.Background(), "auth/token/create", map[string]interface{}{ + resp, err := client.Logical().Write("auth/token/create", map[string]interface{}{ "policies": "default", }) if err != nil { @@ -705,3 +706,134 @@ func TestDebugCommand_PartialPermissions(t *testing.T) { t.Fatal(err) } } + +// set insecure umask to see if the files and directories get created with right permissions +func TestDebugCommand_InsecureUmask(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("test does not work in windows environment") + } + t.Parallel() + + cases := []struct { + name string + compress bool + outputFile string + expectError bool + }{ + { + "with-compress", + true, + "with-compress.tar.gz", + false, + }, + { + "no-compress", + false, + "no-compress", + false, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + // set insecure umask + defer syscall.Umask(syscall.Umask(0)) + + testDir, err := ioutil.TempDir("", "vault-debug") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(testDir) + + client, closer := testVaultServer(t) + defer closer() + + ui, cmd := testDebugCommand(t) + cmd.client = client + cmd.skipTimingChecks = true + + outputPath := filepath.Join(testDir, tc.outputFile) + + args := []string{ + fmt.Sprintf("-compress=%t", tc.compress), + "-duration=1s", + "-interval=1s", + "-metrics-interval=1s", + fmt.Sprintf("-output=%s", outputPath), + } + + code := cmd.Run(args) + if exp := 0; code != exp { + t.Log(ui.ErrorWriter.String()) + t.Fatalf("expected %d to be %d", code, exp) + } + // If we expect an error we're done here + if tc.expectError { + return + } + + bundlePath := filepath.Join(testDir, tc.outputFile) + fs, err := os.Stat(bundlePath) + if os.IsNotExist(err) { + t.Log(ui.OutputWriter.String()) + t.Fatal(err) + } + // check permissions of the parent debug directory + err = isValidFilePermissions(fs) + if err != nil { + t.Fatalf(err.Error()) + } + + // check permissions of the files within the parent directory + switch tc.compress { + case true: + tgz := archiver.NewTarGz() + + err = tgz.Walk(bundlePath, func(f archiver.File) error { + fh, ok := f.Header.(*tar.Header) + if !ok { + return fmt.Errorf("invalid file header: %#v", f.Header) + } + err = isValidFilePermissions(fh.FileInfo()) + if err != nil { + t.Fatalf(err.Error()) + } + return nil + }) + + case false: + err = filepath.Walk(bundlePath, func(path string, info os.FileInfo, err error) error { + err = isValidFilePermissions(info) + if err != nil { + t.Fatalf(err.Error()) + } + return nil + }) + } + + if err != nil { + t.Fatal(err) + } + }) + } +} + +func isValidFilePermissions(info os.FileInfo) (err error) { + mode := info.Mode() + // check group permissions + for i := 4; i < 7; i++ { + if string(mode.String()[i]) != "-" { + return fmt.Errorf("expected no permissions for group but got %s permissions for file %s", string(mode.String()[i]), info.Name()) + } + } + + // check others permissions + for i := 7; i < 10; i++ { + if string(mode.String()[i]) != "-" { + return fmt.Errorf("expected no permissions for others but got %s permissions for file %s", string(mode.String()[i]), info.Name()) + } + } + return err +} diff --git a/command/delete_test.go b/command/delete_test.go index 5cebe3c55cd9..e26d393b16fe 100644 --- a/command/delete_test.go +++ b/command/delete_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -82,7 +81,7 @@ func TestDeleteCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/delete/foo", map[string]interface{}{ + if _, err := client.Logical().Write("secret/delete/foo", map[string]interface{}{ "foo": "bar", }); err != nil { t.Fatal(err) @@ -104,7 +103,7 @@ func TestDeleteCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - secret, _ := client.Logical().ReadWithContext(context.Background(), "secret/delete/foo") + secret, _ := client.Logical().Read("secret/delete/foo") if secret != nil { t.Errorf("expected deletion: %#v", secret) } diff --git a/command/kv.go b/command/kv.go index 3fa91c8c586f..2172576dbd6f 100644 --- a/command/kv.go +++ b/command/kv.go @@ -27,19 +27,25 @@ Usage: vault kv [options] [args] Create or update the key named "foo" in the "secret" mount with the value "bar=baz": - $ vault kv put secret/foo bar=baz + $ vault kv put -mount=secret foo bar=baz Read this value back: - $ vault kv get secret/foo + $ vault kv get -mount=secret foo Get metadata for the key: - $ vault kv metadata get secret/foo + $ vault kv metadata get -mount=secret foo Get a specific version of the key: - $ vault kv get -version=1 secret/foo + $ vault kv get -mount=secret -version=1 foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + + $ vault kv get secret/foo Please see the individual subcommand help for detailed usage information. ` diff --git a/command/kv_delete.go b/command/kv_delete.go index 59e558fbf537..65e0927630ad 100644 --- a/command/kv_delete.go +++ b/command/kv_delete.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "path" "strings" "github.com/hashicorp/vault/api" @@ -18,6 +19,7 @@ type KVDeleteCommand struct { *BaseCommand flagVersions []string + flagMount string } func (c *KVDeleteCommand) Synopsis() string { @@ -34,11 +36,17 @@ Usage: vault kv delete [options] PATH To delete the latest version of the key "foo": + $ vault kv delete -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv delete secret/foo To delete version 3 of key foo: - $ vault kv delete -versions=3 secret/foo + $ vault kv delete -mount=secret -versions=3 foo To delete all versions and metadata, see the "vault kv metadata" subcommand. @@ -61,6 +69,17 @@ func (c *KVDeleteCommand) Flags() *FlagSets { Usage: `Specifies the version numbers to delete.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -96,22 +115,54 @@ func (c *KVDeleteCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } var secret *api.Secret + var fullPath string if v2 { - secret, err = c.deleteV2(path, mountPath, client) + secret, err = c.deleteV2(partialPath, mountPath, client) + fullPath = addPrefixToKVPath(partialPath, mountPath, "data") } else { - secret, err = client.Logical().Delete(path) + // v1 + if mountFlagSyntax { + fullPath = path.Join(mountPath, partialPath) + } else { + fullPath = partialPath + } + secret, err = client.Logical().Delete(fullPath) } if err != nil { - c.UI.Error(fmt.Sprintf("Error deleting %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error deleting %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) } @@ -121,7 +172,7 @@ func (c *KVDeleteCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data deleted (if it existed) at: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data deleted (if it existed) at: %s", fullPath)) } return 0 } @@ -139,22 +190,12 @@ func (c *KVDeleteCommand) deleteV2(path, mountPath string, client *api.Client) ( switch { case len(c.flagVersions) > 0: path = addPrefixToKVPath(path, mountPath, "delete") - if err != nil { - return nil, err - } - data := map[string]interface{}{ "versions": kvParseVersionsFlags(c.flagVersions), } - secret, err = client.Logical().Write(path, data) default: - path = addPrefixToKVPath(path, mountPath, "data") - if err != nil { - return nil, err - } - secret, err = client.Logical().Delete(path) } diff --git a/command/kv_destroy.go b/command/kv_destroy.go index 53a1416a786e..33f5452a74e0 100644 --- a/command/kv_destroy.go +++ b/command/kv_destroy.go @@ -17,6 +17,7 @@ type KVDestroyCommand struct { *BaseCommand flagVersions []string + flagMount string } func (c *KVDestroyCommand) Synopsis() string { @@ -32,6 +33,12 @@ Usage: vault kv destroy [options] KEY To destroy version 3 of key foo: + $ vault kv destroy -mount=secret -versions=3 foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv destroy -versions=3 secret/foo Additional flags and more advanced use cases are detailed below. @@ -53,6 +60,17 @@ func (c *KVDestroyCommand) Flags() *FlagSets { Usage: `Specifies the version numbers to destroy.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -86,8 +104,8 @@ func (c *KVDestroyCommand) Run(args []string) int { c.UI.Error("No versions provided, use the \"-versions\" flag to specify the version to destroy.") return 1 } + var err error - path := sanitizePath(args[0]) client, err := c.Client() if err != nil { @@ -95,16 +113,42 @@ func (c *KVDestroyCommand) Run(args []string) int { return 2 } - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + if !v2 { c.UI.Error("Destroy not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "destroy") + destroyPath := addPrefixToKVPath(partialPath, mountPath, "destroy") if err != nil { c.UI.Error(err.Error()) return 2 @@ -114,9 +158,9 @@ func (c *KVDestroyCommand) Run(args []string) int { "versions": kvParseVersionsFlags(c.flagVersions), } - secret, err := client.Logical().Write(path, data) + secret, err := client.Logical().Write(destroyPath, data) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", destroyPath, err)) if secret != nil { OutputSecret(c.UI, secret) } @@ -125,7 +169,7 @@ func (c *KVDestroyCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", destroyPath)) } return 0 } diff --git a/command/kv_get.go b/command/kv_get.go index 14202d76449b..07033448187a 100644 --- a/command/kv_get.go +++ b/command/kv_get.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "path" "strings" "github.com/mitchellh/cli" @@ -17,6 +18,7 @@ type KVGetCommand struct { *BaseCommand flagVersion int + flagMount string } func (c *KVGetCommand) Synopsis() string { @@ -31,12 +33,18 @@ Usage: vault kv get [options] KEY key exists with that name, an error is returned. If a key exists with that name but has no data, nothing is returned. + $ vault kv get -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv get secret/foo To view the given key name at a specific version in time, specify the "-version" flag: - $ vault kv get -version=1 secret/foo + $ vault kv get -mount=secret -version=1 foo Additional flags and more advanced use cases are detailed below. @@ -57,6 +65,17 @@ func (c *KVGetCommand) Flags() *FlagSets { Usage: `If passed, the value at the version number will be returned.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -92,35 +111,67 @@ func (c *KVGetCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } var versionParam map[string]string - + var fullPath string + // Add /data to v2 paths only if v2 { - path = addPrefixToKVPath(path, mountPath, "data") + fullPath = addPrefixToKVPath(partialPath, mountPath, "data") if c.flagVersion > 0 { versionParam = map[string]string{ "version": fmt.Sprintf("%d", c.flagVersion), } } + } else { + // v1 + if mountFlagSyntax { + fullPath = path.Join(mountPath, partialPath) + } else { + fullPath = partialPath + } } - secret, err := kvReadRequest(client, path, versionParam) + secret, err := kvReadRequest(client, fullPath, versionParam) if err != nil { - c.UI.Error(fmt.Sprintf("Error reading %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error reading %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) } return 2 } if secret == nil { - c.UI.Error(fmt.Sprintf("No value found at %s", path)) + c.UI.Error(fmt.Sprintf("No value found at %s", fullPath)) return 2 } @@ -140,7 +191,7 @@ func (c *KVGetCommand) Run(args []string) int { } return PrintRawField(c.UI, data, c.flagField) } else { - c.UI.Error(fmt.Sprintf("No data found at %s", path)) + c.UI.Error(fmt.Sprintf("No data found at %s", fullPath)) return 2 } } else { @@ -159,7 +210,7 @@ func (c *KVGetCommand) Run(args []string) int { } if v2 { - outputPath(c.UI, path, "Secret Path") + outputPath(c.UI, fullPath, "Secret Path") } if metadata, ok := secret.Data["metadata"]; ok && metadata != nil { diff --git a/command/kv_metadata.go b/command/kv_metadata.go index badb08a48fd1..c4ab37910555 100644 --- a/command/kv_metadata.go +++ b/command/kv_metadata.go @@ -26,16 +26,22 @@ Usage: vault kv metadata [options] [args] Create or update a metadata entry for a key: - $ vault kv metadata put -max-versions=5 -delete-version-after=3h25m19s secret/foo + $ vault kv metadata put -mount=secret -max-versions=5 -delete-version-after=3h25m19s foo Get the metadata for a key, this provides information about each existing version: - $ vault kv metadata get secret/foo + $ vault kv metadata get -mount=secret foo Delete a key and all existing versions: - $ vault kv metadata delete secret/foo + $ vault kv metadata delete -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/metadata/foo) can cause confusion: + + $ vault kv metadata get secret/foo Please see the individual subcommand help for detailed usage information. ` diff --git a/command/kv_metadata_delete.go b/command/kv_metadata_delete.go index 3b9509a2df66..911f00117e76 100644 --- a/command/kv_metadata_delete.go +++ b/command/kv_metadata_delete.go @@ -15,6 +15,7 @@ var ( type KVMetadataDeleteCommand struct { *BaseCommand + flagMount string } func (c *KVMetadataDeleteCommand) Synopsis() string { @@ -27,6 +28,12 @@ Usage: vault kv metadata delete [options] PATH Deletes all versions and metadata for the provided key. + $ vault kv metadata delete -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/metadata/foo) can cause confusion: + $ vault kv metadata delete secret/foo Additional flags and more advanced use cases are detailed below. @@ -37,7 +44,23 @@ Usage: vault kv metadata delete [options] PATH } func (c *KVMetadataDeleteCommand) Flags() *FlagSets { - return c.flagSet(FlagSetHTTP) + set := c.flagSet(FlagSetHTTP) + + // Common Options + f := set.NewFlagSet("Common Options") + + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /metadata/ automatically appended between KV + v2 secrets.`, + }) + + return set } func (c *KVMetadataDeleteCommand) AutocompleteArgs() complete.Predictor { @@ -72,26 +95,51 @@ func (c *KVMetadataDeleteCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + if !v2 { c.UI.Error("Metadata not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "metadata") - if secret, err := client.Logical().Delete(path); err != nil { - c.UI.Error(fmt.Sprintf("Error deleting %s: %s", path, err)) + fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata") + if secret, err := client.Logical().Delete(fullPath); err != nil { + c.UI.Error(fmt.Sprintf("Error deleting %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) } return 2 } - c.UI.Info(fmt.Sprintf("Success! Data deleted (if it existed) at: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data deleted (if it existed) at: %s", fullPath)) return 0 } diff --git a/command/kv_metadata_get.go b/command/kv_metadata_get.go index 61abf3a57c9b..08e401374a70 100644 --- a/command/kv_metadata_get.go +++ b/command/kv_metadata_get.go @@ -17,6 +17,7 @@ var ( type KVMetadataGetCommand struct { *BaseCommand + flagMount string } func (c *KVMetadataGetCommand) Synopsis() string { @@ -30,6 +31,12 @@ Usage: vault kv metadata get [options] KEY Retrieves the metadata from Vault's key-value store at the given key name. If no key exists with that name, an error is returned. + $ vault kv metadata get -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/metadata/foo) can cause confusion: + $ vault kv metadata get secret/foo Additional flags and more advanced use cases are detailed below. @@ -41,6 +48,20 @@ Usage: vault kv metadata get [options] KEY func (c *KVMetadataGetCommand) Flags() *FlagSets { set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat) + // Common Options + f := set.NewFlagSet("Common Options") + + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /metadata/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -76,25 +97,50 @@ func (c *KVMetadataGetCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + if !v2 { c.UI.Error("Metadata not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "metadata") - secret, err := client.Logical().Read(path) + fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata") + secret, err := client.Logical().Read(fullPath) if err != nil { - c.UI.Error(fmt.Sprintf("Error reading %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error reading %s: %s", fullPath, err)) return 2 } if secret == nil { - c.UI.Error(fmt.Sprintf("No value found at %s", path)) + c.UI.Error(fmt.Sprintf("No value found at %s", fullPath)) return 2 } @@ -109,7 +155,7 @@ func (c *KVMetadataGetCommand) Run(args []string) int { versionsRaw, ok := secret.Data["versions"] if !ok || versionsRaw == nil { - c.UI.Error(fmt.Sprintf("No value found at %s", path)) + c.UI.Error(fmt.Sprintf("No value found at %s", fullPath)) OutputSecret(c.UI, secret) return 2 } @@ -117,7 +163,7 @@ func (c *KVMetadataGetCommand) Run(args []string) int { delete(secret.Data, "versions") - outputPath(c.UI, path, "Metadata Path") + outputPath(c.UI, fullPath, "Metadata Path") c.UI.Info(getHeaderForMap("Metadata", secret.Data)) OutputSecret(c.UI, secret) diff --git a/command/kv_metadata_patch.go b/command/kv_metadata_patch.go index c2a0956e1432..cbee81a93b80 100644 --- a/command/kv_metadata_patch.go +++ b/command/kv_metadata_patch.go @@ -23,6 +23,7 @@ type KVMetadataPatchCommand struct { flagCASRequired BoolPtr flagDeleteVersionAfter time.Duration flagCustomMetadata map[string]string + flagMount string testStdin io.Reader // for tests } @@ -39,23 +40,29 @@ Usage: vault metadata kv patch [options] KEY Create a key in the key-value store with no data: + $ vault kv metadata patch -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/metadata/foo) can cause confusion: + $ vault kv metadata patch secret/foo Set a max versions setting on the key: - $ vault kv metadata patch -max-versions=5 secret/foo + $ vault kv metadata patch -mount=secret -max-versions=5 foo Set delete-version-after on the key: - $ vault kv metadata patch -delete-version-after=3h25m19s secret/foo + $ vault kv metadata patch -mount=secret -delete-version-after=3h25m19s foo Require Check-and-Set for this key: - $ vault kv metadata patch -cas-required secret/foo + $ vault kv metadata patch -mount=secret -cas-required foo Set custom metadata on the key: - $ vault kv metadata patch -custom-metadata=foo=abc -custom-metadata=bar=123 secret/foo + $ vault kv metadata patch -mount=secret -custom-metadata=foo=abc -custom-metadata=bar=123 foo Additional flags and more advanced use cases are detailed below. @@ -103,6 +110,17 @@ func (c *KVMetadataPatchCommand) Flags() *FlagSets { This can be specified multiple times to add multiple pieces of metadata.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /metadata/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -139,19 +157,42 @@ func (c *KVMetadataPatchCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } if !v2 { c.UI.Error("Metadata not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "metadata") + fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata") data := map[string]interface{}{} @@ -171,9 +212,9 @@ func (c *KVMetadataPatchCommand) Run(args []string) int { data["custom_metadata"] = c.flagCustomMetadata } - secret, err := client.Logical().JSONMergePatch(context.Background(), path, data) + secret, err := client.Logical().JSONMergePatch(context.Background(), fullPath, data) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) @@ -184,7 +225,7 @@ func (c *KVMetadataPatchCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", fullPath)) } return 0 } diff --git a/command/kv_metadata_patch_test.go b/command/kv_metadata_patch_test.go index 7c10d07fdc4b..40b74dc8d9ee 100644 --- a/command/kv_metadata_patch_test.go +++ b/command/kv_metadata_patch_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "encoding/json" "io" "strings" @@ -63,7 +62,7 @@ func TestKvMetadataPatchCommand_EmptyArgs(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount error: %#v", err) @@ -180,7 +179,7 @@ func TestKvMetadataPatchCommand_Flags(t *testing.T) { secretPath := basePath + "my-secret" metadataPath := basePath + "metadata/" + "my-secret" - if err := client.Sys().MountWithContext(context.Background(), basePath, &api.MountInput{ + if err := client.Sys().Mount(basePath, &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount error: %#v", err) @@ -193,7 +192,7 @@ func TestKvMetadataPatchCommand_Flags(t *testing.T) { t.Fatalf("initial metadata put failed, code: %d, output: %s", code, combined) } - initialMetadata, err := client.Logical().ReadWithContext(context.Background(), metadataPath) + initialMetadata, err := client.Logical().Read(metadataPath) if err != nil { t.Fatalf("metadata read failed, err: %#v", err) } @@ -209,7 +208,7 @@ func TestKvMetadataPatchCommand_Flags(t *testing.T) { t.Fatalf("expected code to be %d but was %d for patch cmd with args %#v", tc.code, code, patchArgs) } - patchedMetadata, err := client.Logical().ReadWithContext(context.Background(), metadataPath) + patchedMetadata, err := client.Logical().Read(metadataPath) if err != nil { t.Fatalf("metadata read failed, err: %#v", err) } @@ -236,7 +235,7 @@ func TestKvMetadataPatchCommand_CasWarning(t *testing.T) { defer closer() basePath := "kv/" - if err := client.Sys().MountWithContext(context.Background(), basePath, &api.MountInput{ + if err := client.Sys().Mount(basePath, &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount error: %#v", err) @@ -255,7 +254,7 @@ func TestKvMetadataPatchCommand_CasWarning(t *testing.T) { "cas_required": true, } - _, err := client.Logical().WriteWithContext(context.Background(), basePath+"config", casConfig) + _, err := client.Logical().Write(basePath+"config", casConfig) if err != nil { t.Fatalf("config write failed, err: #%v", err) } diff --git a/command/kv_metadata_put.go b/command/kv_metadata_put.go index 67a951731f5f..2681583b0ac6 100644 --- a/command/kv_metadata_put.go +++ b/command/kv_metadata_put.go @@ -22,6 +22,7 @@ type KVMetadataPutCommand struct { flagCASRequired BoolPtr flagDeleteVersionAfter time.Duration flagCustomMetadata map[string]string + flagMount string testStdin io.Reader // for tests } @@ -38,23 +39,29 @@ Usage: vault metadata kv put [options] KEY Create a key in the key-value store with no data: + $ vault kv metadata put -mount=secret foo + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/metadata/foo) can cause confusion: + $ vault kv metadata put secret/foo Set a max versions setting on the key: - $ vault kv metadata put -max-versions=5 secret/foo + $ vault kv metadata put -mount=secret -max-versions=5 foo Set delete-version-after on the key: - $ vault kv metadata put -delete-version-after=3h25m19s secret/foo + $ vault kv metadata put -mount=secret -delete-version-after=3h25m19s foo Require Check-and-Set for this key: - $ vault kv metadata put -cas-required secret/foo + $ vault kv metadata put -mount=secret -cas-required foo Set custom metadata on the key: - $ vault kv metadata put -custom-metadata=foo=abc -custom-metadata=bar=123 secret/foo + $ vault kv metadata put -mount=secret -custom-metadata=foo=abc -custom-metadata=bar=123 foo Additional flags and more advanced use cases are detailed below. @@ -102,6 +109,17 @@ func (c *KVMetadataPutCommand) Flags() *FlagSets { "This can be specified multiple times to add multiple pieces of metadata.", }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /metadata/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -138,18 +156,43 @@ func (c *KVMetadataPutCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + if !v2 { c.UI.Error("Metadata not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "metadata") + fullPath := addPrefixToKVPath(partialPath, mountPath, "metadata") data := map[string]interface{}{} if c.flagMaxVersions >= 0 { @@ -168,9 +211,9 @@ func (c *KVMetadataPutCommand) Run(args []string) int { data["custom_metadata"] = c.flagCustomMetadata } - secret, err := client.Logical().Write(path, data) + secret, err := client.Logical().Write(fullPath, data) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) } @@ -179,7 +222,7 @@ func (c *KVMetadataPutCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", fullPath)) } return 0 } diff --git a/command/kv_metadata_put_test.go b/command/kv_metadata_put_test.go index d726ad24fda2..a952802cc469 100644 --- a/command/kv_metadata_put_test.go +++ b/command/kv_metadata_put_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "encoding/json" "strings" "testing" @@ -27,7 +26,7 @@ func TestKvMetadataPutCommand_DeleteVersionAfter(t *testing.T) { defer closer() basePath := t.Name() + "/" - if err := client.Sys().MountWithContext(context.Background(), basePath, &api.MountInput{ + if err := client.Sys().Mount(basePath, &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatal(err) @@ -49,7 +48,7 @@ func TestKvMetadataPutCommand_DeleteVersionAfter(t *testing.T) { t.Fatalf("expected %q but received %q", success, combined) } - secret, err := client.Logical().ReadWithContext(context.Background(), metaFullPath) + secret, err := client.Logical().Read(metaFullPath) if err != nil { t.Fatal(err) } @@ -72,7 +71,7 @@ func TestKvMetadataPutCommand_DeleteVersionAfter(t *testing.T) { t.Errorf("expected %q but received %q", success, combined) } - secret, err = client.Logical().ReadWithContext(context.Background(), metaFullPath) + secret, err = client.Logical().Read(metaFullPath) if err != nil { t.Fatal(err) } @@ -88,7 +87,7 @@ func TestKvMetadataPutCommand_CustomMetadata(t *testing.T) { basePath := t.Name() + "/" secretPath := basePath + "secret/my-secret" - if err := client.Sys().MountWithContext(context.Background(), basePath, &api.MountInput{ + if err := client.Sys().Mount(basePath, &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount error: %#v", err) @@ -111,7 +110,7 @@ func TestKvMetadataPutCommand_CustomMetadata(t *testing.T) { t.Fatalf("Expected command output %q but received %q", expectedOutput, commandOutput) } - metadata, err := client.Logical().ReadWithContext(context.Background(), metaFullPath) + metadata, err := client.Logical().Read(metaFullPath) if err != nil { t.Fatalf("Metadata read error: %#v", err) } @@ -142,7 +141,7 @@ func TestKvMetadataPutCommand_CustomMetadata(t *testing.T) { t.Fatalf("Expected command output %q but received %q", expectedOutput, commandOutput) } - metadata, err = client.Logical().ReadWithContext(context.Background(), metaFullPath) + metadata, err = client.Logical().Read(metaFullPath) if err != nil { t.Fatalf("Metadata read error: %#v", err) @@ -164,7 +163,7 @@ func TestKvMetadataPutCommand_UnprovidedFlags(t *testing.T) { basePath := t.Name() + "/" secretPath := basePath + "my-secret" - if err := client.Sys().MountWithContext(context.Background(), basePath, &api.MountInput{ + if err := client.Sys().Mount(basePath, &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount error: %#v", err) @@ -187,7 +186,7 @@ func TestKvMetadataPutCommand_UnprovidedFlags(t *testing.T) { t.Fatalf("expected 0 exit status but received %d", code) } - secret, err := client.Logical().ReadWithContext(context.Background(), basePath+"metadata/"+"my-secret") + secret, err := client.Logical().Read(basePath + "metadata/" + "my-secret") if err != nil { t.Fatal(err) } diff --git a/command/kv_patch.go b/command/kv_patch.go index 334ba6f463ac..5f813fb15730 100644 --- a/command/kv_patch.go +++ b/command/kv_patch.go @@ -22,6 +22,7 @@ type KVPatchCommand struct { flagCAS int flagMethod string + flagMount string testStdin io.Reader // for tests } @@ -35,25 +36,31 @@ Usage: vault kv patch [options] KEY [DATA] *NOTE*: This is only supported for KV v2 engine mounts. - Writes the data to the given path in the key-value store. The data can be of + Writes the data to the corresponding path in the key-value store. The data can be of any type. + $ vault kv patch -mount=secret foo bar=baz + + The deprecated path-like syntax can also be used, but this should be avoided, + as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv patch secret/foo bar=baz The data can also be consumed from a file on disk by prefixing with the "@" symbol. For example: - $ vault kv patch secret/foo @data.json + $ vault kv patch -mount=secret foo @data.json Or it can be read from stdin using the "-" symbol: - $ echo "abcd1234" | vault kv patch secret/foo bar=- + $ echo "abcd1234" | vault kv patch -mount=secret foo bar=- To perform a Check-And-Set operation, specify the -cas flag with the appropriate version number corresponding to the key you want to perform the CAS operation on: - $ vault kv patch -cas=1 secret/foo bar=baz + $ vault kv patch -mount=secret -cas=1 foo bar=baz By default, this operation will attempt an HTTP PATCH operation. If your policy does not allow that, it will fall back to a read/local update/write approach. @@ -61,12 +68,12 @@ Usage: vault kv patch [options] KEY [DATA] with the -method flag. When -method=patch is specified, only an HTTP PATCH operation will be tried. If it fails, the entire command will fail. - $ vault kv patch -method=patch secret/foo bar=baz + $ vault kv patch -mount=secret -method=patch foo bar=baz When -method=rw is specified, only a read/local update/write approach will be tried. This was the default behavior previous to Vault 1.9. - $ vault kv patch -method=rw secret/foo bar=baz + $ vault kv patch -mount=secret -method=rw foo bar=baz Additional flags and more advanced use cases are detailed below. @@ -98,6 +105,17 @@ func (c *KVPatchCommand) Flags() *FlagSets { performed, then a local update, followed by a remote update.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -134,7 +152,6 @@ func (c *KVPatchCommand) Run(args []string) int { } var err error - path := sanitizePath(args[0]) client, err := c.Client() if err != nil { @@ -148,10 +165,35 @@ func (c *KVPatchCommand) Run(args []string) int { return 1 } - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } if !v2 { @@ -159,7 +201,7 @@ func (c *KVPatchCommand) Run(args []string) int { return 2 } - path = addPrefixToKVPath(path, mountPath, "data") + fullPath := addPrefixToKVPath(partialPath, mountPath, "data") if err != nil { c.UI.Error(err.Error()) return 2 @@ -171,11 +213,11 @@ func (c *KVPatchCommand) Run(args []string) int { switch c.flagMethod { case "rw": - secret, code = c.readThenWrite(client, path, newData) + secret, code = c.readThenWrite(client, fullPath, newData) case "patch": - secret, code = c.mergePatch(client, path, newData, false) + secret, code = c.mergePatch(client, fullPath, newData, false) case "": - secret, code = c.mergePatch(client, path, newData, true) + secret, code = c.mergePatch(client, fullPath, newData, true) default: c.UI.Error(fmt.Sprintf("Unsupported method provided to -method flag: %s", c.flagMethod)) return 2 @@ -186,7 +228,7 @@ func (c *KVPatchCommand) Run(args []string) int { } if Format(c.UI) == "table" { - outputPath(c.UI, path, "Secret Path") + outputPath(c.UI, fullPath, "Secret Path") metadata := secret.Data c.UI.Info(getHeaderForMap("Metadata", metadata)) return OutputData(c.UI, metadata) diff --git a/command/kv_put.go b/command/kv_put.go index ef48d09da194..f380e2d64d99 100644 --- a/command/kv_put.go +++ b/command/kv_put.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "os" + "path" "strings" "github.com/mitchellh/cli" @@ -19,6 +20,7 @@ type KVPutCommand struct { *BaseCommand flagCAS int + flagMount string testStdin io.Reader // for tests } @@ -33,22 +35,28 @@ Usage: vault kv put [options] KEY [DATA] Writes the data to the given path in the key-value store. The data can be of any type. + $ vault kv put -mount=secret foo bar=baz + + The deprecated path-like syntax can also be used, but this should be avoided + for KV v2, as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv put secret/foo bar=baz The data can also be consumed from a file on disk by prefixing with the "@" symbol. For example: - $ vault kv put secret/foo @data.json + $ vault kv put -mount=secret foo @data.json Or it can be read from stdin using the "-" symbol: - $ echo "abcd1234" | vault kv put secret/foo bar=- + $ echo "abcd1234" | vault kv put -mount=secret foo bar=- To perform a Check-And-Set operation, specify the -cas flag with the appropriate version number corresponding to the key you want to perform the CAS operation on: - $ vault kv put -cas=1 secret/foo bar=baz + $ vault kv put -mount=secret -cas=1 foo bar=baz Additional flags and more advanced use cases are detailed below. @@ -73,6 +81,17 @@ func (c *KVPutCommand) Flags() *FlagSets { parameter.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -109,7 +128,6 @@ func (c *KVPutCommand) Run(args []string) int { } var err error - path := sanitizePath(args[0]) client, err := c.Client() if err != nil { @@ -123,14 +141,41 @@ func (c *KVPutCommand) Run(args []string) int { return 1 } - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + // Add /data to v2 paths only + var fullPath string if v2 { - path = addPrefixToKVPath(path, mountPath, "data") + fullPath = addPrefixToKVPath(partialPath, mountPath, "data") data = map[string]interface{}{ "data": data, "options": map[string]interface{}{}, @@ -139,11 +184,18 @@ func (c *KVPutCommand) Run(args []string) int { if c.flagCAS > -1 { data["options"].(map[string]interface{})["cas"] = c.flagCAS } + } else { + // v1 + if mountFlagSyntax { + fullPath = path.Join(mountPath, partialPath) + } else { + fullPath = partialPath + } } - secret, err := client.Logical().Write(path, data) + secret, err := client.Logical().Write(fullPath, data) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", fullPath, err)) if secret != nil { OutputSecret(c.UI, secret) } @@ -152,7 +204,7 @@ func (c *KVPutCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", fullPath)) } return 0 } @@ -162,7 +214,7 @@ func (c *KVPutCommand) Run(args []string) int { } if Format(c.UI) == "table" { - outputPath(c.UI, path, "Secret Path") + outputPath(c.UI, fullPath, "Secret Path") metadata := secret.Data c.UI.Info(getHeaderForMap("Metadata", metadata)) return OutputData(c.UI, metadata) diff --git a/command/kv_rollback.go b/command/kv_rollback.go index c9f3eb3621e5..e69dba6f6c80 100644 --- a/command/kv_rollback.go +++ b/command/kv_rollback.go @@ -18,6 +18,7 @@ type KVRollbackCommand struct { *BaseCommand flagVersion int + flagMount string } func (c *KVRollbackCommand) Synopsis() string { @@ -35,6 +36,12 @@ Usage: vault kv rollback [options] KEY is 5 and the rollback version is 2, the data from version 2 will become version 6. + $ vault kv rollback -mount=secret -version=2 foo + + The deprecated path-like syntax can also be used, but this should be avoided, + as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv rollback -version=2 secret/foo Additional flags and more advanced use cases are detailed below. @@ -55,6 +62,17 @@ func (c *KVRollbackCommand) Flags() *FlagSets { Usage: `Specifies the version number that should be made current again.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -96,7 +114,6 @@ func (c *KVRollbackCommand) Run(args []string) int { } var err error - path := sanitizePath(args[0]) client, err := c.Client() if err != nil { @@ -104,10 +121,35 @@ func (c *KVRollbackCommand) Run(args []string) int { return 2 } - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } if !v2 { @@ -115,7 +157,7 @@ func (c *KVRollbackCommand) Run(args []string) int { return 2 } - path = addPrefixToKVPath(path, mountPath, "data") + fullPath := addPrefixToKVPath(partialPath, mountPath, "data") if err != nil { c.UI.Error(err.Error()) return 2 @@ -124,31 +166,31 @@ func (c *KVRollbackCommand) Run(args []string) int { // First, do a read to get the current version for check-and-set var meta map[string]interface{} { - secret, err := kvReadRequest(client, path, nil) + secret, err := kvReadRequest(client, fullPath, nil) if err != nil { - c.UI.Error(fmt.Sprintf("Error doing pre-read at %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error doing pre-read at %s: %s", fullPath, err)) return 2 } // Make sure a value already exists if secret == nil || secret.Data == nil { - c.UI.Error(fmt.Sprintf("No value found at %s", path)) + c.UI.Error(fmt.Sprintf("No value found at %s", fullPath)) return 2 } // Verify metadata found rawMeta, ok := secret.Data["metadata"] if !ok || rawMeta == nil { - c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", fullPath)) return 2 } meta, ok = rawMeta.(map[string]interface{}) if !ok { - c.UI.Error(fmt.Sprintf("Metadata found at %s is not the expected type (JSON object)", path)) + c.UI.Error(fmt.Sprintf("Metadata found at %s is not the expected type (JSON object)", fullPath)) return 2 } if meta == nil { - c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", fullPath)) return 2 } } @@ -163,31 +205,31 @@ func (c *KVRollbackCommand) Run(args []string) int { // Now run it again and read the version we want to roll back to var data map[string]interface{} { - secret, err := kvReadRequest(client, path, versionParam) + secret, err := kvReadRequest(client, fullPath, versionParam) if err != nil { - c.UI.Error(fmt.Sprintf("Error doing pre-read at %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error doing pre-read at %s: %s", fullPath, err)) return 2 } // Make sure a value already exists if secret == nil || secret.Data == nil { - c.UI.Error(fmt.Sprintf("No value found at %s", path)) + c.UI.Error(fmt.Sprintf("No value found at %s", fullPath)) return 2 } // Verify metadata found rawMeta, ok := secret.Data["metadata"] if !ok || rawMeta == nil { - c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", fullPath)) return 2 } meta, ok := rawMeta.(map[string]interface{}) if !ok { - c.UI.Error(fmt.Sprintf("Metadata found at %s is not the expected type (JSON object)", path)) + c.UI.Error(fmt.Sprintf("Metadata found at %s is not the expected type (JSON object)", fullPath)) return 2 } if meta == nil { - c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No metadata found at %s; rollback only works on existing data", fullPath)) return 2 } @@ -205,34 +247,34 @@ func (c *KVRollbackCommand) Run(args []string) int { // Verify old data found rawData, ok := secret.Data["data"] if !ok || rawData == nil { - c.UI.Error(fmt.Sprintf("No data found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No data found at %s; rollback only works on existing data", fullPath)) return 2 } data, ok = rawData.(map[string]interface{}) if !ok { - c.UI.Error(fmt.Sprintf("Data found at %s is not the expected type (JSON object)", path)) + c.UI.Error(fmt.Sprintf("Data found at %s is not the expected type (JSON object)", fullPath)) return 2 } if data == nil { - c.UI.Error(fmt.Sprintf("No data found at %s; rollback only works on existing data", path)) + c.UI.Error(fmt.Sprintf("No data found at %s; rollback only works on existing data", fullPath)) return 2 } } - secret, err := client.Logical().Write(path, map[string]interface{}{ + secret, err := client.Logical().Write(fullPath, map[string]interface{}{ "data": data, "options": map[string]interface{}{ "cas": casVersion, }, }) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", fullPath, err)) return 2 } if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", fullPath)) } return 0 } diff --git a/command/kv_test.go b/command/kv_test.go index 4909b0cdfa4e..21df402a904f 100644 --- a/command/kv_test.go +++ b/command/kv_test.go @@ -123,6 +123,12 @@ func TestKVPutCommand(t *testing.T) { []string{"Success!"}, 0, }, + { + "v1_mount_flag_syntax", + []string{"-mount", "secret", "write/foo", "foo=bar"}, + []string{"Success!"}, + 0, + }, { "v2_single_value", []string{"kv/write/foo", "foo=bar"}, @@ -141,6 +147,12 @@ func TestKVPutCommand(t *testing.T) { []string{"== Secret Path ==", "kv/data/write/foo"}, 0, }, + { + "v2_mount_flag_syntax", + []string{"-mount", "kv", "write/foo", "foo=bar"}, + v2ExpectedFields, + 0, + }, { "v2_single_value_backslash", []string{"kv/write/foo", "foo=\\"}, @@ -158,7 +170,7 @@ func TestKVPutCommand(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatal(err) @@ -183,7 +195,7 @@ func TestKVPutCommand(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatal(err) @@ -290,7 +302,7 @@ func TestKVPutCommand(t *testing.T) { t.Fatalf("expected 0 to be %d", code) } - secret, err := client.Logical().ReadWithContext(context.Background(), "secret/write/stdin_full") + secret, err := client.Logical().Read("secret/write/stdin_full") if err != nil { t.Fatal(err) } @@ -325,7 +337,7 @@ func TestKVPutCommand(t *testing.T) { t.Fatalf("expected 0 to be %d", code) } - secret, err := client.Logical().ReadWithContext(context.Background(), "secret/write/stdin_value") + secret, err := client.Logical().Read("secret/write/stdin_value") if err != nil { t.Fatal(err) } @@ -353,7 +365,7 @@ func TestKVPutCommand(t *testing.T) { t.Fatalf("expected 0 to be %d", code) } - secret, err := client.Logical().ReadWithContext(context.Background(), "secret/write/integration") + secret, err := client.Logical().Read("secret/write/integration") if err != nil { t.Fatal(err) } @@ -428,20 +440,30 @@ func TestKVGetCommand(t *testing.T) { []string{"bar"}, 0, }, + { + "v1_mount_flag_syntax", + []string{"-mount", "secret", "read/foo"}, + []string{"foo"}, + 0, + }, { "v2_field", []string{"-field", "foo", "kv/read/foo"}, []string{"bar"}, 0, }, - + { + "v2_mount_flag_syntax", + []string{"-mount", "kv", "read/foo"}, + append(baseV2ExpectedFields, "foo"), + 0, + }, { "v2_not_found", []string{"kv/nope/not/once/never"}, []string{"No value found at kv/data/nope/not/once/never"}, 2, }, - { "v2_read", []string{"kv/read/foo"}, @@ -467,7 +489,7 @@ func TestKVGetCommand(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatal(err) @@ -476,13 +498,13 @@ func TestKVGetCommand(t *testing.T) { // Give time for the upgrade code to run/finish time.Sleep(time.Second) - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/read/foo", map[string]interface{}{ + if _, err := client.Logical().Write("secret/read/foo", map[string]interface{}{ "foo": "bar", }); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "kv/data/read/foo", map[string]interface{}{ + if _, err := client.Logical().Write("kv/data/read/foo", map[string]interface{}{ "data": map[string]interface{}{ "foo": "bar", }, @@ -573,6 +595,12 @@ func TestKVMetadataGetCommand(t *testing.T) { append(expectedTopLevelFields, expectedVersionFields[:]...), 0, }, + { + "mount_flag_syntax", + []string{"-mount", "kv", "foo"}, + expectedTopLevelFields, + 0, + }, } t.Run("validations", func(t *testing.T) { @@ -586,7 +614,7 @@ func TestKVMetadataGetCommand(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatal(err) @@ -595,7 +623,7 @@ func TestKVMetadataGetCommand(t *testing.T) { // Give time for the upgrade code to run/finish time.Sleep(time.Second) - if _, err := client.Logical().WriteWithContext(context.Background(), "kv/data/foo", map[string]interface{}{ + if _, err := client.Logical().Write("kv/data/foo", map[string]interface{}{ "data": map[string]interface{}{ "foo": "bar", }, @@ -667,6 +695,12 @@ func TestKVPatchCommand_ArgValidation(t *testing.T) { "Failed to parse K=V data", 1, }, + { + "mount_flag_syntax", + []string{"-mount", "kv"}, + "Not enough arguments", + 1, + }, } for _, tc := range cases { @@ -676,7 +710,7 @@ func TestKVPatchCommand_ArgValidation(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) @@ -708,17 +742,17 @@ func expectedPatchFields() []string { } } -func TestKvPatchCommand_StdinFull(t *testing.T) { +func TestKVPatchCommand_StdinFull(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "kv/data/patch/foo", map[string]interface{}{ + if _, err := client.Logical().Write("kv/data/patch/foo", map[string]interface{}{ "data": map[string]interface{}{ "foo": "a", }, @@ -726,62 +760,66 @@ func TestKvPatchCommand_StdinFull(t *testing.T) { t.Fatalf("write failed, err: %#v\n", err) } - stdinR, stdinW := io.Pipe() - go func() { - stdinW.Write([]byte(`{"foo":"bar"}`)) - stdinW.Close() - }() - - args := []string{"kv/patch/foo", "-"} - code, combined := kvPatchWithRetry(t, client, args, stdinR) + cases := [][]string{ + {"kv/patch/foo", "-"}, + {"-mount", "kv", "patch/foo", "-"}, + } + for i, args := range cases { + stdinR, stdinW := io.Pipe() + go func() { + stdinW.Write([]byte(fmt.Sprintf(`{"foo%d":"bar%d"}`, i, i))) + stdinW.Close() + }() + code, combined := kvPatchWithRetry(t, client, args, stdinR) - for _, str := range expectedPatchFields() { - if !strings.Contains(combined, str) { - t.Errorf("expected %q to contain %q", combined, str) + for _, str := range expectedPatchFields() { + if !strings.Contains(combined, str) { + t.Errorf("expected %q to contain %q", combined, str) + } } - } - if code != 0 { - t.Fatalf("expected code to be 0 but was %d for patch cmd with args %#v\n", code, args) - } + if code != 0 { + t.Fatalf("expected code to be 0 but was %d for patch cmd with args %#v\n", code, args) + } - secret, err := client.Logical().ReadWithContext(context.Background(), "kv/data/patch/foo") - if err != nil { - t.Fatalf("read failed, err: %#v\n", err) - } + secret, err := client.Logical().ReadWithContext(context.Background(), "kv/data/patch/foo") + if err != nil { + t.Fatalf("read failed, err: %#v\n", err) + } - if secret == nil || secret.Data == nil { - t.Fatal("expected secret to have data") - } + if secret == nil || secret.Data == nil { + t.Fatal("expected secret to have data") + } - secretDataRaw, ok := secret.Data["data"] + secretDataRaw, ok := secret.Data["data"] - if !ok { - t.Fatalf("expected secret to have nested data key, data: %#v", secret.Data) - } + if !ok { + t.Fatalf("expected secret to have nested data key, data: %#v", secret.Data) + } - secretData := secretDataRaw.(map[string]interface{}) - foo, ok := secretData["foo"].(string) - if !ok { - t.Fatal("expected foo to be a string but it wasn't") - } + secretData := secretDataRaw.(map[string]interface{}) + foo, ok := secretData[fmt.Sprintf("foo%d", i)].(string) + if !ok { + t.Fatal("expected foo to be a string but it wasn't") + } - if exp, act := "bar", foo; exp != act { - t.Fatalf("expected %q to be %q, data: %#v\n", act, exp, secret.Data) + if exp, act := fmt.Sprintf("bar%d", i), foo; exp != act { + t.Fatalf("expected %q to be %q, data: %#v\n", act, exp, secret.Data) + } } } -func TestKvPatchCommand_StdinValue(t *testing.T) { +func TestKVPatchCommand_StdinValue(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "kv/data/patch/foo", map[string]interface{}{ + if _, err := client.Logical().Write("kv/data/patch/foo", map[string]interface{}{ "data": map[string]interface{}{ "foo": "a", }, @@ -789,43 +827,49 @@ func TestKvPatchCommand_StdinValue(t *testing.T) { t.Fatalf("write failed, err: %#v\n", err) } - stdinR, stdinW := io.Pipe() - go func() { - stdinW.Write([]byte("bar")) - stdinW.Close() - }() - - args := []string{"kv/patch/foo", "foo=-"} - code, combined := kvPatchWithRetry(t, client, args, stdinR) - if code != 0 { - t.Fatalf("expected code to be 0 but was %d for patch cmd with args %#v\n", code, args) + cases := [][]string{ + {"kv/patch/foo", "foo=-"}, + {"-mount", "kv", "patch/foo", "foo=-"}, } - for _, str := range expectedPatchFields() { - if !strings.Contains(combined, str) { - t.Errorf("expected %q to contain %q", combined, str) + for i, args := range cases { + stdinR, stdinW := io.Pipe() + go func() { + stdinW.Write([]byte(fmt.Sprintf("bar%d", i))) + stdinW.Close() + }() + + code, combined := kvPatchWithRetry(t, client, args, stdinR) + if code != 0 { + t.Fatalf("expected code to be 0 but was %d for patch cmd with args %#v\n", code, args) } - } - secret, err := client.Logical().ReadWithContext(context.Background(), "kv/data/patch/foo") - if err != nil { - t.Fatalf("read failed, err: %#v\n", err) - } + for _, str := range expectedPatchFields() { + if !strings.Contains(combined, str) { + t.Errorf("expected %q to contain %q", combined, str) + } + } - if secret == nil || secret.Data == nil { - t.Fatal("expected secret to have data") - } + secret, err := client.Logical().ReadWithContext(context.Background(), "kv/data/patch/foo") + if err != nil { + t.Fatalf("read failed, err: %#v\n", err) + } + + if secret == nil || secret.Data == nil { + t.Fatal("expected secret to have data") + } - secretDataRaw, ok := secret.Data["data"] + secretDataRaw, ok := secret.Data["data"] - if !ok { - t.Fatalf("expected secret to have nested data key, data: %#v\n", secret.Data) - } + if !ok { + t.Fatalf("expected secret to have nested data key, data: %#v\n", secret.Data) + } - secretData := secretDataRaw.(map[string]interface{}) + secretData := secretDataRaw.(map[string]interface{}) - if exp, act := "bar", secretData["foo"].(string); exp != act { - t.Fatalf("expected %q to be %q, data: %#v\n", act, exp, secret.Data) + if exp, act := fmt.Sprintf("bar%d", i), secretData["foo"].(string); exp != act { + t.Fatalf("expected %q to be %q, data: %#v\n", act, exp, secret.Data) + } } } @@ -833,22 +877,28 @@ func TestKVPatchCommand_RWMethodNotExists(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) } - args := []string{"-method", "rw", "kv/patch/foo", "foo=a"} - code, combined := kvPatchWithRetry(t, client, args, nil) - - if code != 2 { - t.Fatalf("expected code to be 2 but was %d for patch cmd with args %#v\n", code, args) + cases := [][]string{ + {"-method", "rw", "kv/patch/foo", "foo=a"}, + {"-method", "rw", "-mount", "kv", "patch/foo", "foo=a"}, } - expectedOutputSubstr := "No value found" - if !strings.Contains(combined, expectedOutputSubstr) { - t.Fatalf("expected output %q to contain %q for patch cmd with args %#v\n", combined, expectedOutputSubstr, args) + for _, args := range cases { + code, combined := kvPatchWithRetry(t, client, args, nil) + + if code != 2 { + t.Fatalf("expected code to be 2 but was %d for patch cmd with args %#v\n", code, args) + } + + expectedOutputSubstr := "No value found" + if !strings.Contains(combined, expectedOutputSubstr) { + t.Fatalf("expected output %q to contain %q for patch cmd with args %#v\n", combined, expectedOutputSubstr, args) + } } } @@ -856,13 +906,13 @@ func TestKVPatchCommand_RWMethodSucceeds(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "kv/data/patch/foo", map[string]interface{}{ + if _, err := client.Logical().Write("kv/data/patch/foo", map[string]interface{}{ "data": map[string]interface{}{ "foo": "a", "bar": "b", @@ -929,6 +979,13 @@ func TestKVPatchCommand_CAS(t *testing.T) { []string{"check-and-set parameter did not match the current version"}, 2, }, + { + "mount_flag_syntax", + []string{"-mount", "kv", "-cas", "1", "foo", "bar=quux"}, + "quux", + expectedPatchFields(), + 0, + }, } for _, tc := range cases { @@ -940,7 +997,7 @@ func TestKVPatchCommand_CAS(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) @@ -960,7 +1017,7 @@ func TestKVPatchCommand_CAS(t *testing.T) { kvClient.SetToken(secretAuth.ClientToken) - _, err = kvClient.Logical().WriteWithContext(context.Background(), "kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) + _, err = kvClient.Logical().Write("kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) if err != nil { t.Fatal(err) } @@ -1019,7 +1076,7 @@ func TestKVPatchCommand_Methods(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) @@ -1039,7 +1096,7 @@ func TestKVPatchCommand_Methods(t *testing.T) { kvClient.SetToken(secretAuth.ClientToken) - _, err = kvClient.Logical().WriteWithContext(context.Background(), "kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) + _, err = kvClient.Logical().Write("kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) if err != nil { t.Fatal(err) } @@ -1093,7 +1150,7 @@ func TestKVPatchCommand_403Fallback(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) @@ -1114,7 +1171,7 @@ func TestKVPatchCommand_403Fallback(t *testing.T) { kvClient.SetToken(secretAuth.ClientToken) // Write a value then attempt to patch it - _, err = kvClient.Logical().WriteWithContext(context.Background(), "kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) + _, err = kvClient.Logical().Write("kv/data/foo", map[string]interface{}{"data": map[string]interface{}{"bar": "baz"}}) if err != nil { t.Fatal(err) } @@ -1174,7 +1231,7 @@ func TestKVPatchCommand_RWMethodPolicyVariations(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "kv/", &api.MountInput{ + if err := client.Sys().Mount("kv/", &api.MountInput{ Type: "kv-v2", }); err != nil { t.Fatalf("kv-v2 mount attempt failed - err: %#v\n", err) @@ -1258,11 +1315,11 @@ func TestPadEqualSigns(t *testing.T) { func createTokenForPolicy(t *testing.T, client *api.Client, policy string) (*api.SecretAuth, error) { t.Helper() - if err := client.Sys().PutPolicyWithContext(context.Background(), "policy", policy); err != nil { + if err := client.Sys().PutPolicy("policy", policy); err != nil { return nil, err } - secret, err := client.Auth().Token().CreateWithContext(context.Background(), &api.TokenCreateRequest{ + secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{ Policies: []string{"policy"}, TTL: "30m", }) diff --git a/command/kv_undelete.go b/command/kv_undelete.go index b734ff876a30..90ea608a7316 100644 --- a/command/kv_undelete.go +++ b/command/kv_undelete.go @@ -17,6 +17,7 @@ type KVUndeleteCommand struct { *BaseCommand flagVersions []string + flagMount string } func (c *KVUndeleteCommand) Synopsis() string { @@ -32,6 +33,12 @@ Usage: vault kv undelete [options] KEY To undelete version 3 of key "foo": + $ vault kv undelete -mount=secret -versions=3 foo + + The deprecated path-like syntax can also be used, but this should be avoided, + as the fact that it is not actually the full API path to + the secret (secret/data/foo) can cause confusion: + $ vault kv undelete -versions=3 secret/foo Additional flags and more advanced use cases are detailed below. @@ -53,6 +60,17 @@ func (c *KVUndeleteCommand) Flags() *FlagSets { Usage: `Specifies the version numbers to undelete.`, }) + f.StringVar(&StringVar{ + Name: "mount", + Target: &c.flagMount, + Default: "", // no default, because the handling of the next arg is determined by whether this flag has a value + Usage: `Specifies the path where the KV backend is mounted. If specified, + the next argument will be interpreted as the secret path. If this flag is + not specified, the next argument will be interpreted as the combined mount + path and secret path, with /data/ automatically appended between KV + v2 secrets.`, + }) + return set } @@ -93,25 +111,50 @@ func (c *KVUndeleteCommand) Run(args []string) int { return 2 } - path := sanitizePath(args[0]) - mountPath, v2, err := isKVv2(path, client) - if err != nil { - c.UI.Error(err.Error()) - return 2 + // If true, we're working with "-mount=secret foo" syntax. + // If false, we're using "secret/foo" syntax. + mountFlagSyntax := (c.flagMount != "") + + var ( + mountPath string + partialPath string + v2 bool + ) + + // Parse the paths and grab the KV version + if mountFlagSyntax { + // In this case, this arg is the secret path (e.g. "foo"). + partialPath = sanitizePath(args[0]) + mountPath = sanitizePath(c.flagMount) + _, v2, err = isKVv2(mountPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + } else { + // In this case, this arg is a path-like combination of mountPath/secretPath. + // (e.g. "secret/foo") + partialPath = sanitizePath(args[0]) + mountPath, v2, err = isKVv2(partialPath, client) + if err != nil { + c.UI.Error(err.Error()) + return 2 + } } + if !v2 { c.UI.Error("Undelete not supported on KV Version 1") return 1 } - path = addPrefixToKVPath(path, mountPath, "undelete") + undeletePath := addPrefixToKVPath(partialPath, mountPath, "undelete") data := map[string]interface{}{ "versions": kvParseVersionsFlags(c.flagVersions), } - secret, err := client.Logical().Write(path, data) + secret, err := client.Logical().Write(undeletePath, data) if err != nil { - c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", path, err)) + c.UI.Error(fmt.Sprintf("Error writing data to %s: %s", undeletePath, err)) if secret != nil { OutputSecret(c.UI, secret) } @@ -120,7 +163,7 @@ func (c *KVUndeleteCommand) Run(args []string) int { if secret == nil { // Don't output anything unless using the "table" format if Format(c.UI) == "table" { - c.UI.Info(fmt.Sprintf("Success! Data written to: %s", path)) + c.UI.Info(fmt.Sprintf("Success! Data written to: %s", undeletePath)) } return 0 } diff --git a/command/lease_lookup_test.go b/command/lease_lookup_test.go index f43405ef27b9..4de63200f5ce 100644 --- a/command/lease_lookup_test.go +++ b/command/lease_lookup_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -23,13 +22,13 @@ func testLeaseLookupCommand(tb testing.TB) (*cli.MockUi, *LeaseLookupCommand) { // testLeaseLookupCommandMountAndLease mounts a leased secret backend and returns // the leaseID of an item. func testLeaseLookupCommandMountAndLease(tb testing.TB, client *api.Client) string { - if err := client.Sys().MountWithContext(context.Background(), "testing", &api.MountInput{ + if err := client.Sys().Mount("testing", &api.MountInput{ Type: "generic-leased", }); err != nil { tb.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "testing/foo", map[string]interface{}{ + if _, err := client.Logical().Write("testing/foo", map[string]interface{}{ "key": "value", "lease": "5m", }); err != nil { @@ -37,7 +36,7 @@ func testLeaseLookupCommandMountAndLease(tb testing.TB, client *api.Client) stri } // Read the secret back to get the leaseID - secret, err := client.Logical().ReadWithContext(context.Background(), "testing/foo") + secret, err := client.Logical().Read("testing/foo") if err != nil { tb.Fatal(err) } diff --git a/command/lease_renew_test.go b/command/lease_renew_test.go index fbbd700ae80e..aa3b32d0d8b3 100644 --- a/command/lease_renew_test.go +++ b/command/lease_renew_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -23,13 +22,13 @@ func testLeaseRenewCommand(tb testing.TB) (*cli.MockUi, *LeaseRenewCommand) { // testLeaseRenewCommandMountAndLease mounts a leased secret backend and returns // the leaseID of an item. func testLeaseRenewCommandMountAndLease(tb testing.TB, client *api.Client) string { - if err := client.Sys().MountWithContext(context.Background(), "testing", &api.MountInput{ + if err := client.Sys().Mount("testing", &api.MountInput{ Type: "generic-leased", }); err != nil { tb.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "testing/foo", map[string]interface{}{ + if _, err := client.Logical().Write("testing/foo", map[string]interface{}{ "key": "value", "lease": "5m", }); err != nil { @@ -37,7 +36,7 @@ func testLeaseRenewCommandMountAndLease(tb testing.TB, client *api.Client) strin } // Read the secret back to get the leaseID - secret, err := client.Logical().ReadWithContext(context.Background(), "testing/foo") + secret, err := client.Logical().Read("testing/foo") if err != nil { tb.Fatal(err) } diff --git a/command/lease_revoke_test.go b/command/lease_revoke_test.go index 0c544c7b4337..1aa58c38ac76 100644 --- a/command/lease_revoke_test.go +++ b/command/lease_revoke_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -79,7 +78,7 @@ func TestLeaseRevokeCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "secret-leased", &api.MountInput{ + if err := client.Sys().Mount("secret-leased", &api.MountInput{ Type: "generic-leased", }); err != nil { t.Fatal(err) @@ -90,10 +89,10 @@ func TestLeaseRevokeCommand_Run(t *testing.T) { "key": "value", "lease": "1m", } - if _, err := client.Logical().WriteWithContext(context.Background(), path, data); err != nil { + if _, err := client.Logical().Write(path, data); err != nil { t.Fatal(err) } - secret, err := client.Logical().ReadWithContext(context.Background(), path) + secret, err := client.Logical().Read(path) if err != nil { t.Fatal(err) } diff --git a/command/list_test.go b/command/list_test.go index fa36b3aedd53..b1b6680507f1 100644 --- a/command/list_test.go +++ b/command/list_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -78,7 +77,7 @@ func TestListCommand_Run(t *testing.T) { "secret/list/baz", } for _, k := range keys { - if _, err := client.Logical().WriteWithContext(context.Background(), k, map[string]interface{}{ + if _, err := client.Logical().Write(k, map[string]interface{}{ "foo": "bar", }); err != nil { t.Fatal(err) diff --git a/command/login_test.go b/command/login_test.go index d6a247318784..aefdd2585190 100644 --- a/command/login_test.go +++ b/command/login_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -49,7 +48,7 @@ func TestLoginCommand_Run(t *testing.T) { if err := client.Sys().EnableAuth("my-auth", "userpass", ""); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/my-auth/users/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/my-auth/users/test", map[string]interface{}{ "password": "test", "policies": "default", }); err != nil { @@ -99,7 +98,7 @@ func TestLoginCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - secret, err := client.Auth().Token().CreateWithContext(context.Background(), &api.TokenCreateRequest{ + secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{ Policies: []string{"default"}, TTL: "30m", }) @@ -145,7 +144,7 @@ func TestLoginCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - secret, err := client.Auth().Token().CreateWithContext(context.Background(), &api.TokenCreateRequest{ + secret, err := client.Auth().Token().Create(&api.TokenCreateRequest{ Policies: []string{"default"}, TTL: "30m", }) @@ -188,7 +187,7 @@ func TestLoginCommand_Run(t *testing.T) { if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{ "password": "test", "policies": "default", }); err != nil { @@ -266,7 +265,7 @@ func TestLoginCommand_Run(t *testing.T) { if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{ "password": "test", "policies": "default", }); err != nil { @@ -303,7 +302,7 @@ func TestLoginCommand_Run(t *testing.T) { client.SetToken(token) // Ensure the resulting token is unwrapped - secret, err := client.Auth().Token().LookupSelfWithContext(context.Background()) + secret, err := client.Auth().Token().LookupSelf() if err != nil { t.Error(err) } @@ -325,7 +324,7 @@ func TestLoginCommand_Run(t *testing.T) { if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{ "password": "test", "policies": "default", }); err != nil { @@ -368,7 +367,7 @@ func TestLoginCommand_Run(t *testing.T) { // Ensure the resulting token is, in fact, still wrapped. client.SetToken(token) - secret, err := client.Logical().UnwrapWithContext(context.Background(), "") + secret, err := client.Logical().Unwrap("") if err != nil { t.Error(err) } @@ -386,7 +385,7 @@ func TestLoginCommand_Run(t *testing.T) { if err := client.Sys().EnableAuth("userpass", "userpass", ""); err != nil { t.Fatal(err) } - if _, err := client.Logical().WriteWithContext(context.Background(), "auth/userpass/users/test", map[string]interface{}{ + if _, err := client.Logical().Write("auth/userpass/users/test", map[string]interface{}{ "password": "test", "policies": "default", }); err != nil { diff --git a/command/operator_diagnose_test.go b/command/operator_diagnose_test.go index 2bf8713c9ed4..bacbbeb2a82c 100644 --- a/command/operator_diagnose_test.go +++ b/command/operator_diagnose_test.go @@ -10,6 +10,7 @@ import ( "strings" "testing" + "github.com/hashicorp/vault/sdk/helper/consts" "github.com/hashicorp/vault/vault/diagnose" "github.com/mitchellh/cli" ) @@ -478,7 +479,7 @@ func TestOperatorDiagnoseCommand_Run(t *testing.T) { t.Parallel() client, closer := testVaultServer(t) defer closer() - + os.Setenv(consts.VaultDisableFilePermissionsCheckEnv, "true") cmd := testOperatorDiagnoseCommand(t) cmd.client = client diff --git a/command/operator_generate_root_test.go b/command/operator_generate_root_test.go index fadd7560623e..b4489718efbe 100644 --- a/command/operator_generate_root_test.go +++ b/command/operator_generate_root_test.go @@ -3,7 +3,6 @@ package command import ( - "context" "encoding/base64" "io" "os" @@ -256,7 +255,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { defer closer() // Initialize a generation - if _, err := client.Sys().GenerateRootInitWithContext(context.Background(), "", ""); err != nil { + if _, err := client.Sys().GenerateRootInit("", ""); err != nil { t.Fatal(err) } @@ -276,7 +275,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().GenerateRootStatusWithContext(context.Background()) + status, err := client.Sys().GenerateRootStatus() if err != nil { t.Fatal(err) } @@ -308,7 +307,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().GenerateRootStatusWithContext(context.Background()) + status, err := client.Sys().GenerateRootStatus() if err != nil { t.Fatal(err) } @@ -344,7 +343,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().GenerateRootStatusWithContext(context.Background()) + status, err := client.Sys().GenerateRootStatus() if err != nil { t.Fatal(err) } @@ -387,7 +386,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { defer closer() // Initialize a generation - status, err := client.Sys().GenerateRootInitWithContext(context.Background(), "", "") + status, err := client.Sys().GenerateRootInit("", "") if err != nil { t.Fatal(err) } @@ -448,7 +447,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { defer closer() // Initialize a generation - status, err := client.Sys().GenerateRootInitWithContext(context.Background(), "", "") + status, err := client.Sys().GenerateRootInit("", "") if err != nil { t.Fatal(err) } diff --git a/command/operator_init_test.go b/command/operator_init_test.go index 7e1466a8743a..491d623a1473 100644 --- a/command/operator_init_test.go +++ b/command/operator_init_test.go @@ -3,7 +3,6 @@ package command import ( - "context" "fmt" "os" "regexp" @@ -144,7 +143,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { } // Now init to verify the init response code - if _, err := client.Sys().InitWithContext(context.Background(), &api.InitRequest{ + if _, err := client.Sys().Init(&api.InitRequest{ SecretShares: 1, SecretThreshold: 1, }); err != nil { @@ -176,7 +175,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String()) } - init, err := client.Sys().InitStatusWithContext(context.Background()) + init, err := client.Sys().InitStatus() if err != nil { t.Fatal(err) } @@ -199,7 +198,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { // Try unsealing with those keys - only use 3, which is the default // threshold. for i, key := range keys[:3] { - resp, err := client.Sys().UnsealWithContext(context.Background(), key) + resp, err := client.Sys().Unseal(key) if err != nil { t.Fatal(err) } @@ -210,7 +209,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { } } - status, err := client.Sys().SealStatusWithContext(context.Background()) + status, err := client.Sys().SealStatus() if err != nil { t.Fatal(err) } @@ -238,7 +237,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String()) } - init, err := client.Sys().InitStatusWithContext(context.Background()) + init, err := client.Sys().InitStatus() if err != nil { t.Fatal(err) } @@ -261,7 +260,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { // Try unsealing with those keys - only use 3, which is the default // threshold. for i, key := range keys[:keyThreshold] { - resp, err := client.Sys().UnsealWithContext(context.Background(), key) + resp, err := client.Sys().Unseal(key) if err != nil { t.Fatal(err) } @@ -272,7 +271,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { } } - status, err := client.Sys().SealStatusWithContext(context.Background()) + status, err := client.Sys().SealStatus() if err != nil { t.Fatal(err) } @@ -321,7 +320,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { // Try unsealing with one key decryptedKey := testPGPDecrypt(t, pgpkeys.TestPrivKey1, keys[0]) - if _, err := client.Sys().UnsealWithContext(context.Background(), decryptedKey); err != nil { + if _, err := client.Sys().Unseal(decryptedKey); err != nil { t.Fatal(err) } diff --git a/command/operator_rekey_test.go b/command/operator_rekey_test.go index 6ba2a4049be3..31617e5ac4bc 100644 --- a/command/operator_rekey_test.go +++ b/command/operator_rekey_test.go @@ -3,7 +3,6 @@ package command import ( - "context" "io" "reflect" "regexp" @@ -118,7 +117,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { } // Now init to verify the init response - if _, err := client.Sys().RekeyInitWithContext(context.Background(), &api.RekeyInitRequest{ + if _, err := client.Sys().RekeyInit(&api.RekeyInitRequest{ SecretShares: 1, SecretThreshold: 1, }); err != nil { @@ -149,7 +148,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { defer closer() // Initialize a rekey - if _, err := client.Sys().RekeyInitWithContext(context.Background(), &api.RekeyInitRequest{ + if _, err := client.Sys().RekeyInit(&api.RekeyInitRequest{ SecretShares: 1, SecretThreshold: 1, }); err != nil { @@ -172,7 +171,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().GenerateRootStatusWithContext(context.Background()) + status, err := client.Sys().GenerateRootStatus() if err != nil { t.Fatal(err) } @@ -206,7 +205,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().RekeyStatusWithContext(context.Background()) + status, err := client.Sys().RekeyStatus() if err != nil { t.Fatal(err) } @@ -243,7 +242,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().RekeyStatusWithContext(context.Background()) + status, err := client.Sys().RekeyStatus() if err != nil { t.Fatal(err) } @@ -262,7 +261,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { defer closer() // Initialize a rekey - status, err := client.Sys().RekeyInitWithContext(context.Background(), &api.RekeyInitRequest{ + status, err := client.Sys().RekeyInit(&api.RekeyInitRequest{ SecretShares: 1, SecretThreshold: 1, }) @@ -308,7 +307,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { if err := client.Sys().Seal(); err != nil { t.Fatal(err) } - sealStatus, err := client.Sys().UnsealWithContext(context.Background(), unsealKey) + sealStatus, err := client.Sys().Unseal(unsealKey) if err != nil { t.Fatal(err) } @@ -324,7 +323,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { defer closer() // Initialize a rekey - status, err := client.Sys().RekeyInitWithContext(context.Background(), &api.RekeyInitRequest{ + status, err := client.Sys().RekeyInit(&api.RekeyInitRequest{ SecretShares: 1, SecretThreshold: 1, }) @@ -384,7 +383,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { if err := client.Sys().Seal(); err != nil { t.Fatal(err) } - sealStatus, err := client.Sys().UnsealWithContext(context.Background(), unsealKey) + sealStatus, err := client.Sys().Unseal(unsealKey) if err != nil { t.Fatal(err) } @@ -417,7 +416,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { } // Get the status for the nonce - status, err := client.Sys().RekeyStatusWithContext(context.Background()) + status, err := client.Sys().RekeyStatus() if err != nil { t.Fatal(err) } @@ -480,7 +479,7 @@ func TestOperatorRekeyCommand_Run(t *testing.T) { t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String()) } - secret, err := client.Sys().RekeyRetrieveBackupWithContext(context.Background()) + secret, err := client.Sys().RekeyRetrieveBackup() if err == nil { t.Errorf("expected error: %#v", secret) } diff --git a/command/operator_seal_test.go b/command/operator_seal_test.go index 7557b0b8ee29..86722d2e84dd 100644 --- a/command/operator_seal_test.go +++ b/command/operator_seal_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -84,7 +83,7 @@ func TestOperatorSealCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - sealStatus, err := client.Sys().SealStatusWithContext(context.Background()) + sealStatus, err := client.Sys().SealStatus() if err != nil { t.Fatal(err) } diff --git a/command/operator_unseal_test.go b/command/operator_unseal_test.go index a633721034fe..06d618cacf72 100644 --- a/command/operator_unseal_test.go +++ b/command/operator_unseal_test.go @@ -2,7 +2,6 @@ package command import ( "bytes" - "context" "encoding/json" "io/ioutil" "os" @@ -60,7 +59,7 @@ func TestOperatorUnsealCommand_Run(t *testing.T) { } // Enter an unseal key - if _, err := client.Sys().UnsealWithContext(context.Background(), keys[0]); err != nil { + if _, err := client.Sys().Unseal(keys[0]); err != nil { t.Fatal(err) } @@ -107,7 +106,7 @@ func TestOperatorUnsealCommand_Run(t *testing.T) { } } - status, err := client.Sys().SealStatusWithContext(context.Background()) + status, err := client.Sys().SealStatus() if err != nil { t.Fatal(err) } diff --git a/command/path_map_upgrade_api_test.go b/command/path_map_upgrade_api_test.go index 83cb18f4f4cd..57c1f773405a 100644 --- a/command/path_map_upgrade_api_test.go +++ b/command/path_map_upgrade_api_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "testing" log "github.com/hashicorp/go-hclog" @@ -46,7 +45,7 @@ func TestPathMap_Upgrade_API(t *testing.T) { } // Create an app-id - _, err = client.Logical().WriteWithContext(context.Background(), "auth/app-id/map/app-id/test-app-id", map[string]interface{}{ + _, err = client.Logical().Write("auth/app-id/map/app-id/test-app-id", map[string]interface{}{ "policy": "test-policy", }) if err != nil { @@ -54,7 +53,7 @@ func TestPathMap_Upgrade_API(t *testing.T) { } // Create a user-id - _, err = client.Logical().WriteWithContext(context.Background(), "auth/app-id/map/user-id/test-user-id", map[string]interface{}{ + _, err = client.Logical().Write("auth/app-id/map/user-id/test-user-id", map[string]interface{}{ "value": "test-app-id", }) if err != nil { @@ -62,7 +61,7 @@ func TestPathMap_Upgrade_API(t *testing.T) { } // Perform a login. It should succeed. - _, err = client.Logical().WriteWithContext(context.Background(), "auth/app-id/login", map[string]interface{}{ + _, err = client.Logical().Write("auth/app-id/login", map[string]interface{}{ "app_id": "test-app-id", "user_id": "test-user-id", }) @@ -71,20 +70,20 @@ func TestPathMap_Upgrade_API(t *testing.T) { } // List the hashed app-ids in the storage - secret, err := client.Logical().ListWithContext(context.Background(), "auth/app-id/map/app-id") + secret, err := client.Logical().List("auth/app-id/map/app-id") if err != nil { t.Fatal(err) } hashedAppID := secret.Data["keys"].([]interface{})[0].(string) // Try reading it. This used to cause an issue which is fixed in [GH-3806]. - _, err = client.Logical().ReadWithContext(context.Background(), "auth/app-id/map/app-id/"+hashedAppID) + _, err = client.Logical().Read("auth/app-id/map/app-id/" + hashedAppID) if err != nil { t.Fatal(err) } // Ensure that there was no issue by performing another login - _, err = client.Logical().WriteWithContext(context.Background(), "auth/app-id/login", map[string]interface{}{ + _, err = client.Logical().Write("auth/app-id/login", map[string]interface{}{ "app_id": "test-app-id", "user_id": "test-user-id", }) diff --git a/command/plugin_deregister_test.go b/command/plugin_deregister_test.go index b8d0fa2d19e0..9696c2f33c66 100644 --- a/command/plugin_deregister_test.go +++ b/command/plugin_deregister_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -89,7 +88,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) { ui, cmd := testPluginDeregisterCommand(t) cmd.client = client - if err := client.Sys().RegisterPluginWithContext(context.Background(), &api.RegisterPluginInput{ + if err := client.Sys().RegisterPlugin(&api.RegisterPluginInput{ Name: pluginName, Type: consts.PluginTypeCredential, Command: pluginName, @@ -112,7 +111,7 @@ func TestPluginDeregisterCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - resp, err := client.Sys().ListPluginsWithContext(context.Background(), &api.ListPluginsInput{ + resp, err := client.Sys().ListPlugins(&api.ListPluginsInput{ Type: consts.PluginTypeCredential, }) if err != nil { diff --git a/command/plugin_register_test.go b/command/plugin_register_test.go index c58df9a5a9a5..05b358e6f478 100644 --- a/command/plugin_register_test.go +++ b/command/plugin_register_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -104,7 +103,7 @@ func TestPluginRegisterCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - resp, err := client.Sys().ListPluginsWithContext(context.Background(), &api.ListPluginsInput{ + resp, err := client.Sys().ListPlugins(&api.ListPluginsInput{ Type: consts.PluginTypeCredential, }) if err != nil { diff --git a/command/plugin_reload_test.go b/command/plugin_reload_test.go index 3ccc997e79fc..99b0c03c7f6e 100644 --- a/command/plugin_reload_test.go +++ b/command/plugin_reload_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -95,7 +94,7 @@ func TestPluginReloadCommand_Run(t *testing.T) { ui, cmd := testPluginReloadCommand(t) cmd.client = client - if err := client.Sys().RegisterPluginWithContext(context.Background(), &api.RegisterPluginInput{ + if err := client.Sys().RegisterPlugin(&api.RegisterPluginInput{ Name: pluginName, Type: consts.PluginTypeCredential, Command: pluginName, diff --git a/command/plugin_test.go b/command/plugin_test.go index 6a0a97770808..786abdb52f4e 100644 --- a/command/plugin_test.go +++ b/command/plugin_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "crypto/sha256" "fmt" "io" @@ -68,7 +67,7 @@ func testPluginCreateAndRegister(tb testing.TB, client *api.Client, dir, name st pth, sha256Sum := testPluginCreate(tb, dir, name) - if err := client.Sys().RegisterPluginWithContext(context.Background(), &api.RegisterPluginInput{ + if err := client.Sys().RegisterPlugin(&api.RegisterPluginInput{ Name: name, Type: pluginType, Command: name, diff --git a/command/policy_delete_test.go b/command/policy_delete_test.go index d8b84654b07a..2c822de9d4fd 100644 --- a/command/policy_delete_test.go +++ b/command/policy_delete_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "reflect" "strings" "testing" @@ -78,7 +77,7 @@ func TestPolicyDeleteCommand_Run(t *testing.T) { defer closer() policy := `path "secret/" {}` - if err := client.Sys().PutPolicyWithContext(context.Background(), "my-policy", policy); err != nil { + if err := client.Sys().PutPolicy("my-policy", policy); err != nil { t.Fatal(err) } @@ -98,7 +97,7 @@ func TestPolicyDeleteCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - policies, err := client.Sys().ListPoliciesWithContext(context.Background()) + policies, err := client.Sys().ListPolicies() if err != nil { t.Fatal(err) } diff --git a/command/policy_read_test.go b/command/policy_read_test.go index ca09c8cb5065..8cd7c066b8ce 100644 --- a/command/policy_read_test.go +++ b/command/policy_read_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -77,7 +76,7 @@ func TestPolicyReadCommand_Run(t *testing.T) { defer closer() policy := `path "secret/" {}` - if err := client.Sys().PutPolicyWithContext(context.Background(), "my-policy", policy); err != nil { + if err := client.Sys().PutPolicy("my-policy", policy); err != nil { t.Fatal(err) } diff --git a/command/policy_write_test.go b/command/policy_write_test.go index f89667cccbd4..c8db7dc9ddc2 100644 --- a/command/policy_write_test.go +++ b/command/policy_write_test.go @@ -2,7 +2,6 @@ package command import ( "bytes" - "context" "io" "io/ioutil" "os" @@ -124,7 +123,7 @@ func TestPolicyWriteCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - policies, err := client.Sys().ListPoliciesWithContext(context.Background()) + policies, err := client.Sys().ListPolicies() if err != nil { t.Fatal(err) } @@ -165,7 +164,7 @@ func TestPolicyWriteCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - policies, err := client.Sys().ListPoliciesWithContext(context.Background()) + policies, err := client.Sys().ListPolicies() if err != nil { t.Fatal(err) } diff --git a/command/read_test.go b/command/read_test.go index 78f7e0ae4340..13f41da7e4a1 100644 --- a/command/read_test.go +++ b/command/read_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -84,7 +83,7 @@ func TestReadCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if _, err := client.Logical().WriteWithContext(context.Background(), "secret/read/foo", map[string]interface{}{ + if _, err := client.Logical().Write("secret/read/foo", map[string]interface{}{ "foo": "bar", }); err != nil { t.Fatal(err) diff --git a/command/rotate_test.go b/command/rotate_test.go index 7560a3f63c7f..37ac32340590 100644 --- a/command/rotate_test.go +++ b/command/rotate_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -84,7 +83,7 @@ func TestOperatorRotateCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - status, err := client.Sys().KeyStatusWithContext(context.Background()) + status, err := client.Sys().KeyStatus() if err != nil { t.Fatal(err) } diff --git a/command/secrets_disable_test.go b/command/secrets_disable_test.go index f6b07a859b9a..567c8956d630 100644 --- a/command/secrets_disable_test.go +++ b/command/secrets_disable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -89,7 +88,7 @@ func TestSecretsDisableCommand_Run(t *testing.T) { client, closer := testVaultServer(t) defer closer() - if err := client.Sys().MountWithContext(context.Background(), "my-secret/", &api.MountInput{ + if err := client.Sys().Mount("my-secret/", &api.MountInput{ Type: "generic", }); err != nil { t.Fatal(err) @@ -111,7 +110,7 @@ func TestSecretsDisableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } diff --git a/command/secrets_enable_test.go b/command/secrets_enable_test.go index a8dff093fe4d..814f4731204f 100644 --- a/command/secrets_enable_test.go +++ b/command/secrets_enable_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "io/ioutil" "strings" "testing" @@ -128,7 +127,7 @@ func TestSecretsEnableCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } diff --git a/command/secrets_move_test.go b/command/secrets_move_test.go index 4aed4460e0d0..153fbeb2cdc0 100644 --- a/command/secrets_move_test.go +++ b/command/secrets_move_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -98,7 +97,7 @@ func TestSecretsMoveCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } diff --git a/command/secrets_tune_test.go b/command/secrets_tune_test.go index b1e35c6d8c9e..f51b8fb34b78 100644 --- a/command/secrets_tune_test.go +++ b/command/secrets_tune_test.go @@ -1,7 +1,6 @@ package command import ( - "context" "strings" "testing" @@ -81,7 +80,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { cmd.client = client // Mount - if err := client.Sys().MountWithContext(context.Background(), "kv", &api.MountInput{ + if err := client.Sys().Mount("kv", &api.MountInput{ Type: "kv", Options: map[string]string{ "version": "2", @@ -91,7 +90,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { } // confirm default max_versions - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } @@ -126,7 +125,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err = client.Sys().ListMountsWithContext(context.Background()) + mounts, err = client.Sys().ListMounts() if err != nil { t.Fatal(err) } @@ -156,7 +155,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { cmd.client = client // Mount - if err := client.Sys().MountWithContext(context.Background(), "mount_tune_integration", &api.MountInput{ + if err := client.Sys().Mount("mount_tune_integration", &api.MountInput{ Type: "pki", }); err != nil { t.Fatal(err) @@ -185,7 +184,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } @@ -233,7 +232,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { cmd.client = client // Mount - if err := client.Sys().MountWithContext(context.Background(), "mount_tune_integration", &api.MountInput{ + if err := client.Sys().Mount("mount_tune_integration", &api.MountInput{ Type: "pki", Description: "initial description", }); err != nil { @@ -254,7 +253,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } @@ -276,7 +275,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { cmd.client = client // Mount - if err := client.Sys().MountWithContext(context.Background(), "mount_tune_integration", &api.MountInput{ + if err := client.Sys().Mount("mount_tune_integration", &api.MountInput{ Type: "pki", Description: "initial description", }); err != nil { @@ -297,7 +296,7 @@ func TestSecretsTuneCommand_Run(t *testing.T) { t.Errorf("expected %q to contain %q", combined, expected) } - mounts, err := client.Sys().ListMountsWithContext(context.Background()) + mounts, err := client.Sys().ListMounts() if err != nil { t.Fatal(err) } diff --git a/command/server.go b/command/server.go index 656bfc28570f..1b0a3231a9c8 100644 --- a/command/server.go +++ b/command/server.go @@ -2518,6 +2518,8 @@ func createCoreConfig(c *ServerCommand, config *server.Config, backend physical. ClusterName: config.ClusterName, CacheSize: config.CacheSize, PluginDirectory: config.PluginDirectory, + PluginFileUid: config.PluginFileUid, + PluginFilePermissions: config.PluginFilePermissions, EnableUI: config.EnableUI, EnableRaw: config.EnableRawEndpoint, DisableSealWrap: config.DisableSealWrap, @@ -2535,6 +2537,7 @@ func createCoreConfig(c *ServerCommand, config *server.Config, backend physical. LicensePath: config.LicensePath, DisableSSCTokens: config.DisableSSCTokens, } + if c.flagDev { coreConfig.EnableRaw = true coreConfig.DevToken = c.flagDevRootTokenID diff --git a/command/server/config.go b/command/server/config.go index 92a911185231..911dfe537ce9 100644 --- a/command/server/config.go +++ b/command/server/config.go @@ -16,7 +16,9 @@ import ( "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/hcl/ast" + "github.com/hashicorp/vault/helper/osutil" "github.com/hashicorp/vault/internalshared/configutil" + "github.com/hashicorp/vault/sdk/helper/consts" ) var entConfigValidate = func(_ *Config, _ string) []configutil.ConfigError { @@ -54,6 +56,11 @@ type Config struct { PluginDirectory string `hcl:"plugin_directory"` + PluginFileUid int `hcl:"plugin_file_uid"` + + PluginFilePermissions int `hcl:"-"` + PluginFilePermissionsRaw interface{} `hcl:"plugin_file_permissions,alias:PluginFilePermissions"` + EnableRawEndpoint bool `hcl:"-"` EnableRawEndpointRaw interface{} `hcl:"raw_storage_endpoint,alias:EnableRawEndpoint"` @@ -127,7 +134,6 @@ telemetry { prometheus_retention_time = "24h" disable_hostname = true } - enable_raw_endpoint = true storage "%s" { @@ -276,6 +282,17 @@ func (c *Config) Merge(c2 *Config) *Config { result.PluginDirectory = c2.PluginDirectory } + result.PluginFileUid = c.PluginFileUid + if c2.PluginFileUid != 0 { + result.PluginFileUid = c2.PluginFileUid + } + + result.PluginFilePermissions = c.PluginFilePermissions + if c2.PluginFilePermissionsRaw != nil { + result.PluginFilePermissions = c2.PluginFilePermissions + result.PluginFilePermissionsRaw = c2.PluginFilePermissionsRaw + } + result.DisablePerformanceStandby = c.DisablePerformanceStandby if c2.DisablePerformanceStandby { result.DisablePerformanceStandby = c2.DisablePerformanceStandby @@ -350,6 +367,13 @@ func LoadConfig(path string) (*Config, error) { } if fi.IsDir() { + // check permissions on the config directory + if os.Getenv(consts.VaultDisableFilePermissionsCheckEnv) != "true" { + err = osutil.OwnerPermissionsMatch(path, 0, 0) + if err != nil { + return nil, err + } + } return CheckConfig(LoadConfigDir(path)) } return CheckConfig(LoadConfigFile(path)) @@ -385,6 +409,21 @@ func LoadConfigFile(path string) (*Config, error) { return nil, err } + if os.Getenv(consts.VaultDisableFilePermissionsCheckEnv) != "true" { + // check permissions of the config file + err = osutil.OwnerPermissionsMatch(path, 0, 0) + if err != nil { + return nil, err + } + // check permissions of the plugin directory + if conf.PluginDirectory != "" { + + err = osutil.OwnerPermissionsMatch(conf.PluginDirectory, conf.PluginFileUid, conf.PluginFilePermissions) + if err != nil { + return nil, err + } + } + } return conf, nil } @@ -459,6 +498,18 @@ func ParseConfig(d, source string) (*Config, error) { } } + if result.PluginFilePermissionsRaw != nil { + octalPermissionsString, err := parseutil.ParseString(result.PluginFilePermissionsRaw) + if err != nil { + return nil, err + } + pluginFilePermissions, err := strconv.ParseInt(octalPermissionsString, 8, 64) + if err != nil { + return nil, err + } + result.PluginFilePermissions = int(pluginFilePermissions) + } + if result.DisableSentinelTraceRaw != nil { if result.DisableSentinelTrace, err = parseutil.ParseBool(result.DisableSentinelTraceRaw); err != nil { return nil, err @@ -838,6 +889,10 @@ func (c *Config) Sanitized() map[string]interface{} { "plugin_directory": c.PluginDirectory, + "plugin_file_uid": c.PluginFileUid, + + "plugin_file_permissions": c.PluginFilePermissions, + "raw_storage_endpoint": c.EnableRawEndpoint, "api_addr": c.APIAddr, diff --git a/command/server/config_test_helpers.go b/command/server/config_test_helpers.go index 30260466feb5..424d8fe816b3 100644 --- a/command/server/config_test_helpers.go +++ b/command/server/config_test_helpers.go @@ -694,6 +694,8 @@ func testConfig_Sanitized(t *testing.T) { "disable_indexing": false, "disable_mlock": true, "disable_performance_standby": false, + "plugin_file_uid": 0, + "plugin_file_permissions": 0, "disable_printable_check": false, "disable_sealwrap": true, "raw_storage_endpoint": true, @@ -855,6 +857,7 @@ func testParseSockaddrTemplate(t *testing.T) { api_addr = <.+)", + Fields: map[string]*FieldSchema{ + "an_int": {Type: TypeInt}, + "a_string": {Type: TypeString}, + "name": {Type: TypeString}, + }, + Operations: map[logical.Operation]OperationHandler{ + logical.UpdateOperation: &PathOperation{Callback: handler}, + }, + }, + }, + } + ctx := context.Background() + resp, err := backend.HandleRequest(ctx, &logical.Request{ + Operation: logical.UpdateOperation, + Path: "foo/bar/baz", + Data: map[string]interface{}{ + "an_int": 10, + "a_string": "accepted", + "unrecognized1": "unrecognized", + "unrecognized2": 20.2, + "name": "noop", + }, + }) + require.NoError(t, err) + require.NotNil(t, resp) + t.Log(resp.Warnings) + require.Len(t, resp.Warnings, 2) + require.True(t, strutil.StrListContains(resp.Warnings, "Endpoint ignored these unrecognized parameters: [unrecognized1 unrecognized2]")) + require.True(t, strutil.StrListContains(resp.Warnings, "Endpoint replaced the value of these parameters with the values captured from the endpoint's path: [name]")) +} + func TestBackendHandleRequest(t *testing.T) { callback := func(ctx context.Context, req *logical.Request, data *FieldData) (*logical.Response, error) { return &logical.Response{ diff --git a/sdk/helper/certutil/helpers.go b/sdk/helper/certutil/helpers.go index b6bee2e342c7..c26020f43575 100644 --- a/sdk/helper/certutil/helpers.go +++ b/sdk/helper/certutil/helpers.go @@ -585,25 +585,17 @@ func DefaultOrValueKeyBits(keyType string, keyBits int) (int, error) { // certain internal circumstances. func DefaultOrValueHashBits(keyType string, keyBits int, hashBits int) (int, error) { if keyType == "ec" { - // To comply with BSI recommendations Section 4.2 and Mozilla root - // store policy section 5.1.2, enforce that NIST P-curves use a hash - // length corresponding to curve length. Note that ed25519 does not - // the "ec" key type. - expectedHashBits := expectedNISTPCurveHashBits[keyBits] - - if expectedHashBits != hashBits && hashBits != 0 { - return hashBits, fmt.Errorf("unsupported signature hash algorithm length (%d) for NIST P-%d", hashBits, keyBits) - } else if hashBits == 0 { - hashBits = expectedHashBits - } + // Enforcement of curve moved to selectSignatureAlgorithmForECDSA. See + // note there about why. } else if keyType == "rsa" && hashBits == 0 { // To match previous behavior (and ignoring NIST's recommendations for // hash size to align with RSA key sizes), default to SHA-2-256. hashBits = 256 - } else if keyType == "ed25519" || keyType == "ed448" { + } else if keyType == "ed25519" || keyType == "ed448" || keyType == "any" { // No-op; ed25519 and ed448 internally specify their own hash and // we do not need to select one. Double hashing isn't supported in - // certificate signing and we must + // certificate signing. Additionally, the any key type can't know + // what hash algorithm to use yet, so default to zero. return 0, nil } @@ -642,7 +634,7 @@ func ValidateDefaultOrValueKeyTypeSignatureLength(keyType string, keyBits int, h // Validates that the length of the hash (in bits) used in the signature // calculation is a known, approved value. func ValidateSignatureLength(keyType string, hashBits int) error { - if keyType == "ed25519" || keyType == "ed448" { + if keyType == "any" || keyType == "ec" || keyType == "ed25519" || keyType == "ed448" { // ed25519 and ed448 include built-in hashing and is not externally // configurable. There are three modes for each of these schemes: // @@ -654,6 +646,13 @@ func ValidateSignatureLength(keyType string, hashBits int) error { // // In all cases, we won't have a hash algorithm to validate here, so // return nil. + // + // Additionally, when KeyType is any, we can't yet validate the + // signature algorithm size, so it takes the default zero value. + // + // When KeyType is ec, we also can't validate this value as we're + // forcefully ignoring the users' choice and specifying a value based + // on issuer type. return nil } @@ -859,16 +858,25 @@ func createCertificate(data *CreationBundle, randReader io.Reader, privateKeyGen } func selectSignatureAlgorithmForECDSA(pub crypto.PublicKey, signatureBits int) x509.SignatureAlgorithm { - // If signature bits are configured, prefer them to the default choice. - switch signatureBits { - case 256: - return x509.ECDSAWithSHA256 - case 384: - return x509.ECDSAWithSHA384 - case 512: - return x509.ECDSAWithSHA512 - } - + // Previously we preferred the user-specified signature bits for ECDSA + // keys. However, this could result in using a longer hash function than + // the underlying NIST P-curve will encode (e.g., a SHA-512 hash with a + // P-256 key). This isn't ideal: the hash is implicitly truncated + // (effectively turning it into SHA-512/256) and we then need to rely + // on the prefix security of the hash. Since both NIST and Mozilla guidance + // suggest instead using the correct hash function, we should prefer that + // over the operator-specified signatureBits. + // + // Lastly, note that pub above needs to be the _signer's_ public key; + // the issue with DefaultOrValueHashBits is that it is called at role + // configuration time, which might _precede_ issuer generation. Thus + // it only has access to the desired key type and not the actual issuer. + // The reference from that function is reproduced below: + // + // > To comply with BSI recommendations Section 4.2 and Mozilla root + // > store policy section 5.1.2, enforce that NIST P-curves use a hash + // > length corresponding to curve length. Note that ed25519 does not + // > implement the "ec" key type. key, ok := pub.(*ecdsa.PublicKey) if !ok { return x509.ECDSAWithSHA256 diff --git a/sdk/helper/consts/consts.go b/sdk/helper/consts/consts.go index b10f57a22925..86d4f06967e9 100644 --- a/sdk/helper/consts/consts.go +++ b/sdk/helper/consts/consts.go @@ -32,4 +32,6 @@ const ( // ReplicationResolverALPN is the negotiated protocol used for // resolving replicaiton addresses ReplicationResolverALPN = "replication_resolver_v1" + + VaultDisableFilePermissionsCheckEnv = "VAULT_DISABLE_FILE_PERMISSIONS_CHECK" ) diff --git a/sdk/helper/pluginutil/multiplexing.pb.go b/sdk/helper/pluginutil/multiplexing.pb.go index fa3357d49045..d0ff51e57b24 100644 --- a/sdk/helper/pluginutil/multiplexing.pb.go +++ b/sdk/helper/pluginutil/multiplexing.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/helper/pluginutil/multiplexing.proto package pluginutil diff --git a/sdk/helper/useragent/useragent.go b/sdk/helper/useragent/useragent.go index 9bc212535cdc..e7e23ac2b6f5 100644 --- a/sdk/helper/useragent/useragent.go +++ b/sdk/helper/useragent/useragent.go @@ -44,12 +44,29 @@ func String(comments ...string) string { // Given comments will be appended to the semicolon-delimited comment section. // // e.g. Vault/0.10.4 (+https://www.vaultproject.io/; azure-auth; go1.10.1; comment-0; comment-1) +// +// Returns an empty string if the given env is nil. func PluginString(env *logical.PluginEnvironment, pluginName string, comments ...string) string { + if env == nil { + return "" + } + + // Construct comments c := []string{"+" + projectURL} if pluginName != "" { c = append(c, pluginName) } c = append(c, rt) c = append(c, comments...) - return fmt.Sprintf("Vault/%s (%s)", env.VaultVersion, strings.Join(c, "; ")) + + // Construct version string + v := env.VaultVersion + if env.VaultVersionPrerelease != "" { + v = fmt.Sprintf("%s-%s", v, env.VaultVersionPrerelease) + } + if env.VaultVersionMetadata != "" { + v = fmt.Sprintf("%s+%s", v, env.VaultVersionMetadata) + } + + return fmt.Sprintf("Vault/%s (%s)", v, strings.Join(c, "; ")) } diff --git a/sdk/helper/useragent/useragent_test.go b/sdk/helper/useragent/useragent_test.go index bfd439603f55..f0d014f6c570 100644 --- a/sdk/helper/useragent/useragent_test.go +++ b/sdk/helper/useragent/useragent_test.go @@ -51,12 +51,10 @@ func TestUserAgent(t *testing.T) { func TestUserAgentPlugin(t *testing.T) { projectURL = "https://vault-test.com" rt = "go5.0" - env := &logical.PluginEnvironment{ - VaultVersion: "1.2.3", - } type args struct { pluginName string + pluginEnv *logical.PluginEnvironment comments []string } tests := []struct { @@ -64,15 +62,38 @@ func TestUserAgentPlugin(t *testing.T) { args args want string }{ + { + name: "Plugin user agent with nil plugin env", + args: args{ + pluginEnv: nil, + }, + want: "", + }, { name: "Plugin user agent without plugin name", - args: args{}, + args: args{ + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, + }, + want: "Vault/1.2.3 (+https://vault-test.com; go5.0)", + }, + { + name: "Plugin user agent without plugin name", + args: args{ + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, + }, want: "Vault/1.2.3 (+https://vault-test.com; go5.0)", }, { name: "Plugin user agent with plugin name", args: args{ pluginName: "azure-auth", + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, }, want: "Vault/1.2.3 (+https://vault-test.com; azure-auth; go5.0)", }, @@ -80,7 +101,10 @@ func TestUserAgentPlugin(t *testing.T) { name: "Plugin user agent with plugin name and additional comment", args: args{ pluginName: "azure-auth", - comments: []string{"pid-abcdefg"}, + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, + comments: []string{"pid-abcdefg"}, }, want: "Vault/1.2.3 (+https://vault-test.com; azure-auth; go5.0; pid-abcdefg)", }, @@ -88,21 +112,64 @@ func TestUserAgentPlugin(t *testing.T) { name: "Plugin user agent with plugin name and additional comments", args: args{ pluginName: "azure-auth", - comments: []string{"pid-abcdefg", "cloud-provider"}, + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, + comments: []string{"pid-abcdefg", "cloud-provider"}, }, want: "Vault/1.2.3 (+https://vault-test.com; azure-auth; go5.0; pid-abcdefg; cloud-provider)", }, { name: "Plugin user agent with no plugin name and additional comments", args: args{ + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + }, comments: []string{"pid-abcdefg", "cloud-provider"}, }, want: "Vault/1.2.3 (+https://vault-test.com; go5.0; pid-abcdefg; cloud-provider)", }, + { + name: "Plugin user agent with version prerelease", + args: args{ + pluginName: "azure-auth", + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + VaultVersionPrerelease: "dev", + }, + comments: []string{"pid-abcdefg", "cloud-provider"}, + }, + want: "Vault/1.2.3-dev (+https://vault-test.com; azure-auth; go5.0; pid-abcdefg; cloud-provider)", + }, + { + name: "Plugin user agent with version metadata", + args: args{ + pluginName: "azure-auth", + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + VaultVersionMetadata: "ent", + }, + comments: []string{"pid-abcdefg", "cloud-provider"}, + }, + want: "Vault/1.2.3+ent (+https://vault-test.com; azure-auth; go5.0; pid-abcdefg; cloud-provider)", + }, + { + name: "Plugin user agent with version prerelease and metadata", + args: args{ + pluginName: "azure-auth", + pluginEnv: &logical.PluginEnvironment{ + VaultVersion: "1.2.3", + VaultVersionPrerelease: "dev", + VaultVersionMetadata: "ent", + }, + comments: []string{"pid-abcdefg", "cloud-provider"}, + }, + want: "Vault/1.2.3-dev+ent (+https://vault-test.com; azure-auth; go5.0; pid-abcdefg; cloud-provider)", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := PluginString(env, tt.args.pluginName, tt.args.comments...); got != tt.want { + if got := PluginString(tt.args.pluginEnv, tt.args.pluginName, tt.args.comments...); got != tt.want { t.Errorf("PluginString() = %v, want %v", got, tt.want) } }) diff --git a/sdk/logical/identity.pb.go b/sdk/logical/identity.pb.go index c472b68a099e..4b1a36b39826 100644 --- a/sdk/logical/identity.pb.go +++ b/sdk/logical/identity.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/logical/identity.proto package logical diff --git a/sdk/logical/plugin.pb.go b/sdk/logical/plugin.pb.go index d4722ce09761..1fb53f9a79c9 100644 --- a/sdk/logical/plugin.pb.go +++ b/sdk/logical/plugin.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/logical/plugin.proto package logical @@ -27,6 +27,10 @@ type PluginEnvironment struct { // VaultVersion is the version of the Vault server VaultVersion string `protobuf:"bytes,1,opt,name=vault_version,json=vaultVersion,proto3" json:"vault_version,omitempty"` + // VaultVersionPrerelease is the prerelease information of the Vault server + VaultVersionPrerelease string `protobuf:"bytes,2,opt,name=vault_version_prerelease,json=vaultVersionPrerelease,proto3" json:"vault_version_prerelease,omitempty"` + // VaultVersionMetadata is the version metadata of the Vault server + VaultVersionMetadata string `protobuf:"bytes,3,opt,name=vault_version_metadata,json=vaultVersionMetadata,proto3" json:"vault_version_metadata,omitempty"` } func (x *PluginEnvironment) Reset() { @@ -68,18 +72,39 @@ func (x *PluginEnvironment) GetVaultVersion() string { return "" } +func (x *PluginEnvironment) GetVaultVersionPrerelease() string { + if x != nil { + return x.VaultVersionPrerelease + } + return "" +} + +func (x *PluginEnvironment) GetVaultVersionMetadata() string { + if x != nil { + return x.VaultVersionMetadata + } + return "" +} + var File_sdk_logical_plugin_proto protoreflect.FileDescriptor var file_sdk_logical_plugin_proto_rawDesc = []byte{ 0x0a, 0x18, 0x73, 0x64, 0x6b, 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6c, 0x6f, 0x67, 0x69, - 0x63, 0x61, 0x6c, 0x22, 0x38, 0x0a, 0x11, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, 0x76, - 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x61, 0x75, 0x6c, - 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x28, 0x5a, - 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, - 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x63, 0x61, 0x6c, 0x22, 0xa8, 0x01, 0x0a, 0x11, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x45, 0x6e, + 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x61, 0x75, + 0x6c, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, + 0x0a, 0x18, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x72, 0x65, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x16, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, + 0x65, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x76, 0x61, 0x75, 0x6c, + 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x28, + 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x2f, 0x73, 0x64, 0x6b, + 0x2f, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/logical/plugin.proto b/sdk/logical/plugin.proto index 5992c21395a3..f2df6c75d97c 100644 --- a/sdk/logical/plugin.proto +++ b/sdk/logical/plugin.proto @@ -7,4 +7,10 @@ package logical; message PluginEnvironment { // VaultVersion is the version of the Vault server string vault_version = 1; + + // VaultVersionPrerelease is the prerelease information of the Vault server + string vault_version_prerelease = 2; + + // VaultVersionMetadata is the version metadata of the Vault server + string vault_version_metadata = 3; } diff --git a/sdk/plugin/grpc_system_test.go b/sdk/plugin/grpc_system_test.go index cf1449f0599f..6e78ba87eba7 100644 --- a/sdk/plugin/grpc_system_test.go +++ b/sdk/plugin/grpc_system_test.go @@ -213,7 +213,9 @@ func TestSystem_GRPC_groupsForEntity(t *testing.T) { func TestSystem_GRPC_pluginEnv(t *testing.T) { sys := logical.TestSystemView() sys.PluginEnvironment = &logical.PluginEnvironment{ - VaultVersion: "0.10.42", + VaultVersion: "0.10.42", + VaultVersionPrerelease: "dev", + VaultVersionMetadata: "prem", } client, _ := plugin.TestGRPCConn(t, func(s *grpc.Server) { pb.RegisterSystemViewServer(s, &gRPCSystemViewServer{ diff --git a/sdk/plugin/pb/backend.pb.go b/sdk/plugin/pb/backend.pb.go index 184717a97540..dbad4da977ce 100644 --- a/sdk/plugin/pb/backend.pb.go +++ b/sdk/plugin/pb/backend.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.19.3 +// protoc v3.19.4 // source: sdk/plugin/pb/backend.proto package pb diff --git a/ui/app/components/auth-form.js b/ui/app/components/auth-form.js index 490711636dab..8497d76ff206 100644 --- a/ui/app/components/auth-form.js +++ b/ui/app/components/auth-form.js @@ -117,7 +117,8 @@ export default Component.extend(DEFAULTS, { if (!methods && !wrappedToken) { return {}; } - if (keyIsPath) { + // if type is provided we can ignore path since we are attempting to lookup a specific backend by type + if (keyIsPath && !type) { return methods.findBy('path', selected); } return BACKENDS.findBy('type', selected); @@ -227,7 +228,7 @@ export default Component.extend(DEFAULTS, { }); this.onSuccess(authResponse, backendType, data); } catch (e) { - this.set('loading', false); + this.set('isLoading', false); if (!this.auth.mfaError) { this.set('error', `Authentication failed: ${this.auth.handleError(e)}`); } @@ -260,7 +261,7 @@ export default Component.extend(DEFAULTS, { error: null, }); // if callback from oidc or jwt we have a token at this point - let backend = ['oidc', 'jwt'].includes(this.selectedAuth) + let backend = ['oidc', 'jwt'].includes(this.providerName) ? this.getAuthBackend('token') : this.selectedAuthBackend || {}; let backendMeta = BACKENDS.find( @@ -279,7 +280,7 @@ export default Component.extend(DEFAULTS, { }, handleError(e) { this.setProperties({ - loading: false, + isLoading: false, error: e ? this.auth.handleError(e) : null, }); }, diff --git a/ui/app/components/auth-jwt.js b/ui/app/components/auth-jwt.js index 6d200fd74744..f5ae2a44fb5a 100644 --- a/ui/app/components/auth-jwt.js +++ b/ui/app/components/auth-jwt.js @@ -178,8 +178,14 @@ export default Component.extend({ if (!this.isOIDC || !this.role || !this.role.authUrl) { return; } - - await this.fetchRole.perform(this.roleName, { debounce: false }); + try { + await this.fetchRole.perform(this.roleName, { debounce: false }); + } catch (error) { + // this task could be cancelled if the instances in didReceiveAttrs resolve after this was started + if (error?.name !== 'TaskCancelation') { + throw error; + } + } let win = this.getWindow(); const POPUP_WIDTH = 500; diff --git a/ui/app/components/secret-create-or-update.js b/ui/app/components/secret-create-or-update.js index 05f97327ecaa..ea7ba030942f 100644 --- a/ui/app/components/secret-create-or-update.js +++ b/ui/app/components/secret-create-or-update.js @@ -129,6 +129,9 @@ export default class SecretCreateOrUpdate extends Component { return secretData .save() .then(() => { + if (!this.args.canReadSecretData && secret.selectedVersion) { + delete secret.selectedVersion.secretData; + } if (!secretData.isError) { if (isV2) { secret.set('id', key); diff --git a/ui/app/components/secret-edit-toolbar.js b/ui/app/components/secret-edit-toolbar.js index f2bfb44c54db..b953c2545155 100644 --- a/ui/app/components/secret-edit-toolbar.js +++ b/ui/app/components/secret-edit-toolbar.js @@ -12,7 +12,6 @@ * @secretDataIsAdvanced={{secretDataIsAdvanced}} * @showAdvancedMode={{showAdvancedMode}} * @modelForData={{this.modelForData}} - * @navToNearestAncestor={{this.navToNearestAncestor}} * @canUpdateSecretData={{canUpdateSecretData}} * @codemirrorString={{codemirrorString}} * @wrappedData={{wrappedData}} @@ -30,7 +29,6 @@ * @param {boolean} secretDataIsAdvanced - used to determine if show JSON toggle * @param {boolean} showAdvacnedMode - used for JSON toggle * @param {object} modelForData - a modified version of the model with secret data - * @param {string} navToNearestAncestor - route to nav to if press cancel * @param {boolean} canUpdateSecretData - permissions that show the create new version button or not. * @param {string} codemirrorString - used to copy the JSON * @param {object} wrappedData - when copy the data it's the token of the secret returned. diff --git a/ui/app/components/secret-edit.js b/ui/app/components/secret-edit.js index 5e968a71586c..9b413d74691e 100644 --- a/ui/app/components/secret-edit.js +++ b/ui/app/components/secret-edit.js @@ -1,84 +1,63 @@ +/* eslint ember/no-computed-properties-in-native-classes: 'warn' */ /** * @module SecretEdit * SecretEdit component manages the secret and model data, and displays either the create, update, empty state or show view of a KV secret. * * @example * ```js - * + * * ``` -/ - * @param {object} model - Model returned from route secret-v2 +/This component is initialized from the secret-edit-layout.hbs file + * @param {object} model - Model returned from secret-v2 which is generated in the secret-edit route + * @param {string} mode - Edit, create, etc. + * @param {string} baseKey - Provided for navigation. + * @param {object} key - Passed through, copy of the model. + * @param {string} initialKey - model's name. + * @param {function} onRefresh - action that refreshes the model + * @param {function} onToggleAdvancedEdit - changes the preferAdvancedEdit to true or false + * @param {boolean} preferAdvancedEdit - property set from the controller of show/edit/create route passed in through secret-edit-layout */ import { inject as service } from '@ember/service'; -import Component from '@ember/component'; -import { computed } from '@ember/object'; -import { alias, or } from '@ember/object/computed'; -import FocusOnInsertMixin from 'vault/mixins/focus-on-insert'; -import WithNavToNearestAncestor from 'vault/mixins/with-nav-to-nearest-ancestor'; +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; import KVObject from 'vault/lib/kv-object'; import { maybeQueryRecord } from 'vault/macros/maybe-query-record'; +import { alias, or } from '@ember/object/computed'; -export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, { - wizard: service(), - store: service(), - - // a key model - key: null, - model: null, - - // a value to pre-fill the key input - this is populated by the corresponding - // 'initialKey' queryParam - initialKey: null, - - // set in the route's setupController hook - mode: null, - - secretData: null, - - // called with a bool indicating if there's been a change in the secretData and customMetadata - onDataChange() {}, - onRefresh() {}, - onToggleAdvancedEdit() {}, - - // did user request advanced mode - preferAdvancedEdit: false, - - // use a named action here so we don't have to pass one in - // this will bubble to the route - toggleAdvancedEdit: 'toggleAdvancedEdit', - - codemirrorString: null, +export default class SecretEdit extends Component { + @service wizard; + @service store; - isV2: false, + @tracked secretData = null; + @tracked isV2 = false; + @tracked codemirrorString = null; - init() { - this._super(...arguments); - let secrets = this.model.secretData; - if (!secrets && this.model.selectedVersion) { - this.set('isV2', true); - secrets = this.model.belongsTo('selectedVersion').value().secretData; + constructor() { + super(...arguments); + let secrets = this.args.model.secretData; + if (!secrets && this.args.model.selectedVersion) { + this.isV2 = true; + secrets = this.args.model.belongsTo('selectedVersion').value().secretData; } const data = KVObject.create({ content: [] }).fromJSON(secrets); - this.set('secretData', data); - this.set('codemirrorString', data.toJSONString()); - if (data.isAdvanced()) { - this.set('preferAdvancedEdit', true); - } - if (this.wizard.featureState === 'details' && this.mode === 'create') { - let engine = this.model.backend.includes('kv') ? 'kv' : this.model.backend; + this.secretData = data; + this.codemirrorString = data.toJSONString(); + if (this.wizard.featureState === 'details' && this.args.mode === 'create') { + let engine = this.args.model.backend.includes('kv') ? 'kv' : this.args.model.backend; this.wizard.transitionFeatureMachine('details', 'CONTINUE', engine); } - }, + } - checkSecretCapabilities: maybeQueryRecord( + @maybeQueryRecord( 'capabilities', (context) => { - if (!context.model || context.mode === 'create') { + if (!context.args.model || context.args.mode === 'create') { return; } - let backend = context.isV2 ? context.get('model.engine.id') : context.model.backend; - let id = context.model.id; + let backend = context.isV2 ? context.args.model.engine.id : context.args.model.backend; + let id = context.args.model.id; let path = context.isV2 ? `${backend}/data/${id}` : `${backend}/${id}`; return { id: path, @@ -88,17 +67,18 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, { 'model', 'model.id', 'mode' - ), - canUpdateSecretData: alias('checkSecretCapabilities.canUpdate'), - canReadSecretData: alias('checkSecretCapabilities.canRead'), + ) + checkSecretCapabilities; + @alias('checkSecretCapabilities.canUpdate') canUpdateSecretData; + @alias('checkSecretCapabilities.canRead') canReadSecretData; - checkMetadataCapabilities: maybeQueryRecord( + @maybeQueryRecord( 'capabilities', (context) => { - if (!context.model || !context.isV2) { + if (!context.args.model || !context.isV2) { return; } - let backend = context.model.backend; + let backend = context.args.model.backend; let path = `${backend}/metadata/`; return { id: path, @@ -108,60 +88,59 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, { 'model', 'model.id', 'mode' - ), - canDeleteSecretMetadata: alias('checkMetadataCapabilities.canDelete'), - canUpdateSecretMetadata: alias('checkMetadataCapabilities.canUpdate'), - canReadSecretMetadata: alias('checkMetadataCapabilities.canRead'), + ) + checkMetadataCapabilities; + @alias('checkMetadataCapabilities.canDelete') canDeleteSecretMetadata; + @alias('checkMetadataCapabilities.canUpdate') canUpdateSecretMetadata; + @alias('checkMetadataCapabilities.canRead') canReadSecretMetadata; - requestInFlight: or('model.isLoading', 'model.isReloading', 'model.isSaving'), + @or('model.isLoading', 'model.isReloading', 'model.isSaving') requestInFlight; + @or('requestInFlight', 'model.isFolder', 'model.flagsIsInvalid') buttonDisabled; - buttonDisabled: or('requestInFlight', 'model.isFolder', 'model.flagsIsInvalid'), - - modelForData: computed('isV2', 'model', function () { - let { model } = this; + get modelForData() { + let { model } = this.args; if (!model) return null; return this.isV2 ? model.belongsTo('selectedVersion').value() : model; - }), + } - basicModeDisabled: computed('secretDataIsAdvanced', 'showAdvancedMode', function () { + get basicModeDisabled() { return this.secretDataIsAdvanced || this.showAdvancedMode === false; - }), + } - secretDataAsJSON: computed('secretData', 'secretData.[]', function () { + get secretDataAsJSON() { return this.secretData.toJSON(); - }), + } - secretDataIsAdvanced: computed('secretData', 'secretData.[]', function () { + get secretDataIsAdvanced() { return this.secretData.isAdvanced(); - }), + } - showAdvancedMode: or('secretDataIsAdvanced', 'preferAdvancedEdit'), + get showAdvancedMode() { + return this.secretDataIsAdvanced || this.args.preferAdvancedEdit; + } - isWriteWithoutRead: computed( - 'model.failedServerRead', - 'modelForData.failedServerRead', - 'isV2', - function () { - if (!this.model) return; - // if the version couldn't be read from the server - if (this.isV2 && this.modelForData.failedServerRead) { - return true; - } - // if the model couldn't be read from the server - if (!this.isV2 && this.model.failedServerRead) { - return true; - } + get isWriteWithoutRead() { + if (!this.args.model) { return false; } - ), - - actions: { - refresh() { - this.onRefresh(); - }, - - toggleAdvanced(bool) { - this.onToggleAdvancedEdit(bool); - }, - }, -}); + // if the version couldn't be read from the server + if (this.isV2 && this.modelForData.failedServerRead) { + return true; + } + // if the model couldn't be read from the server + if (!this.isV2 && this.args.model.failedServerRead) { + return true; + } + return false; + } + + @action + refresh() { + this.args.onRefresh(); + } + + @action + toggleAdvanced(bool) { + this.args.onToggleAdvancedEdit(bool); + } +} diff --git a/ui/app/models/auth-method.js b/ui/app/models/auth-method.js index 3ba5eb335194..a54cc80d4f02 100644 --- a/ui/app/models/auth-method.js +++ b/ui/app/models/auth-method.js @@ -116,9 +116,9 @@ export default attachCapabilities(ModelExport, { deletePath: apiPath`sys/auth/${'id'}`, configPath: function (context) { if (context.type === 'aws') { - return apiPath`auth/${'id'}/config/client`; + return apiPath`auth/${'id'}/config/client`.call(this, context); } else { - return apiPath`auth/${'id'}/config`; + return apiPath`auth/${'id'}/config`.call(this, context); } }, }); diff --git a/ui/app/models/clients/activity.js b/ui/app/models/clients/activity.js index f4edac595bbc..2462336989e2 100644 --- a/ui/app/models/clients/activity.js +++ b/ui/app/models/clients/activity.js @@ -1,10 +1,12 @@ import Model, { attr } from '@ember-data/model'; export default class Activity extends Model { - @attr('string') responseTimestamp; + @attr('array') byMonthTotalClients; + @attr('array') byMonthNewClients; @attr('array') byNamespace; + @attr('object') total; @attr('array') formattedEndTime; @attr('array') formattedStartTime; @attr('string') startTime; @attr('string') endTime; - @attr('object') total; + @attr('string') responseTimestamp; } diff --git a/ui/app/serializers/clients/activity.js b/ui/app/serializers/clients/activity.js index 1b1926ed138c..c831f8b9f86f 100644 --- a/ui/app/serializers/clients/activity.js +++ b/ui/app/serializers/clients/activity.js @@ -11,14 +11,17 @@ export default class ActivitySerializer extends ApplicationSerializer { Object.keys(ns['counts']).forEach((key) => (flattenedNs[key] = ns['counts'][key])); flattenedNs = this.homogenizeClientNaming(flattenedNs); - // TODO CMB check how this works with actual API endpoint // if no mounts, mounts will be an empty array flattenedNs.mounts = ns.mounts ? ns.mounts.map((mount) => { let flattenedMount = {}; - flattenedMount.label = mount['mount_path']; + let label = mount['mount_path']; Object.keys(mount['counts']).forEach((key) => (flattenedMount[key] = mount['counts'][key])); - return flattenedMount; + flattenedMount = this.homogenizeClientNaming(flattenedMount); + return { + label, + ...flattenedMount, + }; }) : []; @@ -29,22 +32,59 @@ export default class ActivitySerializer extends ApplicationSerializer { }); } - // For 1.10 release naming changed from 'distinct_entities' to 'entity_clients' and + // for vault usage - vertical bar chart + flattenByMonths(payload, isNewClients = false) { + if (isNewClients) { + return payload.map((m) => { + return { + month: m.timestamp, + entity_clients: m.new_clients.counts.entity_clients, + non_entity_clients: m.new_clients.counts.non_entity_clients, + total: m.new_clients.counts.clients, + namespaces: this.flattenDataset(m.new_clients.namespaces), + }; + }); + } else { + return payload.map((m) => { + return { + month: m.timestamp, + entity_clients: m.counts.entity_clients, + non_entity_clients: m.counts.non_entity_clients, + total: m.counts.clients, + namespaces: this.flattenDataset(m.namespaces), + new_clients: { + entity_clients: m.new_clients.counts.entity_clients, + non_entity_clients: m.new_clients.counts.non_entity_clients, + total: m.new_clients.counts.clients, + namespaces: this.flattenDataset(m.new_clients.namespaces), + }, + }; + }); + } + } + + // In 1.10 'distinct_entities' changed to 'entity_clients' and // 'non_entity_tokens' to 'non_entity_clients' - // accounting for deprecated API keys here and updating to latest nomenclature homogenizeClientNaming(object) { - // TODO CMB check with API payload, latest draft includes both new and old key names - // TODO CMB Delete old key names IF correct ones exist? - if (Object.keys(object).includes('distinct_entities', 'non_entity_tokens')) { - let entity_clients = object.distinct_entities; - let non_entity_clients = object.non_entity_tokens; - let { clients } = object; + // if new key names exist, only return those key/value pairs + if (Object.keys(object).includes('entity_clients')) { + let { clients, entity_clients, non_entity_clients } = object; return { clients, entity_clients, non_entity_clients, }; } + // if object only has outdated key names, update naming + if (Object.keys(object).includes('distinct_entities')) { + let { clients, distinct_entities, non_entity_tokens } = object; + return { + clients, + entity_clients: distinct_entities, + non_entity_clients: non_entity_tokens, + }; + } + // TODO CMB: test what to return if neither key exists return object; } @@ -64,11 +104,14 @@ export default class ActivitySerializer extends ApplicationSerializer { ...payload, response_timestamp, by_namespace: this.flattenDataset(payload.data.by_namespace), + by_month_total_clients: this.flattenByMonths(payload.data.months), + by_month_new_clients: this.flattenByMonths(payload.data.months, { isNewClients: true }), total: this.homogenizeClientNaming(payload.data.total), formatted_end_time: this.parseRFC3339(payload.data.end_time), formatted_start_time: this.parseRFC3339(payload.data.start_time), }; delete payload.data.by_namespace; + delete payload.data.months; delete payload.data.total; return super.normalizeResponse(store, primaryModelClass, transformedPayload, id, requestType); } diff --git a/ui/app/templates/components/secret-edit-toolbar.hbs b/ui/app/templates/components/secret-edit-toolbar.hbs index 8e4bf21c99af..80eb99556d3c 100644 --- a/ui/app/templates/components/secret-edit-toolbar.hbs +++ b/ui/app/templates/components/secret-edit-toolbar.hbs @@ -19,7 +19,6 @@

- {{#if (eq this.mode "create")}} + {{#if (eq @mode "create")}} Create secret - {{else if (and this.isV2 (eq this.mode "edit"))}} + {{else if (and this.isV2 (eq @mode "edit"))}} Create new version - {{else if (eq this.mode "edit")}} + {{else if (eq @mode "edit")}} Edit secret {{else}} - {{this.key.id}} + {{@key.id}} {{/if}}

{{! tabs for show only }} -{{#if (eq this.mode "show")}} +{{#if (eq @mode "show")}}