Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Description: Platform independent 'API' to access the operating system's
License: MIT + file LICENSE
URL: https://r-lib.github.io/keyring/index.html, https://github.com/r-lib/keyring#readme
BugReports: https://github.com/r-lib/keyring/issues
RoxygenNote: 7.1.2
RoxygenNote: 7.2.1
Roxygen: list(markdown = TRUE, r6 = FALSE)
Imports:
assertthat,
Expand All @@ -31,6 +31,7 @@ Suggests:
callr,
covr,
mockery,
paws,
testthat,
withr
Encoding: UTF-8
Expand All @@ -40,6 +41,7 @@ Collate:
'api.R'
'assertions.R'
'backend-class.R'
'backend-awssecretsmanager.R'
'backend-env.R'
'backend-file.R'
'backend-macos.R'
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by roxygen2: do not edit by hand

export(backend)
export(backend_awssecretsmanager)
export(backend_env)
export(backend_file)
export(backend_keyrings)
Expand Down
260 changes: 260 additions & 0 deletions R/backend-awssecretsmanager.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@


#' AWS Secrets Manager keyring backend
#'
#' This backend must be selected explicitly. It makes calls to the AWS
#' secretsmanager service.
#'
#' This backend does not support keyrings or user names. The call to the
#' AWS service is authenticated by either the user's credentials or the IAM
#' user associated with the process, for example in a docker container.
#'
#' Note that the AWS APIs provide enventual consistency, it can take
#' a noticeable amount of time, up to five minutes, for updates and deletes
#' to propagate and so code that updates, deletes and lists needs to be
#' written to tolerate that.
#'
#'
#' @family keyring backends
#' @export
#' @include backend-class.R
#' @examples
#' \dontrun{
#' ##
#' kb <- backend_awssecretsmanager$new()
#' kb$set_with_value("service", password = "secret")
#' kb$get("service")
#' kb$delete("service")
#' }

backend_awssecretsmanager <- R6Class(
"backend_awssecretsmanager",
inherit = backend_keyrings,
public = list(
name = "aws",
initialize = function(keyring = NULL)
b_aws_init(self, private, keyring),

get = function(service,
username = NULL,
keyring = NULL)
b_aws_get(self, private, service, username, keyring),
get_raw = function(service,
username = NULL,
keyring = NULL)
b_aws_get_raw(self, private, service, username, keyring),
set = function(service,
username = NULL,
keyring = NULL,
prompt = "Password: ")
b_aws_set(self, private, service, username, keyring, prompt),
set_with_value = function(service,
username = NULL,
password = NULL,
keyring = NULL)
b_aws_set_with_value(self, private, service, username, password,
keyring),
set_with_raw_value = function(service,
username = NULL,
password = NULL,
keyring = NULL)
b_aws_set_with_raw_value(self, private, service, username, password,
keyring),
delete = function(service,
username = NULL,
keyring = NULL)
b_aws_delete(self, private, service, username, keyring),
list = function(service = NULL, keyring = NULL)
b_aws_list(self, private, service, keyring),
is_available = function(report_error = FALSE)
b_aws_is_available(self, private, report_error),

has_keyring_support = function()
{
return(FALSE)
},

docs = function() {
modifyList(super$docs(),
list(. = "Store secrets using the AWS Secrets manager."))
}
),

private = list(
keyring = NULL,
sservice = NULL,
requestToken = paste("123456789012345678901234567890", as.character(Sys.time())),
keyring_create_direct = function(keyring, password = NULL)
b_aws_keyring_create_direct(self, private, keyring, password)
)
)

b_aws_init <- function(self, private, keyring) {
if (!is.null(keyring))
stop("keyring parameter is not supported by the aws secrets manager backend")
private$sservice <- paws::secretsmanager()
invisible(self)
}

b_aws_get <- function(self, private, service, username, keyring) {
if (!is.null(username))
stop("username parameter is not supported by the aws secrets manager backend")
if (!is.null(keyring))
stop("keyring parameter is not supported by the aws secrets manager backend")
return(private$sservice$get_secret_value(SecretId = service,)$SecretString)
}

b_aws_get_raw <-
function(self, private, service, username, keyring) {
if (!is.null(username))
stop("username parameter is not supported by the aws secrets manager backend")
if (!is.null(keyring))
stop("keyring parameter is not supported by the aws secrets manager backend")
return(private$sservice$svc$get_secret_value(SecretId = service,)$SecretBinary)
}

b_aws_set <-
function(self,
private,
service,
username,
keyring,
prompt) {
if (!is.null(username))
stop("username parameter is not supported by the aws secrets manager backend")
if (!is.null(keyring))
stop("keyring parameter is not supported by the aws secrets manager backend")
username <- username %||% getOption("keyring_username")
password <- get_pass(prompt)
if (is.null(password))
stop("No secret provided")
private$sservice$create_secret(
ClientRequestToken = private$requestToken,
Description = "",
Name = service,
SecretString = password
)
invisible(self)
}

b_aws_set_with_value <-
function(self,
private,
service,
username,
password,
keyring) {
if (!is.null(username))
stop("username parameter is not supported by the aws secrets manager backend")
if (!is.null(keyring))
stop("keyring parameter is not supported by the aws secrets manager backend")
username <- username %||% getOption("keyring_username")
keyring <- keyring %||% private$keyring
private$sservice$create_secret(
ClientRequestToken = private$requestToken,
Description = "",
Name = service,
SecretString = password
)
invisible(self)
}

b_aws_set_with_raw_value <-
function(self,
private,
service,
username,
password,
keyring) {
if (!is.null(username))
stop("username parameter is not supported by the aws secrets manager backend")
if (!is.null(keyring))
stop("keyring parameter is not supported by the aws secrets manager backend")
username <- username %||% getOption("keyring_username")
keyring <- keyring %||% private$keyring
private$sservice$create_secret(
ClientRequestToken = private$requestToken,
Description = "",
Name = service,
SecretBinaryString = password
)
invisible(self)
}

b_aws_delete <-
function(self, private, service, username, keyring) {
if (!is.null(username))
stop("username parameter is not supported by the aws secrets manager backend")
if (!is.null(keyring))
stop("keyring parameter is not supported by the aws secrets manager backend")
username <- username %||% getOption("keyring_username")
keyring <- keyring %||% private$keyring
private$sservice$delete_secret(ForceDeleteWithoutRecovery = TRUE,
SecretId = service)
invisible(self)
}

b_aws_list <- function(self, private, service, keyring) {
if (!is.null(keyring))
stop("keyring parameter is not supported by the aws secrets manager backend")
keyring <- keyring %||% private$keyring
if (is.null(service) ||
service == "")
# missing defaults to null in calling routine
{
res = private$sservice$list_secrets()
secretList = res$SecretList
while(length(res$NextToken)>0)
{
res = private$sservice$list_secrets(NextToken=res$NextToken)
secretList = append(secretList, res$SecretList)
}
} else
{
res = private$sservice$list_secrets(Filter = list(list(
Key = "name", Values = c(service)
)))
secretList = res$SecretList
while(length(res$NextToken)>0)
{
res = private$sservice$list_secrets(NextToken=res$NextToken, Filter = list(list(
Key = "name", Values = c(service)
)))
secretList = append(secretList, res$SecretList)
}
}
nameList = c()
if (length(secretList) > 0)
{
for (i in 1:length(secretList))
{
nameList = c(nameList, secretList[[i]]$Name)
}
}
df = data.frame(service = nameList,
stringsAsFactors = FALSE)
df$username = NULL

return(df)
}

b_aws_is_available <- function(self, private, report_error) {
if(!requireNamespace("paws"))
{
if(report_error)
{
signalCondition("Paws library not available. It is required for AWS access")
}
return(FALSE)
}
callerID = try(paws::sts()$get_caller_identity())
if (inherits(callerID, "try-error")) {
if(report_error)
{
signalCondition("No AWS credentials or AWS not reachable")
}
return(FALSE)
}
return(TRUE)
}

15 changes: 8 additions & 7 deletions R/default_backend.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@
#' 1. the `keyring_keyring` option.
#' - You can change this by using `options(keyring_keyring = "NEWVALUE")`
#' 1. If this is not set, the `R_KEYRING_KEYRING` environment variable.
#' - Change this value with `Sys.setenv(R_KEYRING_KEYRING = "NEWVALUE")`,
#' either in your script or in your `.Renviron` file.
#' - Change this value with `Sys.setenv(R_KEYRING_KEYRING = "NEWVALUE")`,
#' either in your script or in your `.Renviron` file.
#' See [base::Startup] for information about using `.Renviron`
#' 1. Finally, if neither of these are set, the OS default keyring is used.
#' - Usually the keyring is automatically unlocked when the user logs in.
#'
#' @param keyring Character string, the name of the keyring to use,
#' or `NULL` for the default keyring.
#' @return The backend object itself.
#'
#'
#'
#'
#' @seealso [backend_env], [backend_file], [backend_macos],
#' [backend_secret_service], [backend_wincred]
#' [backend_secret_service], [backend_wincred], [backend_awssecretsmanager]
#'
#' @export
#' @name backends
Expand Down Expand Up @@ -84,7 +84,7 @@ default_backend_auto <- function() {
} else if (sysname == "linux" && "secret_service" %in% names(known_backends) &&
backend_secret_service$new()$is_available()) {
backend_secret_service

} else if ("file" %in% names(known_backends)) {
backend_file

Expand All @@ -111,5 +111,6 @@ known_backends <- list(
"macos" = backend_macos,
"secret_service" = backend_secret_service,
"env" = backend_env,
"file" = backend_file
"file" = backend_file,
"awssecretsmanager" = backend_awssecretsmanager
)
7 changes: 5 additions & 2 deletions R/package.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
#' implementations. Currently supported:
#' * Keychain on macOS,
#' * Credential Store on Windows,
#' * the Secret Service API on Linux, and
#' * the Secret Service API on Linux
#' * the AWS Secrets Manager, and
#' * environment variables on other platforms.
#'
#' @section Configuring an OS-specific backend:
Expand All @@ -15,8 +16,10 @@
#' - MacOS: [backend_macos]
#' - Linux: [backend_secret_service]
#' - Windows: [backend_wincred]
#' - Or store the secrets in environment variables on other operating
#' - Store the secrets in environment variables on other operating
#' systems: [backend_env]
#' - Or in the AWS secrets Manager:
#' [backend_awssecretsmanager]
#'
#' @section Query secret keys in a keyring:
#'
Expand Down
10 changes: 10 additions & 0 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ credential store. Currently supports:
* Keychain on macOS (`backend_macos`),
* Credential Store on Windows (`backend_wincred`),
* the Secret Service API on Linux (`backend_secret_service`),
* AWS Secrets Manager (`backend_awssecretsmanager`),
* encrypted files (`backend_file`), and
* environment variables (`backend_env`).

Expand Down Expand Up @@ -55,6 +56,15 @@ This backend works best on Linux servers. Install these packages:

No additional software is needed.

### AWS Secrets Manager

The paws package must be installed from CRAN to access the AWS services
along with the AWS CLI and credentials.

```{r eval = FALSE}
install.packages("paws")
```

### R package

Install the package from CRAN:
Expand Down
Loading