Last active
August 31, 2022 17:07
-
-
Save tomsing1/feca4d7488346411e78c8ca96394486d to your computer and use it in GitHub Desktop.
Using the paws R package to simulate / check get, put and delete actions on AWS S3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#' Retrieve temporary AWS authentication key, secret key and token | |
#' | |
#' Saturn Cloud instances use web authentication to obtain temporary | |
#' access to AWS resources, e.g. S3 buckets, instead of environmental variables. | |
#' But there are situations where the typical `AWS_ACCESS_KEY_ID`, | |
#' `AWS_SECRET_ACCESS_KEY` and `AWS_SESSION_TOKEN` environmental variables are | |
#' needed, e.g. to authenticate with tools that don't support web | |
#' authentication such as the `aws.s3` and `paws` R packages. This function | |
#' retrieves the temporary variables (but does not export them as environmental | |
#' variables). | |
#' @noRd | |
#' @param duration Scalar integer, the duration, in seconds, of the role | |
#' session (`DurationSeconds`). The maximum is 3600 seconds (1 hour), after | |
#' which the command needs to run again to refresh the credentials. | |
#' @param session Scalar character, the `RoleSessionName`. | |
#' @param verbose Scalar flag, show messages? | |
#' @import paws | |
#' @importFrom checkmate assert_number assert_string assert_flag | |
#' @return A character vector with temporary `AWS_ACCESS_KEY_ID`, | |
#' `AWS_SECRET_ACCESS_KEY` and `AWS_SESSION_TOKEN` variables. | |
#' @note The variables are not exported, e.g. this function does _not_ call | |
#' `Sys.setenv()` to enable the user to choose how to make use of the variables | |
#' downstream. For example, a user might choose to export them only temporarily | |
#' with the [withr::with_envvar()] function. | |
#' @examples | |
#' \dontrun{ | |
#' .aws_keys() | |
#' } | |
.aws_keys <- function(duration = 3600, session = "saturn", verbose = FALSE) { | |
checkmate::assert_number(duration, lower = 1, upper = 3600) | |
checkmate::assert_string(session) | |
checkmate::assert_flag(verbose) | |
if (verbose) { | |
message("Retrieving temporary AWS keys from web identity token file.") | |
} | |
web_id_file <- Sys.getenv("AWS_WEB_IDENTITY_TOKEN_FILE") | |
if (!nzchar(web_id_file)) { | |
stop("No AWS web identity token found. ", | |
"This resource does not seem to use AWS web authentication.") | |
} | |
svc <- sts() | |
credentials <- svc$assume_role_with_web_identity( | |
DurationSeconds = duration, | |
RoleArn = Sys.getenv("AWS_ROLE_ARN"), | |
RoleSessionName = "saturn", | |
WebIdentityToken = readLines(web_id_file, warn = FALSE) | |
)[["Credentials"]] | |
return( | |
c(AWS_ACCESS_KEY_ID = credentials$AccessKeyId, | |
AWS_SECRET_ACCESS_KEY = credentials$SecretAccessKey, | |
AWS_SESSION_TOKEN = credentials$SessionToken | |
)) | |
} | |
#' Check the current user's access to S3 | |
#' | |
#' @noRd | |
#' @param url Scalar character, a URL pointing to a bucket or prefix on AWS S3. | |
#' @param actions Character vector, the actions to check. | |
#' @param verbose Scalar flag, show messages? | |
#' @importFrom paws sts iam | |
#' @importFrom purrr map | |
#' @importFrom withr with_envvar | |
#' @importFrom checkmate assert_string assert_flag | |
#' @return `TRUE` if all three types of access are granted, otherwise `FALSE`. | |
#' @examples | |
#' .s3_permissions("your-bucket-name") | |
#' .s3_permissions("your-bucket-name/*") | |
.s3_permissions <- function(url, actions = c("s3:PutObject", "s3:GetObject", | |
"s3:DeleteObject"), | |
verbose = FALSE) { | |
checkmate::assert_string(url) | |
checkmate::assert_flag(verbose) | |
url <- sub("s3://", "", url, fixed = TRUE) | |
arn <- sprintf('arn:aws:s3:::%s', url) | |
caller_id <- Sys.getenv( | |
"AWS_ROLE_ARN", # only defined on AWS resources, e.g. on Saturn Cloud | |
unset = paws::sts()$get_caller_identity()[["Arn"]] | |
) | |
# This will fail on Saturn Cloud, because paws does not support AWS web | |
# authentication, yet, so we wrap the function in try(). | |
results <- try( | |
paws::iam()$simulate_principal_policy( | |
PolicySourceArn = caller_id, | |
ResourceArns = arn, | |
ActionNames = actions | |
), silent = TRUE | |
) | |
if (inherits(results, "try-error")) { | |
# let's try again, by first extracting the AWS environmental variables | |
# from a web token file. | |
results <- withr::with_envvar( | |
new = .aws_keys(verbose = verbose), | |
code = paws::iam()$simulate_principal_policy( | |
PolicySourceArn = caller_id, | |
ResourceArns = arn, | |
ActionNames = actions | |
)) | |
} | |
setNames( | |
purrr::map(results[['EvaluationResults']], "EvalDecision") == "allowed", | |
purrr::map(results[['EvaluationResults']], "EvalActionName") | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment