R/vault.R

Defines functions vault_initialize vault_delete vault_reset vault_ensure vault_validate_schema vault_healthy vault_path ensure_vault_connection local_vault_connection vault_connection local_vault

Documented in local_vault

# ops ---------------------------------------------------------------------

vault_initialize <- function(con = NULL) {
  con <- ensure_vault_connection(con)
  statement <- paste(
    sep = "\n",
    "CREATE TABLE secrets(",
    "  id      INTEGER PRIMARY KEY,",
    "  app     TEXT    NOT NULL,",
    "  service TEXT    NOT NULL,",
    "  name    TEXT    NOT NULL,",
    "  value   TEXT    NOT NULL,",
    "  nonce   TEXT    NOT NULL,",
    "  tag     TEXT    NOT NULL,",
    "  UNIQUE(app, service, name)",
    ");"
  )
  RSQLite::dbExecute(con, statement)
  invisible()
}

vault_delete <- function() {
  path <- vault_path()
  if (fs::file_exists(path)) {
    fs::file_delete(path)
  }
  invisible()
}

vault_reset <- function() {
  vault_delete()
  vault_initialize()
  invisible()
}

vault_ensure <- function(con = NULL) {
  if (!vault_healthy(con)) {
    vault_initialize(con)
  }
  vault_validate_schema(con)
  invisible()
}

vault_validate_schema <- function(con = NULL) {
  con <- ensure_vault_connection(con)
  expected <- data.frame(
    stringsAsFactors = FALSE,
    id = integer(),
    app = character(),
    service = character(),
    name = character(),
    value = character(),
    nonce = character(),
    tag = character()
  )
  data <- RSQLite::dbGetQuery(con, "SELECT * FROM secrets LIMIT 0")
  if (!identical(data, expected)) {
    code <- ui_code("vault_reset()")
    ui_stop("Invalid vault schema. Please run {code} to reset your vault.")
  }
}

vault_healthy <- function(con = NULL) {
  if (!fs::file_exists(vault_path())) {
    return(FALSE)
  }
  con <- ensure_vault_connection(con)
  RSQLite::dbExistsTable(con, "secrets")
}


# path --------------------------------------------------------------------

vault_path <- function() {
  default <- fs::path(rappdirs::user_data_dir(appname = "r-vault"), "vault.db")
  path <- getOption("vault_path", Sys.getenv("VAULT_PATH", default))
  fs::path_expand(path)
}


# connection --------------------------------------------------------------

ensure_vault_connection <- function(con, .local_envir = parent.frame()) {
  if (is.null(con)) {
    con <- local_vault_connection(.local_envir = .local_envir)
  }
  con
}

local_vault_connection <- function(.local_envir = parent.frame()) {
  withr::local_db_connection(vault_connection(), .local_envir = .local_envir)
}

vault_connection <- function() {
  path <- vault_path()
  fs::dir_create(fs::path_dir(path))
  RSQLite::dbConnect(RSQLite::SQLite(), path)
}


# context -----------------------------------------------------------------

#' Use a different vault
#'
#' Temporarily change the location where `vault` will store secrets for the
#' duration of a function body.
#'
#' @param path File location of where to store secrets
#' @param .local_envir Environment for function body. Does not need to be
#'   changed for most uses.
#' @export
local_vault <- function(path, .local_envir = parent.frame()) {
  withr::local_options(list("vault_path" = path), .local_envir = .local_envir)
}
shunsambongi/vault documentation built on March 19, 2020, 4:58 p.m.