R/vault_client_auth_approle.R

##' Interact with vault's AppRole authentication backend.  For more
##' details about this, see the vault documentation at
##' https://developer.hashicorp.com/vault/docs/auth/approle
##'
##' @title Vault AppRole Authentication Configuration
##' @name vault_client_auth_approle
##'
##' @examples
##'
##' vaultr::vault_client(addr = "https://localhost:8200")$auth$approle
vault_client_auth_approle <- R6::R6Class(
  "vault_client_auth_approle",
  inherit = vault_client_object,
  cloneable = FALSE,

  private = list(
    api_client = NULL,
    mount = NULL
  ),

  public = list(
    ##' @description Create a `vault_client_approle` object. Not typically
    ##'   called by users.
    ##'
    ##' @param api_client A [vaultr::vault_api_client] object
    ##'
    ##' @param mount Mount point for the backend
    initialize = function(api_client, mount) {
      super$initialize("Interact and configure vault's AppRole support")
      assert_scalar_character(mount)
      private$mount <- sub("^/", "", mount)
      private$api_client <- api_client
    },

    ##' @description Set up a `vault_client_auth_approle` object at a
    ##'   custom mount. For example, suppose you mounted the `approle`
    ##'   authentication backend at `/approle-dev` you might use `ar <-
    ##'   vault$auth$approle2$custom_mount("/approle-dev")` - this pattern
    ##'   is repeated for other secret and authentication backends.
    ##'
    ##' @param mount String, indicating the path that the engine is mounted at.
    custom_mount = function(mount) {
      vault_client_auth_approle$new(private$api_client, mount)
    },

    ##' @description
    ##' This endpoint returns a list the existing AppRoles in the method.
    role_list = function() {
      path <- sprintf("/auth/%s/role", private$mount)
      tryCatch(
        list_to_character(private$api_client$LIST(path)$data$keys),
        vault_invalid_path = function(e) character(0))
    },

    ##' @description
    ##' Creates a new AppRole or updates an existing AppRole. This
    ##' endpoint supports both create and update capabilities. There can
    ##' be one or more constraints enabled on the role. It is required to
    ##' have at least one of them enabled while creating or updating a
    ##' role.
    ##'
    ##' @param role_name Name of the AppRole
    ##'
    ##' @param bind_secret_id Require secret_id to be presented when
    ##'   logging in using this AppRole (boolean, default is `TRUE`).
    ##'
    ##' @param secret_id_bound_cidrs Character vector of CIDR blocks;
    ##'   if set, specifies blocks of IP addresses which can perform
    ##'   the login operation.
    ##'
    ##' @param token_bound_cidrs Character vector of if set, specifies
    ##'   blocks of IP addresses which can use the auth tokens
    ##'   generated by this role.
    ##'
    ##' @param policies Character vector of policies set on tokens
    ##'   issued via this AppRole.
    ##'
    ##' @param secret_id_num_uses Number of times any particular
    ##'   SecretID can be used to fetch a token from this AppRole,
    ##'   after which the SecretID will expire. A value of zero will
    ##'   allow unlimited uses.
    ##'
    ##' @param secret_id_ttl Duration, after which any SecretID expires.
    ##'
    ##' @param token_num_uses Number of times issued tokens can be
    ##'   used. A value of 0 means unlimited uses
    ##'
    ##' @param token_ttl Duration to set as the TTL for issued tokens
    ##' and at renewal time.
    ##'
    ##' @param token_max_ttl Duration, after which the issued token can
    ##'   no longer be renewed.
    ##'
    ##' @param period A duration; when set, the token generated using
    ##'   this AppRole is a periodic token; so long as it is renewed it
    ##'   never expires, but the TTL set on the token at each renewal
    ##'   is fixed to the value specified here. If this value is
    ##'   modified, the token will pick up the new value at its next
    ##'   renewal.
    ##'
    ##' @param enable_local_secret_ids Boolean, if `TRUE`, then the
    ##'   secret IDs generated using this role will be cluster
    ##'   local. This can only be set during role creation and once
    ##'   set, it can't be reset later.
    ##'
    ##' @param token_type The type of token that should be generated
    ##'   via this role. Can be `service`, `batch`, or `default` to use
    ##'   the mount's default (which unless changed will be service
    ##'   tokens).
    role_write = function(role_name, bind_secret_id = NULL,
                          secret_id_bound_cidrs = NULL,
                          token_bound_cidrs = NULL,
                          policies = NULL,
                          secret_id_num_uses = NULL, secret_id_ttl = NULL,
                          token_num_uses = NULL, token_ttl = NULL,
                          token_max_ttl = NULL, period = NULL,
                          enable_local_secret_ids = NULL, token_type = NULL) {
      role_name <- assert_scalar_character(role_name)
      body <- list(
        bind_secret_id =
          bind_secret_id %&&% assert_scalar_boolean(bind_secret_id),
        secret_id_bound_cidrs =
          secret_id_bound_cidrs %&&% I(assert_character(secret_id_bound_cidrs)),
        token_bound_cidrs =
          token_bound_cidrs %&&% I(assert_character(token_bound_cidrs)),
        policies = policies %&&% paste(assert_character(policies),
                                       collapse = ","),
        secret_id_num_uses =
          secret_id_num_uses %&&% assert_scalar_integer(secret_id_num_uses),
        secret_id_ttl = secret_id_ttl %&&% assert_is_duration(secret_id_ttl),
        token_num_uses =
          token_num_uses %&&% assert_scalar_integer(token_num_uses),
        token_ttl = token_ttl %&&% assert_is_duration(token_ttl),
        token_max_ttl = token_max_ttl %&&% assert_is_duration(token_max_ttl),
        enable_local_secret_ids =
          enable_local_secret_ids %&&%
          assert_scalar_character(enable_local_secret_ids),
        period = period %&&% assert_is_duration(period),
        token_type = token_type %&&% assert_scalar_character(token_type))
      path <- sprintf("/auth/%s/role/%s", private$mount, role_name)
      private$api_client$POST(path, body = drop_null(body))
      invisible(NULL)
    },

    ##' @description Reads the properties of an existing AppRole.
    ##'
    ##' @param role_name Name of the AppRole
    role_read = function(role_name) {
      assert_scalar_character(role_name)
      path <- sprintf("/auth/%s/role/%s", private$mount, role_name)
      ret <- private$api_client$GET(path)$data
      ret$policies <- list_to_character(ret$policies)
      ret
    },

    ##' @description Deletes an existing AppRole from the method.
    ##'
    ##' @param role_name Name of the AppRole to delete
    role_delete = function(role_name) {
      assert_scalar_character(role_name)
      path <- sprintf("/auth/%s/role/%s", private$mount, role_name)
      private$api_client$DELETE(path)
      invisible(NULL)
    },

    ##' @description Reads the RoleID of an existing AppRole.
    ##'
    ##' @param role_name Name of the AppRole
    role_id_read = function(role_name) {
      assert_scalar_character(role_name)
      path <- sprintf("/auth/%s/role/%s/role-id", private$mount, role_name)
      private$api_client$GET(path)$data$role_id
    },

    ##' @description Updates the RoleID of an existing AppRole to a
    ##'   custom value.
    ##'
    ##' @param role_name Name of the AppRole (string)
    ##'
    ##' @param role_id Value to be set as RoleID (string)
    role_id_write = function(role_name, role_id) {
      assert_scalar_character(role_name)
      body <- list(role_id = assert_scalar_character(role_id))
      path <- sprintf("/auth/%s/role/%s/role-id", private$mount, role_name)
      private$api_client$POST(path, body = body)
      invisible(NULL)
    },

    ##' @description Generates and issues a new SecretID on an existing
    ##'   AppRole. Similar to tokens, the response will also contain a
    ##'   `secret_id_accessor` value which can be used to read the
    ##'   properties of the SecretID without divulging the SecretID
    ##'   itself, and also to delete the SecretID from the AppRole.
    ##'
    ##' @param role_name Name of the AppRole.
    ##'
    ##' @param metadata Metadata to be tied to the SecretID. This
    ##'   should be a named list of key-value pairs. This metadata will
    ##'   be set on tokens issued with this SecretID, and is logged in
    ##'   audit logs in plaintext.
    ##'
    ##' @param cidr_list Character vector CIDR blocks enforcing secret
    ##'   IDs to be used from specific set of IP addresses. If
    ##'   `bound_cidr_list` is set on the role, then the list of CIDR
    ##'   blocks listed here should be a subset of the CIDR blocks
    ##'   listed on the role.
    ##'
    ##' @param token_bound_cidrs Character vector of CIDR blocks; if
    ##'   set, specifies blocks of IP addresses which can use the auth
    ##'   tokens generated by this SecretID. Overrides any role-set
    ##'   value but must be a subset.
    secret_id_generate = function(role_name, metadata = NULL,
                                  cidr_list = NULL, token_bound_cidrs = NULL) {
      assert_scalar_character(role_name)
      ## TODO: cidr_list interacts with bound_cidr_list but I don't
      ## see that as a parameter in the POST endpoints
      body <- list(
        metadata = metadata %&&% as.character(to_json(metadata)),
        cidr_list = cidr_list %&&% I(assert_character(cidr_list)),
        token_bound_cidrs =
          token_bound_cidrs %&&% I(assert_character(token_bound_cidrs)))
      path <- sprintf("/auth/%s/role/%s/secret-id", private$mount, role_name)
      res <- private$api_client$POST(path, body = body)
      list(id = res$data$secret_id, accessor = res$data$secret_id_accessor)
    },

    ##' @description Lists the accessors of all the SecretIDs issued
    ##'   against the AppRole. This includes the accessors for "custom"
    ##'   SecretIDs as well.
    ##'
    ##' @param role_name Name of the AppRole
    secret_id_list = function(role_name) {
      assert_scalar_character(role_name)
      path <- sprintf("/auth/%s/role/%s/secret-id", private$mount, role_name)
      tryCatch(
        list_to_character(private$api_client$LIST(path)$data$keys),
        vault_invalid_path = function(e) character(0))
    },

    ##' @description Reads out the properties of a SecretID.
    ##'
    ##' @param role_name Name of the AppRole
    ##'
    ##' @param secret_id Secret ID attached to the role
    ##'
    ##' @param accessor Logical, if `TRUE`, treat `secret_id` as an
    ##'   accessor rather than a secret id.
    secret_id_read = function(role_name, secret_id, accessor = FALSE) {
      assert_scalar_character(role_name)
      if (accessor) {
        path <- sprintf("/auth/%s/role/%s/secret-id-accessor/lookup",
                        private$mount, role_name)
        body <- list(secret_id_accessor = assert_scalar_character(secret_id))
      } else {
        path <- sprintf("/auth/%s/role/%s/secret-id/lookup",
                        private$mount, role_name)
        body <- list(secret_id = assert_scalar_character(secret_id))
      }
      private$api_client$POST(path, body = body)$data
    },

    ##' @description Delete an AppRole secret ID
    ##'
    ##' @param role_name Name of the AppRole
    ##'
    ##' @param secret_id Secret ID attached to the role
    ##'
    ##' @param accessor Logical, if `TRUE`, treat `secret_id` as an
    ##'     accessor rather than a secret id.
    secret_id_delete = function(role_name, secret_id, accessor = FALSE) {
      assert_scalar_character(role_name)
      if (accessor) {
        path <- sprintf("/auth/%s/role/%s/secret-id-accessor/destroy",
                        private$mount, role_name)
        body <- list(secret_id_accessor = assert_scalar_character(secret_id))
      } else {
        path <- sprintf("/auth/%s/role/%s/secret-id/destroy",
                        private$mount, role_name)
        body <- list(secret_id = assert_scalar_character(secret_id))
      }
      private$api_client$POST(path, body = body)
      invisible(NULL)
    },

    ## Create Custom AppRole Secret ID (push)
    ## Read, Update, or Delete AppRole Properties (separate here)
    ## Tidy Tokens

    ##' @description Log into the vault using AppRole authentication.
    ##'   Normally you would not call this directly but instead use
    ##'   `$login` with `method = "approle"` and proving the `role_id`
    ##'   and `secret_id` arguments.  This function returns a vault
    ##'   token but does not set it as the client token.
    ##'
    ##' @param role_id RoleID of the AppRole
    ##' @param secret_id SecretID belonging to AppRole
    login = function(role_id, secret_id) {
      body <- list(role_id = assert_scalar_character(role_id),
                   secret_id = assert_scalar_character(secret_id))
      path <- sprintf("/auth/%s/login", private$mount)
      res <- private$api_client$POST(path, body = body,
                                     allow_missing_token = TRUE)
      res$auth
    }
  ))
vimc/vaultr documentation built on Nov. 11, 2023, 8:21 a.m.