├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ └── test-coverage.yaml ├── .gitignore ├── .lintr ├── DESCRIPTION ├── LICENSE ├── Makefile ├── NAMESPACE ├── NEWS.md ├── R ├── aaa.R ├── cache.R ├── format.R ├── http.R ├── server_manager.R ├── util.R ├── util_assert.R ├── vault_api_client.R ├── vault_client.R ├── vault_client_audit.R ├── vault_client_auth.R ├── vault_client_auth_approle.R ├── vault_client_auth_github.R ├── vault_client_auth_ldap.R ├── vault_client_auth_userpass.R ├── vault_client_cubbyhole.R ├── vault_client_kv1.R ├── vault_client_kv2.R ├── vault_client_operator.R ├── vault_client_policy.R ├── vault_client_secrets.R ├── vault_client_token.R ├── vault_client_tools.R ├── vault_client_transit.R ├── vault_resolve_secrets.R ├── vault_server_instance.R ├── vaultr.R └── zzz.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── development.md ├── inst ├── WORDLIST └── server │ ├── README.md │ ├── client-cert.pem │ ├── client-key.pem │ ├── client.cnf │ ├── generate.sh │ ├── server-cert.pem │ ├── server-key.pem │ ├── server.cnf │ └── vault-tls.hcl ├── man ├── vault_api_client.Rd ├── vault_client.Rd ├── vault_client_audit.Rd ├── vault_client_auth.Rd ├── vault_client_auth_approle.Rd ├── vault_client_auth_github.Rd ├── vault_client_auth_ldap.Rd ├── vault_client_auth_userpass.Rd ├── vault_client_cubbyhole.Rd ├── vault_client_kv1.Rd ├── vault_client_kv2.Rd ├── vault_client_object.Rd ├── vault_client_operator.Rd ├── vault_client_policy.Rd ├── vault_client_secrets.Rd ├── vault_client_token.Rd ├── vault_client_tools.Rd ├── vault_client_transit.Rd ├── vault_resolve_secrets.Rd ├── vault_test_server.Rd └── vaultr-package.Rd ├── tests ├── testthat.R └── testthat │ ├── helper-ldap.R │ ├── helper-vault.R │ ├── test-cache.R │ ├── test-object.R │ ├── test-server-manager.R │ ├── test-util-assert.R │ ├── test-util.R │ ├── test-vault-api.R │ ├── test-vault-audit.R │ ├── test-vault-auth-approle.R │ ├── test-vault-auth-github.R │ ├── test-vault-auth-ldap.R │ ├── test-vault-auth-userpass.R │ ├── test-vault-auth.R │ ├── test-vault-basic.R │ ├── test-vault-cubbyhole.R │ ├── test-vault-kv1.R │ ├── test-vault-kv2.R │ ├── test-vault-operator.R │ ├── test-vault-policy.R │ ├── test-vault-resolve-secrets.R │ ├── test-vault-secrets.R │ ├── test-vault-token.R │ ├── test-vault-transit.R │ ├── test-vault.R │ └── test-vaultr-tools.R ├── vignettes ├── .gitignore ├── packages.Rmd └── vaultr.Rmd └── vignettes_src ├── packages.Rmd └── vaultr.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^Meta$ 2 | ^doc$ 3 | ^.*\.Rproj$ 4 | ^\.Rproj\.user$ 5 | ^Makefile$ 6 | ^\.travis\.yml$ 7 | ^\.vault$ 8 | ^README.Rmd$ 9 | ^vignettes_src$ 10 | ^scripts$ 11 | ^TODO\.md$ 12 | ^man-roxygen$ 13 | ^appveyor\.yml$ 14 | ^\.github$ 15 | ^\.lintr$ 16 | ^development\.md$ 17 | ^_pkgdown\.yml$ 18 | ^docs$ 19 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: R-CMD-check 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ${{ matrix.config.os }} 14 | 15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - {os: macos-latest, r: 'release'} 22 | - {os: windows-latest, r: 'release'} 23 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 24 | - {os: ubuntu-latest, r: 'release'} 25 | - {os: ubuntu-latest, r: 'oldrel-1'} 26 | 27 | env: 28 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 29 | R_KEEP_PKG_SOURCE: yes 30 | VAULTR_TEST_SERVER_BIN_PATH: auto 31 | VAULTR_TEST_SERVER_PORT: 18200 32 | 33 | steps: 34 | - uses: actions/checkout@v3 35 | 36 | - uses: r-lib/actions/setup-pandoc@v2 37 | 38 | - uses: r-lib/actions/setup-r@v2 39 | with: 40 | r-version: ${{ matrix.config.r }} 41 | http-user-agent: ${{ matrix.config.http-user-agent }} 42 | use-public-rspm: true 43 | 44 | - uses: eLco/setup-vault@v1 45 | 46 | - uses: r-lib/actions/setup-r-dependencies@v2 47 | with: 48 | extra-packages: any::rcmdcheck 49 | needs: check 50 | 51 | - name: Move real vignettes 52 | run: | 53 | cp vignettes_src/* vignettes 54 | 55 | - uses: r-lib/actions/check-r-package@v2 56 | with: 57 | upload-snapshots: true 58 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | release: 9 | types: [published] 10 | workflow_dispatch: 11 | 12 | name: pkgdown 13 | 14 | jobs: 15 | pkgdown: 16 | runs-on: ubuntu-latest 17 | # Only restrict concurrency for non-PR jobs 18 | concurrency: 19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 20 | env: 21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 22 | VAULTR_TEST_SERVER_BIN_PATH: auto 23 | VAULTR_TEST_SERVER_PORT: 18200 24 | permissions: 25 | contents: write 26 | steps: 27 | - uses: actions/checkout@v3 28 | 29 | - uses: r-lib/actions/setup-pandoc@v2 30 | 31 | - uses: r-lib/actions/setup-r@v2 32 | with: 33 | use-public-rspm: true 34 | 35 | - uses: eLco/setup-vault@v1 36 | 37 | - uses: r-lib/actions/setup-r-dependencies@v2 38 | with: 39 | extra-packages: any::pkgdown, local::. 40 | needs: website 41 | 42 | - name: Move real vignettes 43 | run: | 44 | cp vignettes_src/* vignettes 45 | 46 | - name: Build site 47 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 48 | shell: Rscript {0} 49 | 50 | - name: Deploy to GitHub pages 🚀 51 | if: github.event_name != 'pull_request' 52 | uses: JamesIves/github-pages-deploy-action@v4.4.1 53 | with: 54 | clean: false 55 | branch: gh-pages 56 | folder: docs 57 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | branches: [main, master] 8 | 9 | name: test-coverage 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: ubuntu-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | VAULTR_TEST_SERVER_BIN_PATH: auto 17 | VAULTR_TEST_SERVER_PORT: 18200 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - uses: r-lib/actions/setup-r@v2 23 | with: 24 | use-public-rspm: true 25 | 26 | - uses: eLco/setup-vault@v1 27 | 28 | - uses: r-lib/actions/setup-r-dependencies@v2 29 | with: 30 | extra-packages: any::covr 31 | needs: coverage 32 | 33 | - name: Test coverage 34 | # We should re-enable an environment variable here 35 | # VAULTR_TEST_GITHUB_PAT to run the last test, but that would 36 | # be better as an interaction test really. 37 | run: | 38 | covr::codecov( 39 | quiet = FALSE, 40 | clean = FALSE, 41 | install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package") 42 | ) 43 | shell: Rscript {0} 44 | 45 | - name: Show testthat output 46 | if: always() 47 | run: | 48 | ## -------------------------------------------------------------------- 49 | find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true 50 | shell: bash 51 | 52 | - name: Upload test results 53 | if: failure() 54 | uses: actions/upload-artifact@v3 55 | with: 56 | name: coverage-test-failures 57 | path: ${{ runner.temp }}/package 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Meta 2 | doc 3 | inst/doc 4 | .Rproj.user 5 | .Rhistory 6 | .RData 7 | .vault 8 | .vault-env 9 | docs 10 | /doc/ 11 | /Meta/ 12 | -------------------------------------------------------------------------------- /.lintr: -------------------------------------------------------------------------------- 1 | linters: linters_with_defaults( 2 | indentation_linter = NULL, 3 | object_length_linter = NULL, 4 | object_usage_linter = NULL, 5 | cyclocomp_linter = NULL 6 | ) 7 | exclusions: list("tests/testthat.R") 8 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: vaultr 2 | Authors@R: c(person("Rich", "FitzJohn", role = c("aut", "cre"), 3 | email = "rich.fitzjohn@gmail.com"), 4 | person("Robert", "Ashton", role = "aut"), 5 | person("Wes", "Hinsley", role = "aut"), 6 | person("Imperial College of Science, Technology and Medicine", 7 | role = "cph")) 8 | Title: Vault Client for Secrets and Sensitive Data 9 | Version: 1.2.0 10 | Description: Provides an interface to a 'HashiCorp' vault server over 11 | its http API (typically these are self-hosted; see 12 | ). This allows for secure storage and 13 | retrieval of secrets over a network, such as tokens, passwords and 14 | certificates. Authentication with vault is supported through 15 | several backends including user name/password and authentication via 16 | 'GitHub'. 17 | License: MIT + file LICENSE 18 | URL: https://github.com/vimc/vaultr, https://www.vaccineimpact.org/vaultr/ 19 | BugReports: https://github.com/vimc/vaultr/issues 20 | SystemRequirements: vault 21 | Imports: 22 | R6, 23 | getPass, 24 | httr, 25 | jsonlite 26 | Suggests: 27 | knitr, 28 | mockery, 29 | processx, 30 | rmarkdown, 31 | testthat, 32 | withr 33 | RoxygenNote: 7.2.3 34 | Encoding: UTF-8 35 | VignetteBuilder: knitr 36 | Language: en-GB 37 | Roxygen: list(markdown = TRUE) 38 | Config/testthat/edition: 3 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2018 2 | COPYRIGHT HOLDER: Imperial College of Science, Technology and Medicine 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RSCRIPT = Rscript 2 | 3 | test: 4 | ${RSCRIPT} -e 'library(methods); devtools::test()' 5 | 6 | roxygen: 7 | @mkdir -p man 8 | ${RSCRIPT} -e "library(methods); devtools::document()" 9 | 10 | install: 11 | R CMD INSTALL . 12 | 13 | build: 14 | R CMD build . 15 | 16 | check: 17 | _R_CHECK_CRAN_INCOMING_=FALSE make check_all 18 | 19 | check_all: 20 | ${RSCRIPT} -e "rcmdcheck::rcmdcheck(args = c('--as-cran', '--no-manual'))" 21 | 22 | vignettes/vaultr.Rmd: vignettes_src/vaultr.Rmd 23 | cd vignettes_src && Rscript -e 'knitr::knit("vaultr.Rmd")' 24 | mv vignettes_src/vaultr.md $@ 25 | sed -i.bak 's/[[:space:]]*$$//' $@ 26 | rm -f $@.bak 27 | 28 | vignettes/packages.Rmd: vignettes_src/packages.Rmd 29 | cd vignettes_src && Rscript -e 'knitr::knit("packages.Rmd")' 30 | mv vignettes_src/packages.md $@ 31 | sed -i.bak 's/[[:space:]]*$$//' $@ 32 | rm -f $@.bak 33 | 34 | vignettes_install: vignettes/vaultr.Rmd vignettes/packages.Rmd 35 | Rscript -e 'library(methods); devtools::build_vignettes()' 36 | 37 | vignettes: 38 | make vignettes_install 39 | 40 | README.md: README.Rmd 41 | Rscript -e "options(warnPartialMatchArgs=FALSE); knitr::knit('$<')" 42 | sed -i.bak 's/[[:space:]]*$$//' README.md 43 | rm -f $@.bak 44 | 45 | pkgdown: 46 | Rscript -e 'pkgdown::build_site()' 47 | 48 | manual: 49 | R CMD Rd2pdf --no-clean . 50 | 51 | clean: 52 | rm -rf .Rd2pdf* 53 | 54 | 55 | .PHONY: test roxygen install build check check_all vignettes 56 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(vault_client) 4 | export(vault_resolve_secrets) 5 | export(vault_test_server) 6 | importFrom(R6,R6Class) 7 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # 1.1.5 2 | 3 | * Support for authentication with LDAP (#42) 4 | 5 | # 1.1.2 6 | 7 | * Support for vault [namespaces](https://developer.hashicorp.com/vault/tutorials/enterprise/namespaces) on enterprise versions of vault 8 | 9 | # 1.0.4 10 | 11 | * `vaultr::vault_resolve_secrets` (and `$read` in the kv1 secrets engine) provide more information about what was being read at the point of failure (VIMC-3437) 12 | 13 | # 1.0.3 14 | 15 | * New convenience argument to `vaultr::vault_resolve_secrets` to pass in all connection arguments at once, designed to make this easier to use from scripts (VIMC-3397). 16 | 17 | # 1.0.2 18 | 19 | * First public release 20 | 21 | # 0.2.6 22 | 23 | * All objects gain a `help()` method, with the aim of making the main help easier to find. 24 | 25 | # 0.2.5 26 | 27 | * Support for AppRole authentication 28 | * Move secrets support under secrets top level element (so `vault$secrets$kv1` rather than `vault$kv1`); VIMC-2891 29 | 30 | # 0.2.4 31 | 32 | * Support for the [`cubbyhole`](https://www.vaultproject.io/docs/secrets/cubbyhole/index.html) secret engine and response wrapping 33 | * Faster testing on windows due to improved timeouts while looking for free ports 34 | 35 | # 0.2.3 36 | 37 | * Fix windows filename issue with test server 38 | 39 | # 0.2.2 40 | 41 | * Add vault_resolve_secrets method 42 | 43 | # 0.2.1 44 | 45 | * Documentation for core classes 46 | 47 | # 0.2.0 48 | 49 | * Complete rewrite based on use over the last year: 50 | - supporting many more vault methods 51 | - a better base for ongoing method support 52 | - rationalised authentication and caching 53 | - easier to use server for tests 54 | 55 | # 0.1.0 56 | 57 | * Initial internal release 58 | -------------------------------------------------------------------------------- /R/aaa.R: -------------------------------------------------------------------------------- 1 | ##' Base object used by vaultr for all objects 2 | ##' 3 | ##' @title Base object type 4 | ##' 5 | ##' @name vault_client_object 6 | ##' 7 | ##' @importFrom R6 R6Class 8 | ##' @examples 9 | ##' 10 | ##' server <- vaultr::vault_test_server(if_disabled = message) 11 | ##' 12 | ##' if (!is.null(server)) { 13 | ##' client <- vaultr::vault_client(addr = server$addr) 14 | ##' client$operator$format() 15 | ##' client$operator$format(TRUE) 16 | ##' } 17 | vault_client_object <- R6::R6Class( 18 | "vault_client_object", 19 | cloneable = FALSE, 20 | 21 | private = list( 22 | name = NULL, 23 | help_name = NULL, 24 | description = NULL 25 | ), 26 | 27 | public = list( 28 | ##' @description Construct an object 29 | ##' 30 | ##' @param description Description for the object, will be printed 31 | initialize = function(description) { 32 | private$name <- sub("^(vault_|vault_client_)", "", class(self)[[1L]]) 33 | private$help_name <- class(self)[[1L]] 34 | private$description <- description 35 | }, 36 | 37 | ##' @description Format method, overriding the R6 default 38 | ##' 39 | ##' @param brief Logical, indicating if this is the full format or 40 | ##' a brief (one line) format. 41 | format = function(brief = FALSE) { 42 | vault_client_format(self, brief, private$name, private$description) 43 | }, 44 | 45 | ##' @description Display help for this object 46 | help = function() { 47 | utils::help(private$help_name, package = "vaultr") 48 | } 49 | )) 50 | 51 | 52 | add_const_member <- function(target, name, object) { 53 | target[[name]] <- object 54 | lockBinding(name, target) 55 | } 56 | -------------------------------------------------------------------------------- /R/cache.R: -------------------------------------------------------------------------------- 1 | token_cache <- R6::R6Class( 2 | "token_cache", 3 | 4 | public = list( 5 | tokens = setNames(list(), character()), 6 | 7 | client_addr = function(api_client) { 8 | api_client$addr 9 | }, 10 | 11 | set = function(api_client, token, use_cache = TRUE) { 12 | if (use_cache) { 13 | self$tokens[[self$client_addr(api_client)]] <- token 14 | } 15 | }, 16 | 17 | get = function(api_client, use_cache = TRUE, quiet = TRUE) { 18 | if (!use_cache) { 19 | return(NULL) 20 | } 21 | addr <- self$client_addr(api_client) 22 | token <- self$tokens[[addr]] 23 | if (is.null(token)) { 24 | return(NULL) 25 | } 26 | if (!api_client$verify_token(token, quiet)$success) { 27 | self$tokens[[addr]] <- NULL 28 | return(NULL) 29 | } 30 | token 31 | }, 32 | 33 | clear = function() { 34 | self$tokens <- setNames(list(), character()) 35 | }, 36 | 37 | delete = function(api_client) { 38 | self$tokens[[self$client_addr(api_client)]] <- NULL 39 | }, 40 | 41 | list = function() { 42 | names(self$tokens) 43 | } 44 | )) 45 | -------------------------------------------------------------------------------- /R/format.R: -------------------------------------------------------------------------------- 1 | vault_client_format <- function(object, brief, name, description) { 2 | if (brief) { 3 | return(description) 4 | } 5 | nms <- setdiff(ls(object), c("format", "clone", "initialize")) 6 | fns <- vlapply(nms, function(x) is.function(object[[x]])) 7 | is_obj <- vlapply(nms, function(x) inherits(object[[x]], "R6")) 8 | 9 | calls <- vcapply(nms[fns], function(x) capture_args(object[[x]], x), 10 | USE.NAMES = FALSE) 11 | if (any(is_obj)) { 12 | objs <- c( 13 | " Command groups:", 14 | vcapply(nms[is_obj], function(x) { 15 | sprintf(" %s: %s", x, object[[x]]$format(TRUE)) 16 | }, USE.NAMES = FALSE)) 17 | } else { 18 | objs <- NULL 19 | } 20 | 21 | c(sprintf("", name), 22 | objs, 23 | " Commands:", 24 | calls) 25 | } 26 | 27 | 28 | capture_args <- function(f, name, indent = 4, width = getOption("width"), 29 | exdent = 4L) { 30 | args <- formals(f) 31 | 32 | if (length(args) == 0L) { 33 | return(sprintf("%s%s()", strrep(" ", indent), name)) 34 | } 35 | 36 | args_default <- vcapply(args, deparse) 37 | args_str <- sprintf("%s = %s", names(args), args_default) 38 | args_str[!nzchar(args_default)] <- names(args)[!nzchar(args_default)] 39 | args_str[[1]] <- sprintf("%s(%s", name, args_str[[1]]) 40 | args_str[[length(args)]] <- paste0(args_str[[length(args)]], ")") 41 | 42 | w <- width - indent - 2L 43 | ret <- character() 44 | s <- "" 45 | 46 | for (i in args_str) { 47 | ns <- nchar(s) 48 | ni <- nchar(i) 49 | if (ns == 0) { 50 | s <- paste0(strrep(" ", indent + if (length(ret) > 0L) exdent else 0L), i) 51 | } else if (ns + ni + 2 < w) { 52 | s <- paste(s, i, sep = ", ") 53 | } else { 54 | ret <- c(ret, paste0(s, ",")) 55 | s <- paste0(strrep(" ", indent + exdent), i) 56 | } 57 | } 58 | 59 | ret <- c(ret, s) 60 | 61 | paste0(trimws(ret, "right"), collapse = "\n") 62 | } 63 | -------------------------------------------------------------------------------- /R/http.R: -------------------------------------------------------------------------------- 1 | vault_request <- function(verb, url, verify, token, namespace, path, ..., 2 | body = NULL, wrap_ttl = NULL, 3 | to_json = TRUE, 4 | allow_missing_token = FALSE) { 5 | if (!is.null(token)) { 6 | token <- httr::add_headers("X-Vault-Token" = token) 7 | } else if (!allow_missing_token) { 8 | stop("Have not authenticated against vault", call. = FALSE) 9 | } 10 | if (!is.null(namespace)) { 11 | namespace <- httr::add_headers("X-Vault-Namespace" = namespace) 12 | } 13 | if (!is.null(wrap_ttl)) { 14 | assert_is_duration(wrap_ttl) 15 | wrap_ttl <- httr::add_headers("X-Vault-Wrap-TTL" = wrap_ttl) 16 | } 17 | res <- verb(paste0(url, prepare_path(path)), verify, token, namespace, 18 | httr::accept_json(), wrap_ttl, 19 | body = body, encode = "json", ...) 20 | vault_client_response(res, to_json) 21 | } 22 | 23 | 24 | vault_client_response <- function(res, to_json = TRUE) { 25 | code <- httr::status_code(res) 26 | if (code >= 400 && code < 600) { 27 | if (response_is_json(res)) { 28 | dat <- response_to_json(res) 29 | ## https://developer.hashicorp.com/vault/api-docs#error-response 30 | errors <- list_to_character(dat$errors) 31 | text <- paste(errors, collapse = "\n") 32 | } else { 33 | errors <- NULL 34 | text <- trimws(httr::content(res, "text", encoding = "UTF-8")) 35 | } 36 | stop(vault_error(code, text, errors)) 37 | } 38 | 39 | if (code == 204) { 40 | res <- NULL 41 | } else if (to_json) { 42 | res <- response_to_json(res) 43 | } 44 | res 45 | } 46 | 47 | vault_error <- function(code, text, errors) { 48 | if (!nzchar(text)) { 49 | text <- httr::http_status(code)$message 50 | } 51 | type <- switch(as.character(code), 52 | "400" = "vault_invalid_request", 53 | "401" = "vault_unauthorized", 54 | "403" = "vault_forbidden", 55 | "404" = "vault_invalid_path", 56 | "429" = "vault_rate_limit_exceeded", 57 | "500" = "vault_internal_server_error", 58 | "501" = "vault_not_initialized", 59 | "503" = "vault_down", 60 | "vault_unknown_error") 61 | err <- list(code = code, 62 | errors = errors, 63 | message = text) 64 | class(err) <- c(type, "vault_error", "error", "condition") 65 | err 66 | } 67 | -------------------------------------------------------------------------------- /R/util.R: -------------------------------------------------------------------------------- 1 | response_to_json <- function(res) { 2 | jsonlite::fromJSON(httr::content(res, "text", encoding = "UTF-8"), 3 | simplifyVector = FALSE) 4 | } 5 | 6 | 7 | to_json <- function(x) { 8 | jsonlite::toJSON(x) 9 | } 10 | 11 | 12 | `%||%` <- function(a, b) { # nolint 13 | if (is.null(a)) b else a 14 | } 15 | 16 | `%&&%` <- function(a, b) { # nolint 17 | if (is.null(a)) NULL else b 18 | } 19 | 20 | list_to_character <- function(x) { 21 | vapply(x, identity, character(1)) 22 | } 23 | 24 | response_is_json <- function(x) { 25 | content_type <- httr::headers(x)[["Content-Type"]] 26 | dat <- httr::parse_media(content_type) 27 | dat$type == "application" && dat$subtype == "json" 28 | } 29 | 30 | is_absolute_path <- function(path) { 31 | substr(path, 1, 1) == "/" 32 | } 33 | 34 | vlapply <- function(X, FUN, ...) { # nolint 35 | vapply(X, FUN, logical(1), ...) 36 | } 37 | 38 | vcapply <- function(X, FUN, ...) { # nolint 39 | vapply(X, FUN, character(1), ...) 40 | } 41 | 42 | data_frame <- function(...) { 43 | data.frame(..., stringsAsFactors = FALSE) 44 | } 45 | 46 | strsub <- function(str, tr) { 47 | assert_character(tr) 48 | assert_named(tr) 49 | from <- names(tr) 50 | to <- unname(tr) 51 | for (i in seq_along(from)) { 52 | str <- gsub(from[[i]], to[[i]], str, fixed = TRUE) 53 | } 54 | str 55 | } 56 | 57 | Sys_getenv <- function(name, unset = NULL, mode = "character") { # nolint 58 | value <- Sys.getenv(name, NA_character_) 59 | if (is.na(value)) { 60 | value <- unset 61 | } else if (mode == "integer") { 62 | if (!grepl("^-?[0-9]+$", value)) { 63 | stop(sprintf("Invalid input for integer '%s'", value)) 64 | } 65 | value <- as.integer(value) 66 | } else if (mode != "character") { 67 | stop("Invalid value for 'mode'") 68 | } 69 | value 70 | } 71 | 72 | vault_arg <- function(x, name, mode = "character") { 73 | x %||% Sys_getenv(name, NULL, mode) 74 | } 75 | 76 | 77 | drop_null <- function(x) { 78 | x[!vlapply(x, is.null)] 79 | } 80 | 81 | 82 | read_password <- function(prompt) { 83 | getPass::getPass(prompt, TRUE) 84 | } 85 | 86 | 87 | prepare_path <- function(path) { 88 | assert_scalar_character(path) 89 | if (!is_absolute_path(path)) { 90 | path <- paste0("/", path) 91 | } 92 | path 93 | } 94 | 95 | 96 | rand_str <- function(n) { 97 | paste0(sample(letters, n, TRUE), collapse = "") 98 | } 99 | 100 | string_starts_with <- function(x, sub) { 101 | substr(x, 1, nchar(sub)) == sub 102 | } 103 | 104 | 105 | is_directory <- function(path) { 106 | file.info(path, extra_cols = FALSE)$isdir 107 | } 108 | 109 | 110 | free_port <- function(port, max_tries = 20) { 111 | for (i in seq_len(max_tries)) { 112 | if (check_port(port)) { 113 | return(port) 114 | } 115 | port <- port + 1L 116 | } 117 | stop(sprintf("Did not find a free port between %d..%d", 118 | port - max_tries, port - 1), 119 | call. = FALSE) 120 | } 121 | 122 | 123 | check_port <- function(port) { 124 | timeout <- if (vault_platform() == "windows") 1 else 0.1 125 | con <- tryCatch(suppressWarnings(socketConnection( 126 | "localhost", port = port, timeout = timeout, open = "r")), 127 | error = function(e) NULL) 128 | if (is.null(con)) { 129 | return(TRUE) 130 | } 131 | close(con) 132 | FALSE 133 | } 134 | 135 | 136 | pretty_sec <- function(n) { 137 | if (n < 60) { # less than a minute 138 | sprintf("%ds", n) 139 | } else if (n < 60 * 60) { # less than an hour 140 | sprintf("~%dm", round(n / 60)) 141 | } else if (n < 60 * 60 * 24) { # less than a day 142 | sprintf("~%dh", round(n / 60 / 60)) 143 | } else { # more than a day 144 | sprintf("~%dd", round(n / 60 / 60 / 24)) 145 | } 146 | } 147 | 148 | 149 | pretty_lease <- function(lease) { 150 | sprintf("ok, duration: %s s (%s)", lease, pretty_sec(lease)) 151 | } 152 | 153 | 154 | squote <- function(x) { 155 | sprintf("'%s'", x) 156 | } 157 | 158 | 159 | encode64 <- function(input) { 160 | jsonlite::base64_enc(input) 161 | } 162 | 163 | 164 | decode64 <- function(input) { 165 | jsonlite::base64_dec(input) 166 | } 167 | 168 | 169 | isFALSE <- function(x) { # nolint 170 | is.logical(x) && length(x) == 1L && !is.na(x) && !x 171 | } 172 | 173 | 174 | ## vault raw data inputs must be base64 175 | raw_data_input <- function(data, name = deparse(substitute(data))) { 176 | if (!is.raw(data)) { 177 | ## TODO: should this support base64 data? 178 | stop(sprintf("Expected raw data for '%s'", name), call. = FALSE) 179 | } 180 | encode64(data) 181 | } 182 | 183 | 184 | message_quietly <- function(..., quiet) { 185 | if (!quiet) { 186 | message(...) 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /R/util_assert.R: -------------------------------------------------------------------------------- 1 | assert_is <- function(x, what, name = deparse(substitute(x))) { 2 | if (!inherits(x, what)) { 3 | stop(sprintf("'%s' must be a %s", 4 | name, paste(what, collapse = " / "))) 5 | } 6 | invisible(x) 7 | } 8 | 9 | 10 | assert_length <- function(x, len, name = deparse(substitute(x))) { 11 | if (length(x) != len) { 12 | stop(sprintf("'%s' must have length %d", name, len)) 13 | } 14 | invisible(x) 15 | } 16 | 17 | 18 | assert_scalar <- function(x, name = deparse(substitute(x))) { 19 | if (length(x) != 1) { 20 | stop(sprintf("'%s' must be a scalar", name), call. = FALSE) 21 | } 22 | invisible(x) 23 | } 24 | 25 | 26 | assert_character <- function(x, name = deparse(substitute(x))) { 27 | if (!is.character(x)) { 28 | stop(sprintf("'%s' must be a character", name), call. = FALSE) 29 | } 30 | invisible(x) 31 | } 32 | 33 | assert_integer <- function(x, strict = FALSE, name = deparse(substitute(x)), 34 | what = "integer") { 35 | if (!(is.integer(x))) { 36 | usable_as_integer <- 37 | !strict && is.numeric(x) && (max(abs(round(x) - x)) < 1e-8) 38 | if (!usable_as_integer) { 39 | stop(sprintf("'%s' must be %s", name, what), call. = FALSE) 40 | } 41 | } 42 | invisible(x) 43 | } 44 | 45 | 46 | assert_logical <- function(x, name = deparse(substitute(x))) { 47 | if (!is.logical(x)) { 48 | stop(sprintf("'%s' must be a logical", name), call. = FALSE) 49 | } 50 | invisible(x) 51 | } 52 | 53 | 54 | assert_named <- function(x, name = deparse(substitute(x))) { 55 | if (is.null(names(x)) && length(x) > 0L) { 56 | stop(sprintf("'%s' must be named", name)) 57 | } 58 | invisible(x) 59 | } 60 | 61 | 62 | assert_scalar_character <- function(x, name = deparse(substitute(x))) { 63 | assert_scalar(x, name) 64 | assert_character(x, name) 65 | } 66 | 67 | 68 | assert_scalar_integer <- function(x, strict = FALSE, 69 | name = deparse(substitute(x))) { 70 | assert_scalar(x, name) 71 | assert_integer(x, strict, name) 72 | } 73 | 74 | 75 | assert_scalar_logical <- function(x, name = deparse(substitute(x))) { 76 | assert_scalar(x, name) 77 | assert_logical(x, name) 78 | } 79 | 80 | 81 | assert_scalar_logical_or_null <- function(x, name = deparse(substitute(x))) { 82 | if (!is.null(x)) { 83 | assert_scalar_logical(x, name) 84 | } 85 | invisible(x) 86 | } 87 | 88 | 89 | assert_scalar_character_or_null <- function(x, name = deparse(substitute(x))) { 90 | if (!is.null(x)) { 91 | assert_scalar_character(x, name) 92 | } 93 | invisible(x) 94 | } 95 | 96 | 97 | assert_absolute_path <- function(path) { 98 | if (!is_absolute_path(path)) { 99 | stop("Expected an absolute path") 100 | } 101 | invisible(path) 102 | } 103 | 104 | 105 | assert_file_exists <- function(path, name = deparse(substitute(path))) { 106 | assert_scalar_character(path, name) 107 | if (!file.exists(path)) { 108 | stop(sprintf("The path '%s' does not exist (for '%s')", path, name), 109 | call. = FALSE) 110 | } 111 | } 112 | 113 | 114 | assert_is_duration <- function(x, name = deparse(substitute(x))) { 115 | assert_scalar_character(x) 116 | if (!grepl("^[0-9]+h$", x)) { 117 | stop(sprintf("'%s' is not a valid time duration for '%s'", x, name), 118 | call. = FALSE) 119 | } 120 | invisible(x) 121 | } 122 | 123 | 124 | assert_vault_version <- function(required, api_client, api, description) { 125 | have <- api_client$server_version() 126 | if (required > have) { 127 | stop(vault_invalid_version(required, have, api, description)) 128 | } 129 | } 130 | 131 | 132 | vault_invalid_version <- function(required, server_version, api, description) { 133 | str <- sprintf("%s (%s) requires vault version >= %s but server is %s", 134 | description, api, required, server_version) 135 | err <- list(message = str) 136 | class(err) <- c("vault_invalid_version", 137 | "vault_error", "error", "condition") 138 | err 139 | } 140 | 141 | 142 | match_value <- function(arg, choices, name = deparse(substitute(arg))) { 143 | assert_scalar_character(arg) 144 | if (!(arg %in% choices)) { 145 | stop(sprintf("%s must be one of %s", 146 | name, paste(squote(choices), collapse = ", "))) 147 | } 148 | arg 149 | } 150 | -------------------------------------------------------------------------------- /R/vault_client_audit.R: -------------------------------------------------------------------------------- 1 | ##' Interact with vault's audit devices. For more details, see 2 | ##' https://developer.hashicorp.com/vault/docs/audit 3 | ##' 4 | ##' @title Vault Audit Devices 5 | ##' @name vault_client_audit 6 | ##' 7 | ##' @examples 8 | ##' server <- vaultr::vault_test_server(if_disabled = message) 9 | ##' if (!is.null(server)) { 10 | ##' client <- server$client() 11 | ##' # By default no audit engines are enabled with the testing server 12 | ##' client$audit$list() 13 | ##' 14 | ##' # Create a file-based audit device on a temporary file: 15 | ##' path <- tempfile() 16 | ##' client$audit$enable("file", options = list(file_path = path)) 17 | ##' client$audit$list() 18 | ##' 19 | ##' # Generate some activity on the server: 20 | ##' client$write("/secret/mysecret", list(key = "value")) 21 | ##' 22 | ##' # The audit logs contain details about the activity - see the 23 | ##' # vault documentation for details in interpreting this 24 | ##' readLines(path) 25 | ##' 26 | ##' # cleanup 27 | ##' server$kill() 28 | ##' unlink(path) 29 | ##' } 30 | vault_client_audit <- R6::R6Class( 31 | "vault_client_audit", 32 | inherit = vault_client_object, 33 | cloneable = FALSE, 34 | 35 | private = list(api_client = NULL), 36 | 37 | public = list( 38 | ##' @description Create an audit object 39 | ##' 40 | ##' @param api_client a [vaultr::vault_api_client] object 41 | initialize = function(api_client) { 42 | super$initialize("Interact with vault's audit devices") 43 | private$api_client <- api_client 44 | }, 45 | 46 | ##' @description List active audit devices. Returns a [data.frame] 47 | ##' of names, paths and descriptions of active audit devices. 48 | list = function() { 49 | dat <- private$api_client$GET("/sys/audit") 50 | cols <- c("path", "type", "description") 51 | ret <- lapply(cols, function(v) { 52 | vcapply(dat$data, "[[", v, USE.NAMES = FALSE) 53 | }) 54 | names(ret) <- cols 55 | as.data.frame(ret, stringsAsFactors = FALSE, check.names = FALSE) 56 | }, 57 | 58 | ##' @description This endpoint enables a new audit device at the 59 | ##' supplied path. 60 | ##' 61 | ##' @param type Name of the audit device to enable 62 | ##' 63 | ##' @param description Human readable description for this audit device 64 | ##' 65 | ##' @param options Options to configure the device with. These vary 66 | ##' by device. This must be a named list of strings. 67 | ##' 68 | ##' @param path Path to mount the audit device. By default, `type` is used 69 | ##' as the path. 70 | enable = function(type, description = NULL, options = NULL, path = NULL) { 71 | assert_scalar_character(type) 72 | if (is.null(description)) { 73 | description <- "" 74 | } else { 75 | assert_scalar_character(description) 76 | } 77 | if (is.null(path)) { 78 | path <- type 79 | } 80 | if (!is.null(options)) { 81 | assert_named(options) 82 | } 83 | 84 | body <- drop_null(list(type = type, 85 | description = description, 86 | options = options)) 87 | private$api_client$PUT(paste0("/sys/audit", prepare_path(path)), 88 | body = body) 89 | invisible(NULL) 90 | }, 91 | 92 | ##' @description Disable an audit device 93 | ##' 94 | ##' @param path Path of the audit device to remove 95 | disable = function(path) { 96 | private$api_client$DELETE(paste0("/sys/audit", prepare_path(path))) 97 | invisible(NULL) 98 | }, 99 | 100 | ##' @description The `hash` method is used to calculate the hash of the 101 | ##' data used by an audit device's hash function and salt. This can be 102 | ##' used to search audit logs for a hashed value when the original 103 | ##' value is known. 104 | ##' 105 | ##' @param input The input string to hash 106 | ##' 107 | ##' @param device The path of the audit device 108 | hash = function(input, device) { 109 | assert_scalar_character(input) 110 | body <- list(input = input) 111 | path <- paste0("/sys/audit-hash", prepare_path(device)) 112 | private$api_client$POST(path, body = body)$hash 113 | } 114 | )) 115 | -------------------------------------------------------------------------------- /R/vault_client_auth.R: -------------------------------------------------------------------------------- 1 | ##' Interact with vault's authentication backends. 2 | ##' 3 | ##' @title Vault Authentication Configuration 4 | ##' @name vault_client_auth 5 | ##' 6 | ##' @examples 7 | ##' server <- vaultr::vault_test_server(if_disabled = message) 8 | ##' if (!is.null(server)) { 9 | ##' client <- server$client() 10 | ##' 11 | ##' # List configured authentication backends 12 | ##' client$auth$list() 13 | ##' 14 | ##' # cleanup 15 | ##' server$kill() 16 | ##' } 17 | vault_client_auth <- R6::R6Class( 18 | "vault_client_auth", 19 | inherit = vault_client_object, 20 | cloneable = FALSE, 21 | 22 | private = list(api_client = NULL), 23 | 24 | public = list( 25 | ##' @field approle Interact with vault's AppRole authentication. See 26 | ##' [`vaultr::vault_client_auth_approle`] for more information. 27 | approle = NULL, 28 | 29 | ##' @field github Interact with vault's GitHub authentication. See 30 | ##' [`vaultr::vault_client_auth_github`] for more information. 31 | github = NULL, 32 | 33 | ##' @field token Interact with vault's token authentication. See 34 | ##' [`vaultr::vault_client_token`] for more information. 35 | token = NULL, 36 | 37 | ##' @field userpass Interact with vault's username/password based 38 | ##' authentication. See [`vaultr::vault_client_auth_userpass`] for 39 | ##' more information. 40 | userpass = NULL, 41 | 42 | ##' @field ldap Interact with vault's LDAP based 43 | ##' authentication. See [`vaultr::vault_client_auth_ldap`] for 44 | ##' more information. 45 | ldap = NULL, 46 | 47 | ##' @description Create a `vault_client_auth` object. Not typically 48 | ##' called by users. 49 | ##' 50 | ##' @param api_client A [vaultr::vault_api_client] object 51 | initialize = function(api_client) { 52 | super$initialize("administer vault's authentication methods") 53 | private$api_client <- api_client 54 | 55 | add_const_member( 56 | self, "token", 57 | vault_client_token$new(private$api_client)) 58 | add_const_member( 59 | self, "github", 60 | vault_client_auth_github$new(private$api_client, "github")) 61 | add_const_member( 62 | self, "userpass", 63 | vault_client_auth_userpass$new(private$api_client, "userpass")) 64 | add_const_member( 65 | self, "ldap", 66 | vault_client_auth_ldap$new(private$api_client, "ldap")) 67 | add_const_member( 68 | self, "approle", 69 | vault_client_auth_approle$new(private$api_client, "approle")) 70 | }, 71 | 72 | ##' @description Return a character vector of supported 73 | ##' authentication backends. If a backend `x` is present, then 74 | ##' you can access it with `$auth$x`. Note that vault calls 75 | ##' these authentication *methods* but we use *backends* here to 76 | ##' differentiate with R6 methods. Note that these are backends 77 | ##' supported by `vaultr` and not necessarily supported by the 78 | ##' server - the server may not have enabled some of these 79 | ##' backends, and may support other authentication backends not 80 | ##' directly supported by vaultr. See the `$list()` method to 81 | ##' query what the server supports. 82 | backends = function() { 83 | nms <- ls(self) 84 | i <- vlapply(nms, function(x) inherits(self[[x]], "R6")) 85 | sort(nms[i]) 86 | }, 87 | 88 | ##' @description List authentication backends supported by the 89 | ##' vault server, including information about where these 90 | ##' backends are mounted. 91 | ##' 92 | ##' @param detailed Logical, indicating if detailed information 93 | ##' should be returned 94 | list = function(detailed = FALSE) { 95 | if (detailed) { 96 | stop("Detailed auth information not supported") 97 | } 98 | dat <- private$api_client$GET("/sys/auth") 99 | 100 | cols <- c("type", "accessor", "description") 101 | ## TODO: later versions include config etc 102 | 103 | ret <- lapply(cols, function(v) { 104 | vapply(dat$data, "[[", "", v, USE.NAMES = FALSE) 105 | }) 106 | names(ret) <- cols 107 | 108 | ## TODO: empty strings here might be better as NA 109 | as.data.frame(c(list(path = names(dat$data)), ret), 110 | stringsAsFactors = FALSE, check.names = FALSE) 111 | }, 112 | 113 | ##' @description Enable an authentication backend in the vault 114 | ##' server. 115 | ##' 116 | ##' @param type The type of authentication backend (e.g., 117 | ##' `userpass`, `github`, `ldap`) 118 | ##' 119 | ##' @param description Human-friendly description of the backend; 120 | ##' will be returned by `$list()` 121 | ##' 122 | ##' @param local Specifies if the auth method is local only. Local 123 | ##' auth methods are not replicated nor (if a secondary) removed 124 | ##' by replication. 125 | ##' 126 | ##' @param path Specifies the path in which to enable the auth 127 | ##' method. Defaults to be the same as `type`. 128 | enable = function(type, description = NULL, local = FALSE, path = NULL) { 129 | ## TODO: not passing in config here 130 | assert_scalar_character(type) 131 | if (is.null(description)) { 132 | description <- "" 133 | } else { 134 | assert_scalar_character(description) 135 | } 136 | if (is.null(path)) { 137 | path <- type 138 | } 139 | 140 | data <- drop_null(list(type = type, 141 | description = description, 142 | local = assert_scalar_logical(local))) 143 | private$api_client$POST(paste0("/sys/auth/", path), body = data) 144 | invisible(NULL) 145 | }, 146 | 147 | ##' @description Disable an active authentication backend. 148 | ##' 149 | ##' @param path The path of the authentication backend to disable. 150 | disable = function(path) { 151 | private$api_client$DELETE(paste0("/sys/auth/", path)) 152 | invisible(NULL) 153 | } 154 | )) 155 | -------------------------------------------------------------------------------- /R/vault_client_auth_github.R: -------------------------------------------------------------------------------- 1 | ##' Interact with vault's GitHub authentication backend. For more 2 | ##' details, please see the vault documentation at 3 | ##' https://developer.hashicorp.com/vault/docs/auth/github 4 | ##' 5 | ##' @title Vault GitHub Authentication Configuration 6 | ##' @name vault_client_auth_github 7 | ##' 8 | ##' @examples 9 | ##' server <- vaultr::vault_test_server(if_disabled = message) 10 | ##' token <- Sys.getenv("VAULT_TEST_AUTH_GITHUB_TOKEN") 11 | ##' if (!is.null(server) && nzchar(token)) { 12 | ##' client <- server$client() 13 | ##' 14 | ##' client$auth$enable("github") 15 | ##' # To enable login for members of the organisation "example": 16 | ##' client$auth$github$configure(organization = "example") 17 | ##' # To map members of the "robots" team *within* that organisation 18 | ##' # to the "defaut" policy: 19 | ##' client$auth$github$write("development", "default") 20 | ##' 21 | ##' # Once configured like this, if we have a PAT for a member of 22 | ##' # the "development" team saved as an environment variable 23 | ##' # "VAULT_AUTH_GITHUB_TOKEN" then doing 24 | ##' # 25 | ##' # vaultr::vault_client(addr = ..., login = "github") 26 | ##' # 27 | ##' # will contact GitHub to verify the user token and vault will 28 | ##' # then issue a client token 29 | ##' 30 | ##' # cleanup 31 | ##' server$kill() 32 | ##' } 33 | vault_client_auth_github <- R6::R6Class( 34 | "vault_client_auth_github", 35 | inherit = vault_client_object, 36 | cloneable = FALSE, 37 | 38 | private = list( 39 | api_client = NULL, 40 | mount = NULL 41 | ), 42 | 43 | public = list( 44 | ##' @description Create a `vault_client_github` object. Not typically 45 | ##' called by users. 46 | ##' 47 | ##' @param api_client A [vaultr::vault_api_client] object 48 | ##' 49 | ##' @param mount Mount point for the backend 50 | initialize = function(api_client, mount) { 51 | super$initialize("Interact and configure vault's github support") 52 | assert_scalar_character(mount) 53 | private$mount <- sub("^/", "", mount) 54 | private$api_client <- api_client 55 | }, 56 | 57 | ##' @description Set up a `vault_client_auth_github` object at a 58 | ##' custom mount. For example, suppose you mounted the `github` 59 | ##' authentication backend at `/github-myorg` you might use `gh 60 | ##' <- vault$auth$github2$custom_mount("/github-myorg")` - this 61 | ##' pattern is repeated for other secret and authentication 62 | ##' backends. 63 | ##' 64 | ##' @param mount String, indicating the path that the engine is 65 | ##' mounted at. 66 | custom_mount = function(mount) { 67 | vault_client_auth_github$new(private$api_client, mount) 68 | }, 69 | 70 | ##' @description Configures the connection parameters for 71 | ##' GitHub-based authentication. 72 | ##' 73 | ##' @param organization The organization users must be part of 74 | ##' (note American spelling). 75 | ##' 76 | ##' @param base_url The API endpoint to 77 | ##' use. Useful if you are running GitHub Enterprise or an 78 | ##' API-compatible authentication server. 79 | ##' 80 | ##' @param ttl Duration after which authentication will be expired 81 | ##' 82 | ##' @param max_ttl Maximum duration after which authentication will 83 | ##' be expired 84 | configure = function(organization, base_url = NULL, ttl = NULL, 85 | max_ttl = NULL) { 86 | path <- sprintf("/auth/%s/config", private$mount) 87 | assert_scalar_character(organization) 88 | body <- list(organization = organization, 89 | base_url = base_url, 90 | ttl = ttl, 91 | max_ttl = max_ttl) 92 | private$api_client$POST(path, body = drop_null(body)) 93 | invisible(TRUE) 94 | }, 95 | 96 | ##' @description Reads the connection parameters for GitHub-based 97 | ##' authentication. 98 | configuration = function() { 99 | private$api_client$GET(sprintf("/auth/%s/config", private$mount))$data 100 | }, 101 | 102 | ##' @description Write a mapping between a GitHub team or user and 103 | ##' a set of vault policies. 104 | ##' 105 | ##' @param team_name String, with the GitHub team name 106 | ##' 107 | ##' @param policies A character vector of vault policies that this 108 | ##' user or team will have for vault access if they match this 109 | ##' team or user. 110 | ##' 111 | ##' @param user Scalar logical - if `TRUE`, then `team_name` is 112 | ##' interpreted as a *user* instead. 113 | write = function(team_name, policies, user = FALSE) { 114 | type <- if (assert_scalar_logical(user)) "users" else "teams" 115 | assert_scalar_character(team_name) 116 | path <- sprintf("/auth/%s/map/%s/%s", private$mount, type, team_name) 117 | 118 | assert_character(policies) 119 | body <- list(value = paste(policies, collapse = ",")) 120 | 121 | private$api_client$POST(path, body = body) 122 | invisible(NULL) 123 | }, 124 | 125 | ##' @description Write a mapping between a GitHub team or user and 126 | ##' a set of vault policies. 127 | ##' 128 | ##' @param team_name String, with the GitHub team name 129 | ##' 130 | ##' @param user Scalar logical - if `TRUE`, then `team_name` is 131 | ##' interpreted as a *user* instead. 132 | read = function(team_name, user = FALSE) { 133 | type <- if (assert_scalar_logical(user)) "users" else "teams" 134 | assert_scalar_character(team_name) 135 | path <- sprintf("/auth/%s/map/%s/%s", private$mount, type, team_name) 136 | private$api_client$GET(path)$data 137 | }, 138 | 139 | ##' @description Log into the vault using GitHub authentication. 140 | ##' Normally you would not call this directly but instead use 141 | ##' `$login` with `method = "github"` and proving the `token` 142 | ##' argument. This function returns a vault token but does not 143 | ##' set it as the client token. 144 | ##' 145 | ##' @param token A GitHub token to authenticate with. 146 | login = function(token = NULL) { 147 | path <- sprintf("/auth/%s/login", private$mount) 148 | body <- list(token = vault_auth_github_token(token)) 149 | res <- private$api_client$POST(path, body = body, 150 | allow_missing_token = TRUE) 151 | res$auth 152 | } 153 | )) 154 | 155 | 156 | vault_auth_github_token <- function(token) { 157 | if (is.null(token)) { 158 | token <- Sys_getenv("VAULT_AUTH_GITHUB_TOKEN", NULL) 159 | } 160 | if (is.null(token)) { 161 | stop( 162 | "GitHub token was not found: perhaps set 'VAULT_AUTH_GITHUB_TOKEN'") 163 | } 164 | assert_scalar_character(token) 165 | token 166 | } 167 | -------------------------------------------------------------------------------- /R/vault_client_cubbyhole.R: -------------------------------------------------------------------------------- 1 | ##' Interact with vault's cubbyhole key-value store. This is useful 2 | ##' for storing simple key-value data without versioning or metadata 3 | ##' (c.f. [vaultr::vault_client_kv2]) that is scoped to your 4 | ##' current token only and not accessible to anyone else. For more 5 | ##' details please see the vault documentation 6 | ##' https://developer.hashicorp.com/vault/docs/secrets/cubbyhole 7 | ##' 8 | ##' @title Cubbyhole secret store 9 | ##' @name vault_client_cubbyhole 10 | ##' 11 | ##' @examples 12 | ##' 13 | ##' server <- vaultr::vault_test_server(if_disabled = message) 14 | ##' if (!is.null(server)) { 15 | ##' client <- server$client() 16 | ##' 17 | ##' # Shorter path for easier reading: 18 | ##' cubbyhole <- client$secrets$cubbyhole 19 | ##' cubbyhole 20 | ##' 21 | ##' # Write a value 22 | ##' cubbyhole$write("cubbyhole/secret", list(key = "value")) 23 | ##' # List it 24 | ##' cubbyhole$list("cubbyhole") 25 | ##' # Read it 26 | ##' cubbyhole$read("cubbyhole/secret") 27 | ##' # Delete it 28 | ##' cubbyhole$delete("cubbyhole/secret") 29 | ##' 30 | ##' # cleanup 31 | ##' server$kill() 32 | ##' } 33 | vault_client_cubbyhole <- R6::R6Class( 34 | "vault_client_cubbyhole", 35 | inherit = vault_client_object, 36 | cloneable = FALSE, 37 | 38 | private = list( 39 | api_client = NULL, 40 | mount = "cubbyhole" 41 | ), 42 | 43 | public = list( 44 | ##' @description Create a `vault_client_cubbyhole` object. Not typically 45 | ##' called by users. 46 | ##' 47 | ##' @param api_client A [vaultr::vault_api_client] object 48 | initialize = function(api_client) { 49 | super$initialize("Interact with vault's cubbyhole secret backend") 50 | private$api_client <- api_client 51 | }, 52 | 53 | ##' @description Read a value from your cubbyhole 54 | ##' 55 | ##' @param path Path for the secret to read, such as 56 | ##' `/cubbyhole/mysecret` 57 | ##' 58 | ##' @param field Optional field to read from the secret. Each 59 | ##' secret is stored as a key/value set (represented in R as a 60 | ##' named list) and this is equivalent to using `[[field]]` 61 | ##' on the return value. The default, `NULL`, returns the 62 | ##' full set of values. 63 | ##' 64 | ##' @param metadata Logical, indicating if we should return 65 | ##' metadata for this secret (lease information etc) as an 66 | ##' attribute along with the values itself. Ignored if 67 | ##' `field` is specified. 68 | read = function(path, field = NULL, metadata = FALSE) { 69 | vault_kv_read(private$api_client, private$mount, path, field, metadata) 70 | }, 71 | 72 | ##' @description Write data into your cubbyhole. 73 | ##' 74 | ##' @param path Path for the secret to write, such as 75 | ##' `/cubbyhole/mysecret` 76 | ##' 77 | ##' @param data A named list of values to write into the vault at 78 | ##' this path. This *replaces* any existing values. 79 | write = function(path, data) { 80 | vault_kv_write(private$api_client, private$mount, path, data) 81 | }, 82 | 83 | ##' @description List data in the vault at a give path. This can 84 | ##' be used to list keys, etc (e.g., at `/cubbyhole`). 85 | ##' 86 | ##' @param path The path to list 87 | ##' 88 | ##' @param full_names Logical, indicating if full paths (relative 89 | ##' to the vault root) should be returned. 90 | ##' 91 | ##' @param value A character vector (of zero length if no keys are 92 | ##' found). Paths that are "directories" (i.e., that contain 93 | ##' keys and could themselves be listed) will be returned with 94 | ##' a trailing forward slash, e.g. `path/` 95 | list = function(path, full_names = FALSE) { 96 | vault_kv_list(private$api_client, private$mount, path, full_names) 97 | }, 98 | 99 | ##' @description Delete a value from the vault 100 | ##' 101 | ##' @param path The path to delete 102 | delete = function(path) { 103 | vault_kv_delete(private$api_client, private$mount, path) 104 | } 105 | )) 106 | -------------------------------------------------------------------------------- /R/vault_client_policy.R: -------------------------------------------------------------------------------- 1 | ##' Interact with vault's policies. To get started, you may want to 2 | ##' read up on policies as described in the vault manual, here: 3 | ##' https://developer.hashicorp.com/vault/docs/concepts/policies 4 | ##' 5 | ##' @title Vault Policy Configuration 6 | ##' @name vault_client_policy 7 | ##' @examples 8 | ##' server <- vaultr::vault_test_server(if_disabled = message) 9 | ##' if (!is.null(server)) { 10 | ##' client <- server$client() 11 | ##' 12 | ##' # The test server starts with only the policies "root" (do 13 | ##' # everything) and "default" (do nothing). 14 | ##' client$policy$list() 15 | ##' 16 | ##' # Here let's make a policy that allows reading secrets from the 17 | ##' # path /secret/develop/* but nothing else 18 | ##' rules <- 'path "secret/develop/*" {policy = "read"}' 19 | ##' client$policy$write("read-secret-develop", rules) 20 | ##' 21 | ##' # Our new rule is listed and can be read 22 | ##' client$policy$list() 23 | ##' client$policy$read("read-secret-develop") 24 | ##' 25 | ##' # For testing, let's create a secret under this path, and under 26 | ##' # a different path: 27 | ##' client$write("/secret/develop/password", list(value = "password")) 28 | ##' client$write("/secret/production/password", list(value = "k2e89be@rdC#")) 29 | ##' 30 | ##' # Create a token that can use this policy: 31 | ##' token <- client$auth$token$create(policies = "read-secret-develop") 32 | ##' 33 | ##' # Login to the vault using this token: 34 | ##' alice <- vaultr::vault_client(addr = server$addr, 35 | ##' login = "token", token = token) 36 | ##' 37 | ##' # We can read the paths that we have been granted access to: 38 | ##' alice$read("/secret/develop/password") 39 | ##' 40 | ##' # We can't read secrets that are outside our path: 41 | ##' try(alice$read("/secret/production/password")) 42 | ##' 43 | ##' # And we can't write: 44 | ##' try(alice$write("/secret/develop/password", list(value = "secret"))) 45 | ##' 46 | ##' # cleanup 47 | ##' server$kill() 48 | ##' } 49 | vault_client_policy <- R6::R6Class( 50 | "vault_client_policy", 51 | inherit = vault_client_object, 52 | cloneable = FALSE, 53 | 54 | private = list(api_client = NULL), 55 | 56 | public = list( 57 | ##' @description Create a `vault_client_policy` object. Not typically 58 | ##' called by users. 59 | ##' 60 | ##' @param api_client A [vaultr::vault_api_client] object 61 | initialize = function(api_client) { 62 | super$initialize("Interact with policies") 63 | private$api_client <- api_client 64 | }, 65 | 66 | ##' @description This endpoint deletes the policy with the given 67 | ##' name. This will immediately affect all users associated with 68 | ##' this policy. 69 | ##' 70 | ##' @param name Specifies the name of the policy to delete. 71 | delete = function(name) { 72 | assert_scalar_character(name) 73 | private$api_client$DELETE(paste0("/sys/policy/", name)) 74 | invisible(NULL) 75 | }, 76 | 77 | ##' @description Lists all configured policies. 78 | list = function() { 79 | dat <- private$api_client$GET("/sys/policy") 80 | list_to_character(dat$data$keys) 81 | }, 82 | 83 | ##' @description Retrieve the policy body for the named policy 84 | ##' 85 | ##' @param name Specifies the name of the policy to retrieve 86 | read = function(name) { 87 | assert_scalar_character(name) 88 | dat <- private$api_client$GET(paste0("/sys/policy/", name)) 89 | dat$data$rules 90 | }, 91 | 92 | ##' @description Create or update a policy. Once a policy is 93 | ##' updated, it takes effect immediately to all associated users. 94 | ##' 95 | ##' @param name Name of the policy to update 96 | ##' 97 | ##' @param rules Specifies the policy document. This is a string 98 | ##' in "HashiCorp configuration language". At present this must 99 | ##' be read in as a single string (not a character vector of 100 | ##' strings); future versions of vaultr may allow more flexible 101 | ##' specification such as `@filename` 102 | write = function(name, rules) { 103 | assert_scalar_character(name) 104 | assert_scalar_character(rules) 105 | body <- list(rules = rules) 106 | private$api_client$PUT(paste0("/sys/policy/", name), body = body) 107 | invisible(NULL) 108 | } 109 | )) 110 | -------------------------------------------------------------------------------- /R/vault_client_secrets.R: -------------------------------------------------------------------------------- 1 | ##' Interact with vault's secret backends. 2 | ##' 3 | ##' @title Vault Secret Configuration 4 | ##' @name vault_client_secrets 5 | ##' @examples 6 | ##' 7 | ##' server <- vaultr::vault_test_server(if_disabled = message) 8 | ##' if (!is.null(server)) { 9 | ##' client <- server$client() 10 | ##' 11 | ##' # To remove the default version 1 kv store and replace with a 12 | ##' # version 2 store: 13 | ##' client$secrets$disable("/secret") 14 | ##' client$secrets$enable("kv", "/secret", version = 2) 15 | ##' 16 | ##' # cleanup 17 | ##' server$kill() 18 | ##' } 19 | vault_client_secrets <- R6::R6Class( 20 | "vault_client_secrets", 21 | inherit = vault_client_object, 22 | cloneable = FALSE, 23 | 24 | private = list(api_client = NULL), 25 | 26 | public = list( 27 | ##' @field cubbyhole The cubbyhole backend: 28 | ##' [vaultr::vault_client_cubbyhole] 29 | cubbyhole = NULL, 30 | 31 | ##' @field kv1 The version 1 key-value backend: 32 | ##' [vaultr::vault_client_kv1] 33 | kv1 = NULL, 34 | 35 | ##' @field kv2 The version 2 key-value backend: 36 | ##' [vaultr::vault_client_kv2] 37 | kv2 = NULL, 38 | 39 | ##' @field transit The transit backend: 40 | ##' [vaultr::vault_client_transit] 41 | transit = NULL, 42 | 43 | ##' @description Create a `vault_client_secrets` object. Not typically 44 | ##' called by users. 45 | ##' 46 | ##' @param api_client A [vaultr::vault_api_client] object 47 | initialize = function(api_client) { 48 | super$initialize("Interact with secret engines") 49 | private$api_client <- api_client 50 | add_const_member(self, "cubbyhole", 51 | vault_client_cubbyhole$new(api_client)) 52 | add_const_member(self, "kv1", 53 | vault_client_kv1$new(api_client, NULL)) 54 | add_const_member(self, "kv2", 55 | vault_client_kv2$new(api_client, "secret")) 56 | add_const_member(self, "transit", 57 | vault_client_transit$new(api_client, "transit")) 58 | }, 59 | 60 | ##' @description Disable a previously-enabled secret engine 61 | ##' 62 | ##' @param path Path of the secret engine 63 | disable = function(path) { 64 | if (!is_absolute_path(path)) { 65 | path <- paste0("/", path) 66 | } 67 | private$api_client$DELETE(paste0("/sys/mounts", path)) 68 | invisible(NULL) 69 | }, 70 | 71 | ##' @description Enable a secret backend in the vault server 72 | ##' 73 | ##' @param type The type of secret backend (e.g., `transit`, `kv`). 74 | ##' 75 | ##' @param description Human-friendly description of the backend; 76 | ##' will be returned by `$list()` 77 | ##' 78 | ##' @param path Specifies the path in which to enable the auth 79 | ##' method. Defaults to be the same as `type`. 80 | ##' 81 | ##' @param version Used only for the `kv` backend, where an integer 82 | ##' is used to select between [vaultr::vault_client_kv1] and 83 | ##' [vaultr::vault_client_kv2] engines. 84 | enable = function(type, path = type, description = NULL, version = NULL) { 85 | ## TODO: there are many additional options here that are not 86 | ## currently supported and which would come through the "config" 87 | ## argument. 88 | assert_scalar_character(type) 89 | assert_scalar_character(path) 90 | assert_scalar_character_or_null(description) 91 | 92 | if (!is_absolute_path(path)) { 93 | path <- paste0("/", path) 94 | } 95 | data <- list(type = type, 96 | description = description) 97 | if (!is.null(version)) { 98 | data$options <- list(version = as.character(version)) 99 | } 100 | private$api_client$POST(paste0("/sys/mounts", path), body = data) 101 | invisible(path) 102 | }, 103 | 104 | ##' @description List enabled secret engines 105 | ##' 106 | ##' @param detailed Logical, indicating if detailed output is 107 | ##' wanted. 108 | list = function(detailed = FALSE) { 109 | if (detailed) { 110 | stop("Detailed secret information not supported") 111 | } 112 | dat <- private$api_client$GET("/sys/mounts") 113 | cols <- c("type", "accessor", "description") 114 | ret <- lapply(cols, function(v) { 115 | vcapply(dat$data, "[[", v, USE.NAMES = FALSE) 116 | }) 117 | names(ret) <- cols 118 | as.data.frame(c(list(path = names(dat$data)), ret), 119 | stringsAsFactors = FALSE, check.names = FALSE) 120 | }, 121 | 122 | ##' @description Move the path that a secret engine is mounted at 123 | ##' 124 | ##' @param from Original path 125 | ##' 126 | ##' @param to New path 127 | move = function(from, to) { 128 | assert_scalar_character(from) 129 | assert_scalar_character(to) 130 | body <- list(from = from, to = to) 131 | private$api_client$POST("/sys/remount", body = body) 132 | invisible(NULL) 133 | } 134 | )) 135 | -------------------------------------------------------------------------------- /R/vault_client_tools.R: -------------------------------------------------------------------------------- 1 | ##' Interact with vault's cryptographic tools. This provides support 2 | ##' for high-quality random numbers and cryptographic hashes. This 3 | ##' functionality is also available through the transit secret engine. 4 | ##' 5 | ##' @title Vault Tools 6 | ##' @name vault_client_tools 7 | ##' @examples 8 | ##' server <- vaultr::vault_test_server(if_disabled = message) 9 | ##' if (!is.null(server)) { 10 | ##' client <- server$client() 11 | ##' 12 | ##' # Random bytes in hex 13 | ##' client$tools$random() 14 | ##' # base64 15 | ##' client$tools$random(format = "base64") 16 | ##' # raw 17 | ##' client$tools$random(10, format = "raw") 18 | ##' 19 | ##' # Hash data: 20 | ##' data <- charToRaw("hello vault") 21 | ##' # will produce 55e702...92efd40c2a4 22 | ##' client$tools$hash(data) 23 | ##' 24 | ##' # sha2-512 hash: 25 | ##' client$tools$hash(data, "sha2-512") 26 | ##' 27 | ##' # cleanup 28 | ##' server$kill() 29 | ##' } 30 | vault_client_tools <- R6::R6Class( 31 | "vault_client_tools", 32 | inherit = vault_client_object, 33 | cloneable = FALSE, 34 | 35 | private = list( 36 | api_client = NULL, 37 | mount = NULL 38 | ), 39 | 40 | public = list( 41 | ##' @description Create a `vault_client_tools` object. Not typically 42 | ##' called by users. 43 | ##' 44 | ##' @param api_client A [vaultr::vault_api_client] object 45 | initialize = function(api_client) { 46 | private$api_client <- api_client 47 | super$initialize("General tools provided by vault") 48 | }, 49 | 50 | ##' @description Generates high-quality random bytes of the 51 | ##' specified length. This is totally independent of R's random 52 | ##' number stream and provides random numbers suitable for 53 | ##' cryptographic purposes. 54 | ##' 55 | ##' @param bytes Number of bytes to generate (as an integer) 56 | ##' 57 | ##' @param format The output format to produce; must be one of 58 | ##' `hex` (a single hex string such as `d1189e2f83b72ab6`), 59 | ##' `base64` (a single base64 encoded string such as 60 | ##' `8TDJekY0mYs=`) or `raw` (a raw vector of length `bytes`). 61 | random = function(bytes = 32, format = "hex") { 62 | body <- list(bytes = assert_scalar_integer(bytes), 63 | format = assert_scalar_character(format)) 64 | if (format == "raw") { 65 | body$format <- "base64" 66 | } 67 | res <- private$api_client$POST("/sys/tools/random", body = body) 68 | bytes <- res$data$random_bytes 69 | if (format == "raw") { 70 | decode64(bytes) 71 | } else { 72 | bytes 73 | } 74 | }, 75 | 76 | ##' @description Generates a cryptographic hash of given data using 77 | ##' the specified algorithm. 78 | ##' 79 | ##' @param data A raw vector of data to hash. To generate a raw 80 | ##' vector from an R object, one option is to use `unserialize(x, 81 | ##' NULL)` but be aware that version information may be included. 82 | ##' Alternatively, for a string, one might use `charToRaw`. 83 | ##' 84 | ##' @param algorithm A string indicating the hash algorithm to use. 85 | ##' The exact set of supported algorithms may depend by vault 86 | ##' server version, but as of version 1.0.0 vault supports 87 | ##' `sha2-224`, `sha2-256`, `sha2-384` and `sha2-512`. The 88 | ##' default is `sha2-256`. 89 | ##' 90 | ##' @param format The format of the output - must be one of `hex` 91 | ##' or `base64`. 92 | hash = function(data, algorithm = NULL, format = "hex") { 93 | body <- list(input = raw_data_input(data), 94 | algorithm = algorithm, 95 | format = assert_scalar_character(format)) 96 | private$api_client$POST("/sys/tools/hash", body = body)$data$sum 97 | } 98 | )) 99 | -------------------------------------------------------------------------------- /R/vault_resolve_secrets.R: -------------------------------------------------------------------------------- 1 | ##' Use vault to resolve secrets. This is a convenience function that 2 | ##' wraps a pattern that we have used in a few applications of vault. 3 | ##' The idea is to allow replacement of data in configuration with 4 | ##' special strings that indicate that the string refers to a vault 5 | ##' secret. This function resolves those secrets. 6 | ##' 7 | ##' For each element of the data, if a string matches the form: 8 | ##' 9 | ##' ``` 10 | ##' VAULT:: 11 | ##' ``` 12 | ##' 13 | ##' then it will be treated as a vault secret and resolved. The 14 | ##' `` will be something like 15 | ##' `/secret/path/password` and the `` the name of a 16 | ##' field in the key/value data stored at that path. For example, 17 | ##' suppose you have the data `list(username = "alice", password = 18 | ##' "s3cret!")` stored at `/secret/database/user`, then the 19 | ##' string 20 | ##' 21 | ##' ``` 22 | ##' VAULT:/secret/database/user:password 23 | ##' ``` 24 | ##' 25 | ##' would refer to the value `s3cret!` 26 | ##' 27 | ##' @title Resolve secrets from R objects 28 | ##' 29 | ##' @param x List of values, some of which may refer to vault secrets 30 | ##' (see Details for pattern). Any values that are not strings or 31 | ##' do not match the pattern of a secret are left as-is. 32 | ##' 33 | ##' @param ... Args to be passed to [vaultr::vault_client] call. 34 | ##' 35 | ##' @param login Login method to be passed to call to 36 | ##' [vaultr::vault_client]. 37 | ##' 38 | ##' @param vault_args As an alternative to using `login` and `...`, a 39 | ##' list of (named) arguments can be provided here, equivalent to 40 | ##' the full set of arguments that you might pass to 41 | ##' [vaultr::vault_client]. If provided, then `login` is ignored 42 | ##' and if additional arguments are provided through `...` an error 43 | ##' will be thrown. 44 | ##' 45 | ##' @return List of properties with any vault secrets resolved. 46 | ##' 47 | ##' @export 48 | ##' 49 | ##' @examples 50 | ##' 51 | ##' server <- vaultr::vault_test_server(if_disabled = message) 52 | ##' 53 | ##' if (!is.null(server)) { 54 | ##' client <- server$client() 55 | ##' # The example from above: 56 | ##' client$write("/secret/database/user", 57 | ##' list(username = "alice", password = "s3cret!")) 58 | ##' 59 | ##' # A list of data that contains a mix of secrets to be resolved 60 | ##' # and other data: 61 | ##' x <- list(user = "alice", 62 | ##' password = "VAULT:/secret/database/user:password", 63 | ##' port = 5678) 64 | ##' 65 | ##' # Explicitly pass in the login details and resolve the secrets: 66 | ##' vaultr::vault_resolve_secrets(x, login = "token", token = server$token, 67 | ##' addr = server$addr) 68 | ##' 69 | ##' # Alternatively, if appropriate environment variables are set 70 | ##' # then this can be done more easily: 71 | ##' if (requireNamespace("withr", quietly = TRUE)) { 72 | ##' env <- c(VAULTR_AUTH_METHOD = "token", 73 | ##' VAULT_TOKEN = server$token, 74 | ##' VAULT_ADDR = server$addr) 75 | ##' withr::with_envvar(env, vault_resolve_secrets(x)) 76 | ##' } 77 | ##' } 78 | vault_resolve_secrets <- function(x, ..., login = TRUE, vault_args = NULL) { 79 | re <- "^VAULT:(.+):(.+)" 80 | if (is.list(x)) { 81 | i <- vlapply(x, function(el) is.character(el) && grepl(re, el)) 82 | if (any(i)) { 83 | x[i] <- vault_resolve_secrets(vcapply(x[i], identity), 84 | ..., login = login, 85 | vault_args = vault_args) 86 | } 87 | } else { 88 | i <- grepl(re, x) 89 | if (any(i)) { 90 | if (is.null(vault_args)) { 91 | vault_args <- list(login = login, ...) 92 | } else { 93 | vault_args$login <- vault_args$login %||% TRUE 94 | if (length(list(...)) > 0L) { 95 | stop("Do not provide both '...' and 'vault_args'") 96 | } 97 | } 98 | vault <- do.call(vault_client, vault_args) 99 | key <- unname(sub(re, "\\1", x[i])) 100 | field <- unname(sub(re, "\\2", x[i])) 101 | x[i] <- unname(Map(vault$read, key, field)) 102 | } 103 | } 104 | x 105 | } 106 | -------------------------------------------------------------------------------- /R/vault_server_instance.R: -------------------------------------------------------------------------------- 1 | ##' @rdname vault_test_server 2 | vault_server_instance <- R6::R6Class( 3 | "vault_server_instance", 4 | inherit = vault_client_object, 5 | cloneable = FALSE, 6 | 7 | private = list( 8 | process = NULL, 9 | 10 | finalize = function() { 11 | self$kill() 12 | } 13 | ), 14 | 15 | public = list( 16 | ##' @field port The vault port (read-only). 17 | port = NULL, 18 | 19 | ##' @field addr The vault address; this is suitable for using with 20 | ##' [vaultr::vault_client] (read-only). 21 | addr = NULL, 22 | 23 | ##' @field token The vault root token, from when the testing vault 24 | ##' server was created. If the vault is rekeyed this will no 25 | ##' longer be accurate (read-only). 26 | token = NULL, 27 | 28 | ##' @field keys Key shares from when the vault was initialised 29 | ##' (read-only). 30 | keys = NULL, 31 | 32 | ##' @field cacert Path to the https certificate, if running in 33 | ##' https mode (read-only). 34 | cacert = NULL, 35 | 36 | ##' @description Create a `vault_server_instance` object. Not typically 37 | ##' called by users. 38 | ##' 39 | ##' @param bin Path to the vault binary 40 | ##' 41 | ##' @param port Port to use 42 | ##' 43 | ##' @param https Logical, indicating if we should use TLS/https 44 | ##' 45 | ##' @param init Logical, indicating if we should initialise 46 | ##' 47 | ##' @param quiet Logical, indicating if startup should be quiet 48 | initialize = function(bin, port, https, init, quiet = FALSE) { 49 | super$initialize("Vault server instance") 50 | assert_scalar_integer(port) 51 | self$port <- port 52 | 53 | bin <- normalizePath(bin, mustWork = TRUE) 54 | if (https) { 55 | assert_scalar_logical(init) 56 | dat <- vault_server_start_https(bin, self$port, init, quiet) 57 | } else { 58 | dat <- vault_server_start_dev(bin, self$port, quiet) 59 | } 60 | 61 | private$process <- dat$process 62 | 63 | self$addr <- dat$addr 64 | self$token <- dat$token 65 | self$cacert <- dat$cacert 66 | self$keys <- dat$keys 67 | 68 | for (v in c("addr", "port", "token", "keys", "cacert")) { 69 | lockBinding(v, self) 70 | } 71 | }, 72 | 73 | ##' @description Return the server version, as a [numeric_version] 74 | ##' object. 75 | version = function() { 76 | self$client(FALSE)$api()$server_version() 77 | }, 78 | 79 | ##' @description Create a new client that can use this server. The 80 | ##' client will be a [vaultr::vault_client] object. 81 | ##' 82 | ##' @param login Logical, indicating if the client should login to 83 | ##' the server (default is `TRUE`). 84 | ##' 85 | ##' @param quiet Logical, indicating if informational messages 86 | ##' should be suppressed. Default is `TRUE`, in contrast with 87 | ##' most other methods. 88 | client = function(login = TRUE, quiet = TRUE) { 89 | vault_client(if (login) "token" else FALSE, token = self$token, 90 | quiet = quiet, addr = self$addr, tls_config = self$cacert, 91 | use_cache = FALSE) 92 | }, 93 | 94 | ##' @description Return a named character vector of environment 95 | ##' variables that can be used to communicate with this vault 96 | ##' server (`VAULT_ADDR`, `VAULT_TOKEN`, etc). 97 | env = function() { 98 | c(VAULT_ADDR = self$addr, 99 | VAULT_TOKEN = self$token %||% NA_character_, 100 | VAULT_CACERT = self$cacert %||% NA_character_, 101 | VAULTR_AUTH_METHOD = "token") 102 | }, 103 | 104 | ##' @description Export the variables returned by the `$env()` 105 | ##' method to the environment. This makes them available to 106 | ##' child processes. 107 | export = function() { 108 | env <- self$env() 109 | i <- is.na(env) 110 | do.call("Sys.setenv", as.list(env[!i])) 111 | if (any(i)) { 112 | Sys.unsetenv(names(env[i])) 113 | } 114 | }, 115 | 116 | ##' @description Clear any session-cached token for this server. 117 | ##' This is intended for testing new authentication backends. 118 | clear_cached_token = function() { 119 | vault_env$cache$delete(self) 120 | }, 121 | 122 | ##' @description Kill the server. 123 | kill = function() { 124 | if (!is.null(private$process)) { 125 | private$process$kill() 126 | private$process <- NULL 127 | } 128 | } 129 | )) 130 | -------------------------------------------------------------------------------- /R/vaultr.R: -------------------------------------------------------------------------------- 1 | ##' Vault client for secrets and sensitive data; this package provides 2 | ##' wrappers for HashiCorp's [vault server](https://vaultproject.io). 3 | ##' The package wraps most of the high-level API, and includes support 4 | ##' for authentication via a number of backends (tokens, username and 5 | ##' password, github, and "AppRole"), as well as a number of secrets 6 | ##' engines (two key-value stores, vault's cubbyhole and the transit 7 | ##' backend for encryption-as-a-service). 8 | ##' 9 | ##' To get started, you might want to start with the "vaultr" 10 | ##' vignette, available from the package with `vignette("vaultr")`. 11 | ##' 12 | ##' The basic design of the package is that it has very few 13 | ##' entrypoints - for most uses one will interact almost entirely with 14 | ##' the [vaultr::vault_client] function. That function returns an 15 | ##' R6 object with several methods (functions) but also several 16 | ##' objects that themselves contain more methods and objects, creating 17 | ##' a nested tree of functionality. 18 | ##' 19 | ##' From any object, online help is available via the help method, for 20 | ##' example 21 | ##' 22 | ##' ``` 23 | ##' client <- vaultr::vault_client() 24 | ##' client$secrets$transit$help() 25 | ##' ``` 26 | ##' 27 | ##' For testing packages that rely on vault, there is support for 28 | ##' creating temporary vault servers; see `vaultr::vault_test_server` 29 | ##' and the "packages" vignette. 30 | ##' 31 | ##' @title Vault Client for Secrets and Sensitive Data 32 | "_PACKAGE" 33 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | vault_env <- new.env(parent = new.env()) 2 | vault_env$cache <- token_cache$new() 3 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | # vaultr 2 | 3 | 4 | [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 5 | [![R-CMD-check](https://github.com/vimc/vaultr/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/vimc/vaultr/actions/workflows/R-CMD-check.yaml) 6 | [![codecov.io](https://codecov.io/github/vimc/vaultr/coverage.svg?branch=master)](https://app.codecov.io/github/vimc/vaultr?branch=master) 7 | [![CodeFactor](https://www.codefactor.io/repository/github/vimc/vaultr/badge)](https://www.codefactor.io/repository/github/vimc/vaultr) 8 | [![](http://www.r-pkg.org/badges/version/vaultr)](https://cran.r-project.org/package=vaultr) 9 | 10 | 11 | Interact with [HashiCorp's vault](https://www.vaultproject.io/), to securely use secrets from R. This package wraps the [vault http API](https://www.vaultproject.io/api/index.html) to allow secrets to be accessed from R. Secrets might be passwords, tokens, certificates or any other sensitive data. 12 | 13 | * Authenticate with several different providers (token, username and password, GitHub, LDAP, and "approle") 14 | * Read and write secrets into vault using its key-value stores (version 1 or 2), cubbyhole and in-transit "encryption-as-a-service" 15 | * Inspect and work with vault tokens 16 | * Read, write and update vault policies 17 | * Allows a degree of access to operator maintenance 18 | * Work with vault's audit devices 19 | 20 | ## Usage 21 | 22 | ```{r, include = FALSE} 23 | srv <- vaultr::vault_test_server() 24 | srv$export() 25 | local({ 26 | cl <- srv$client() 27 | cl$write("/secret/database/admin", list(value = "s3cret")) 28 | cl$write("/secret/database/readonly", list(value = "passw0rd")) 29 | }) 30 | knitr::opts_chunk$set(error = FALSE) 31 | ``` 32 | 33 | Create a vault client with the `vault_client` function: 34 | 35 | ```{r} 36 | vault <- vaultr::vault_client(login = TRUE) 37 | ``` 38 | 39 | Interact with vault using this object: 40 | 41 | ```{r} 42 | vault$list("secret/database") 43 | ``` 44 | 45 | and read secrets with 46 | 47 | ```{r} 48 | vault$read("secret/database/admin") 49 | ``` 50 | 51 | ```{r} 52 | vault$read("secret/database/readonly", field = "value") 53 | ``` 54 | 55 | or set secrets with 56 | 57 | ```r 58 | vault$write("secret/webserver", list(password = "horsestaple")) 59 | vault$read("secret/webserver") 60 | ``` 61 | 62 | or delete secrets with 63 | 64 | ```r 65 | vault$delete("/secret/database/readonly") 66 | ``` 67 | 68 | ## Installation 69 | 70 | Install `vaultr` from CRAN with 71 | 72 | ```r 73 | install.packages("vaultr") 74 | ``` 75 | 76 | To install our internally released version (which might be ahead of CRAN) via our r-universe, use 77 | 78 | ```r 79 | install.packages( 80 | "vaultr", 81 | repos = c("https://vimc.r-universe.dev", "https://cloud.r-project.org")) 82 | ``` 83 | 84 | or install the bleeding edge with 85 | 86 | ```r 87 | remotes::install_gitub("vimc/vaultr", upgrade = FALSE) 88 | ``` 89 | 90 | ## License 91 | 92 | MIT © Imperial College of Science, Technology and Medicine 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vaultr 2 | 3 | 4 | [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) 5 | [![R-CMD-check](https://github.com/vimc/vaultr/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/vimc/vaultr/actions/workflows/R-CMD-check.yaml) 6 | [![codecov.io](https://codecov.io/github/vimc/vaultr/coverage.svg?branch=master)](https://app.codecov.io/github/vimc/vaultr?branch=master) 7 | [![CodeFactor](https://www.codefactor.io/repository/github/vimc/vaultr/badge)](https://www.codefactor.io/repository/github/vimc/vaultr) 8 | [![](http://www.r-pkg.org/badges/version/vaultr)](https://cran.r-project.org/package=vaultr) 9 | 10 | 11 | Interact with [HashiCorp's vault](https://www.vaultproject.io/), to securely use secrets from R. This package wraps the [vault http API](https://www.vaultproject.io/api/index.html) to allow secrets to be accessed from R. Secrets might be passwords, tokens, certificates or any other sensitive data. 12 | 13 | * Authenticate with several different providers (token, username and password, GitHub, LDAP, and "approle") 14 | * Read and write secrets into vault using its key-value stores (version 1 or 2), cubbyhole and in-transit "encryption-as-a-service" 15 | * Inspect and work with vault tokens 16 | * Read, write and update vault policies 17 | * Allows a degree of access to operator maintenance 18 | * Work with vault's audit devices 19 | 20 | ## Usage 21 | 22 | 23 | 24 | Create a vault client with the `vault_client` function: 25 | 26 | 27 | ```r 28 | vault <- vaultr::vault_client(login = TRUE) 29 | ``` 30 | 31 | ``` 32 | ## Verifying token 33 | ``` 34 | 35 | Interact with vault using this object: 36 | 37 | 38 | ```r 39 | vault$list("secret/database") 40 | ``` 41 | 42 | ``` 43 | ## [1] "admin" "readonly" 44 | ``` 45 | 46 | and read secrets with 47 | 48 | 49 | ```r 50 | vault$read("secret/database/admin") 51 | ``` 52 | 53 | ``` 54 | ## $value 55 | ## [1] "s3cret" 56 | ``` 57 | 58 | 59 | ```r 60 | vault$read("secret/database/readonly", field = "value") 61 | ``` 62 | 63 | ``` 64 | ## [1] "passw0rd" 65 | ``` 66 | 67 | or set secrets with 68 | 69 | ```r 70 | vault$write("secret/webserver", list(password = "horsestaple")) 71 | vault$read("secret/webserver") 72 | ``` 73 | 74 | or delete secrets with 75 | 76 | ```r 77 | vault$delete("/secret/database/readonly") 78 | ``` 79 | 80 | ## Installation 81 | 82 | Install `vaultr` from CRAN with 83 | 84 | ```r 85 | install.packages("vaultr") 86 | ``` 87 | 88 | To install our internally released version (which might be ahead of CRAN) via r-universe, use 89 | 90 | 91 | ```r 92 | install.packages( 93 | "vaultr", 94 | repos = c("https://vimc.r-universe.dev", "https://cloud.r-project.org")) 95 | ``` 96 | 97 | or install the bleeding edge with 98 | 99 | ```r 100 | remotes::install_gitub("vimc/vaultr", upgrade = FALSE) 101 | ``` 102 | 103 | ## License 104 | 105 | MIT © Imperial College of Science, Technology and Medicine 106 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://vaccineimpact.org/vaultr/ 2 | 3 | template: 4 | bootstrap: 5 5 | 6 | reference: 7 | - title: High level functions 8 | desc: The main functions called in the package 9 | contents: 10 | - vault_client 11 | - vault_resolve_secrets 12 | 13 | - title: Authentication backends 14 | desc: >- 15 | Different ways of logging into the vault, typically you just 16 | need to use the `$login()` method (or `login` argument to 17 | `vault_client`), but you can also configure these authentication 18 | backends here. 19 | contents: 20 | - vault_client_auth 21 | - vault_client_auth_approle 22 | - vault_client_auth_github 23 | - vault_client_auth_ldap 24 | - vault_client_auth_userpass 25 | 26 | - title: Secret backends 27 | desc: >- 28 | Different ways of storing and retrieving secrets from the 29 | vault. Which of these you want to use depends on how your vault 30 | instance has been configured. 31 | contents: 32 | - vault_client_secrets 33 | - vault_client_kv1 34 | - vault_client_kv2 35 | - vault_client_cubbyhole 36 | - vault_client_transit 37 | 38 | - title: Vault administration 39 | desc: >- 40 | Perform administrative tasks for a vault server 41 | contents: 42 | - vault_client_audit 43 | - vault_client_operator 44 | - vault_client_policy 45 | - vault_client_token 46 | - vault_client_tools 47 | 48 | - title: Using vault objects 49 | desc: Common documentation for all the above objects 50 | contents: 51 | - vaultr 52 | - vault_client_object 53 | - vault_api_client 54 | 55 | - title: Testing 56 | desc: 'Support for testing (see also `vignette("packages")`)' 57 | contents: 58 | - vault_test_server 59 | -------------------------------------------------------------------------------- /development.md: -------------------------------------------------------------------------------- 1 | # Development notes 2 | 3 | This file contains (hopefully) useful information for developing `vaultr`. 4 | 5 | ## Running all the tests 6 | 7 | Set three environment variables before loading the package: 8 | 9 | ``` 10 | VAULTR_TEST_SERVER_PORT=18200 11 | VAULTR_TEST_SERVER_BIN_PATH=/usr/bin (or "C:/Program Files/Vault/") 12 | NOT_CRAN=true 13 | ``` 14 | 15 | With these set, and the package loaded with `pkgload::load_all()` you should now be able to run 16 | 17 | ```r 18 | srv <- vault_test_server() 19 | ``` 20 | 21 | to bring up a test server. 22 | 23 | ## Configuring LDAP 24 | 25 | This is quite fiddly and worth checking that it works with the vault dev server before getting too involved with the R client. 26 | 27 | Start the server with 28 | 29 | ``` 30 | vault server -dev -dev-root-token-id=MlF2MrMpmwfCZOkCHoZSDCJT 31 | ``` 32 | 33 | then try the instructions that follow. 34 | 35 | There are two easy-to-use development servers: 36 | 37 | An [online test server](https://www.forumsys.com/2022/05/10/online-ldap-test-server/) using a couple of groups of mathematicians and scientists. 38 | 39 | ``` 40 | export VAULT_ADDR=http://127.0.0.1:8200 41 | vault login -method=token token=MlF2MrMpmwfCZOkCHoZSDCJT 42 | 43 | vault secrets disable /secret 44 | vault secrets enable -version=1 -path=/secret kv 45 | vault write /secret/password value=mypass 46 | vault read /secret/password 47 | 48 | echo 'path "secret/*" { capabilities = ["read"] }' | vault policy write read-secret - 49 | 50 | vault auth disable ldap 51 | vault auth enable ldap 52 | 53 | vault write auth/ldap/config \ 54 | url="ldap://ldap.forumsys.com" \ 55 | binddn='cn=read-only-admin,dc=example,dc=com' \ 56 | bindpass='password' \ 57 | userdn='dc=example,dc=com' \ 58 | userattr='uid' \ 59 | groupfilter='(uniqueMember={{.UserDN}})' \ 60 | groupdn='dc=example,dc=com' \ 61 | groupattr='ou' 62 | 63 | vault write auth/ldap/groups/scientists policies=read-secret 64 | 65 | vault login -method=ldap username=einstein password=password 66 | vault read /secret/password 67 | ``` 68 | 69 | (The instructions were worked out using [this forum post](https://groups.google.com/g/vault-tool/c/DKNvMfLf-5w?pli=1)) 70 | 71 | A docker image [`rroemhild/test-openldap`](https://github.com/rroemhild/docker-test-openldap) 72 | 73 | In another terminal, start the LDAP server with: 74 | 75 | ``` 76 | docker run --rm -p 127.0.0.1:10389:10389 rroemhild/test-openldap 77 | ``` 78 | 79 | Then a similar set of commands to before 80 | 81 | ``` 82 | export VAULT_ADDR=http://127.0.0.1:8200 83 | vault login -method=token token=MlF2MrMpmwfCZOkCHoZSDCJT 84 | 85 | vault secrets disable /secret 86 | vault secrets enable -version=1 -path=/secret kv 87 | vault write /secret/password value=mypass 88 | vault read /secret/password 89 | 90 | echo 'path "secret/*" { capabilities = ["read"] }' | vault policy write read-secret - 91 | 92 | vault auth disable ldap 93 | vault auth enable ldap 94 | 95 | vault write auth/ldap/config \ 96 | url="ldap://localhost:10389" \ 97 | userdn="ou=people,dc=planetexpress,dc=com" \ 98 | groupdn="ou=people,dc=planetexpress,dc=com" \ 99 | groupattr="cn" \ 100 | userattr=uid \ 101 | binddn="cn=admin,dc=planetexpress,dc=com" \ 102 | bindpass='GoodNewsEveryone' 103 | 104 | vault write auth/ldap/groups/admin_staff policies=read-secret 105 | 106 | vault login -method=ldap username=hermes password=hermes 107 | vault read /secret/password 108 | ``` 109 | 110 | (The instructions were worked out using [this github issue](https://github.com/hashicorp/vault/issues/6325)) 111 | 112 | The test suite will use the public forumsys version by default, but set the environment variable `VAULTR_TEST_LDAP_USE_DOCKER` to `true` to use that instead. 113 | 114 | ``` 115 | VAULTR_TEST_LDAP_USE_DOCKER=true 116 | ``` 117 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | AEAD 2 | AppRole 3 | AppRoles 4 | Args 5 | CIDR 6 | CIDRS 7 | CIDRs 8 | CMD 9 | ChaCha 10 | CodeFactor 11 | ECDSA 12 | FitzJohn 13 | GCM 14 | HMAC 15 | HMACs 16 | HashiCorp 17 | HashiCorp's 18 | R's 19 | RoleID 20 | SecretID 21 | SecretIDs 22 | TTLs 23 | Undeletes 24 | VIMC 25 | VignetteEncoding 26 | VignetteEngine 27 | VignetteIndexEntry 28 | api 29 | approle 30 | backend 31 | backend's 32 | backends 33 | cas 34 | cleanup 35 | cli 36 | codecov 37 | com 38 | config 39 | darwin 40 | datakey 41 | entrypoints 42 | etc 43 | github 44 | hashicorp 45 | hmac 46 | io 47 | knitr 48 | kv 49 | ldap 50 | linux 51 | nonstandard 52 | reevaluated 53 | rekey 54 | rekeyed 55 | rekeying 56 | rmarkdown 57 | sudo 58 | ttl 59 | userpass 60 | vaultproject 61 | www 62 | -------------------------------------------------------------------------------- /inst/server/README.md: -------------------------------------------------------------------------------- 1 | # Test certificates 2 | 3 | These certificates are used to test TLS authentication in vault. Do not reuse them for any other purposes. They are generated as-needed by the `generate.sh` command. This is the same approach for testing as used by [hvac](https://github.com/ianunruh/hvac), and this implementation follows theirs verbatim. 4 | -------------------------------------------------------------------------------- /inst/server/client-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICtTCCAZ2gAwIBAgIJALdz2R5esggYMA0GCSqGSIb3DQEBCwUAMBExDzANBgNV 3 | BAMMBmNsaWVudDAeFw0xNzA4MjExNjIyMjNaFw0yNzA4MTkxNjIyMjNaMBExDzAN 4 | BgNVBAMMBmNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKje 5 | u0Cso2hgc7Dqyg62ooHe5rvLkDA/Yyu3si43OJ1lcKdLzKXTDX9J4mvr+yzeCSHS 6 | 9VzF903PLuvxu5HGvAlg2Sx9nyzkaM8moc6xusGMyELTbofg+SSGNEl4Fou9CKIY 7 | xHAviKZr4TkTBUddJA/fdrGq7v6C+EiiBbNKX+vuY54AGwLrgJoeo8FoqxY4OBen 8 | lGIpnzxhPXITamjjeR5MLuSmmoRhKMRh2KTOGODpBElnBRVqwn+D8xTjwFAo//6Q 9 | WDKu3kFcx+QzcHYyAKIoSoHkQl/1asLcFtmlb7dh32uNvZR0acrA8OVg9nN0uYUr 10 | +IoKMki1qokLzaENyYECAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B 11 | AQsFAAOCAQEAI6Sn3fb1ulwHL40z2IHLCQhKX2M3Gr68G/XAzqqBWVwJOrks+/2q 12 | WoafrRp4u9Of6Fnm0wO9viStiZK2xnfjpz2yQEMIolMbHkmqGiZXA6gELxAAhYQn 13 | sZrMxTC5TSTIHX7yMgTfFBIs2zbUyi/WjgQOYYnmFrkqe/5ia5VgicXOvv/SYc5d 14 | aNl8zITsUrg/Mh68KXGMUDAce+sWkieD6FblqCMlBh1d4G1c1HQ/lX1lPGun6sjO 15 | 7sZwBannqPMKK0XBxXFRu3QMtSMAY0pk4PZ+dOyLZ9t1qvy/FRO0uaAkJlAgfHAw 16 | 62pE5a2UQ/hJfXgEdM13W6I3v8u7YcrUiw== 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /inst/server/client-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCo3rtArKNoYHOw 3 | 6soOtqKB3ua7y5AwP2Mrt7IuNzidZXCnS8yl0w1/SeJr6/ss3gkh0vVcxfdNzy7r 4 | 8buRxrwJYNksfZ8s5GjPJqHOsbrBjMhC026H4PkkhjRJeBaLvQiiGMRwL4ima+E5 5 | EwVHXSQP33axqu7+gvhIogWzSl/r7mOeABsC64CaHqPBaKsWODgXp5RiKZ88YT1y 6 | E2po43keTC7kppqEYSjEYdikzhjg6QRJZwUVasJ/g/MU48BQKP/+kFgyrt5BXMfk 7 | M3B2MgCiKEqB5EJf9WrC3BbZpW+3Yd9rjb2UdGnKwPDlYPZzdLmFK/iKCjJItaqJ 8 | C82hDcmBAgMBAAECggEBAIE434WgFfM7S4xoPnJ/418KGPo/zXh+KP89NIYVD6ey 9 | BkCeyl1iYYO0ICWBkNVGmypc6hEyFApgroEym9vzfdSXXdij0Hhj2Kgb4dnvOv/9 10 | IQmyDXTA/SWr5LsLwATyhHa2CSvn0+O1zcUE/xca+QuEbqmKKhRsWF4MTdxi34AD 11 | zx+bjvY3KjEd3FeyWcsdj5mHrWf7bKpBRFLqpTNVoRyepoJX15jMKlfyE1va5uxB 12 | ecir79U8wno2Vu9yDV8+rx5CrTg2C8umcIGSyQyUSp0pE/7ry+xwvZgHhyRKCa52 13 | Pllo8TtJhfGxJUclRDf12z/VYJCNLr+8ELSX+1Tb/XkCgYEA3kA9rVkxYKPmt952 14 | 2EbDeUtGwlLfZ5a+IiA1gIZqLF/B4vTbPfu/CtvPm22D3/ncSnaP3w5vrL1oxOYm 15 | SdV5K5trxOyOv3UuZ6qaFaKoMt2FYdIgXyxp+CBNUlDBOfjpD5CchGkUxD6c/lwz 16 | k2vUVOa7NeDgWGjI/8y8kA0WhyMCgYEAwoNeCWXdXZLr/KRwdo+r0js+1sc3vC/N 17 | CJ2DasnNwnegjBuUoRtX2RjEfkKe+fyJ1NEBEM5jIVILP3fYJ/kHC+QMJZxL23Kq 18 | B+8tJHmRBAYEzPp1QCgOR90vM7c5y5WdSS9odI5hWGjPeFk908Fiu4MwPs6fehC1 19 | /lY2WbiPSQsCgYAP061QuehNRH66m0oFTsy+x4CLeBFWtCJoFbkZpPjMnikVe3Bd 20 | cp8BK/QV0m+wtH9egiDutn13ZKmgU+9oFJ6jX1cGV/42Xibm1PxSDBpEPQgxliAe 21 | BvCo7cvzz7Ji6XnXVOoAd+c1rmvJpVE671PYk/HC/XecdCHULYVEENqI1wKBgFyT 22 | 2bXvXaSjwN3GBiDn0IeG2ymuN8DBn3xyO5If6macOTV2Cw+CzBTvzNWrL68bmx5H 23 | O5KcKGI7gk76405jIA9wwrdD5HbV5EdSdtHdaj/X/YDx08xtRB7ADy82DbPvyaSD 24 | g9u1yG8js1s8XkjiyfMlRVkfTpfyTCx5K/UX+lpxAoGAMBeE2ic+xP4Ev36+k1AT 25 | KK5uo1kfgQG7GFesHBOwM3GXj0CEJnaaj/mhIN4FsDKzbeffEGdTTwT/d9vvVX7I 26 | EEiVUtnMDBNYwmc0mpJVh/10ZNO5c1Ke+tLMuPipGO0JenYo29xQB7P2/B+JfuMv 27 | W7D/bxsOc0O3TOOWRnmkmQA= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /inst/server/client.cnf: -------------------------------------------------------------------------------- 1 | [req] 2 | distinguished_name = req_distinguished_name 3 | x509_extensions = v3_req 4 | prompt = no 5 | 6 | [req_distinguished_name] 7 | CN = client 8 | 9 | [v3_req] 10 | basicConstraints = CA:TRUE 11 | -------------------------------------------------------------------------------- /inst/server/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | openssl req -x509 -nodes -days 3650 \ 3 | -newkey rsa:2048 -keyout server-key.pem \ 4 | -out server-cert.pem -config server.cnf 5 | 6 | openssl req -x509 -nodes -days 3650 \ 7 | -newkey rsa:2048 -keyout client-key.pem \ 8 | -out client-cert.pem -config client.cnf 9 | -------------------------------------------------------------------------------- /inst/server/server-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC1zCCAb+gAwIBAgIJAMode/LnM6/oMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV 3 | BAMMCWxvY2FsaG9zdDAeFw0xNzA4MjExNjIyMjNaFw0yNzA4MTkxNjIyMjNaMBQx 4 | EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC 5 | ggEBANoR2fWjnDQz8flvT28ww1WwP8NryalX9/hhjItUA/HbzWYKRhovUawZAlfH 6 | /iom8qQhX6Sg+VuGbV3i7ppgUOCapj3sJXuqjBDpU9pY0qW3qD4DgOVbxLbA2Ly9 7 | 6s3+G+fSVQQzviFEdplq7L6clxusDjpgDACNEnC2juhNN8hXd3Ee6uG3adPA/ZX7 8 | ohjPq904+3q6aShmN9ZTn/z+NDq9lSIDis81/LrImRFRTtCEWU4iQuAR7LX/ZG9X 9 | tq19Xdv7Oy3XCg/XLyhv93E/O2Nk7xsiX4i+qBlYAPF4N+tOrlC4BdA43g+myV7d 10 | Q3pa4+0HNSUb/v1rwCNrXBqyWLECAwEAAaMsMCowDAYDVR0TBAUwAwEB/zAaBgNV 11 | HREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQELBQADggEBAC9OUdOE 12 | P350M1uiFpEAZ9vt19wzN2FPkCIsHMMsPTr4J3tEEj0KhJoJ+EZIAIZruW0kMbvV 13 | VoEi7ONmMR/FhOkDo+WL1/6s3MqavzwZvnXD6wBMl14mynZB6KYJ3M00OrOBYnF4 14 | 06j1uhnI54ZcAWy6LcUZSudVGDggYhBtyu0zbp9a7xx9k0dYCtbdhAxhqESytkM4 15 | cwuFW3EfcYbowl/EcLvz5GcJ2MAMAQgBMKD3llx0QseaKCZpKDz01Oo8BnlpzHNy 16 | hDTMirWsiBarEeaz6uU36Hfa5H1rd7u/k5M8ZpwYAQZKQdHxWTYuyJIVKW9VJTcm 17 | 6a6BBO/H5cRM+Sg= 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /inst/server/server-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDaEdn1o5w0M/H5 3 | b09vMMNVsD/Da8mpV/f4YYyLVAPx281mCkYaL1GsGQJXx/4qJvKkIV+koPlbhm1d 4 | 4u6aYFDgmqY97CV7qowQ6VPaWNKlt6g+A4DlW8S2wNi8verN/hvn0lUEM74hRHaZ 5 | auy+nJcbrA46YAwAjRJwto7oTTfIV3dxHurht2nTwP2V+6IYz6vdOPt6umkoZjfW 6 | U5/8/jQ6vZUiA4rPNfy6yJkRUU7QhFlOIkLgEey1/2RvV7atfV3b+zst1woP1y8o 7 | b/dxPztjZO8bIl+IvqgZWADxeDfrTq5QuAXQON4Ppsle3UN6WuPtBzUlG/79a8Aj 8 | a1waslixAgMBAAECggEBAIlZgQzEVhhTN29B+qgv8HhCIIc3lrbAn4CkAF+pNSra 9 | pI5GnkzTzP82aVFct+tYyK80DgelFCnfi3b8/gfh7k7pWRfbauhvTPVB4Py/wT17 10 | Zc5ZapALsuTgpiKLBB9mk7D5u26AIJrXglOzExYjjtFxFy0RXb3jHOA9O9l4+U+H 11 | DXzTSpYACLkC6bMamXjNBXPcVpH9twnTxJc+Nd1mSqqjHklwGZeNLkkeAaqJHf2r 12 | GwuPFq5L9kuZQ7dzf6qT3pMesXvQSvu3H+/rOzC9oHDU3JCNvtRP9SkSrquiFipo 13 | 5Qcc+LXkfqCkOGfbQZmBB4usGLrVtrRQ9eGMIAgYkLkCgYEA/Ge8ZgBDGYnCxmmp 14 | OmIlgMLNxQ2B8rKTFMsUPjUdKHMZx/CSDdI20tlkdlO3zuw9HgjUl9M4INoj+JRU 15 | 1HaMvjIqZF0adOpmL33Tj9zmJ04ccEfuT9LKlP3ddPLR4xUrmyrAIAKPGcSkjTEf 16 | rxef2vmH7T6h1NH4JToWbl83GVcCgYEA3Szt1CUM+VH4iukRMf8vpJ3mOtcfIMQT 17 | H/+ur5d0PYZ8CryioBSbzG3ZTwGN/FVLQiA3KpbASmdze4eDsGz5LNLgZuse5tye 18 | XHbns1qXXM2eccR9m1OTJJpCBlfHLXn4jvMZzE7zrV0pmRHMDuFBwekqyMs2C+Rh 19 | ZTAYRvNn8TcCgYBHyACuSXjLtH/uCXKVJgBgZAY8+iBwsxRdH3v8TQKj1EgKsoH2 20 | 6EerIyQM+rYVZN3kwsIjA3C89wyvzw9o9OYF2SJxNIEnqtmwu+oEyd+yYeZ4kBxY 21 | Gyx9vszSS5QhLo4eLMRC1jd4LpJVjRjjpKZTkg90CxHfAm+9to1WikGTNQKBgQDY 22 | Ozyo0zBD7+fVptaw0pN5lQWpMPe1yPwJpKbb9G4oWHqc5J75cix/SEXT/+kYcSsK 23 | kwRcFtYX59v3QR12NpPFRsUs2WF0wuvv6i/MIR0qFbx57Wf/m02X01gJa3fG9iAT 24 | YYgvPmDtdM9eXw/o3EC1m4fAlytXpQezLboOY5mTMQKBgQDu6Esk+ZEoLmYvKkAd 25 | Uchx1jbDL2iXC51jysgYAoMPPnRbAUYzTIc+AvWil8xjC2wjReFB4hNvuhc3SryN 26 | KdAH9If0s4gL+p9Rpr3OPFXjYEKjzbVHxKMVLIilYsR0cpsy31P7sApKA8KeNmxr 27 | 9gC4wdMSFO+Zl/cV97L2gm+DJg== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /inst/server/server.cnf: -------------------------------------------------------------------------------- 1 | [req] 2 | distinguished_name = req_distinguished_name 3 | x509_extensions = v3_req 4 | prompt = no 5 | 6 | [req_distinguished_name] 7 | CN = localhost 8 | 9 | [v3_req] 10 | basicConstraints = CA:TRUE 11 | subjectAltName = @alt_names 12 | 13 | [alt_names] 14 | DNS.1 = localhost 15 | IP.1 = 127.0.0.1 16 | -------------------------------------------------------------------------------- /inst/server/vault-tls.hcl: -------------------------------------------------------------------------------- 1 | backend "inmem" { 2 | } 3 | 4 | listener "tcp" { 5 | tls_cert_file = "VAULT_CONFIG_PATH/server-cert.pem" 6 | tls_key_file = "VAULT_CONFIG_PATH/server-key.pem" 7 | address = "VAULT_ADDR" 8 | } 9 | 10 | disable_mlock = true 11 | -------------------------------------------------------------------------------- /man/vault_client_audit.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vault_client_audit.R 3 | \name{vault_client_audit} 4 | \alias{vault_client_audit} 5 | \title{Vault Audit Devices} 6 | \description{ 7 | Vault Audit Devices 8 | 9 | Vault Audit Devices 10 | } 11 | \details{ 12 | Interact with vault's audit devices. For more details, see 13 | https://developer.hashicorp.com/vault/docs/audit 14 | } 15 | \examples{ 16 | server <- vaultr::vault_test_server(if_disabled = message) 17 | if (!is.null(server)) { 18 | client <- server$client() 19 | # By default no audit engines are enabled with the testing server 20 | client$audit$list() 21 | 22 | # Create a file-based audit device on a temporary file: 23 | path <- tempfile() 24 | client$audit$enable("file", options = list(file_path = path)) 25 | client$audit$list() 26 | 27 | # Generate some activity on the server: 28 | client$write("/secret/mysecret", list(key = "value")) 29 | 30 | # The audit logs contain details about the activity - see the 31 | # vault documentation for details in interpreting this 32 | readLines(path) 33 | 34 | # cleanup 35 | server$kill() 36 | unlink(path) 37 | } 38 | } 39 | \section{Super class}{ 40 | \code{\link[vaultr:vault_client_object]{vaultr::vault_client_object}} -> \code{vault_client_audit} 41 | } 42 | \section{Methods}{ 43 | \subsection{Public methods}{ 44 | \itemize{ 45 | \item \href{#method-vault_client_audit-new}{\code{vault_client_audit$new()}} 46 | \item \href{#method-vault_client_audit-list}{\code{vault_client_audit$list()}} 47 | \item \href{#method-vault_client_audit-enable}{\code{vault_client_audit$enable()}} 48 | \item \href{#method-vault_client_audit-disable}{\code{vault_client_audit$disable()}} 49 | \item \href{#method-vault_client_audit-hash}{\code{vault_client_audit$hash()}} 50 | } 51 | } 52 | \if{html}{\out{ 53 |
Inherited methods 54 | 58 |
59 | }} 60 | \if{html}{\out{
}} 61 | \if{html}{\out{}} 62 | \if{latex}{\out{\hypertarget{method-vault_client_audit-new}{}}} 63 | \subsection{Method \code{new()}}{ 64 | Create an audit object 65 | \subsection{Usage}{ 66 | \if{html}{\out{
}}\preformatted{vault_client_audit$new(api_client)}\if{html}{\out{
}} 67 | } 68 | 69 | \subsection{Arguments}{ 70 | \if{html}{\out{
}} 71 | \describe{ 72 | \item{\code{api_client}}{a \link{vault_api_client} object} 73 | } 74 | \if{html}{\out{
}} 75 | } 76 | } 77 | \if{html}{\out{
}} 78 | \if{html}{\out{}} 79 | \if{latex}{\out{\hypertarget{method-vault_client_audit-list}{}}} 80 | \subsection{Method \code{list()}}{ 81 | List active audit devices. Returns a \link{data.frame} 82 | of names, paths and descriptions of active audit devices. 83 | \subsection{Usage}{ 84 | \if{html}{\out{
}}\preformatted{vault_client_audit$list()}\if{html}{\out{
}} 85 | } 86 | 87 | } 88 | \if{html}{\out{
}} 89 | \if{html}{\out{}} 90 | \if{latex}{\out{\hypertarget{method-vault_client_audit-enable}{}}} 91 | \subsection{Method \code{enable()}}{ 92 | This endpoint enables a new audit device at the 93 | supplied path. 94 | \subsection{Usage}{ 95 | \if{html}{\out{
}}\preformatted{vault_client_audit$enable( 96 | type, 97 | description = NULL, 98 | options = NULL, 99 | path = NULL 100 | )}\if{html}{\out{
}} 101 | } 102 | 103 | \subsection{Arguments}{ 104 | \if{html}{\out{
}} 105 | \describe{ 106 | \item{\code{type}}{Name of the audit device to enable} 107 | 108 | \item{\code{description}}{Human readable description for this audit device} 109 | 110 | \item{\code{options}}{Options to configure the device with. These vary 111 | by device. This must be a named list of strings.} 112 | 113 | \item{\code{path}}{Path to mount the audit device. By default, \code{type} is used 114 | as the path.} 115 | } 116 | \if{html}{\out{
}} 117 | } 118 | } 119 | \if{html}{\out{
}} 120 | \if{html}{\out{}} 121 | \if{latex}{\out{\hypertarget{method-vault_client_audit-disable}{}}} 122 | \subsection{Method \code{disable()}}{ 123 | Disable an audit device 124 | \subsection{Usage}{ 125 | \if{html}{\out{
}}\preformatted{vault_client_audit$disable(path)}\if{html}{\out{
}} 126 | } 127 | 128 | \subsection{Arguments}{ 129 | \if{html}{\out{
}} 130 | \describe{ 131 | \item{\code{path}}{Path of the audit device to remove} 132 | } 133 | \if{html}{\out{
}} 134 | } 135 | } 136 | \if{html}{\out{
}} 137 | \if{html}{\out{}} 138 | \if{latex}{\out{\hypertarget{method-vault_client_audit-hash}{}}} 139 | \subsection{Method \code{hash()}}{ 140 | The \code{hash} method is used to calculate the hash of the 141 | data used by an audit device's hash function and salt. This can be 142 | used to search audit logs for a hashed value when the original 143 | value is known. 144 | \subsection{Usage}{ 145 | \if{html}{\out{
}}\preformatted{vault_client_audit$hash(input, device)}\if{html}{\out{
}} 146 | } 147 | 148 | \subsection{Arguments}{ 149 | \if{html}{\out{
}} 150 | \describe{ 151 | \item{\code{input}}{The input string to hash} 152 | 153 | \item{\code{device}}{The path of the audit device} 154 | } 155 | \if{html}{\out{
}} 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /man/vault_client_auth.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vault_client_auth.R 3 | \name{vault_client_auth} 4 | \alias{vault_client_auth} 5 | \title{Vault Authentication Configuration} 6 | \description{ 7 | Vault Authentication Configuration 8 | 9 | Vault Authentication Configuration 10 | } 11 | \details{ 12 | Interact with vault's authentication backends. 13 | } 14 | \examples{ 15 | server <- vaultr::vault_test_server(if_disabled = message) 16 | if (!is.null(server)) { 17 | client <- server$client() 18 | 19 | # List configured authentication backends 20 | client$auth$list() 21 | 22 | # cleanup 23 | server$kill() 24 | } 25 | } 26 | \section{Super class}{ 27 | \code{\link[vaultr:vault_client_object]{vaultr::vault_client_object}} -> \code{vault_client_auth} 28 | } 29 | \section{Public fields}{ 30 | \if{html}{\out{
}} 31 | \describe{ 32 | \item{\code{approle}}{Interact with vault's AppRole authentication. See 33 | \code{\link{vault_client_auth_approle}} for more information.} 34 | 35 | \item{\code{github}}{Interact with vault's GitHub authentication. See 36 | \code{\link{vault_client_auth_github}} for more information.} 37 | 38 | \item{\code{token}}{Interact with vault's token authentication. See 39 | \code{\link{vault_client_token}} for more information.} 40 | 41 | \item{\code{userpass}}{Interact with vault's username/password based 42 | authentication. See \code{\link{vault_client_auth_userpass}} for 43 | more information.} 44 | 45 | \item{\code{ldap}}{Interact with vault's LDAP based 46 | authentication. See \code{\link{vault_client_auth_ldap}} for 47 | more information.} 48 | } 49 | \if{html}{\out{
}} 50 | } 51 | \section{Methods}{ 52 | \subsection{Public methods}{ 53 | \itemize{ 54 | \item \href{#method-vault_client_auth-new}{\code{vault_client_auth$new()}} 55 | \item \href{#method-vault_client_auth-backends}{\code{vault_client_auth$backends()}} 56 | \item \href{#method-vault_client_auth-list}{\code{vault_client_auth$list()}} 57 | \item \href{#method-vault_client_auth-enable}{\code{vault_client_auth$enable()}} 58 | \item \href{#method-vault_client_auth-disable}{\code{vault_client_auth$disable()}} 59 | } 60 | } 61 | \if{html}{\out{ 62 |
Inherited methods 63 | 67 |
68 | }} 69 | \if{html}{\out{
}} 70 | \if{html}{\out{}} 71 | \if{latex}{\out{\hypertarget{method-vault_client_auth-new}{}}} 72 | \subsection{Method \code{new()}}{ 73 | Create a \code{vault_client_auth} object. Not typically 74 | called by users. 75 | \subsection{Usage}{ 76 | \if{html}{\out{
}}\preformatted{vault_client_auth$new(api_client)}\if{html}{\out{
}} 77 | } 78 | 79 | \subsection{Arguments}{ 80 | \if{html}{\out{
}} 81 | \describe{ 82 | \item{\code{api_client}}{A \link{vault_api_client} object} 83 | } 84 | \if{html}{\out{
}} 85 | } 86 | } 87 | \if{html}{\out{
}} 88 | \if{html}{\out{}} 89 | \if{latex}{\out{\hypertarget{method-vault_client_auth-backends}{}}} 90 | \subsection{Method \code{backends()}}{ 91 | Return a character vector of supported 92 | authentication backends. If a backend \code{x} is present, then 93 | you can access it with \verb{$auth$x}. Note that vault calls 94 | these authentication \emph{methods} but we use \emph{backends} here to 95 | differentiate with R6 methods. Note that these are backends 96 | supported by \code{vaultr} and not necessarily supported by the 97 | server - the server may not have enabled some of these 98 | backends, and may support other authentication backends not 99 | directly supported by vaultr. See the \verb{$list()} method to 100 | query what the server supports. 101 | \subsection{Usage}{ 102 | \if{html}{\out{
}}\preformatted{vault_client_auth$backends()}\if{html}{\out{
}} 103 | } 104 | 105 | } 106 | \if{html}{\out{
}} 107 | \if{html}{\out{}} 108 | \if{latex}{\out{\hypertarget{method-vault_client_auth-list}{}}} 109 | \subsection{Method \code{list()}}{ 110 | List authentication backends supported by the 111 | vault server, including information about where these 112 | backends are mounted. 113 | \subsection{Usage}{ 114 | \if{html}{\out{
}}\preformatted{vault_client_auth$list(detailed = FALSE)}\if{html}{\out{
}} 115 | } 116 | 117 | \subsection{Arguments}{ 118 | \if{html}{\out{
}} 119 | \describe{ 120 | \item{\code{detailed}}{Logical, indicating if detailed information 121 | should be returned} 122 | } 123 | \if{html}{\out{
}} 124 | } 125 | } 126 | \if{html}{\out{
}} 127 | \if{html}{\out{}} 128 | \if{latex}{\out{\hypertarget{method-vault_client_auth-enable}{}}} 129 | \subsection{Method \code{enable()}}{ 130 | Enable an authentication backend in the vault 131 | server. 132 | \subsection{Usage}{ 133 | \if{html}{\out{
}}\preformatted{vault_client_auth$enable(type, description = NULL, local = FALSE, path = NULL)}\if{html}{\out{
}} 134 | } 135 | 136 | \subsection{Arguments}{ 137 | \if{html}{\out{
}} 138 | \describe{ 139 | \item{\code{type}}{The type of authentication backend (e.g., 140 | \code{userpass}, \code{github}, \code{ldap})} 141 | 142 | \item{\code{description}}{Human-friendly description of the backend; 143 | will be returned by \verb{$list()}} 144 | 145 | \item{\code{local}}{Specifies if the auth method is local only. Local 146 | auth methods are not replicated nor (if a secondary) removed 147 | by replication.} 148 | 149 | \item{\code{path}}{Specifies the path in which to enable the auth 150 | method. Defaults to be the same as \code{type}.} 151 | } 152 | \if{html}{\out{
}} 153 | } 154 | } 155 | \if{html}{\out{
}} 156 | \if{html}{\out{}} 157 | \if{latex}{\out{\hypertarget{method-vault_client_auth-disable}{}}} 158 | \subsection{Method \code{disable()}}{ 159 | Disable an active authentication backend. 160 | \subsection{Usage}{ 161 | \if{html}{\out{
}}\preformatted{vault_client_auth$disable(path)}\if{html}{\out{
}} 162 | } 163 | 164 | \subsection{Arguments}{ 165 | \if{html}{\out{
}} 166 | \describe{ 167 | \item{\code{path}}{The path of the authentication backend to disable.} 168 | } 169 | \if{html}{\out{
}} 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /man/vault_client_cubbyhole.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vault_client_cubbyhole.R 3 | \name{vault_client_cubbyhole} 4 | \alias{vault_client_cubbyhole} 5 | \title{Cubbyhole secret store} 6 | \description{ 7 | Cubbyhole secret store 8 | 9 | Cubbyhole secret store 10 | } 11 | \details{ 12 | Interact with vault's cubbyhole key-value store. This is useful 13 | for storing simple key-value data without versioning or metadata 14 | (c.f. \link{vault_client_kv2}) that is scoped to your 15 | current token only and not accessible to anyone else. For more 16 | details please see the vault documentation 17 | https://developer.hashicorp.com/vault/docs/secrets/cubbyhole 18 | } 19 | \examples{ 20 | 21 | server <- vaultr::vault_test_server(if_disabled = message) 22 | if (!is.null(server)) { 23 | client <- server$client() 24 | 25 | # Shorter path for easier reading: 26 | cubbyhole <- client$secrets$cubbyhole 27 | cubbyhole 28 | 29 | # Write a value 30 | cubbyhole$write("cubbyhole/secret", list(key = "value")) 31 | # List it 32 | cubbyhole$list("cubbyhole") 33 | # Read it 34 | cubbyhole$read("cubbyhole/secret") 35 | # Delete it 36 | cubbyhole$delete("cubbyhole/secret") 37 | 38 | # cleanup 39 | server$kill() 40 | } 41 | } 42 | \section{Super class}{ 43 | \code{\link[vaultr:vault_client_object]{vaultr::vault_client_object}} -> \code{vault_client_cubbyhole} 44 | } 45 | \section{Methods}{ 46 | \subsection{Public methods}{ 47 | \itemize{ 48 | \item \href{#method-vault_client_cubbyhole-new}{\code{vault_client_cubbyhole$new()}} 49 | \item \href{#method-vault_client_cubbyhole-read}{\code{vault_client_cubbyhole$read()}} 50 | \item \href{#method-vault_client_cubbyhole-write}{\code{vault_client_cubbyhole$write()}} 51 | \item \href{#method-vault_client_cubbyhole-list}{\code{vault_client_cubbyhole$list()}} 52 | \item \href{#method-vault_client_cubbyhole-delete}{\code{vault_client_cubbyhole$delete()}} 53 | } 54 | } 55 | \if{html}{\out{ 56 |
Inherited methods 57 | 61 |
62 | }} 63 | \if{html}{\out{
}} 64 | \if{html}{\out{}} 65 | \if{latex}{\out{\hypertarget{method-vault_client_cubbyhole-new}{}}} 66 | \subsection{Method \code{new()}}{ 67 | Create a \code{vault_client_cubbyhole} object. Not typically 68 | called by users. 69 | \subsection{Usage}{ 70 | \if{html}{\out{
}}\preformatted{vault_client_cubbyhole$new(api_client)}\if{html}{\out{
}} 71 | } 72 | 73 | \subsection{Arguments}{ 74 | \if{html}{\out{
}} 75 | \describe{ 76 | \item{\code{api_client}}{A \link{vault_api_client} object} 77 | } 78 | \if{html}{\out{
}} 79 | } 80 | } 81 | \if{html}{\out{
}} 82 | \if{html}{\out{}} 83 | \if{latex}{\out{\hypertarget{method-vault_client_cubbyhole-read}{}}} 84 | \subsection{Method \code{read()}}{ 85 | Read a value from your cubbyhole 86 | \subsection{Usage}{ 87 | \if{html}{\out{
}}\preformatted{vault_client_cubbyhole$read(path, field = NULL, metadata = FALSE)}\if{html}{\out{
}} 88 | } 89 | 90 | \subsection{Arguments}{ 91 | \if{html}{\out{
}} 92 | \describe{ 93 | \item{\code{path}}{Path for the secret to read, such as 94 | \verb{/cubbyhole/mysecret}} 95 | 96 | \item{\code{field}}{Optional field to read from the secret. Each 97 | secret is stored as a key/value set (represented in R as a 98 | named list) and this is equivalent to using \verb{[[field]]} 99 | on the return value. The default, \code{NULL}, returns the 100 | full set of values.} 101 | 102 | \item{\code{metadata}}{Logical, indicating if we should return 103 | metadata for this secret (lease information etc) as an 104 | attribute along with the values itself. Ignored if 105 | \code{field} is specified.} 106 | } 107 | \if{html}{\out{
}} 108 | } 109 | } 110 | \if{html}{\out{
}} 111 | \if{html}{\out{}} 112 | \if{latex}{\out{\hypertarget{method-vault_client_cubbyhole-write}{}}} 113 | \subsection{Method \code{write()}}{ 114 | Write data into your cubbyhole. 115 | \subsection{Usage}{ 116 | \if{html}{\out{
}}\preformatted{vault_client_cubbyhole$write(path, data)}\if{html}{\out{
}} 117 | } 118 | 119 | \subsection{Arguments}{ 120 | \if{html}{\out{
}} 121 | \describe{ 122 | \item{\code{path}}{Path for the secret to write, such as 123 | \verb{/cubbyhole/mysecret}} 124 | 125 | \item{\code{data}}{A named list of values to write into the vault at 126 | this path. This \emph{replaces} any existing values.} 127 | } 128 | \if{html}{\out{
}} 129 | } 130 | } 131 | \if{html}{\out{
}} 132 | \if{html}{\out{}} 133 | \if{latex}{\out{\hypertarget{method-vault_client_cubbyhole-list}{}}} 134 | \subsection{Method \code{list()}}{ 135 | List data in the vault at a give path. This can 136 | be used to list keys, etc (e.g., at \verb{/cubbyhole}). 137 | \subsection{Usage}{ 138 | \if{html}{\out{
}}\preformatted{vault_client_cubbyhole$list(path, full_names = FALSE)}\if{html}{\out{
}} 139 | } 140 | 141 | \subsection{Arguments}{ 142 | \if{html}{\out{
}} 143 | \describe{ 144 | \item{\code{path}}{The path to list} 145 | 146 | \item{\code{full_names}}{Logical, indicating if full paths (relative 147 | to the vault root) should be returned.} 148 | 149 | \item{\code{value}}{A character vector (of zero length if no keys are 150 | found). Paths that are "directories" (i.e., that contain 151 | keys and could themselves be listed) will be returned with 152 | a trailing forward slash, e.g. \verb{path/}} 153 | } 154 | \if{html}{\out{
}} 155 | } 156 | } 157 | \if{html}{\out{
}} 158 | \if{html}{\out{}} 159 | \if{latex}{\out{\hypertarget{method-vault_client_cubbyhole-delete}{}}} 160 | \subsection{Method \code{delete()}}{ 161 | Delete a value from the vault 162 | \subsection{Usage}{ 163 | \if{html}{\out{
}}\preformatted{vault_client_cubbyhole$delete(path)}\if{html}{\out{
}} 164 | } 165 | 166 | \subsection{Arguments}{ 167 | \if{html}{\out{
}} 168 | \describe{ 169 | \item{\code{path}}{The path to delete} 170 | } 171 | \if{html}{\out{
}} 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /man/vault_client_object.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aaa.R 3 | \name{vault_client_object} 4 | \alias{vault_client_object} 5 | \title{Base object type} 6 | \description{ 7 | Base object type 8 | 9 | Base object type 10 | } 11 | \details{ 12 | Base object used by vaultr for all objects 13 | } 14 | \examples{ 15 | 16 | server <- vaultr::vault_test_server(if_disabled = message) 17 | 18 | if (!is.null(server)) { 19 | client <- vaultr::vault_client(addr = server$addr) 20 | client$operator$format() 21 | client$operator$format(TRUE) 22 | } 23 | } 24 | \section{Methods}{ 25 | \subsection{Public methods}{ 26 | \itemize{ 27 | \item \href{#method-vault_client_object-new}{\code{vault_client_object$new()}} 28 | \item \href{#method-vault_client_object-format}{\code{vault_client_object$format()}} 29 | \item \href{#method-vault_client_object-help}{\code{vault_client_object$help()}} 30 | } 31 | } 32 | \if{html}{\out{
}} 33 | \if{html}{\out{}} 34 | \if{latex}{\out{\hypertarget{method-vault_client_object-new}{}}} 35 | \subsection{Method \code{new()}}{ 36 | Construct an object 37 | \subsection{Usage}{ 38 | \if{html}{\out{
}}\preformatted{vault_client_object$new(description)}\if{html}{\out{
}} 39 | } 40 | 41 | \subsection{Arguments}{ 42 | \if{html}{\out{
}} 43 | \describe{ 44 | \item{\code{description}}{Description for the object, will be printed} 45 | } 46 | \if{html}{\out{
}} 47 | } 48 | } 49 | \if{html}{\out{
}} 50 | \if{html}{\out{}} 51 | \if{latex}{\out{\hypertarget{method-vault_client_object-format}{}}} 52 | \subsection{Method \code{format()}}{ 53 | Format method, overriding the R6 default 54 | \subsection{Usage}{ 55 | \if{html}{\out{
}}\preformatted{vault_client_object$format(brief = FALSE)}\if{html}{\out{
}} 56 | } 57 | 58 | \subsection{Arguments}{ 59 | \if{html}{\out{
}} 60 | \describe{ 61 | \item{\code{brief}}{Logical, indicating if this is the full format or 62 | a brief (one line) format.} 63 | } 64 | \if{html}{\out{
}} 65 | } 66 | } 67 | \if{html}{\out{
}} 68 | \if{html}{\out{}} 69 | \if{latex}{\out{\hypertarget{method-vault_client_object-help}{}}} 70 | \subsection{Method \code{help()}}{ 71 | Display help for this object 72 | \subsection{Usage}{ 73 | \if{html}{\out{
}}\preformatted{vault_client_object$help()}\if{html}{\out{
}} 74 | } 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /man/vault_client_policy.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vault_client_policy.R 3 | \name{vault_client_policy} 4 | \alias{vault_client_policy} 5 | \title{Vault Policy Configuration} 6 | \description{ 7 | Vault Policy Configuration 8 | 9 | Vault Policy Configuration 10 | } 11 | \details{ 12 | Interact with vault's policies. To get started, you may want to 13 | read up on policies as described in the vault manual, here: 14 | https://developer.hashicorp.com/vault/docs/concepts/policies 15 | } 16 | \examples{ 17 | server <- vaultr::vault_test_server(if_disabled = message) 18 | if (!is.null(server)) { 19 | client <- server$client() 20 | 21 | # The test server starts with only the policies "root" (do 22 | # everything) and "default" (do nothing). 23 | client$policy$list() 24 | 25 | # Here let's make a policy that allows reading secrets from the 26 | # path /secret/develop/* but nothing else 27 | rules <- 'path "secret/develop/*" {policy = "read"}' 28 | client$policy$write("read-secret-develop", rules) 29 | 30 | # Our new rule is listed and can be read 31 | client$policy$list() 32 | client$policy$read("read-secret-develop") 33 | 34 | # For testing, let's create a secret under this path, and under 35 | # a different path: 36 | client$write("/secret/develop/password", list(value = "password")) 37 | client$write("/secret/production/password", list(value = "k2e89be@rdC#")) 38 | 39 | # Create a token that can use this policy: 40 | token <- client$auth$token$create(policies = "read-secret-develop") 41 | 42 | # Login to the vault using this token: 43 | alice <- vaultr::vault_client(addr = server$addr, 44 | login = "token", token = token) 45 | 46 | # We can read the paths that we have been granted access to: 47 | alice$read("/secret/develop/password") 48 | 49 | # We can't read secrets that are outside our path: 50 | try(alice$read("/secret/production/password")) 51 | 52 | # And we can't write: 53 | try(alice$write("/secret/develop/password", list(value = "secret"))) 54 | 55 | # cleanup 56 | server$kill() 57 | } 58 | } 59 | \section{Super class}{ 60 | \code{\link[vaultr:vault_client_object]{vaultr::vault_client_object}} -> \code{vault_client_policy} 61 | } 62 | \section{Methods}{ 63 | \subsection{Public methods}{ 64 | \itemize{ 65 | \item \href{#method-vault_client_policy-new}{\code{vault_client_policy$new()}} 66 | \item \href{#method-vault_client_policy-delete}{\code{vault_client_policy$delete()}} 67 | \item \href{#method-vault_client_policy-list}{\code{vault_client_policy$list()}} 68 | \item \href{#method-vault_client_policy-read}{\code{vault_client_policy$read()}} 69 | \item \href{#method-vault_client_policy-write}{\code{vault_client_policy$write()}} 70 | } 71 | } 72 | \if{html}{\out{ 73 |
Inherited methods 74 | 78 |
79 | }} 80 | \if{html}{\out{
}} 81 | \if{html}{\out{}} 82 | \if{latex}{\out{\hypertarget{method-vault_client_policy-new}{}}} 83 | \subsection{Method \code{new()}}{ 84 | Create a \code{vault_client_policy} object. Not typically 85 | called by users. 86 | \subsection{Usage}{ 87 | \if{html}{\out{
}}\preformatted{vault_client_policy$new(api_client)}\if{html}{\out{
}} 88 | } 89 | 90 | \subsection{Arguments}{ 91 | \if{html}{\out{
}} 92 | \describe{ 93 | \item{\code{api_client}}{A \link{vault_api_client} object} 94 | } 95 | \if{html}{\out{
}} 96 | } 97 | } 98 | \if{html}{\out{
}} 99 | \if{html}{\out{}} 100 | \if{latex}{\out{\hypertarget{method-vault_client_policy-delete}{}}} 101 | \subsection{Method \code{delete()}}{ 102 | This endpoint deletes the policy with the given 103 | name. This will immediately affect all users associated with 104 | this policy. 105 | \subsection{Usage}{ 106 | \if{html}{\out{
}}\preformatted{vault_client_policy$delete(name)}\if{html}{\out{
}} 107 | } 108 | 109 | \subsection{Arguments}{ 110 | \if{html}{\out{
}} 111 | \describe{ 112 | \item{\code{name}}{Specifies the name of the policy to delete.} 113 | } 114 | \if{html}{\out{
}} 115 | } 116 | } 117 | \if{html}{\out{
}} 118 | \if{html}{\out{}} 119 | \if{latex}{\out{\hypertarget{method-vault_client_policy-list}{}}} 120 | \subsection{Method \code{list()}}{ 121 | Lists all configured policies. 122 | \subsection{Usage}{ 123 | \if{html}{\out{
}}\preformatted{vault_client_policy$list()}\if{html}{\out{
}} 124 | } 125 | 126 | } 127 | \if{html}{\out{
}} 128 | \if{html}{\out{}} 129 | \if{latex}{\out{\hypertarget{method-vault_client_policy-read}{}}} 130 | \subsection{Method \code{read()}}{ 131 | Retrieve the policy body for the named policy 132 | \subsection{Usage}{ 133 | \if{html}{\out{
}}\preformatted{vault_client_policy$read(name)}\if{html}{\out{
}} 134 | } 135 | 136 | \subsection{Arguments}{ 137 | \if{html}{\out{
}} 138 | \describe{ 139 | \item{\code{name}}{Specifies the name of the policy to retrieve} 140 | } 141 | \if{html}{\out{
}} 142 | } 143 | } 144 | \if{html}{\out{
}} 145 | \if{html}{\out{}} 146 | \if{latex}{\out{\hypertarget{method-vault_client_policy-write}{}}} 147 | \subsection{Method \code{write()}}{ 148 | Create or update a policy. Once a policy is 149 | updated, it takes effect immediately to all associated users. 150 | \subsection{Usage}{ 151 | \if{html}{\out{
}}\preformatted{vault_client_policy$write(name, rules)}\if{html}{\out{
}} 152 | } 153 | 154 | \subsection{Arguments}{ 155 | \if{html}{\out{
}} 156 | \describe{ 157 | \item{\code{name}}{Name of the policy to update} 158 | 159 | \item{\code{rules}}{Specifies the policy document. This is a string 160 | in "HashiCorp configuration language". At present this must 161 | be read in as a single string (not a character vector of 162 | strings); future versions of vaultr may allow more flexible 163 | specification such as \verb{@filename}} 164 | } 165 | \if{html}{\out{
}} 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /man/vault_client_secrets.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vault_client_secrets.R 3 | \name{vault_client_secrets} 4 | \alias{vault_client_secrets} 5 | \title{Vault Secret Configuration} 6 | \description{ 7 | Vault Secret Configuration 8 | 9 | Vault Secret Configuration 10 | } 11 | \details{ 12 | Interact with vault's secret backends. 13 | } 14 | \examples{ 15 | 16 | server <- vaultr::vault_test_server(if_disabled = message) 17 | if (!is.null(server)) { 18 | client <- server$client() 19 | 20 | # To remove the default version 1 kv store and replace with a 21 | # version 2 store: 22 | client$secrets$disable("/secret") 23 | client$secrets$enable("kv", "/secret", version = 2) 24 | 25 | # cleanup 26 | server$kill() 27 | } 28 | } 29 | \section{Super class}{ 30 | \code{\link[vaultr:vault_client_object]{vaultr::vault_client_object}} -> \code{vault_client_secrets} 31 | } 32 | \section{Public fields}{ 33 | \if{html}{\out{
}} 34 | \describe{ 35 | \item{\code{cubbyhole}}{The cubbyhole backend: 36 | \link{vault_client_cubbyhole}} 37 | 38 | \item{\code{kv1}}{The version 1 key-value backend: 39 | \link{vault_client_kv1}} 40 | 41 | \item{\code{kv2}}{The version 2 key-value backend: 42 | \link{vault_client_kv2}} 43 | 44 | \item{\code{transit}}{The transit backend: 45 | \link{vault_client_transit}} 46 | } 47 | \if{html}{\out{
}} 48 | } 49 | \section{Methods}{ 50 | \subsection{Public methods}{ 51 | \itemize{ 52 | \item \href{#method-vault_client_secrets-new}{\code{vault_client_secrets$new()}} 53 | \item \href{#method-vault_client_secrets-disable}{\code{vault_client_secrets$disable()}} 54 | \item \href{#method-vault_client_secrets-enable}{\code{vault_client_secrets$enable()}} 55 | \item \href{#method-vault_client_secrets-list}{\code{vault_client_secrets$list()}} 56 | \item \href{#method-vault_client_secrets-move}{\code{vault_client_secrets$move()}} 57 | } 58 | } 59 | \if{html}{\out{ 60 |
Inherited methods 61 | 65 |
66 | }} 67 | \if{html}{\out{
}} 68 | \if{html}{\out{}} 69 | \if{latex}{\out{\hypertarget{method-vault_client_secrets-new}{}}} 70 | \subsection{Method \code{new()}}{ 71 | Create a \code{vault_client_secrets} object. Not typically 72 | called by users. 73 | \subsection{Usage}{ 74 | \if{html}{\out{
}}\preformatted{vault_client_secrets$new(api_client)}\if{html}{\out{
}} 75 | } 76 | 77 | \subsection{Arguments}{ 78 | \if{html}{\out{
}} 79 | \describe{ 80 | \item{\code{api_client}}{A \link{vault_api_client} object} 81 | } 82 | \if{html}{\out{
}} 83 | } 84 | } 85 | \if{html}{\out{
}} 86 | \if{html}{\out{}} 87 | \if{latex}{\out{\hypertarget{method-vault_client_secrets-disable}{}}} 88 | \subsection{Method \code{disable()}}{ 89 | Disable a previously-enabled secret engine 90 | \subsection{Usage}{ 91 | \if{html}{\out{
}}\preformatted{vault_client_secrets$disable(path)}\if{html}{\out{
}} 92 | } 93 | 94 | \subsection{Arguments}{ 95 | \if{html}{\out{
}} 96 | \describe{ 97 | \item{\code{path}}{Path of the secret engine} 98 | } 99 | \if{html}{\out{
}} 100 | } 101 | } 102 | \if{html}{\out{
}} 103 | \if{html}{\out{}} 104 | \if{latex}{\out{\hypertarget{method-vault_client_secrets-enable}{}}} 105 | \subsection{Method \code{enable()}}{ 106 | Enable a secret backend in the vault server 107 | \subsection{Usage}{ 108 | \if{html}{\out{
}}\preformatted{vault_client_secrets$enable( 109 | type, 110 | path = type, 111 | description = NULL, 112 | version = NULL 113 | )}\if{html}{\out{
}} 114 | } 115 | 116 | \subsection{Arguments}{ 117 | \if{html}{\out{
}} 118 | \describe{ 119 | \item{\code{type}}{The type of secret backend (e.g., \code{transit}, \code{kv}).} 120 | 121 | \item{\code{path}}{Specifies the path in which to enable the auth 122 | method. Defaults to be the same as \code{type}.} 123 | 124 | \item{\code{description}}{Human-friendly description of the backend; 125 | will be returned by \verb{$list()}} 126 | 127 | \item{\code{version}}{Used only for the \code{kv} backend, where an integer 128 | is used to select between \link{vault_client_kv1} and 129 | \link{vault_client_kv2} engines.} 130 | } 131 | \if{html}{\out{
}} 132 | } 133 | } 134 | \if{html}{\out{
}} 135 | \if{html}{\out{}} 136 | \if{latex}{\out{\hypertarget{method-vault_client_secrets-list}{}}} 137 | \subsection{Method \code{list()}}{ 138 | List enabled secret engines 139 | \subsection{Usage}{ 140 | \if{html}{\out{
}}\preformatted{vault_client_secrets$list(detailed = FALSE)}\if{html}{\out{
}} 141 | } 142 | 143 | \subsection{Arguments}{ 144 | \if{html}{\out{
}} 145 | \describe{ 146 | \item{\code{detailed}}{Logical, indicating if detailed output is 147 | wanted.} 148 | } 149 | \if{html}{\out{
}} 150 | } 151 | } 152 | \if{html}{\out{
}} 153 | \if{html}{\out{}} 154 | \if{latex}{\out{\hypertarget{method-vault_client_secrets-move}{}}} 155 | \subsection{Method \code{move()}}{ 156 | Move the path that a secret engine is mounted at 157 | \subsection{Usage}{ 158 | \if{html}{\out{
}}\preformatted{vault_client_secrets$move(from, to)}\if{html}{\out{
}} 159 | } 160 | 161 | \subsection{Arguments}{ 162 | \if{html}{\out{
}} 163 | \describe{ 164 | \item{\code{from}}{Original path} 165 | 166 | \item{\code{to}}{New path} 167 | } 168 | \if{html}{\out{
}} 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /man/vault_client_tools.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vault_client_tools.R 3 | \name{vault_client_tools} 4 | \alias{vault_client_tools} 5 | \title{Vault Tools} 6 | \description{ 7 | Vault Tools 8 | 9 | Vault Tools 10 | } 11 | \details{ 12 | Interact with vault's cryptographic tools. This provides support 13 | for high-quality random numbers and cryptographic hashes. This 14 | functionality is also available through the transit secret engine. 15 | } 16 | \examples{ 17 | server <- vaultr::vault_test_server(if_disabled = message) 18 | if (!is.null(server)) { 19 | client <- server$client() 20 | 21 | # Random bytes in hex 22 | client$tools$random() 23 | # base64 24 | client$tools$random(format = "base64") 25 | # raw 26 | client$tools$random(10, format = "raw") 27 | 28 | # Hash data: 29 | data <- charToRaw("hello vault") 30 | # will produce 55e702...92efd40c2a4 31 | client$tools$hash(data) 32 | 33 | # sha2-512 hash: 34 | client$tools$hash(data, "sha2-512") 35 | 36 | # cleanup 37 | server$kill() 38 | } 39 | } 40 | \section{Super class}{ 41 | \code{\link[vaultr:vault_client_object]{vaultr::vault_client_object}} -> \code{vault_client_tools} 42 | } 43 | \section{Methods}{ 44 | \subsection{Public methods}{ 45 | \itemize{ 46 | \item \href{#method-vault_client_tools-new}{\code{vault_client_tools$new()}} 47 | \item \href{#method-vault_client_tools-random}{\code{vault_client_tools$random()}} 48 | \item \href{#method-vault_client_tools-hash}{\code{vault_client_tools$hash()}} 49 | } 50 | } 51 | \if{html}{\out{ 52 |
Inherited methods 53 | 57 |
58 | }} 59 | \if{html}{\out{
}} 60 | \if{html}{\out{}} 61 | \if{latex}{\out{\hypertarget{method-vault_client_tools-new}{}}} 62 | \subsection{Method \code{new()}}{ 63 | Create a \code{vault_client_tools} object. Not typically 64 | called by users. 65 | \subsection{Usage}{ 66 | \if{html}{\out{
}}\preformatted{vault_client_tools$new(api_client)}\if{html}{\out{
}} 67 | } 68 | 69 | \subsection{Arguments}{ 70 | \if{html}{\out{
}} 71 | \describe{ 72 | \item{\code{api_client}}{A \link{vault_api_client} object} 73 | } 74 | \if{html}{\out{
}} 75 | } 76 | } 77 | \if{html}{\out{
}} 78 | \if{html}{\out{}} 79 | \if{latex}{\out{\hypertarget{method-vault_client_tools-random}{}}} 80 | \subsection{Method \code{random()}}{ 81 | Generates high-quality random bytes of the 82 | specified length. This is totally independent of R's random 83 | number stream and provides random numbers suitable for 84 | cryptographic purposes. 85 | \subsection{Usage}{ 86 | \if{html}{\out{
}}\preformatted{vault_client_tools$random(bytes = 32, format = "hex")}\if{html}{\out{
}} 87 | } 88 | 89 | \subsection{Arguments}{ 90 | \if{html}{\out{
}} 91 | \describe{ 92 | \item{\code{bytes}}{Number of bytes to generate (as an integer)} 93 | 94 | \item{\code{format}}{The output format to produce; must be one of 95 | \code{hex} (a single hex string such as \code{d1189e2f83b72ab6}), 96 | \code{base64} (a single base64 encoded string such as 97 | \verb{8TDJekY0mYs=}) or \code{raw} (a raw vector of length \code{bytes}).} 98 | } 99 | \if{html}{\out{
}} 100 | } 101 | } 102 | \if{html}{\out{
}} 103 | \if{html}{\out{}} 104 | \if{latex}{\out{\hypertarget{method-vault_client_tools-hash}{}}} 105 | \subsection{Method \code{hash()}}{ 106 | Generates a cryptographic hash of given data using 107 | the specified algorithm. 108 | \subsection{Usage}{ 109 | \if{html}{\out{
}}\preformatted{vault_client_tools$hash(data, algorithm = NULL, format = "hex")}\if{html}{\out{
}} 110 | } 111 | 112 | \subsection{Arguments}{ 113 | \if{html}{\out{
}} 114 | \describe{ 115 | \item{\code{data}}{A raw vector of data to hash. To generate a raw 116 | vector from an R object, one option is to use \code{unserialize(x, NULL)} but be aware that version information may be included. 117 | Alternatively, for a string, one might use \code{charToRaw}.} 118 | 119 | \item{\code{algorithm}}{A string indicating the hash algorithm to use. 120 | The exact set of supported algorithms may depend by vault 121 | server version, but as of version 1.0.0 vault supports 122 | \code{sha2-224}, \code{sha2-256}, \code{sha2-384} and \code{sha2-512}. The 123 | default is \code{sha2-256}.} 124 | 125 | \item{\code{format}}{The format of the output - must be one of \code{hex} 126 | or \code{base64}.} 127 | } 128 | \if{html}{\out{
}} 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /man/vault_resolve_secrets.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vault_resolve_secrets.R 3 | \name{vault_resolve_secrets} 4 | \alias{vault_resolve_secrets} 5 | \title{Resolve secrets from R objects} 6 | \usage{ 7 | vault_resolve_secrets(x, ..., login = TRUE, vault_args = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{List of values, some of which may refer to vault secrets 11 | (see Details for pattern). Any values that are not strings or 12 | do not match the pattern of a secret are left as-is.} 13 | 14 | \item{...}{Args to be passed to \link{vault_client} call.} 15 | 16 | \item{login}{Login method to be passed to call to 17 | \link{vault_client}.} 18 | 19 | \item{vault_args}{As an alternative to using \code{login} and \code{...}, a 20 | list of (named) arguments can be provided here, equivalent to 21 | the full set of arguments that you might pass to 22 | \link{vault_client}. If provided, then \code{login} is ignored 23 | and if additional arguments are provided through \code{...} an error 24 | will be thrown.} 25 | } 26 | \value{ 27 | List of properties with any vault secrets resolved. 28 | } 29 | \description{ 30 | Use vault to resolve secrets. This is a convenience function that 31 | wraps a pattern that we have used in a few applications of vault. 32 | The idea is to allow replacement of data in configuration with 33 | special strings that indicate that the string refers to a vault 34 | secret. This function resolves those secrets. 35 | } 36 | \details{ 37 | For each element of the data, if a string matches the form: 38 | 39 | \if{html}{\out{
}}\preformatted{ VAULT:: 40 | }\if{html}{\out{
}} 41 | 42 | then it will be treated as a vault secret and resolved. The 43 | \verb{} will be something like 44 | \verb{/secret/path/password} and the \verb{} the name of a 45 | field in the key/value data stored at that path. For example, 46 | suppose you have the data \code{list(username = "alice", password = "s3cret!")} stored at \verb{/secret/database/user}, then the 47 | string 48 | 49 | \if{html}{\out{
}}\preformatted{ VAULT:/secret/database/user:password 50 | }\if{html}{\out{
}} 51 | 52 | would refer to the value \verb{s3cret!} 53 | } 54 | \examples{ 55 | 56 | server <- vaultr::vault_test_server(if_disabled = message) 57 | 58 | if (!is.null(server)) { 59 | client <- server$client() 60 | # The example from above: 61 | client$write("/secret/database/user", 62 | list(username = "alice", password = "s3cret!")) 63 | 64 | # A list of data that contains a mix of secrets to be resolved 65 | # and other data: 66 | x <- list(user = "alice", 67 | password = "VAULT:/secret/database/user:password", 68 | port = 5678) 69 | 70 | # Explicitly pass in the login details and resolve the secrets: 71 | vaultr::vault_resolve_secrets(x, login = "token", token = server$token, 72 | addr = server$addr) 73 | 74 | # Alternatively, if appropriate environment variables are set 75 | # then this can be done more easily: 76 | if (requireNamespace("withr", quietly = TRUE)) { 77 | env <- c(VAULTR_AUTH_METHOD = "token", 78 | VAULT_TOKEN = server$token, 79 | VAULT_ADDR = server$addr) 80 | withr::with_envvar(env, vault_resolve_secrets(x)) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /man/vaultr-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/vaultr.R 3 | \docType{package} 4 | \name{vaultr-package} 5 | \alias{vaultr} 6 | \alias{vaultr-package} 7 | \title{Vault Client for Secrets and Sensitive Data} 8 | \description{ 9 | Vault client for secrets and sensitive data; this package provides 10 | wrappers for HashiCorp's \href{https://vaultproject.io}{vault server}. 11 | The package wraps most of the high-level API, and includes support 12 | for authentication via a number of backends (tokens, username and 13 | password, github, and "AppRole"), as well as a number of secrets 14 | engines (two key-value stores, vault's cubbyhole and the transit 15 | backend for encryption-as-a-service). 16 | } 17 | \details{ 18 | To get started, you might want to start with the "vaultr" 19 | vignette, available from the package with \code{vignette("vaultr")}. 20 | 21 | The basic design of the package is that it has very few 22 | entrypoints - for most uses one will interact almost entirely with 23 | the \link{vault_client} function. That function returns an 24 | R6 object with several methods (functions) but also several 25 | objects that themselves contain more methods and objects, creating 26 | a nested tree of functionality. 27 | 28 | From any object, online help is available via the help method, for 29 | example 30 | 31 | \if{html}{\out{
}}\preformatted{client <- vaultr::vault_client() 32 | client$secrets$transit$help() 33 | }\if{html}{\out{
}} 34 | 35 | For testing packages that rely on vault, there is support for 36 | creating temporary vault servers; see \code{vaultr::vault_test_server} 37 | and the "packages" vignette. 38 | } 39 | \seealso{ 40 | Useful links: 41 | \itemize{ 42 | \item \url{https://github.com/vimc/vaultr} 43 | \item \url{https://www.vaccineimpact.org/vaultr/} 44 | \item Report bugs at \url{https://github.com/vimc/vaultr/issues} 45 | } 46 | 47 | } 48 | \author{ 49 | \strong{Maintainer}: Rich FitzJohn \email{rich.fitzjohn@gmail.com} 50 | 51 | Authors: 52 | \itemize{ 53 | \item Robert Ashton 54 | \item Wes Hinsley 55 | } 56 | 57 | Other contributors: 58 | \itemize{ 59 | \item Imperial College of Science, Technology and Medicine [copyright holder] 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(vaultr) 3 | 4 | test_check("vaultr") 5 | -------------------------------------------------------------------------------- /tests/testthat/helper-ldap.R: -------------------------------------------------------------------------------- 1 | ldap_configure <- function(object) { 2 | use_docker <- Sys_getenv("VAULTR_TEST_LDAP_USE_DOCKER", "false") 3 | if (tolower(use_docker) == "true") { 4 | ldap_configure_docker(object) 5 | } else { 6 | ldap_configure_public(object) 7 | } 8 | } 9 | 10 | 11 | ldap_configure_docker <- function(object) { 12 | configuration <- list( 13 | url = "ldap://localhost:10389", 14 | binddn = "cn=admin,dc=planetexpress,dc=com", 15 | bindpass = "GoodNewsEveryone", 16 | userdn = "ou=people,dc=planetexpress,dc=com", 17 | userattr = "uid", 18 | groupdn = "ou=people,dc=planetexpress,dc=com", 19 | groupattr = "cn") 20 | do.call(object$configure, configuration) 21 | list(username = "hermes", 22 | password = "hermes", 23 | group = "admin_staff", 24 | configuration = configuration) 25 | } 26 | 27 | 28 | ldap_configure_public <- function(object) { 29 | configuration <- list( 30 | url = "ldap://ldap.forumsys.com", 31 | binddn = "cn=read-only-admin,dc=example,dc=com", 32 | bindpass = "password", 33 | userdn = "dc=example,dc=com", 34 | userattr = "uid", 35 | groupdn = "dc=example,dc=com", 36 | groupattr = "ou", 37 | groupfilter = "(uniqueMember={{.UserDN}})") 38 | do.call(object$configure, configuration) 39 | list(username = "einstein", 40 | password = "password", 41 | group = "scientists", 42 | configuration = configuration) 43 | } 44 | -------------------------------------------------------------------------------- /tests/testthat/helper-vault.R: -------------------------------------------------------------------------------- 1 | skip_if_no_vaultr_test_github_pat <- function() { 2 | if (tolower(Sys.info()[["sysname"]]) == "darwin") { 3 | # Fails with rate limit issues, quite tedious. 4 | testthat::skip_on_ci() 5 | } 6 | if (has_vaultr_test_github_pat()) { 7 | return(invisible(TRUE)) 8 | } 9 | testthat::skip("No access token set") 10 | } 11 | 12 | 13 | test_vault_test_server <- function(..., quiet = TRUE) { 14 | skip_on_cran() 15 | vault_test_server(..., quiet = quiet) 16 | } 17 | 18 | has_vaultr_test_github_pat <- function() { 19 | nzchar(vaultr_test_github_pat()) 20 | } 21 | 22 | vaultr_test_github_pat <- function() { 23 | Sys.getenv("VAULTR_TEST_GITHUB_PAT", "") 24 | } 25 | 26 | get_error <- function(expr) { 27 | tryCatch(expr, error = identity) 28 | } 29 | 30 | has_internet <- function() { 31 | !is.null(suppressWarnings(utils::nsl("www.google.com"))) 32 | } 33 | 34 | skip_if_no_internet <- function() { 35 | testthat::skip_on_cran() # not worth it 36 | testthat::skip_on_os("windows") 37 | if (has_internet()) { 38 | return() 39 | } 40 | testthat::skip("no internet") 41 | } 42 | 43 | 44 | skip_if_no_vault_bin <- function() { 45 | path <- vault_server_manager_bin() 46 | if (is.null(path)) { 47 | testthat::skip("vault bin path not found") 48 | } 49 | testthat::skip_on_cran() 50 | } 51 | 52 | 53 | skip_if_vault_before <- function(required, server, api, description) { 54 | have <- server$version() 55 | if (have < required) { 56 | testthat::skip( 57 | vault_invalid_version(required, have, api, description)$message) 58 | } 59 | } 60 | 61 | 62 | read_vault_env <- function() { 63 | tmp <- tempfile() 64 | on.exit(unlink(tmp)) 65 | writeLines(sub("^export\\s+", "", readLines(".vault-env")), tmp) 66 | readRenviron(tmp) 67 | } 68 | 69 | 70 | ## This wants refactoring because we'll move away from global state 71 | ## and instead use a version that starts and stops at each use. 72 | test_vault_client <- function(..., login = TRUE) { 73 | read_vault_env() 74 | cl <- vault_client(...) 75 | if (login) { 76 | cl$login() 77 | } 78 | cl 79 | } 80 | 81 | 82 | ## Enough interface to use for the token cache: 83 | fake_api_client <- function(addr, success) { 84 | force(success) 85 | list(addr = addr, 86 | verify_token = function(token, quiet) list(success = success)) 87 | } 88 | 89 | 90 | wait_kv_upgrade <- function(kv, p, n = 50, poll = 0.2) { # max 10s by default 91 | force(p) 92 | force(kv) 93 | for (i in seq_len(n)) { 94 | ok <- tryCatch({ 95 | kv$list(p) 96 | TRUE 97 | }, error = function(e) FALSE) 98 | if (ok) { 99 | break 100 | } 101 | Sys.sleep(poll) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tests/testthat/test-cache.R: -------------------------------------------------------------------------------- 1 | test_that("cache", { 2 | cache <- token_cache$new() 3 | expect_s3_class(cache, "token_cache") 4 | expect_s3_class(vault_env$cache, "token_cache") 5 | }) 6 | 7 | 8 | ## integration tests: 9 | test_that("invalidation is handled gracefully", { 10 | srv <- test_vault_test_server() 11 | cl <- srv$client() 12 | vault_env$cache$clear() 13 | cl$auth$enable("userpass", "user / password based auth") 14 | cl$auth$userpass$write("rich", "pass") 15 | 16 | cl2 <- srv$client(login = FALSE) 17 | cl2$login(username = "rich", password = "pass", method = "userpass", 18 | quiet = TRUE) 19 | 20 | expect_equal(vault_env$cache$list(), cl$api()$addr) 21 | expect_equal(vault_env$cache$get(cl$api()), cl2$api()$token) 22 | 23 | cl3 <- srv$client(login = FALSE) 24 | cl3$login(username = "rich", password = "pass", method = "userpass", 25 | quiet = TRUE) 26 | expect_equal(cl3$api()$token, cl2$api()$token) 27 | expect_false(cl3$api()$token == cl$api()$token) 28 | 29 | ## Then invalidate our token: 30 | cl2$token$revoke_self() 31 | expect_null(vault_env$cache$get(cl$api())) 32 | 33 | ## Next login gets fresh token: 34 | cl4 <- srv$client(login = FALSE) 35 | cl4$login(username = "rich", password = "pass", method = "userpass", 36 | quiet = TRUE) 37 | expect_false(cl4$api()$token == cl$api()$token) 38 | expect_false(cl4$api()$token == cl2$api()$token) 39 | }) 40 | 41 | 42 | test_that("cache behaviour", { 43 | cache <- token_cache$new() 44 | cl <- fake_api_client("addr", TRUE) 45 | t <- fake_token() 46 | 47 | ## empty 48 | expect_null(cache$get(cl)) 49 | expect_equal(cache$list(), character()) 50 | 51 | cache$set(cl, t, FALSE) 52 | 53 | expect_null(cache$get(cl)) 54 | expect_equal(cache$list(), character()) 55 | 56 | cache$set(cl, t, TRUE) 57 | expect_equal(cache$get(cl), t) 58 | expect_equal(cache$list(), cl$addr) 59 | 60 | expect_null(cache$get(cl, FALSE)) 61 | 62 | ## A failed lookup invalidates the cache: 63 | expect_null(cache$get(fake_api_client(cl$addr, FALSE))) 64 | expect_equal(cache$list(), character()) 65 | }) 66 | 67 | 68 | test_that("multiple servers", { 69 | cache <- token_cache$new() 70 | cl1 <- fake_api_client("a", TRUE) 71 | cl2 <- fake_api_client("b", TRUE) 72 | t1 <- fake_token() 73 | t2 <- fake_token() 74 | 75 | cache$set(cl1, t1) 76 | cache$set(cl2, t2) 77 | 78 | expect_setequal(cache$list(), c(cl1$addr, cl2$addr)) 79 | expect_equal(cache$get(cl1, TRUE), t1) 80 | expect_equal(cache$get(cl2, TRUE), t2) 81 | 82 | cache$clear() 83 | expect_equal(cache$list(), character()) 84 | }) 85 | 86 | 87 | test_that("delete", { 88 | cache <- token_cache$new() 89 | cl1 <- fake_api_client("a", TRUE) 90 | cl2 <- fake_api_client("b", TRUE) 91 | t1 <- fake_token() 92 | t2 <- fake_token() 93 | 94 | cache$set(cl1, t1) 95 | cache$set(cl2, t2) 96 | 97 | cache$delete(cl1) 98 | 99 | expect_setequal(cache$list(), c(cl2$addr)) 100 | }) 101 | 102 | 103 | test_that("token_only skips cache", { 104 | srv <- test_vault_test_server() 105 | cl <- srv$client() 106 | vault_env$cache$clear() 107 | cl$auth$enable("userpass", "user / password based auth") 108 | cl$auth$userpass$write("rich", "pass") 109 | 110 | cl2 <- srv$client(login = FALSE) 111 | cl2$login(username = "rich", password = "pass", method = "userpass", 112 | quiet = TRUE) 113 | 114 | t <- cl2$login(username = "rich", password = "pass", method = "userpass", 115 | token_only = TRUE, quiet = TRUE) 116 | expect_false(t == cl2$token$client()) 117 | expect_equal(vault_env$cache$get(cl2$api()), cl2$token$client()) 118 | }) 119 | 120 | 121 | test_that("token_only works with no cache", { 122 | srv <- test_vault_test_server() 123 | cl <- srv$client() 124 | vault_env$cache$clear() 125 | cl$auth$enable("userpass", "user / password based auth") 126 | cl$auth$userpass$write("rich", "pass") 127 | 128 | cl2 <- srv$client(login = FALSE) 129 | 130 | t <- cl2$login(username = "rich", password = "pass", method = "userpass", 131 | token_only = TRUE, quiet = TRUE) 132 | expect_type(t, "character") 133 | expect_null(cl2$token$client()) 134 | expect_null(vault_env$cache$get(cl2$api())) 135 | }) 136 | -------------------------------------------------------------------------------- /tests/testthat/test-object.R: -------------------------------------------------------------------------------- 1 | test_that("format", { 2 | x <- vault_client_object$new("my description") 3 | private <- environment(x$initialize)$private 4 | expect_equal(private$name, "object") 5 | expect_equal(private$description, "my description") 6 | expect_equal(x$format(TRUE), "my description") 7 | expect_equal(x$format(FALSE)[[1]], "") 8 | }) 9 | 10 | 11 | test_that("help: base class", { 12 | x <- vault_client_object$new("my description") 13 | mock_help <- mockery::mock(NULL) 14 | mockery::stub(x$help, "utils::help", mock_help) 15 | x$help() 16 | args <- mockery::mock_args(mock_help)[[1]] 17 | expect_equal(args, list("vault_client_object", package = "vaultr")) 18 | }) 19 | 20 | 21 | test_that("help: derived class", { 22 | other <- R6::R6Class( 23 | "other", 24 | inherit = vault_client_object, 25 | public = list( 26 | initialize = function() super$initialize("description"))) 27 | x <- other$new() 28 | mock_help <- mockery::mock(NULL) 29 | mockery::stub(x$help, "utils::help", mock_help) 30 | x$help() 31 | args <- mockery::mock_args(mock_help)[[1]] 32 | expect_equal(args, list("other", package = "vaultr")) 33 | }) 34 | -------------------------------------------------------------------------------- /tests/testthat/test-server-manager.R: -------------------------------------------------------------------------------- 1 | test_that("safeguards for run", { 2 | skip_on_cran() 3 | 4 | withr::with_envvar(c(NOT_CRAN = NA_character_), { 5 | expect_null(vault_server_manager_bin()) 6 | }) 7 | 8 | withr::with_envvar(c(VAULTR_TEST_SERVER_BIN_PATH = NA_character_), { 9 | expect_null(vault_server_manager_bin()) 10 | }) 11 | 12 | withr::with_envvar(c(VAULTR_TEST_SERVER_BIN_PATH = tempfile()), { 13 | expect_null(vault_server_manager_bin()) 14 | }) 15 | 16 | path <- tempfile() 17 | dir.create(path) 18 | withr::with_envvar(c(VAULTR_TEST_SERVER_BIN_PATH = path), { 19 | expect_null(vault_server_manager_bin()) 20 | }) 21 | 22 | vault <- file.path(path, vault_exe_filename()) 23 | file.create(vault) 24 | withr::with_envvar(c(VAULTR_TEST_SERVER_BIN_PATH = path), { 25 | expect_equal(normalizePath(vault_server_manager_bin()), 26 | normalizePath(vault)) 27 | }) 28 | 29 | withr::with_envvar(c(VAULTR_TEST_SERVER_PORT = NA_character_), { 30 | expect_equal(vault_server_manager_port(), 18200L) 31 | }) 32 | withr::with_envvar(c(VAULTR_TEST_SERVER_PORT = "1000"), { 33 | expect_equal(vault_server_manager_port(), 1000) 34 | }) 35 | withr::with_envvar(c(VAULTR_TEST_SERVER_PORT = "port"), { 36 | expect_error(vault_server_manager_port(), "Invalid port 'port'") 37 | }) 38 | }) 39 | 40 | 41 | test_that("allow use of directory containing a file vault", { 42 | skip_on_cran() 43 | skip_if_no_vault_bin() 44 | path <- withr::local_tempdir() 45 | exe <- file.path(path, vault_exe_filename()) 46 | file.create(exe) 47 | withr::with_envvar(c(VAULTR_TEST_SERVER_BIN_PATH = path), { 48 | expect_equal(vault_server_manager_bin(), 49 | normalizePath(exe)) 50 | }) 51 | withr::with_envvar(c(VAULTR_TEST_SERVER_BIN_PATH = exe), { 52 | expect_equal(vault_server_manager_bin(), 53 | normalizePath(exe)) 54 | }) 55 | }) 56 | 57 | 58 | test_that("use value found by which if requested", { 59 | skip_on_cran() 60 | skip_if_no_vault_bin() 61 | skip_if_not_installed("mockery") 62 | path <- withr::local_tempdir() 63 | exe <- file.path(path, vault_exe_filename()) 64 | file.create(exe) 65 | 66 | mock_which <- mockery::mock(setNames(exe, "vault"), "") 67 | mockery::stub(vault_server_manager_bin, "Sys.which", mock_which) 68 | 69 | withr::with_envvar(c(VAULTR_TEST_SERVER_BIN_PATH = "auto"), { 70 | ## First mock call finds path 71 | expect_equal(vault_server_manager_bin(), 72 | normalizePath(exe)) 73 | ## Second mock call does not 74 | expect_null(vault_server_manager_bin()) 75 | }) 76 | }) 77 | 78 | 79 | test_that("disabled server manager", { 80 | skip_on_cran() 81 | res <- vault_server_manager$new(NULL) 82 | expect_false(res$enabled) 83 | expect_equal(res$new_server(if_disabled = identity), 84 | "vault is not enabled") 85 | expect_error(res$new_server(if_disabled = stop), 86 | "vault is not enabled") 87 | }) 88 | 89 | 90 | test_that("timeout catch", { 91 | skip_on_cran() 92 | test <- function() FALSE 93 | path <- tempfile() 94 | txt <- c("information about the process", 95 | "on two lines") 96 | writeLines(txt, path) 97 | process <- list(is_alive = function() FALSE, 98 | get_error_file = function() path) 99 | expect_error(vault_server_wait(test, process), 100 | paste(c("vault has died:", txt), collapse = "\n"), 101 | fixed = TRUE) 102 | }) 103 | 104 | 105 | test_that("vault_platform", { 106 | skip_on_cran() 107 | expect_equal(vault_platform("Darwin"), "darwin") 108 | expect_equal(vault_platform("Windows"), "windows") 109 | expect_equal(vault_platform("Linux"), "linux") 110 | expect_error(vault_platform("Solaris"), "Unknown sysname") 111 | }) 112 | 113 | 114 | test_that("env", { 115 | srv <- test_vault_test_server() 116 | env <- srv$env() 117 | expect_equal(env[["VAULT_ADDR"]], srv$addr) 118 | expect_equal(env[["VAULT_TOKEN"]], srv$token) 119 | expect_equal(env[["VAULTR_AUTH_METHOD"]], "token") 120 | expect_equal(env[["VAULT_CACERT"]], NA_character_) 121 | expect_setequal(names(env), 122 | c("VAULT_ADDR", "VAULT_TOKEN", "VAULT_CACERT", 123 | "VAULTR_AUTH_METHOD")) 124 | 125 | env[] <- NA_character_ 126 | withr::with_envvar(env, { 127 | srv$export() 128 | expect_equal(Sys.getenv("VAULT_ADDR"), srv$addr) 129 | expect_equal(Sys.getenv("VAULT_TOKEN"), srv$token) 130 | expect_equal(Sys.getenv("VAULTR_AUTH_METHOD"), "token") 131 | expect_identical(Sys.getenv("VAULT_CACERT", NA_character_), NA_character_) 132 | }) 133 | }) 134 | 135 | 136 | test_that("clear tokens", { 137 | srv <- test_vault_test_server() 138 | vault_env$cache$clear() 139 | 140 | cl <- srv$client() 141 | cl$auth$enable("userpass") 142 | cl$auth$userpass$write("alice", "password") 143 | cl2 <- srv$client(login = FALSE) 144 | cl2$login(method = "userpass", username = "alice", password = "password", 145 | quiet = TRUE) 146 | 147 | expect_equal(vault_env$cache$list(), srv$addr) 148 | srv$clear_cached_token() 149 | expect_equal(vault_env$cache$list(), character(0)) 150 | }) 151 | 152 | 153 | test_that("skip if server does not come up", { 154 | testthat::skip_on_cran() 155 | testthat::skip_on_os("windows") 156 | tmp <- withr::local_tempfile() 157 | file.create(tmp) 158 | port <- vault_server_manager_port() + 20 159 | mgr <- vault_server_manager$new(tmp, port) 160 | err <- tryCatch(mgr$new_server(), 161 | condition = identity) 162 | expect_s3_class(err, "skip") 163 | expect_match(err$message, "vault server failed to start") 164 | }) 165 | 166 | 167 | test_that("correct exe on different platforms", { 168 | skip_on_cran() 169 | expect_equal(vault_exe_filename("windows"), "vault.exe") 170 | expect_equal(vault_exe_filename("linux"), "vault") 171 | expect_equal(vault_exe_filename("darwin"), "vault") 172 | }) 173 | -------------------------------------------------------------------------------- /tests/testthat/test-util-assert.R: -------------------------------------------------------------------------------- 1 | test_that("assert_scalar", { 2 | object <- 1:5 3 | expect_error(assert_scalar(object), "'object' must be a scalar") 4 | 5 | expect_error(assert_scalar(NULL), "must be a scalar") 6 | 7 | expect_silent(assert_scalar(TRUE)) 8 | }) 9 | 10 | test_that("assert_length", { 11 | object <- 1:5 12 | expect_error(assert_length(object, 3), "'object' must have length 3") 13 | 14 | expect_error(assert_length(NULL, 3), "must have length 3") 15 | 16 | expect_silent(assert_length(1:3, 3)) 17 | }) 18 | 19 | test_that("assert_character", { 20 | object <- NULL 21 | expect_error(assert_character(object), "'object' must be a character") 22 | 23 | expect_error(assert_character(1), "must be a character") 24 | expect_error(assert_character(pi), "must be a character") 25 | 26 | expect_silent(assert_character("a")) 27 | }) 28 | 29 | test_that("assert_is", { 30 | object <- NULL 31 | expect_error(assert_is(object, "data.frame"), "'object' must be a data.frame") 32 | 33 | expect_error(assert_is(1, "data.frame"), "must be a data.frame") 34 | expect_error(assert_is(pi, "data.frame"), "must be a data.frame") 35 | 36 | expect_silent(assert_is(mtcars, "data.frame")) 37 | }) 38 | 39 | test_that("assert_named", { 40 | object <- 1:3 41 | expect_error(assert_named(object), "'object' must be named") 42 | names(object) <- letters[1:3] 43 | expect_silent(assert_named(object)) 44 | }) 45 | 46 | test_that("assert_absolute_path", { 47 | expect_error(assert_absolute_path("foo/bar"), "Expected an absolute path") 48 | expect_silent(assert_absolute_path("/foo")) 49 | }) 50 | 51 | 52 | test_that("assert_file_exists", { 53 | thing <- tempfile() 54 | expect_error(assert_file_exists(thing), 55 | "The path '.+' does not exist \\(for 'thing'\\)") 56 | file.create(thing) 57 | expect_silent(assert_file_exists(thing)) 58 | }) 59 | 60 | 61 | test_that("assert_is_duration", { 62 | var <- "1" 63 | expect_error(assert_is_duration(var), 64 | "'1' is not a valid time duration for 'var'", fixed = TRUE) 65 | var <- "1h" 66 | expect_silent(assert_is_duration(var)) 67 | }) 68 | 69 | 70 | test_that("assert_integer", { 71 | expect_error(assert_integer(pi), "'pi' must be integer") 72 | expect_silent(assert_integer(1L)) 73 | expect_silent(assert_integer(1)) 74 | expect_silent(assert_integer(1 + 1e-15)) 75 | }) 76 | 77 | 78 | test_that("assert_logical", { 79 | expect_error(assert_logical(pi), "'pi' must be a logical") 80 | expect_silent(assert_logical(TRUE)) 81 | expect_silent(assert_logical(FALSE)) 82 | }) 83 | 84 | 85 | test_that("assert_vault_version", { 86 | cl <- list(server_version = function() numeric_version("0.9.4")) 87 | expect_error( 88 | assert_vault_version("1.0.0", cl, "/api/path", "action"), 89 | "action (/api/path) requires vault version >= 1.0.0 but server is 0.9.4", 90 | class = "vault_invalid_version", 91 | fixed = TRUE) 92 | expect_silent( 93 | assert_vault_version("0.9.4", cl, "/api/path", "action")) 94 | }) 95 | 96 | 97 | test_that("match_value", { 98 | expect_error(match_value("foo", letters), "must be one of") 99 | expect_silent(match_value("a", letters)) 100 | }) 101 | 102 | 103 | test_that("assert_scalar_logical_or_null", { 104 | expect_null(assert_scalar_logical_or_null(NULL)) 105 | expect_true(assert_scalar_logical_or_null(TRUE)) 106 | expect_error(assert_scalar_logical_or_null("1", "data"), 107 | "'data' must be a logical") 108 | }) 109 | 110 | 111 | test_that("assert_scalar_character_or_null", { 112 | expect_null(assert_scalar_character_or_null(NULL)) 113 | expect_equal(assert_scalar_character_or_null("string"), "string") 114 | expect_error(assert_scalar_character_or_null(TRUE, "data"), 115 | "'data' must be a character") 116 | }) 117 | -------------------------------------------------------------------------------- /tests/testthat/test-util.R: -------------------------------------------------------------------------------- 1 | test_that("Sys_getenv", { 2 | expect_null(Sys_getenv("VAULTR_NONEXISTANT")) 3 | 4 | withr::with_envvar(c("VAULTR_NONEXISTANT" = 123), { 5 | expect_equal(Sys_getenv("VAULTR_NONEXISTANT"), "123") 6 | expect_equal(Sys_getenv("VAULTR_NONEXISTANT", mode = "integer"), 123L) 7 | }) 8 | 9 | withr::with_envvar(c("VAULTR_NONEXISTANT" = "foo"), { 10 | expect_equal(Sys_getenv("VAULTR_NONEXISTANT"), "foo") 11 | expect_error(Sys_getenv("VAULTR_NONEXISTANT", mode = "integer"), 12 | "Invalid input for integer 'foo'") 13 | expect_error(Sys_getenv("VAULTR_NONEXISTANT", mode = "other"), 14 | "Invalid value for 'mode'") 15 | }) 16 | }) 17 | 18 | 19 | test_that("pretty_sec", { 20 | expect_equal(pretty_sec(1), "1s") 21 | expect_equal(pretty_sec(10), "10s") 22 | expect_equal(pretty_sec(100), "~2m") 23 | expect_equal(pretty_sec(1000), "~17m") 24 | expect_equal(pretty_sec(10000), "~3h") 25 | expect_equal(pretty_sec(100000), "~1d") 26 | expect_equal(pretty_sec(1000000), "~12d") 27 | }) 28 | 29 | 30 | test_that("free_port: failure", { 31 | skip_on_cran() 32 | skip_if_not_installed("mockery") 33 | mockery::stub(free_port, "check_port", FALSE) 34 | expect_error(free_port(10000, 0), 35 | "Did not find a free port between 10000..9999") 36 | expect_error(free_port(10000, 10), 37 | "Did not find a free port between 10000..10009") 38 | }) 39 | 40 | 41 | test_that("free_port: used", { 42 | srv <- test_vault_test_server() 43 | expect_false(check_port(srv$port)) 44 | }) 45 | 46 | 47 | test_that("raw_data_input", { 48 | d <- "foo" 49 | expect_error(raw_data_input(d), "Expected raw data for 'd'") 50 | d <- as.raw(0:255) 51 | expect_silent(raw_data_input(d)) 52 | expect_identical(raw_data_input(d), encode64(d)) 53 | }) 54 | 55 | 56 | test_that("format lease", { 57 | expect_equal(pretty_lease(10), 58 | "ok, duration: 10 s (10s)") 59 | expect_equal(pretty_lease(1000), 60 | "ok, duration: 1000 s (~17m)") 61 | expect_equal(pretty_lease(10000), 62 | "ok, duration: 10000 s (~3h)") 63 | }) 64 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-api.R: -------------------------------------------------------------------------------- 1 | test_that("vault api client rejects unauthenticated attempts", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client(login = FALSE) 4 | api <- cl$api() 5 | 6 | expect_error(api$GET("/secret"), 7 | "Have not authenticated against vault") 8 | err <- tryCatch(api$GET("/secret", allow_missing_token = TRUE), 9 | error = identity) 10 | expect_true(err$code %in% c(400, 403)) 11 | }) 12 | 13 | 14 | test_that("error fallback", { 15 | ## this test depends on mocking httr internals, and that feels like 16 | ## an unwise thing to hope to have work over a medium term. I'll 17 | ## probably move over to curl at some point. 18 | skip_on_cran() 19 | res <- list(status_code = 400L, 20 | headers = structure(list("content-type" = "text/plain"), 21 | class = c("insensitive", "list")), 22 | content = charToRaw("an error message")) 23 | class(res) <- "response" 24 | err <- tryCatch(vault_client_response(res), error = identity) 25 | expect_s3_class(err, "vault_error") 26 | expect_s3_class(err, "vault_invalid_request") 27 | expect_equal(err$code, 400L) 28 | expect_equal(err$message, "an error message") 29 | }) 30 | 31 | 32 | test_that("token validation", { 33 | srv <- test_vault_test_server() 34 | cl <- srv$client(login = FALSE) 35 | api <- cl$api() 36 | 37 | expect_silent(api$verify_token(fake_token(), TRUE)) 38 | expect_message(api$verify_token(fake_token(), FALSE), "Verifying token") 39 | 40 | expect_error(api$set_token(fake_token(), verify = TRUE, quiet = TRUE), 41 | "Token validation failed with error") 42 | }) 43 | 44 | 45 | test_that("skip ssl validation", { 46 | skip_on_os("windows") 47 | srv <- test_vault_test_server(https = TRUE) 48 | 49 | cl1 <- vault_client(addr = srv$addr, tls_config = FALSE) 50 | cl1$login(token = srv$token, quiet = TRUE) 51 | expect_equal(cl1$list("/secret"), character(0)) 52 | }) 53 | 54 | 55 | test_that("vault_base_url", { 56 | withr::with_envvar(c(VAULT_ADDR = NA_character_), { 57 | expect_error(vault_addr(NULL), "vault address not found") 58 | }) 59 | 60 | expect_error(vault_addr("file://foo"), 61 | "Expected an http or https url for vault addr") 62 | 63 | expect_equal( 64 | vault_base_url("https://vault.example.com", "/v1"), 65 | "https://vault.example.com/v1") 66 | }) 67 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-audit.R: -------------------------------------------------------------------------------- 1 | test_that("audit", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | 5 | d <- cl$audit$list() 6 | expect_equal(d, data_frame(path = character(), 7 | type = character(), 8 | description = character())) 9 | }) 10 | 11 | 12 | test_that("enable/disable", { 13 | srv <- test_vault_test_server() 14 | cl <- srv$client() 15 | 16 | path <- tempfile() 17 | options <- list(file_path = path) 18 | description <- "a file audit device" 19 | cl$audit$enable("file", description, options = options) 20 | d <- cl$audit$list() 21 | expect_equal(d, data_frame(path = "file/", type = "file", 22 | description = description)) 23 | 24 | cl$audit$disable("file") 25 | d <- cl$audit$list() 26 | expect_equal(d, data_frame(path = character(), 27 | type = character(), 28 | description = character())) 29 | }) 30 | 31 | 32 | test_that("calculate hash", { 33 | srv <- test_vault_test_server() 34 | cl <- srv$client() 35 | 36 | cl$audit$enable("file", options = list(file_path = tempfile())) 37 | res <- cl$audit$hash("foo", "file") 38 | ## TODO: this is as far as the python tests push things - the vault 39 | ## docs are a bit vague on how one can use the audit logs really. 40 | expect_match(res, "^hmac-sha256:") 41 | }) 42 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-auth-approle.R: -------------------------------------------------------------------------------- 1 | test_that("approle", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | cl$auth$enable("approle") 5 | 6 | d <- cl$auth$list() 7 | expect_setequal(d$path, c("token/", "approle/")) 8 | expect_setequal(d$type, c("token", "approle")) 9 | 10 | ar <- cl$auth$approle 11 | expect_equal(ar$role_list(), character(0)) 12 | }) 13 | 14 | 15 | test_that("approle auth", { 16 | srv <- test_vault_test_server() 17 | cl <- srv$client() 18 | cl$auth$enable("approle") 19 | 20 | role_name <- "myrole" 21 | 22 | ar <- cl$auth$approle 23 | ar$role_write(role_name) 24 | expect_equal(ar$role_list(), role_name) 25 | 26 | d <- ar$role_read(role_name) 27 | expect_type(d, "list") 28 | 29 | role_id <- ar$role_id_read(role_name) 30 | expect_type(role_id, "character") 31 | expect_equal(length(role_id), 1L) 32 | 33 | secret <- ar$secret_id_generate(role_name) 34 | expect_setequal(names(secret), c("id", "accessor")) 35 | auth <- ar$login(role_id, secret$id) 36 | 37 | token <- auth$client_token 38 | expect_type(token, "character") 39 | 40 | cl2 <- srv$client(login = FALSE) 41 | expect_error(cl2$login(token = token, quiet = TRUE), NA) 42 | }) 43 | 44 | 45 | test_that("custom mount", { 46 | srv <- test_vault_test_server() 47 | cl <- srv$client() 48 | 49 | cl$auth$enable("approle", path = "approle2") 50 | ar <- cl$auth$approle$custom_mount("approle2") 51 | expect_s3_class(ar, "vault_client_auth_approle") 52 | 53 | ar$role_write("server") 54 | expect_type(ar$role_read("server"), "list") 55 | expect_error(cl$auth$approle$role_read("server")) 56 | }) 57 | 58 | 59 | test_that("full login", { 60 | srv <- test_vault_test_server() 61 | cl <- srv$client() 62 | cl$auth$enable("approle") 63 | cl$write("/secret/test", list(a = 1)) 64 | cl$policy$write("standard", 'path "secret/*" {\n policy = "read"\n}') 65 | 66 | role_name <- "myrole" 67 | 68 | cl$auth$approle$role_write(role_name, policies = "standard") 69 | 70 | cl$auth$approle$role_read(role_name) 71 | role_id <- cl$auth$approle$role_id_read(role_name) 72 | secret <- cl$auth$approle$secret_id_generate(role_name) 73 | 74 | cl2 <- srv$client(login = FALSE) 75 | cl2$login(method = "approle", 76 | role_id = role_id, 77 | secret_id = secret$id, 78 | quiet = TRUE) 79 | expect_equal(cl2$read("/secret/test"), list(a = 1)) 80 | expect_error(cl2$write("/secret/test", list(a = 2))) 81 | }) 82 | 83 | 84 | test_that("role delete", { 85 | srv <- test_vault_test_server() 86 | cl <- srv$client() 87 | cl$auth$enable("approle") 88 | 89 | ar <- cl$auth$approle 90 | 91 | ar$role_write("a") 92 | ar$role_write("b") 93 | expect_setequal(ar$role_list(), c("a", "b")) 94 | ar$role_delete("a") 95 | expect_equal(ar$role_list(), "b") 96 | }) 97 | 98 | 99 | test_that("role set id", { 100 | srv <- test_vault_test_server() 101 | cl <- srv$client() 102 | cl$auth$enable("approle") 103 | 104 | ar <- cl$auth$approle 105 | role_name <- "myrole" 106 | role_id <- rand_str(10) 107 | 108 | ar$role_write(role_name) 109 | ar$role_id_write(role_name, role_id) 110 | expect_equal(ar$role_id_read(role_name), role_id) 111 | }) 112 | 113 | 114 | test_that("secret id list", { 115 | srv <- test_vault_test_server() 116 | cl <- srv$client() 117 | cl$auth$enable("approle") 118 | 119 | ar <- cl$auth$approle 120 | role_name <- "myrole" 121 | ar$role_write(role_name) 122 | 123 | expect_equal(ar$secret_id_list(role_name), character(0)) 124 | s1 <- ar$secret_id_generate(role_name) 125 | expect_equal(ar$secret_id_list(role_name), s1$accessor) 126 | s2 <- ar$secret_id_generate(role_name) 127 | expect_setequal(ar$secret_id_list(role_name), 128 | c(s1$accessor, s2$accessor)) 129 | }) 130 | 131 | 132 | test_that("secret id read", { 133 | srv <- test_vault_test_server() 134 | cl <- srv$client() 135 | cl$auth$enable("approle") 136 | 137 | ar <- cl$auth$approle 138 | role_name <- "myrole" 139 | ar$role_write(role_name) 140 | 141 | metadata <- list(key = jsonlite::unbox("value")) 142 | 143 | s1 <- ar$secret_id_generate(role_name, metadata) 144 | d <- ar$secret_id_read(role_name, s1$id) 145 | expect_equal(d$metadata, list(key = "value")) 146 | 147 | expect_equal(ar$secret_id_read(role_name, s1$accessor, TRUE), d) 148 | }) 149 | 150 | 151 | test_that("secret id delete", { 152 | srv <- test_vault_test_server() 153 | cl <- srv$client() 154 | cl$auth$enable("approle") 155 | 156 | ar <- cl$auth$approle 157 | role_name <- "myrole" 158 | ar$role_write(role_name) 159 | 160 | s1 <- ar$secret_id_generate(role_name) 161 | s2 <- ar$secret_id_generate(role_name) 162 | 163 | ar$secret_id_delete(role_name, s1$id) 164 | expect_equal(ar$secret_id_list(role_name), s2$accessor) 165 | 166 | ar$secret_id_delete(role_name, s2$accessor, TRUE) 167 | expect_equal(ar$secret_id_list(role_name), character(0)) 168 | }) 169 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-auth-github.R: -------------------------------------------------------------------------------- 1 | test_that("custom mount", { 2 | skip_if_no_vaultr_test_github_pat() 3 | srv <- test_vault_test_server() 4 | cl <- srv$client() 5 | 6 | cl$auth$enable("github", path = "github2") 7 | gh <- cl$auth$github$custom_mount("github2") 8 | expect_s3_class(gh, "vault_client_auth_github") 9 | 10 | gh$configure(organization = "vimc") 11 | expect_equal(gh$configuration()$organization, "vimc") 12 | expect_error(cl$auth$github$configuration()$organization) 13 | }) 14 | 15 | 16 | test_that("github set policy: group", { 17 | skip_if_no_vaultr_test_github_pat() 18 | srv <- test_vault_test_server() 19 | cl <- srv$client() 20 | cl$auth$enable("github") 21 | cl$auth$github$configure(organization = "vimc") 22 | cl$auth$github$write("robots", "default") 23 | d <- cl$auth$github$read("robots") 24 | expect_equal(d$value, "default") 25 | expect_equal(cl$read("/auth/github/map/teams/robots"), d) 26 | }) 27 | 28 | 29 | test_that("github set policy: user", { 30 | skip_if_no_vaultr_test_github_pat() 31 | srv <- test_vault_test_server() 32 | cl <- srv$client() 33 | cl$auth$enable("github") 34 | cl$auth$github$configure(organization = "vimc") 35 | cl$auth$github$write("richfitz", "default", TRUE) 36 | 37 | d <- cl$auth$github$read("richfitz", TRUE) 38 | expect_equal(d$value, "default") 39 | expect_equal(cl$read("/auth/github/map/users/richfitz"), d) 40 | }) 41 | 42 | 43 | ## In github.com/settings/token for vimc-robot, with label "vaultr-testing" 44 | ## 45 | ## Integration test - this one is (lots) slower because it calls out 46 | ## to github. So we do all the bits in here that want to be run after 47 | ## authenticating with github. 48 | test_that("github auth", { 49 | skip_if_no_vaultr_test_github_pat() 50 | skip_if_no_internet() 51 | gh_token <- vaultr_test_github_pat() 52 | 53 | srv <- test_vault_test_server() 54 | cl <- srv$client() 55 | 56 | ## Set up a basic policy: 57 | cl$policy$write("standard", 'path "secret/a/*" {\n policy = "write"\n}') 58 | 59 | ## Configure github: 60 | cl$auth$enable("github") 61 | cl$auth$github$configure(organization = "vimc") 62 | cl$auth$github$write("vimc-robot", "standard", TRUE) 63 | cl$auth$github$read("vimc-robot", TRUE) 64 | 65 | ## Login: 66 | auth <- cl$auth$github$login(token = gh_token) 67 | token <- auth$client_token 68 | 69 | ## Check our token: 70 | cl2 <- srv$client(login = FALSE) 71 | cl2$login(token = token) 72 | expect_true("standard" %in% cl2$token$lookup_self()$policies) 73 | 74 | ## Can we read and write where expected: 75 | cl2$write("secret/a/b", list(value = 1)) 76 | expect_equal(cl2$read("secret/a/b"), list(value = 1)) 77 | 78 | ## Are we forbidden where expected: 79 | err <- tryCatch(cl2$write("secret/b", list(value = 1)), error = identity) 80 | expect_s3_class(err, "vault_error") 81 | expect_s3_class(err, "vault_forbidden") 82 | }) 83 | 84 | 85 | test_that("github token", { 86 | fake1 <- fake_token() 87 | fake2 <- fake_token() 88 | 89 | withr::with_envvar(c("VAULT_AUTH_GITHUB_TOKEN" = NA_character_), { 90 | expect_error( 91 | vault_auth_github_token(NULL), 92 | "GitHub token was not found: perhaps set 'VAULT_AUTH_GITHUB_TOKEN'") 93 | expect_equal(vault_auth_github_token(fake1), fake1) 94 | }) 95 | 96 | withr::with_envvar(c("VAULT_AUTH_GITHUB_TOKEN" = fake2), { 97 | expect_equal(vault_auth_github_token(NULL), fake2) 98 | expect_equal(vault_auth_github_token(fake1), fake1) 99 | }) 100 | }) 101 | 102 | 103 | test_that("missing github token", { 104 | srv <- test_vault_test_server() 105 | cl <- srv$client() 106 | 107 | ## Configure github: 108 | cl$auth$enable("github") 109 | withr::with_envvar(c("VAULT_AUTH_GITHUB_TOKEN" = NA_character_), { 110 | expect_error( 111 | cl$auth$github$login(NULL), 112 | "GitHub token was not found: perhaps set 'VAULT_AUTH_GITHUB_TOKEN'") 113 | }) 114 | }) 115 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-auth-ldap.R: -------------------------------------------------------------------------------- 1 | ## See development.md (in project root) for information about the test 2 | ## setup here. 3 | 4 | test_that("custom mount", { 5 | srv <- test_vault_test_server() 6 | cl <- srv$client() 7 | 8 | cl$auth$enable("ldap", path = "ldap2") 9 | ldap <- cl$auth$ldap$custom_mount("ldap2") 10 | expect_s3_class(ldap, "vault_client_auth_ldap") 11 | 12 | info <- ldap_configure(ldap) 13 | expect_equal(ldap$configuration()$binddn, info$configuration$binddn) 14 | }) 15 | 16 | 17 | test_that("ldap set policy: group", { 18 | srv <- test_vault_test_server() 19 | cl <- srv$client() 20 | cl$auth$enable("ldap") 21 | info <- ldap_configure(cl$auth$ldap) 22 | cl$auth$ldap$write(info$group, "default") 23 | expect_equal(cl$auth$ldap$list(), info$group) 24 | d <- cl$auth$ldap$read(info$group) 25 | expect_equal(d$policies, "default") 26 | }) 27 | 28 | 29 | test_that("ldap set policy: user", { 30 | srv <- test_vault_test_server() 31 | cl <- srv$client() 32 | cl$auth$enable("ldap") 33 | info <- ldap_configure(cl$auth$ldap) 34 | cl$auth$ldap$write(info$username, "default", TRUE) 35 | expect_equal(cl$auth$ldap$list(TRUE), info$username) 36 | 37 | d <- cl$auth$ldap$read(info$username, TRUE) 38 | expect_equal(d$policies, "default") 39 | }) 40 | 41 | 42 | test_that("delete group", { 43 | srv <- test_vault_test_server() 44 | cl <- srv$client() 45 | cl$auth$enable("ldap") 46 | info <- ldap_configure(cl$auth$ldap) 47 | cl$auth$ldap$write(info$group, "default") 48 | cl$auth$ldap$delete(info$group) 49 | expect_equal(cl$auth$ldap$list(), character(0)) 50 | }) 51 | 52 | 53 | test_that("delete user", { 54 | srv <- test_vault_test_server() 55 | cl <- srv$client() 56 | cl$auth$enable("ldap") 57 | info <- ldap_configure(cl$auth$ldap) 58 | cl$auth$ldap$write(info$group, "default", TRUE) 59 | cl$auth$ldap$delete(info$group, TRUE) 60 | expect_equal(cl$auth$ldap$list(TRUE), character(0)) 61 | }) 62 | 63 | 64 | test_that("can use ldap", { 65 | srv <- test_vault_test_server() 66 | cl <- srv$client() 67 | cl$auth$enable("ldap") 68 | d <- cl$auth$list() 69 | expect_setequal(d$path, c("token/", "ldap/")) 70 | expect_setequal(d$type, c("token", "ldap")) 71 | 72 | cl$policy$write("example", 'path "secret/a/*" {\n policy = "write"\n}') 73 | 74 | info <- ldap_configure(cl$auth$ldap) 75 | cl$auth$ldap$write(info$group, "example") 76 | 77 | result <- cl$auth$ldap$login(username = info$username, 78 | password = info$password) 79 | expect_true("example" %in% result$policies) 80 | token <- result$client_token 81 | 82 | cl2 <- srv$client(login = FALSE) 83 | cl2$login(token = token, use_cache = FALSE, quiet = TRUE) 84 | expect_true("example" %in% cl2$token$lookup_self()$policies) 85 | cl2$write("secret/a/thing", list(a = 1)) 86 | 87 | cl3 <- srv$client(login = FALSE) 88 | cl3$login(method = "ldap", use_cache = FALSE, quiet = TRUE, 89 | username = info$username, password = info$password) 90 | expect_equal(cl3$read("secret/a/thing"), list(a = 1)) 91 | }) 92 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-auth.R: -------------------------------------------------------------------------------- 1 | test_that("auth", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | expect_s3_class(cl$auth, "vault_client_auth") 5 | d <- cl$auth$list() 6 | expect_equal(d$path, "token/") 7 | expect_equal(d$type, "token") 8 | expect_error(cl$auth$list(TRUE), 9 | "Detailed auth information not supported") 10 | }) 11 | 12 | 13 | test_that("introspect methods", { 14 | srv <- test_vault_test_server() 15 | cl <- srv$client() 16 | 17 | expect_setequal(cl$auth$backends(), 18 | c("token", "github", "ldap", "userpass", "approle")) 19 | }) 20 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-basic.R: -------------------------------------------------------------------------------- 1 | test_that("read/write/list", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | 5 | path <- sprintf("/secret/%s/key1", rand_str(10)) 6 | value <- rand_str(20) 7 | data <- list(value = value) 8 | 9 | cl$write(path, data) 10 | expect_equal(cl$read(path), data) 11 | expect_equal(cl$read(path, "value"), value) 12 | expect_null(cl$read(path, "other"), value) 13 | expect_equal(cl$list(dirname(path)), "key1") 14 | expect_equal(cl$list(dirname(path)), "key1") 15 | }) 16 | 17 | 18 | test_that("status", { 19 | srv <- test_vault_test_server() 20 | cl <- srv$client() 21 | status <- cl$status() 22 | 23 | expect_type(status, "list") 24 | expect_equal(status$progress, 0L) 25 | }) 26 | 27 | 28 | test_that("re-login", { 29 | srv <- test_vault_test_server() 30 | cl <- srv$client() 31 | expect_null(cl$login(method = "impossible")) 32 | expect_error(cl$login(method = "impossible", renew = TRUE), 33 | "Unknown login method 'impossible' - must be one of") 34 | }) 35 | 36 | 37 | test_that("format", { 38 | srv <- test_vault_test_server() 39 | cl <- srv$client(login = FALSE) 40 | 41 | str <- withr::with_options(list(width = 80), cl$format()) 42 | expect_equal(str[[1]], "") 43 | expect_match(str, "login\\(.+\n", all = FALSE) 44 | 45 | ## recurse: 46 | str <- withr::with_options(list(width = 80), cl$auth$format()) 47 | expect_true(any(grepl("Command groups:", str))) 48 | expect_match(str, "github:", fixed = TRUE, all = FALSE) 49 | 50 | ## recurse: 51 | str <- withr::with_options(list(width = 80), cl$audit$format()) 52 | expect_false(any(grepl("Command groups:", str))) 53 | 54 | str <- withr::with_options(list(width = 80), cl$secrets$format()) 55 | expect_true(any(grepl("Command groups:", str))) 56 | expect_match(str, "transit:", fixed = TRUE, all = FALSE) 57 | }) 58 | 59 | 60 | test_that("login method", { 61 | withr::with_envvar(c("VAULTR_AUTH_METHOD" = NA_character_), { 62 | expect_null(vault_client_login_method(NULL)) 63 | expect_null(vault_client_login_method(FALSE)) 64 | expect_error(vault_client_login_method(TRUE), 65 | "Default login method not set in 'VAULTR_AUTH_METHOD'") 66 | expect_equal(vault_client_login_method("token"), "token") 67 | }) 68 | 69 | withr::with_envvar(c("VAULTR_AUTH_METHOD" = "github"), { 70 | expect_equal(vault_client_login_method(NULL), "github") 71 | expect_null(vault_client_login_method(FALSE)) 72 | expect_equal(vault_client_login_method(TRUE), "github") 73 | expect_equal(vault_client_login_method("token"), "token") 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-cubbyhole.R: -------------------------------------------------------------------------------- 1 | test_that("basic set/get/list/del", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | 5 | expect_equal(cl$secrets$cubbyhole$list("/cubbyhole"), character(0)) 6 | 7 | p <- "cubbyhole/mysecret" 8 | cl$secrets$cubbyhole$write(p, list(a = "data")) 9 | expect_equal(cl$secrets$cubbyhole$read(p), list(a = "data")) 10 | expect_equal(cl$secrets$cubbyhole$read(p, "a"), "data") 11 | expect_null(cl$secrets$cubbyhole$read(p, "b")) 12 | d <- cl$secrets$cubbyhole$read(p, metadata = TRUE) 13 | expect_true("metadata" %in% names(attributes(d))) 14 | 15 | expect_null(cl$secrets$cubbyhole$read("/cubbyhole/other")) 16 | 17 | expect_equal(cl$list("/cubbyhole"), "mysecret") 18 | expect_equal(cl$list("/cubbyhole", full_names = TRUE), p) 19 | 20 | cl$secrets$cubbyhole$delete(p) 21 | expect_equal(cl$list("/cubbyhole"), character(0)) 22 | expect_silent(cl$delete(p)) 23 | }) 24 | 25 | 26 | ## https://learn.hashicorp.com/vault/secrets-management/sm-cubbyhole 27 | test_that("response wrapping example", { 28 | srv <- test_vault_test_server() 29 | cl <- srv$client() 30 | 31 | ## create an apps policy - I have mucked this up 32 | cl$policy$write("apps", 'path "secret/dev/*" {\n policy = "read"}') 33 | cl$write("secret/dev/mysecret", list(a = 1)) 34 | 35 | token <- cl$token$create(policies = "apps", wrap_ttl = "1h") 36 | 37 | cl_app <- srv$client(login = FALSE) 38 | info <- cl_app$wrap_lookup(token) 39 | 40 | response <- cl_app$unwrap(token) 41 | cl_app$login(method = "token", token = response$auth$client_token, 42 | quiet = TRUE) 43 | expect_equal(cl_app$read("/secret/dev/mysecret"), 44 | list(a = 1)) 45 | 46 | ## Can't look up the token now 47 | expect_error(cl_app$wrap_lookup(token)) 48 | }) 49 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-kv1.R: -------------------------------------------------------------------------------- 1 | test_that("basic write/read", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | 5 | data <- list(key = rand_str(10)) 6 | path <- "secret/a" 7 | cl$write(path, data) 8 | 9 | expect_equal(cl$read(path), data) 10 | expect_equal(cl$read(path, "key"), data$key) 11 | res <- cl$read(path, metadata = TRUE) 12 | expect_type(attr(res, "metadata"), "list") 13 | }) 14 | 15 | 16 | test_that("read non-existant data", { 17 | srv <- test_vault_test_server() 18 | cl <- srv$client() 19 | 20 | expect_null(cl$read("secret/missing")) 21 | expect_null(cl$read("secret/missing", metadata = TRUE)) 22 | expect_null(cl$read("secret/missing", field = "field")) 23 | 24 | cl$write("secret/a", list(key = 1)) 25 | expect_null(cl$read("secret/a", field = "field")) 26 | }) 27 | 28 | 29 | test_that("delete data", { 30 | srv <- test_vault_test_server() 31 | cl <- srv$client() 32 | 33 | cl$write("secret/a", list(key = 1)) 34 | expect_equal(cl$read("secret/a", "key"), 1) 35 | cl$delete("secret/a") 36 | expect_null(cl$read("secret/a", "key")) 37 | }) 38 | 39 | 40 | test_that("list", { 41 | srv <- test_vault_test_server() 42 | cl <- srv$client() 43 | 44 | cl$write("secret/a", list(key = 1)) 45 | cl$write("secret/b/c", list(key = 2)) 46 | cl$write("secret/b/d/e", list(key = 2)) 47 | 48 | expect_setequal(cl$list("secret"), c("a", "b/")) 49 | expect_setequal(cl$list("secret", TRUE), c("secret/a", "secret/b/")) 50 | expect_setequal(cl$list("secret/b"), c("c", "d/")) 51 | expect_setequal(cl$list("secret/b", TRUE), c("secret/b/c", "secret/b/d/")) 52 | }) 53 | 54 | 55 | test_that("custom mount", { 56 | srv <- test_vault_test_server() 57 | cl <- srv$client() 58 | 59 | mount <- "/secret1" 60 | 61 | cl$secrets$enable("kv", mount, "", 1L) 62 | kv <- cl$secrets$kv1$custom_mount(mount) 63 | 64 | kv$write("/secret1/a", list(key = 1)) 65 | kv$read("/secret1/a") 66 | 67 | expect_error( 68 | kv$read("/secret/a"), 69 | "Invalid mount given for this path - expected 'secret1'") 70 | }) 71 | 72 | 73 | test_that("error messages when failing to read", { 74 | srv <- test_vault_test_server() 75 | cl <- srv$client() 76 | cl$write("/secret/users/alice", list(password = "ALICE")) 77 | cl$write("/secret/users/bob", list(password = "BOB")) 78 | 79 | rules <- paste('path "secret/users/alice" {', 80 | ' policy = "read"', 81 | "}", 82 | sep = "\n") 83 | cl$policy$write("read-secret-alice", rules) 84 | token <- cl$token$create(policies = "read-secret-alice") 85 | 86 | cl2 <- srv$client(FALSE) 87 | cl2$login(token = token, quiet = TRUE) 88 | expect_equal(cl2$read("/secret/users/alice", "password"), "ALICE") 89 | expect_error(cl2$read("/secret/users/bob", "password"), 90 | "While reading secret/users/bob:", 91 | class = "vault_forbidden") 92 | }) 93 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-kv2.R: -------------------------------------------------------------------------------- 1 | test_that("basic set/get", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | 5 | p <- rand_str(10) 6 | cl$secrets$enable("kv", p, version = 2) 7 | 8 | kv <- cl$secrets$kv2$custom_mount(p) 9 | wait_kv_upgrade(kv, p) 10 | 11 | path <- sprintf("%s/a", p) 12 | data <- list(key = rand_str(10)) 13 | meta <- kv$put(path, data) 14 | 15 | expect_type(meta, "list") 16 | expect_equal(meta$version, 1L) 17 | 18 | expect_equal(kv$get(path), data) 19 | expect_equal(kv$get(path, field = "key"), data$key) 20 | expect_equal(kv$get(path, metadata = TRUE), 21 | structure(data, metadata = meta)) 22 | }) 23 | 24 | 25 | test_that("config", { 26 | srv <- test_vault_test_server() 27 | cl <- srv$client() 28 | 29 | p <- rand_str(10) 30 | cl$secrets$enable("kv", p, version = 2) 31 | kv <- cl$secrets$kv2$custom_mount(p) 32 | wait_kv_upgrade(kv, p) 33 | 34 | config <- kv$config(p) 35 | expect_type(config, "list") 36 | expect_equal(config$cas_required, FALSE) 37 | expect_equal(config$max_versions, 0) 38 | }) 39 | 40 | 41 | test_that("versions", { 42 | srv <- test_vault_test_server() 43 | cl <- srv$client() 44 | 45 | p <- rand_str(10) 46 | cl$secrets$enable("kv", p, version = 2) 47 | 48 | kv <- cl$secrets$kv2$custom_mount(p) 49 | wait_kv_upgrade(kv, p) 50 | 51 | path <- sprintf("%s/a", p) 52 | 53 | kv$put(path, list(key = 1)) 54 | kv$put(path, list(key = 2)) 55 | 56 | expect_equal(kv$get(path, 1), list(key = 1)) 57 | expect_equal(kv$get(path, 2), list(key = 2)) 58 | m <- kv$metadata_get(path) 59 | expect_equal(length(m$versions), 2) 60 | expect_setequal(names(m$versions), c("1", "2")) 61 | expect_equal(m$current_version, 2L) 62 | }) 63 | 64 | 65 | test_that("delete latest version", { 66 | srv <- test_vault_test_server() 67 | cl <- srv$client() 68 | 69 | p <- rand_str(10) 70 | cl$secrets$enable("kv", p, version = 2) 71 | 72 | kv <- cl$secrets$kv2$custom_mount(p) 73 | wait_kv_upgrade(kv, p) 74 | 75 | path <- sprintf("%s/a", p) 76 | 77 | kv$put(path, list(key = 1)) 78 | kv$put(path, list(key = 2)) 79 | 80 | kv$delete(path) 81 | expect_equal(kv$get(path, version = 1), list(key = 1)) 82 | expect_null(kv$get(path)) 83 | 84 | m <- kv$metadata_get(path) 85 | expect_false(nzchar(m$versions[["1"]]$deletion_time)) 86 | expect_true(nzchar(m$versions[["2"]]$deletion_time)) 87 | }) 88 | 89 | 90 | test_that("delete multiple versions", { 91 | srv <- test_vault_test_server() 92 | cl <- srv$client() 93 | 94 | p <- rand_str(10) 95 | cl$secrets$enable("kv", p, version = 2) 96 | 97 | kv <- cl$secrets$kv2$custom_mount(p) 98 | wait_kv_upgrade(kv, p) 99 | 100 | path <- sprintf("%s/a", p) 101 | 102 | kv$put(path, list(key = 1)) 103 | kv$put(path, list(key = 2)) 104 | kv$put(path, list(key = 3)) 105 | 106 | kv$delete(path, version = 1:2) 107 | 108 | m <- kv$metadata_get(path) 109 | expect_true(nzchar(m$versions[["1"]]$deletion_time)) 110 | expect_true(nzchar(m$versions[["2"]]$deletion_time)) 111 | expect_false(nzchar(m$versions[["3"]]$deletion_time)) 112 | }) 113 | 114 | 115 | test_that("list", { 116 | srv <- test_vault_test_server() 117 | cl <- srv$client() 118 | 119 | p <- rand_str(10) 120 | cl$secrets$enable("kv", p, version = 2) 121 | 122 | kv <- cl$secrets$kv2$custom_mount(p) 123 | wait_kv_upgrade(kv, p) 124 | 125 | path <- sprintf("%s/a", p) 126 | kv$put(path, list(key = 1)) 127 | 128 | expect_equal(kv$list(p), "a") 129 | expect_equal(kv$list(p, TRUE), file.path(p, "a")) 130 | 131 | expect_equal(kv$list(path), character(0)) 132 | 133 | kv$put(sprintf("%s/b/c", p), list(key = 1)) 134 | expect_setequal(kv$list(p), c("a", "b/")) 135 | }) 136 | 137 | 138 | test_that("undelete", { 139 | srv <- test_vault_test_server() 140 | cl <- srv$client() 141 | 142 | p <- rand_str(10) 143 | cl$secrets$enable("kv", p, version = 2) 144 | 145 | kv <- cl$secrets$kv2$custom_mount(p) 146 | wait_kv_upgrade(kv, p) 147 | 148 | path <- sprintf("%s/a", p) 149 | kv$put(path, list(key = 1)) 150 | kv$put(path, list(key = 2)) 151 | 152 | kv$delete(path, 1) 153 | expect_null(kv$get(path, 1)) 154 | kv$undelete(path, 1) 155 | expect_equal(kv$get(path, 1), list(key = 1)) 156 | }) 157 | 158 | 159 | test_that("destroy", { 160 | srv <- test_vault_test_server() 161 | cl <- srv$client() 162 | 163 | p <- rand_str(10) 164 | cl$secrets$enable("kv", p, version = 2) 165 | 166 | kv <- cl$secrets$kv2$custom_mount(p) 167 | wait_kv_upgrade(kv, p) 168 | 169 | path <- sprintf("%s/a", p) 170 | kv$put(path, list(key = 1)) 171 | kv$put(path, list(key = 2)) 172 | 173 | kv$destroy(path, 2) 174 | expect_null(kv$get(path)) 175 | kv$undelete(path, 2) 176 | expect_null(kv$get(path)) 177 | }) 178 | 179 | 180 | test_that("metadata put", { 181 | srv <- test_vault_test_server() 182 | cl <- srv$client() 183 | 184 | p <- rand_str(10) 185 | cl$secrets$enable("kv", p, version = 2) 186 | 187 | path <- sprintf("%s/a", p) 188 | kv <- cl$secrets$kv2$custom_mount(p) 189 | wait_kv_upgrade(kv, p) 190 | 191 | kv$metadata_put(path, cas_required = TRUE, max_versions = 10) 192 | d <- kv$metadata_get(path) 193 | expect_true(d$cas_required) 194 | expect_equal(d$max_versions, 10) 195 | expect_equal(d$versions, setNames(list(), character())) 196 | }) 197 | 198 | 199 | test_that("metadata delete", { 200 | srv <- test_vault_test_server() 201 | cl <- srv$client() 202 | 203 | p <- rand_str(10) 204 | cl$secrets$enable("kv", p, version = 2) 205 | 206 | kv <- cl$secrets$kv2$custom_mount(p) 207 | wait_kv_upgrade(kv, p) 208 | 209 | path <- sprintf("%s/a", p) 210 | kv$put(path, list(key = 1)) 211 | kv$put(path, list(key = 2)) 212 | 213 | kv$metadata_delete(path) 214 | expect_null(kv$get(path)) 215 | expect_null(kv$metadata_get(path)) 216 | }) 217 | 218 | 219 | test_that("mount validation", { 220 | srv <- test_vault_test_server() 221 | cl <- srv$client() 222 | 223 | cl$secrets$enable("kv", "secret2", version = 2) 224 | kv <- cl$secrets$kv2$custom_mount("secret2") 225 | wait_kv_upgrade(kv, "secret2") 226 | 227 | expect_error( 228 | kv$list("/secret"), 229 | "Invalid mount given for this path - expected 'secret2'") 230 | expect_error( 231 | kv$put("/secret2", list(a = 1)), 232 | "Invalid path") 233 | }) 234 | 235 | 236 | test_that("put+cas", { 237 | srv <- test_vault_test_server() 238 | cl <- srv$client() 239 | 240 | cl$secrets$enable("kv", "secret2", version = 2) 241 | kv <- cl$secrets$kv2$custom_mount("secret2") 242 | wait_kv_upgrade(kv, "secret2") 243 | 244 | d <- kv$put("secret2/a", list(a = 1)) 245 | expect_error(kv$put("secret2/a", list(a = 2), cas = 2)) 246 | expect_equal(kv$get("secret2/a", field = "a"), 1) 247 | expect_silent(kv$put("secret2/a", list(a = 2), cas = 1)) 248 | expect_equal(kv$get("secret2/a", field = "a"), 2) 249 | }) 250 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-operator.R: -------------------------------------------------------------------------------- 1 | test_that("rekey", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | 5 | ans <- cl$operator$rekey_start(5, 3) 6 | expect_type(ans$nonce, "character") 7 | expect_true(ans$started) 8 | expect_equal(ans$progress, 0) 9 | expect_equal(ans$required, 1) 10 | 11 | res <- cl$operator$rekey_submit(srv$keys[[1]], ans$nonce) 12 | expect_type(res$keys, "character") 13 | expect_type(res$keys_base64, "character") 14 | expect_equal(length(res$keys), 5) 15 | expect_equal(length(res$keys_base64), 5) 16 | 17 | ## and again! 18 | ans <- cl$operator$rekey_start(5, 3) 19 | v <- c("started", "progress", "required") 20 | expect_equal( 21 | cl$operator$rekey_submit(res$keys_base64[[1]], ans$nonce)[v], 22 | list(started = TRUE, progress = 1, required = 3)) 23 | expect_equal( 24 | cl$operator$rekey_submit(res$keys_base64[[2]], ans$nonce)[v], 25 | list(started = TRUE, progress = 2, required = 3)) 26 | res <- cl$operator$rekey_submit(res$keys_base64[[3]], ans$nonce) 27 | expect_true(res$complete) 28 | expect_type(res$keys, "character") 29 | expect_type(res$keys_base64, "character") 30 | expect_equal(length(res$keys), 5) 31 | expect_equal(length(res$keys_base64), 5) 32 | }) 33 | 34 | 35 | test_that("cancel rekey", { 36 | srv <- test_vault_test_server() 37 | cl <- srv$client() 38 | 39 | ans <- cl$operator$rekey_start(5, 3) 40 | expect_equal(cl$operator$rekey_status(), ans) 41 | 42 | expect_null(cl$operator$rekey_cancel()) 43 | expect_null(cl$operator$rekey_cancel()) 44 | 45 | ans <- cl$operator$rekey_status() 46 | expect_false(ans$started) 47 | }) 48 | 49 | 50 | test_that("init", { 51 | skip_on_os("windows") 52 | srv <- test_vault_test_server(https = TRUE, init = FALSE) 53 | cl <- srv$client(login = FALSE) 54 | 55 | dat <- cl$operator$init(5, 3) 56 | expect_type(dat$keys, "character") 57 | expect_type(dat$keys_base64, "character") 58 | expect_equal(length(dat$keys), 5) 59 | expect_equal(length(dat$keys_base64), 5) 60 | 61 | v <- c("sealed", "progress") 62 | expect_equal(cl$operator$unseal(dat$keys[[1]])[v], 63 | list(sealed = TRUE, progress = 1L)) 64 | expect_equal(cl$operator$unseal(dat$keys[[2]])[v], 65 | list(sealed = TRUE, progress = 2L)) 66 | expect_equal(cl$operator$unseal(dat$keys[[3]])[v], 67 | list(sealed = FALSE, progress = 0L)) 68 | }) 69 | 70 | 71 | test_that("seal", { 72 | srv <- test_vault_test_server() 73 | cl <- srv$client() 74 | 75 | expect_false(cl$operator$seal_status()$sealed) 76 | cl$operator$seal() 77 | expect_true(cl$operator$seal_status()$sealed) 78 | }) 79 | 80 | 81 | test_that("rotate", { 82 | srv <- test_vault_test_server() 83 | cl <- srv$client() 84 | 85 | d1 <- cl$operator$key_status() 86 | expect_null(cl$operator$rotate()) 87 | d2 <- cl$operator$key_status() 88 | 89 | expect_equal(d1$term, 1) 90 | expect_equal(d2$term, 2) 91 | }) 92 | 93 | 94 | test_that("leader status", { 95 | srv <- test_vault_test_server() 96 | cl <- srv$client() 97 | 98 | d <- cl$operator$leader_status() 99 | expect_false(d$ha_enabled) 100 | }) 101 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-policy.R: -------------------------------------------------------------------------------- 1 | test_that("read policy", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | 5 | ## There's not much we can take from this one yet - this gets a 6 | ## better test after we start *writing* policies 7 | rules <- cl$policy$read("default") 8 | expect_type(rules, "character") 9 | }) 10 | 11 | 12 | test_that("write_policy", { 13 | srv <- test_vault_test_server() 14 | cl <- srv$client() 15 | 16 | rules <- paste('path "secret/*" {', 17 | ' policy = "read"', 18 | "}", 19 | sep = "\n") 20 | cl$policy$write("read-secret", rules) 21 | expect_true("read-secret" %in% cl$policy$list()) 22 | expect_equal(cl$policy$read("read-secret"), rules) 23 | cl$policy$delete("read-secret") 24 | expect_false("read-secret" %in% cl$policy$list()) 25 | }) 26 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-resolve-secrets.R: -------------------------------------------------------------------------------- 1 | test_that("vault secrets can be resolved", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | cl$write("/secret/users/alice", list(password = "ALICE")) 5 | cl$write("/secret/users/bob", list(password = "BOB")) 6 | 7 | config <- list(path = tempfile(), 8 | vault_server = srv$addr) 9 | 10 | x <- list(name = "alice", 11 | password = "VAULT:/secret/users/alice:password") 12 | withr::with_envvar(c(VAULTR_AUTH_METHOD = NA_character_), { 13 | expect_error(vault_resolve_secrets(x, addr = config$vault_server), 14 | "Default login method not set in 'VAULTR_AUTH_METHOD'") 15 | }) 16 | withr::with_envvar(c(VAULTR_AUTH_METHOD = "token", VAULT_TOKEN = NA), { 17 | expect_error(vault_resolve_secrets(x, addr = config$vault_server), 18 | "Vault token was not found") 19 | }) 20 | withr::with_envvar(c(VAULTR_AUTH_METHOD = "token", VAULT_TOKEN = "fake"), { 21 | expect_error( 22 | suppressMessages(vault_resolve_secrets(x, addr = config$vault_server)), 23 | "Token login failed with error") 24 | }) 25 | 26 | withr::with_envvar(c(VAULTR_AUTH_METHOD = "token", VAULT_TOKEN = srv$token), { 27 | expect_equal( 28 | suppressMessages(vault_resolve_secrets(x, addr = config$vault_server)), 29 | list(name = "alice", password = "ALICE")) 30 | expect_equal( 31 | suppressMessages( 32 | vault_resolve_secrets(unlist(x), addr = config$vault_server)), 33 | list(name = "alice", password = "ALICE")) 34 | }) 35 | 36 | withr::with_envvar(c(VAULTR_AUTH_METHOD = NA_character_), { 37 | args <- list(login = "token", token = srv$token, 38 | addr = config$vault_server, quiet = TRUE) 39 | expect_equal(vault_resolve_secrets(x, vault_args = args), 40 | list(name = "alice", password = "ALICE")) 41 | expect_error( 42 | vault_resolve_secrets(x, vault_args = args, addr = "somewhere"), 43 | "Do not provide both '...' and 'vault_args'", fixed = TRUE) 44 | }) 45 | }) 46 | 47 | 48 | test_that("Provide better error messages when failing to read", { 49 | srv <- test_vault_test_server() 50 | cl <- srv$client() 51 | cl$write("/secret/users/alice", list(password = "ALICE")) 52 | cl$write("/secret/users/bob", list(password = "BOB")) 53 | 54 | rules <- paste('path "secret/users/alice" {', 55 | ' policy = "read"', 56 | "}", 57 | sep = "\n") 58 | cl$policy$write("read-secret-alice", rules) 59 | token <- cl$token$create(policies = "read-secret-alice") 60 | 61 | x <- list(alice = "VAULT:/secret/users/alice:password", 62 | bob = "VAULT:/secret/users/bob:password") 63 | 64 | args <- list(login = "token", token = token, addr = srv$addr, quiet = TRUE) 65 | expect_error( 66 | vault_resolve_secrets(x, vault_args = args), 67 | "While reading secret/users/bob:", 68 | class = "vault_forbidden") 69 | }) 70 | -------------------------------------------------------------------------------- /tests/testthat/test-vault-secrets.R: -------------------------------------------------------------------------------- 1 | test_that("secrets", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | expect_s3_class(cl$secrets, "vault_client_secrets") 5 | d <- cl$secrets$list() 6 | expect_s3_class(d, "data.frame") 7 | expect_true("secret/" %in% d$path) 8 | expect_true("kv" %in% d$type) 9 | expect_error(cl$secrets$list(TRUE), 10 | "Detailed secret information not supported") 11 | }) 12 | 13 | 14 | test_that("enable/disable a secret engine", { 15 | srv <- test_vault_test_server() 16 | cl <- srv$client() 17 | cl$secrets$enable("kv", version = 2) 18 | d <- cl$secrets$list() 19 | expect_true("kv/" %in% d$path) 20 | expect_equal(d$type["kv/" == d$path], "kv") 21 | cl$secrets$disable("kv") 22 | d <- cl$secrets$list() 23 | expect_false("kv/" %in% d$path) 24 | }) 25 | 26 | 27 | test_that("move a secret engine", { 28 | srv <- test_vault_test_server() 29 | cl <- srv$client() 30 | p1 <- "oldpath" 31 | p2 <- "newpath" 32 | cl$secrets$enable("kv", p1, version = 2) 33 | # On CI, these tests are being flakey, and it's not at all obvious 34 | # why; it could be a race condition so adding a little sleep here to see. 35 | Sys.sleep(1) 36 | cl$secrets$move(p1, p2) 37 | Sys.sleep(1) 38 | d <- cl$secrets$list() 39 | expect_true(paste0(p2, "/") %in% d$path) 40 | expect_false(paste0(p1, "/") %in% d$path) 41 | }) 42 | -------------------------------------------------------------------------------- /tests/testthat/test-vault.R: -------------------------------------------------------------------------------- 1 | test_that("api", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | 5 | cl$write("/secret/a", list(key = 1)) 6 | 7 | api <- cl$api() 8 | expect_s3_class(api, "vault_api_client") 9 | expect_null(api$namespace) 10 | ## Unauthenticated route: 11 | expect_equal(api$GET("/sys/seal-status"), 12 | cl$operator$seal_status()) 13 | 14 | ## Authenticated route 15 | d <- api$GET("/secret/a") 16 | expect_equal(d$data$key, 1) 17 | }) 18 | 19 | 20 | test_that("Can send namespace with api requests", { 21 | skip_on_cran() 22 | skip_if_not_installed("mockery") 23 | cl <- vault_api_client$new("https://example.com", namespace = "foo") 24 | cl$token <- "secret" 25 | expect_equal(cl$namespace, "foo") 26 | mock_response <- structure( 27 | list(status_code = 204), 28 | class = "response") 29 | mock_get <- mockery::mock(mock_response) 30 | 31 | expect_null(cl$request(mock_get, "/some/path")) 32 | mockery::expect_called(mock_get, 1) 33 | args <- mockery::mock_args(mock_get)[[1]] 34 | expect_equal(args[[3]], 35 | httr::add_headers("X-Vault-Token" = "secret")) 36 | expect_equal(args[[4]], 37 | httr::add_headers("X-Vault-Namespace" = "foo")) 38 | }) 39 | 40 | 41 | test_that("No namespace sent by default", { 42 | skip_on_cran() 43 | skip_if_not_installed("mockery") 44 | cl <- vault_api_client$new("https://example.com", namespace = NULL) 45 | cl$token <- "secret" 46 | expect_null(cl$namespace, NULL) 47 | mock_response <- structure( 48 | list(status_code = 204), 49 | class = "response") 50 | mock_get <- mockery::mock(mock_response) 51 | 52 | expect_null(cl$request(mock_get, "/some/path")) 53 | mockery::expect_called(mock_get, 1) 54 | args <- mockery::mock_args(mock_get)[[1]] 55 | expect_equal(args[[3]], 56 | httr::add_headers("X-Vault-Token" = "secret")) 57 | expect_null(args[[4]]) 58 | }) 59 | 60 | 61 | test_that("can set namespace when building client", { 62 | skip_on_cran() 63 | skip_if_not_installed("withr") 64 | withr::with_envvar( 65 | c(VAULT_NAMESPACE = "foo", VAULT_ADDR = "https://example.com"), { 66 | expect_equal(vault_client()$api()$namespace, "foo") 67 | expect_equal(vault_client(namespace = "bar")$api()$namespace, "bar") 68 | }) 69 | 70 | withr::with_envvar( 71 | c(VAULT_NAMESPACE = NA_character_, VAULT_ADDR = "https://example.com"), { 72 | expect_null(vault_client()$api()$namespace) 73 | expect_equal(vault_client(namespace = "bar")$api()$namespace, "bar") 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /tests/testthat/test-vaultr-tools.R: -------------------------------------------------------------------------------- 1 | test_that("random", { 2 | srv <- test_vault_test_server() 3 | cl <- srv$client() 4 | 5 | res <- cl$tools$random() 6 | expect_equal(nchar(res), 64) 7 | 8 | res <- cl$tools$random(format = "base64") 9 | expect_equal(nchar(res), 44) 10 | 11 | res <- cl$tools$random(format = "raw") 12 | expect_type(res, "raw") 13 | expect_equal(length(res), 32) 14 | }) 15 | 16 | 17 | test_that("hash", { 18 | srv <- test_vault_test_server() 19 | cl <- srv$client() 20 | 21 | data <- charToRaw("hello vault") 22 | expect_equal( 23 | cl$tools$hash(data), 24 | "55e702c93bd83f5dc1eabdc7e0c268b8a7626b2e8008a7b96023192efd40c2a4") 25 | expect_equal( 26 | cl$tools$hash(data, "sha2-224"), 27 | "e2a3ef7fdcbf9bb6b862ab2bcddc99b2decebca260ba60ae4c8d58e0") 28 | expect_equal( 29 | cl$tools$hash(data, "sha2-384"), 30 | paste0( 31 | "08a5d9c42fe137f6299adcf2a583501821f8e1b43648c57ba15c6a9558bd4dd4059edc", 32 | "9ea1303dbf207a8d36ae10b450")) 33 | expect_equal( 34 | cl$tools$hash(data, "sha2-512"), 35 | paste0( 36 | "700bfd8ed566cbdcec20ce39db81aec29d489286a97206cd99824d8db1c2e6b3468848", 37 | "766dd791febb1cf7c4dd7faecc98430891698fbe162badfa502186d380")) 38 | 39 | expect_equal( 40 | cl$tools$hash(data, format = "base64"), 41 | "VecCyTvYP13B6r3H4MJouKdiay6ACKe5YCMZLv1AwqQ=") 42 | 43 | expect_error( 44 | cl$tools$hash("data", format = "base64"), 45 | "Expected raw data") 46 | }) 47 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/packages.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Using vaultr in packages" 3 | author: "Rich FitzJohn" 4 | date: "2023-11-09" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Using vaultr in packages} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | `vaultr` includes some machinery for using `vault` and `vaultr` 13 | within packages, and within tests in particular. They are designed 14 | to work well with 15 | [`testthat`](https://cran.r-project.org/package=testthat) but 16 | should be easily adapted to work with any other testing framework. 17 | 18 | In order to use this, you must set some environment variables: 19 | 20 | * `VAULTR_TEST_SERVER_BIN_PATH` must be set to a directory where 21 | the `vault` binary can be found, the path to the vault executable, 22 | or to the string `auto` to find vault on the `PATH` 23 | * `VAULTR_TEST_SERVER_PORT` can be set to the port where we start 24 | creating vault servers (by default this is 18200 but any high 25 | port number can be selected - we'll create servers *starting* at 26 | this port number and incrementing - see below for details) 27 | 28 | To create a vault server, run: 29 | 30 | ```r 31 | srv <- vaultr::vault_test_server() 32 | ``` 33 | 34 | ``` 35 | ## ...waiting for Vault to start 36 | ``` 37 | 38 | As soon as `srv` goes out of scope and is garbage collected, the 39 | vault server will be stopped. So keep `srv` within the scope of 40 | your tests. 41 | 42 | This object contains 43 | 44 | * `addr`: which is vault's address 45 | * `token`: a root token for this vault 46 | * `keys`: a vector of unseal keys 47 | 48 | By default the `vault` server is stared in ["Dev" server 49 | mode](https://www.vaultproject.io/docs/concepts/dev-server.html) in 50 | which we run with http (not https), a single unseal key and 51 | in-memory storage. **It is not suited for any production use**. 52 | 53 | You can create clients using `vaultr::vault_client()` and passing 54 | in appropriate parameters, but it may be more convenient to use 55 | `srv$client()`: 56 | 57 | ```r 58 | vault <- srv$client() 59 | vault 60 | ``` 61 | 62 | ``` 63 | ## 64 | ## Command groups: 65 | ## audit: Interact with vault's audit devices 66 | ## auth: administer vault's authentication methods 67 | ## operator: Administration commands for vault operators 68 | ## policy: Interact with policies 69 | ## secrets: Interact with secret engines 70 | ## token: Interact and configure vault's token support 71 | ## tools: General tools provided by vault 72 | ## Commands: 73 | ## api() 74 | ## delete(path) 75 | ## help() 76 | ## list(path, full_names = FALSE) 77 | ## login(..., method = "token", mount = NULL, renew = FALSE, 78 | ## quiet = FALSE, token_only = FALSE, use_cache = TRUE) 79 | ## read(path, field = NULL, metadata = FALSE) 80 | ## status() 81 | ## unwrap(token) 82 | ## wrap_lookup(token) 83 | ## write(path, data) 84 | ``` 85 | 86 | ```r 87 | vault$list("secret") 88 | ``` 89 | 90 | ``` 91 | ## character(0) 92 | ``` 93 | 94 | By default the client is logged in, but you can pass `login = 95 | FALSE` to create a client that needs to log in: 96 | 97 | ```r 98 | vault <- srv$client(login = FALSE) 99 | ``` 100 | 101 | 102 | ```r 103 | vault$list("secret") 104 | ``` 105 | 106 | ``` 107 | ## Error: Have not authenticated against vault 108 | ``` 109 | 110 | ```r 111 | vault$login(token = srv$token) 112 | ``` 113 | 114 | ``` 115 | ## Verifying token 116 | ``` 117 | 118 | ```r 119 | vault$list("secret") 120 | ``` 121 | 122 | ``` 123 | ## character(0) 124 | ``` 125 | 126 | You can use `$export` to export appropriate environment variables 127 | to connect to your vault: 128 | 129 | ```r 130 | srv$export() 131 | Sys.getenv("VAULT_ADDR") 132 | ``` 133 | 134 | ``` 135 | ## [1] "http://127.0.0.1:18200" 136 | ``` 137 | 138 | ```r 139 | Sys.getenv("VAULT_TOKEN") 140 | ``` 141 | 142 | ``` 143 | ## [1] "870e5c90-c908-bb4f-331e-c0cba32a457e" 144 | ``` 145 | 146 | ## Handling lack of vault gracefully 147 | 148 | The `vaultr::vault_test_server` function takes an argument 149 | `if_disabled` which is a callback function that will be called on 150 | failure to start a vault server. This could be for reasons such as: 151 | 152 | * the user has not opted in by setting `VAULTR_TEST_SERVER_BIN_PATH` 153 | * the binary is not in place 154 | * a port could not be opened 155 | 156 | 157 | 158 | By default this calls `testthat::skip`, which interactively will 159 | appear to cause an error but if called within a `test_that` block 160 | in a test will gracefully skip a test 161 | 162 | ```r 163 | Sys.setenv("VAULTR_TEST_SERVER_BIN_PATH" = NA_character_) 164 | ``` 165 | 166 | ```r 167 | srv <- vaultr::vault_test_server() 168 | ## Error: vault is not enabled 169 | ``` 170 | 171 | 172 | Alternatively, provide your own handler: 173 | 174 | ```r 175 | srv <- vaultr::vault_test_server(if_disabled = message) 176 | ``` 177 | 178 | ``` 179 | ## vault is not enabled 180 | ``` 181 | 182 | ```r 183 | srv 184 | ``` 185 | 186 | ``` 187 | ## NULL 188 | ``` 189 | 190 | With that approach, you might wrap vault-requiring tests with 191 | 192 | ```r 193 | if (!is.null(srv)) { 194 | # ... vault requiring code here ... 195 | } 196 | ``` 197 | 198 | All together (and assuming `testthat`), use of vault within tests 199 | might look like this example from the `vaultr` tests: 200 | 201 | ```r 202 | test_that("list", { 203 | srv <- vault_test_server() 204 | cl <- srv$client() 205 | 206 | cl$write("secret/a", list(key = 1)) 207 | cl$write("secret/b/c", list(key = 2)) 208 | cl$write("secret/b/d/e", list(key = 2)) 209 | 210 | expect_setequal(cl$list("secret"), c("a", "b/")) 211 | expect_setequal(cl$list("secret", TRUE), c("secret/a", "secret/b/")) 212 | expect_setequal(cl$list("secret/b"), c("c", "d/")) 213 | expect_setequal(cl$list("secret/b", TRUE), c("secret/b/c", "secret/b/d/")) 214 | }) 215 | ``` 216 | 217 | If you use one vault per test, as here, there's no need to clean up 218 | - we can assume that the vault is empty at the start of the test 219 | block and not worry about cleanup at the end. If vault is not 220 | enabled this test will be skipped over gracefully. 221 | 222 | ## Installing vault 223 | 224 | To develop your package, you will need vault installed; please see [the official vault docs](https://developer.hashicorp.com/vault/docs/install) for this. 225 | 226 | If you use github actions, you can follow the same approach as `vaultr` itself; add the environment variables `VAULTR_TEST_SERVER_BIN_PATH` and `VAULTR_TEST_SERVER_PORT`: 227 | 228 | ```yaml 229 | env: 230 | ... 231 | VAULTR_TEST_SERVER_BIN_PATH: auto 232 | VAULTR_TEST_SERVER_PORT: 18200 233 | ``` 234 | 235 | then use the [`eLco/setup-vault`](https://github.com/marketplace/actions/setup-vault-cli) action to install a suitable vault binary: 236 | 237 | ```yaml 238 | - uses: eLco/setup-vault@v1 239 | ``` 240 | 241 | See [the `vaultr` actions](https://github.com/vimc/vaultr/blob/master/.github/workflows/R-CMD-check.yaml) for full details. 242 | -------------------------------------------------------------------------------- /vignettes_src/packages.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Using vaultr in packages" 3 | author: "Rich FitzJohn" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Using vaultr in packages} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | `vaultr` includes some machinery for using `vault` and `vaultr` 13 | within packages, and within tests in particular. They are designed 14 | to work well with 15 | [`testthat`](https://cran.r-project.org/package=testthat) but 16 | should be easily adapted to work with any other testing framework. 17 | 18 | In order to use this, you must set some environment variables: 19 | 20 | * `VAULTR_TEST_SERVER_BIN_PATH` must be set to a directory where 21 | the `vault` binary can be found, the path to the vault executable, 22 | or to the string `auto` to find vault on the `PATH` 23 | * `VAULTR_TEST_SERVER_PORT` can be set to the port where we start 24 | creating vault servers (by default this is 18200 but any high 25 | port number can be selected - we'll create servers *starting* at 26 | this port number and incrementing - see below for details) 27 | 28 | To create a vault server, run: 29 | ``` {r } 30 | srv <- vaultr::vault_test_server() 31 | ``` 32 | 33 | As soon as `srv` goes out of scope and is garbage collected, the 34 | vault server will be stopped. So keep `srv` within the scope of 35 | your tests. 36 | 37 | This object contains 38 | 39 | * `addr`: which is vault's address 40 | * `token`: a root token for this vault 41 | * `keys`: a vector of unseal keys 42 | 43 | By default the `vault` server is stared in ["Dev" server 44 | mode](https://www.vaultproject.io/docs/concepts/dev-server.html) in 45 | which we run with http (not https), a single unseal key and 46 | in-memory storage. **It is not suited for any production use**. 47 | 48 | You can create clients using `vaultr::vault_client()` and passing 49 | in appropriate parameters, but it may be more convenient to use 50 | `srv$client()`: 51 | ``` {r } 52 | vault <- srv$client() 53 | vault 54 | vault$list("secret") 55 | ``` 56 | 57 | By default the client is logged in, but you can pass `login = 58 | FALSE` to create a client that needs to log in: 59 | ``` {r } 60 | vault <- srv$client(login = FALSE) 61 | ``` 62 | 63 | ``` {r error = TRUE} 64 | vault$list("secret") 65 | vault$login(token = srv$token) 66 | vault$list("secret") 67 | ``` 68 | 69 | You can use `$export` to export appropriate environment variables 70 | to connect to your vault: 71 | ``` {r } 72 | srv$export() 73 | Sys.getenv("VAULT_ADDR") 74 | Sys.getenv("VAULT_TOKEN") 75 | ``` 76 | 77 | ## Handling lack of vault gracefully 78 | 79 | The `vaultr::vault_test_server` function takes an argument 80 | `if_disabled` which is a callback function that will be called on 81 | failure to start a vault server. This could be for reasons such as: 82 | 83 | * the user has not opted in by setting `VAULTR_TEST_SERVER_BIN_PATH` 84 | * the binary is not in place 85 | * a port could not be opened 86 | 87 | By default this calls `testthat::skip`, which interactively will 88 | appear to cause an error but if called within a `test_that` block 89 | in a test will gracefully skip a test 90 | 91 | ```r 92 | srv <- vaultr::vault_test_server() 93 | ## Error: vault is not enabled 94 | ``` 95 | 96 | Alternatively, provide your own handler: 97 | 98 | ```r 99 | srv <- vaultr::vault_test_server(if_disabled = message) 100 | ``` 101 | 102 | In this case, `vaultr::vault_test_server` will return `NULL` and you might wrap vault-requiring tests with: 103 | 104 | ```r 105 | if (!is.null(srv)) { 106 | # ... vault requiring code here ... 107 | } 108 | ``` 109 | 110 | All together (and assuming `testthat`), use of vault within tests 111 | might look like this example from the `vaultr` tests: 112 | 113 | ```r 114 | test_that("list", { 115 | srv <- vault_test_server() 116 | cl <- srv$client() 117 | 118 | cl$write("secret/a", list(key = 1)) 119 | cl$write("secret/b/c", list(key = 2)) 120 | cl$write("secret/b/d/e", list(key = 2)) 121 | 122 | expect_setequal(cl$list("secret"), c("a", "b/")) 123 | expect_setequal(cl$list("secret", TRUE), c("secret/a", "secret/b/")) 124 | expect_setequal(cl$list("secret/b"), c("c", "d/")) 125 | expect_setequal(cl$list("secret/b", TRUE), c("secret/b/c", "secret/b/d/")) 126 | }) 127 | ``` 128 | 129 | If you use one vault per test, as here, there's no need to clean up 130 | - we can assume that the vault is empty at the start of the test 131 | block and not worry about cleanup at the end. If vault is not 132 | enabled this test will be skipped over gracefully. 133 | 134 | ## Installing vault 135 | 136 | To develop your package, you will need vault installed; please see [the official vault docs](https://developer.hashicorp.com/vault/docs/install) for this. 137 | 138 | If you use github actions, you can follow the same approach as `vaultr` itself; add the environment variables `VAULTR_TEST_SERVER_BIN_PATH` and `VAULTR_TEST_SERVER_PORT`: 139 | 140 | ```yaml 141 | env: 142 | ... 143 | VAULTR_TEST_SERVER_BIN_PATH: auto 144 | VAULTR_TEST_SERVER_PORT: 18200 145 | ``` 146 | 147 | then use the [`eLco/setup-vault`](https://github.com/marketplace/actions/setup-vault-cli) action to install a suitable vault binary: 148 | 149 | ```yaml 150 | - uses: eLco/setup-vault@v1 151 | ``` 152 | 153 | See [the `vaultr` actions](https://github.com/vimc/vaultr/blob/master/.github/workflows/R-CMD-check.yaml) for full details. 154 | -------------------------------------------------------------------------------- /vignettes_src/vaultr.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "vaultr" 3 | author: "Rich FitzJohn" 4 | date: "`r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{vaultr} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ``` {r include = FALSE} 13 | srv <- vaultr::vault_test_server() 14 | srv$export() 15 | local({ 16 | cl <- srv$client() 17 | cl$write("/secret/database/admin", list(value = "s3cret")) 18 | cl$write("/secret/database/readonly", list(value = "passw0rd")) 19 | cl$policy$write("basic", 'path "secret/*" {\n policy = "read"}') 20 | cl$auth$enable("userpass") 21 | cl$auth$userpass$write("alice", "p4ssw0rd", "basic") 22 | }) 23 | ``` 24 | 25 | ## Connecting to vault 26 | 27 | The first part of the vignette assumes that vault is set up; later 28 | we show how to control login behaviour and configure vault itself. 29 | Access of vault requires several environment variables configured, 30 | in particular: 31 | 32 | * `VAULT_ADDR`: the address of vault 33 | * `VAULT_TOKEN`: the token to authenticate to vault with 34 | * `VAULTR_AUTH_METHOD`: the method to use to authenticate with 35 | (login to) vault. 36 | 37 | (environment variables starting with `VAULT_` are shared with the 38 | vault cli, variables starting `VAULTR_` are specific to this 39 | package). 40 | 41 | in this vignette, these are already configured: 42 | ``` {r } 43 | Sys.getenv(c("VAULT_ADDR", "VAULT_TOKEN", "VAULTR_AUTH_METHOD")) 44 | ``` 45 | 46 | To access vault, first create a client: 47 | ``` {r } 48 | vault <- vaultr::vault_client(login = TRUE, quiet = TRUE) 49 | ``` 50 | 51 | This creates an [`R6`](https://CRAN.R-project.org/package=R6) 52 | object with methods for interacting with vault: 53 | ``` {r } 54 | vault 55 | ``` 56 | 57 | Because there are many methods, these are organised 58 | _hierarchically_, similar to the vault cli client. For example 59 | `vault$auth` contains commands for interacting with authentication 60 | backends (and itself contains further command groups): 61 | ``` {r } 62 | vault$auth 63 | ``` 64 | 65 | ## Reading, writing, listing and deleting secrets 66 | 67 | It is anticipated that the vast majority of `vaultr` usage will be 68 | interacting with vault's key-value stores - this is is done with 69 | the `$read`, `$write`, `$list` and `$delete` methods of the base 70 | vault client object. By default, a vault server will have a 71 | [version-1 key value 72 | store](https://www.vaultproject.io/docs/secrets/kv/kv-v1.html) 73 | mounted at `/secret`. 74 | 75 | List secrets with `$list`: 76 | ``` {r } 77 | vault$list("secret") 78 | vault$list("secret/database") 79 | ``` 80 | 81 | values that terminate in `/` are "directories". 82 | 83 | Read secrets with `$read`: 84 | ``` {r } 85 | vault$read("secret/database/readonly") 86 | ``` 87 | 88 | secrets are returned as a `list`, because multiple secrets may be 89 | stored at a path. To access a single field, use the `field` 90 | argument: 91 | ``` {r } 92 | vault$read("secret/database/readonly", field = "value") 93 | ``` 94 | 95 | Delete secrets with `$delete`: 96 | ``` {r } 97 | vault$delete("secret/database/readonly") 98 | ``` 99 | 100 | After which the data is no longer available: 101 | ``` {r } 102 | vault$read("secret/database/readonly") 103 | ``` 104 | 105 | Write new secrets with `$write`: 106 | ``` {r } 107 | vault$write("secret/webserver", list(password = "horsestaple")) 108 | ``` 109 | 110 | (be aware that this may well write the secret into your R history 111 | file `.Rhistory` - to be more secure you may want to read these in 112 | from environment variables and use `Sys.getenv()` to read them into 113 | R). 114 | 115 | ## Alternative login approaches 116 | 117 | Using the `token` approach for authentication requires that you 118 | have already authenticated with vault to get a token. It is 119 | usually more convenient to instead use some other method. Vault 120 | itself supports [many authentication 121 | methods](https://www.vaultproject.io/docs/auth/index.html) but 122 | `vaultr` currently supports only GitHub and username/password at 123 | this point. 124 | 125 | **This document should not be used as a reference point for 126 | configuring vault in any situation other than testing. Please 127 | refer to the [vault 128 | documentation](https://www.vaultproject.io/docs/auth/index.html) 129 | first.** 130 | 131 | If you want to configure vault from R rather than the command line 132 | client, you will find a very close mapping between argument 133 | names. We will here assume that the methods are already configured 134 | by your vault administrator and show how to interact with them. 135 | 136 | ### Username and password (`userpass`) 137 | 138 | Assume vault has been configured to support 139 | [userpass](https://www.vaultproject.io/docs/auth/userpass.html) 140 | authentication and that a user `alice` exists with password 141 | `p4ssw0rd`. 142 | ``` {r } 143 | cl <- vaultr::vault_client(login = "userpass", username = "alice", 144 | password = "p4ssw0rd") 145 | cl$read("secret/webserver") 146 | ``` 147 | 148 | This is obviously insecure! `vaultr` can use 149 | [`getPass`](https://cran.r-project.org/package=getPass) to securely 150 | prompt for a password: 151 | 152 | ```r 153 | cl <- vaultr::vault_client(login = "userpass", username = "alice") 154 | ## Password for 'alice': ******** 155 | ## ok, duration: 2764800 s (~32d) 156 | ``` 157 | 158 | ### GitHub (`github`) 159 | 160 | Assuming that vault has been configured to support 161 | [GitHub](https://developer.hashicorp.com/vault/docs/auth/github), and 162 | that the environment variable `VAULT_AUTH_GITHUB_TOKEN` contains a 163 | personal access token for a team that has been configured to have 164 | vault access, then you can log in with github: 165 | 166 | ```r 167 | cl <- vaultr::vault_client(login = "github") 168 | ``` 169 | 170 | See `?vault_client_auth_github` for details. 171 | 172 | ### LDAP (`ldap`) 173 | 174 | If your organisation uses LDAP and has configured vault to enable that at the server level, you can login using that by passing login = "ldap" along with your username: 175 | 176 | ```r 177 | cl <- vaultr::vault_client(login = "ldap", username = "alice") 178 | ## Password for 'alice': ******** 179 | ## ok, duration: 2764800 s (~32d) 180 | ``` 181 | 182 | This will authenticate with the LDAP server, and generate an appropriate token. See `?vault_client_auth_ldap` for details. 183 | --------------------------------------------------------------------------------