diff --git a/NAMESPACE b/NAMESPACE index a4e119a..f159530 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -36,7 +36,9 @@ export(aws_iam_client) export(aws_policies) export(aws_policy) export(aws_policy_attach) +export(aws_policy_create) export(aws_policy_detach) +export(aws_policy_document_create) export(aws_policy_exists) export(aws_role) export(aws_role_create) diff --git a/R/policies.R b/R/policies.R index 0a24053..5ee6468 100644 --- a/R/policies.R +++ b/R/policies.R @@ -74,6 +74,102 @@ aws_policy_exists <- function(name) { !is.null(purrr::safely(aws_policy)(name)$result) } +#' Create a policy +#' +#' @export +#' @param name (character) a policy name. required +#' @param document (character) the policy document you want to use +#' as the content for the new policy. required. +#' @param path (character) the path for the policy. if not given +#' default is "/". optional +#' @param description (character) a friendly description of the policy. +#' optional. cannot be changed after assigning it +#' @param tags (character) a vector of tags that you want to attach to +#' the new IAM policy. Each tag consists of a key name and an associated +#' value. optional +#' @return a tibble with policy details +#' @details see docs +#' @family policies +#' @examples \dontrun{ +#' aws_db_rds_list() +#' aws_policy_document_create() +#' aws_policy_create("RdsAllow", document = doc) +#' } +aws_policy_create <- function( + name, document, path = NULL, + description = NULL, tags = NULL) { + env64$iam$create_policy( + PolicyName = name, + PolicyDocument = document, + Path = path, + Description = description, + Tags = tags + ) +} + +#' Create a policy document +#' +#' @export +#' @param region (character) the AWS Region for the DB instance. length==1 +#' @param account_id (character) the AWS account number for the DB instance. +#' length==1. The user must be in the same account as the account for the +#' DB instance +#' @param resource_id (character) the identifier for the DB instance. length==1 +#' @param user (character) a user name that has an IAM account. length>=1 +#' @param action (character) an action. required. see Actions below. +#' @param effect (character) valid values: "Allow" (default), "Deny". length==1 +#' @param ... named args passed to [jsonlite::toJSON()] +#' @references # nolint start +#' +#' # nolint end +#' @return a json class string. use [as.character()] to coerce to a regular +#' string +#' @note a few document items are hard-coded: +#' - `Version` is set to 2012-10-17" +#' - `Action` is set to "rds-db:connect" +#' @section Actions: +#' Actions documentation appears to be all over the web. Here's a start: +#' - S3: # nolint +#' - EC2: # nolint +#' - IAM: # nolint +#' @examplesIf interactive() +#' ### DB account = user in a database that has access to it +#' # all DB instances & DB accounts for a AWS account and AWS Region +#' aws_policy_document_create("us-east-2", "1234567890", "*", "*") +#' # all DB instances for a AWS account and AWS Region, single DB account +#' aws_policy_document_create("us-east-2", "1234567890", "*", "jane_doe") +#' # single DB instasnce, single DB account +#' aws_policy_document_create( +#' "us-east-2", +#' "1234567890", "db-ABCDEFGHIJKL01234", "jane_doe" +#' ) +#' # single DB instance, many users +#' aws_policy_document_create( +#' region = "us-east-2", +#' account_id = "1234567890", +#' resource_id = "db-ABCDEFGHIJKL01234", +#' user = c("jane_doe", "mary_roe"), +#' action = "rds-db:connect" +#' ) +aws_policy_document_create <- function( + region, account_id, resource_id, user, + action, effect = "Allow", ...) { + resource <- glue( + "arn:aws:rds-db:{region}:{account_id}:dbuser:{resource_id}/{user}" + ) + doc <- list( + Version = "2012-10-17", + Statement = list( + list( + Effect = effect, + Action = action, + Resource = resource + ) + ) + ) + jsonlite::toJSON(doc, auto_unbox = TRUE, ...) +} + #' Convert a policy name to a policy ARN #' #' @export diff --git a/man/as_policy_arn.Rd b/man/as_policy_arn.Rd index 78f0ac5..3dce3d4 100644 --- a/man/as_policy_arn.Rd +++ b/man/as_policy_arn.Rd @@ -33,6 +33,7 @@ as_policy_arn("AmazonRDSDataFullAccess") Other policies: \code{\link{aws_policies}()}, \code{\link{aws_policy_attach}()}, +\code{\link{aws_policy_create}()}, \code{\link{aws_policy_detach}()}, \code{\link{aws_policy_exists}()}, \code{\link{aws_policy}()} diff --git a/man/aws_policies.Rd b/man/aws_policies.Rd index e66f238..5084a5e 100644 --- a/man/aws_policies.Rd +++ b/man/aws_policies.Rd @@ -34,6 +34,7 @@ aws_policies(refresh = TRUE) Other policies: \code{\link{as_policy_arn}()}, \code{\link{aws_policy_attach}()}, +\code{\link{aws_policy_create}()}, \code{\link{aws_policy_detach}()}, \code{\link{aws_policy_exists}()}, \code{\link{aws_policy}()} diff --git a/man/aws_policy.Rd b/man/aws_policy.Rd index 9995b5c..b687040 100644 --- a/man/aws_policy.Rd +++ b/man/aws_policy.Rd @@ -29,6 +29,7 @@ Other policies: \code{\link{as_policy_arn}()}, \code{\link{aws_policies}()}, \code{\link{aws_policy_attach}()}, +\code{\link{aws_policy_create}()}, \code{\link{aws_policy_detach}()}, \code{\link{aws_policy_exists}()} } diff --git a/man/aws_policy_attach.Rd b/man/aws_policy_attach.Rd index e01d4a8..36e1270 100644 --- a/man/aws_policy_attach.Rd +++ b/man/aws_policy_attach.Rd @@ -32,6 +32,7 @@ aws_user()$attached_policies Other policies: \code{\link{as_policy_arn}()}, \code{\link{aws_policies}()}, +\code{\link{aws_policy_create}()}, \code{\link{aws_policy_detach}()}, \code{\link{aws_policy_exists}()}, \code{\link{aws_policy}()} diff --git a/man/aws_policy_create.Rd b/man/aws_policy_create.Rd new file mode 100644 index 0000000..5d05c20 --- /dev/null +++ b/man/aws_policy_create.Rd @@ -0,0 +1,50 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/policies.R +\name{aws_policy_create} +\alias{aws_policy_create} +\title{Create a policy} +\usage{ +aws_policy_create(name, document, path = NULL, description = NULL, tags = NULL) +} +\arguments{ +\item{name}{(character) a policy name. required} + +\item{document}{(character) the policy document you want to use +as the content for the new policy. required.} + +\item{path}{(character) the path for the policy. if not given +default is "/". optional} + +\item{description}{(character) a friendly description of the policy. +optional. cannot be changed after assigning it} + +\item{tags}{(character) a vector of tags that you want to attach to +the new IAM policy. Each tag consists of a key name and an associated +value. optional} +} +\value{ +a tibble with policy details +} +\description{ +Create a policy +} +\details{ +see docs \url{https://www.paws-r-sdk.com/docs/iam_create_policy/} +} +\examples{ +\dontrun{ +aws_db_rds_list() +aws_policy_document_create() +aws_policy_create("RdsAllow", document = doc) +} +} +\seealso{ +Other policies: +\code{\link{as_policy_arn}()}, +\code{\link{aws_policies}()}, +\code{\link{aws_policy_attach}()}, +\code{\link{aws_policy_detach}()}, +\code{\link{aws_policy_exists}()}, +\code{\link{aws_policy}()} +} +\concept{policies} diff --git a/man/aws_policy_detach.Rd b/man/aws_policy_detach.Rd index bc5fbad..df3dd67 100644 --- a/man/aws_policy_detach.Rd +++ b/man/aws_policy_detach.Rd @@ -33,6 +33,7 @@ Other policies: \code{\link{as_policy_arn}()}, \code{\link{aws_policies}()}, \code{\link{aws_policy_attach}()}, +\code{\link{aws_policy_create}()}, \code{\link{aws_policy_exists}()}, \code{\link{aws_policy}()} } diff --git a/man/aws_policy_document_create.Rd b/man/aws_policy_document_create.Rd new file mode 100644 index 0000000..d0b85d1 --- /dev/null +++ b/man/aws_policy_document_create.Rd @@ -0,0 +1,84 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/policies.R +\name{aws_policy_document_create} +\alias{aws_policy_document_create} +\title{Create a policy document} +\usage{ +aws_policy_document_create( + region, + account_id, + resource_id, + user, + action, + effect = "Allow", + ... +) +} +\arguments{ +\item{region}{(character) the AWS Region for the DB instance. length==1} + +\item{account_id}{(character) the AWS account number for the DB instance. +length==1. The user must be in the same account as the account for the +DB instance} + +\item{resource_id}{(character) the identifier for the DB instance. length==1} + +\item{user}{(character) a user name that has an IAM account. length>=1} + +\item{action}{(character) an action. required. see Actions below.} + +\item{effect}{(character) valid values: "Allow" (default), "Deny". length==1} + +\item{...}{named args passed to \code{\link[jsonlite:fromJSON]{jsonlite::toJSON()}}} +} +\value{ +a json class string. use \code{\link[=as.character]{as.character()}} to coerce to a regular +string +} +\description{ +Create a policy document +} +\note{ +a few document items are hard-coded: +\itemize{ +\item \code{Version} is set to 2012-10-17" +\item \code{Action} is set to "rds-db:connect" +} +} +\section{Actions}{ + +Actions documentation appears to be all over the web. Here's a start: +\itemize{ +\item S3: \url{https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3.html} # nolint +\item EC2: \url{https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_Operations.html} # nolint +\item IAM: \url{https://docs.aws.amazon.com/IAM/latest/APIReference/API_Operations.html} # nolint +} +} + +\examples{ +\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +### DB account = user in a database that has access to it +# all DB instances & DB accounts for a AWS account and AWS Region +aws_policy_document_create("us-east-2", "1234567890", "*", "*") +# all DB instances for a AWS account and AWS Region, single DB account +aws_policy_document_create("us-east-2", "1234567890", "*", "jane_doe") +# single DB instasnce, single DB account +aws_policy_document_create( + "us-east-2", + "1234567890", "db-ABCDEFGHIJKL01234", "jane_doe" +) +# single DB instance, many users +aws_policy_document_create( + region = "us-east-2", + account_id = "1234567890", + resource_id = "db-ABCDEFGHIJKL01234", + user = c("jane_doe", "mary_roe"), + action = "rds-db:connect" +) +\dontshow{\}) # examplesIf} +} +\references{ +#no lint start +\url{https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html} +#no lint end +} diff --git a/man/aws_policy_exists.Rd b/man/aws_policy_exists.Rd index ab97fcb..48cab67 100644 --- a/man/aws_policy_exists.Rd +++ b/man/aws_policy_exists.Rd @@ -28,6 +28,7 @@ Other policies: \code{\link{as_policy_arn}()}, \code{\link{aws_policies}()}, \code{\link{aws_policy_attach}()}, +\code{\link{aws_policy_create}()}, \code{\link{aws_policy_detach}()}, \code{\link{aws_policy}()} } diff --git a/tests/fixtures/aws_policy_create.yml b/tests/fixtures/aws_policy_create.yml new file mode 100644 index 0000000..aa0af65 --- /dev/null +++ b/tests/fixtures/aws_policy_create.yml @@ -0,0 +1,50 @@ +http_interactions: +- request: + method: post + uri: https://iam.amazonaws.com/ + body: + encoding: '' + string: Action=CreatePolicy&PolicyDocument=%7B%22Version%22%3A%222012-10-17%22%2C%22Statement%22%3A%5B%7B%22Effect%22%3A%22Allow%22%2C%22Action%22%3A%22s3%3AListAllMyBuckets%22%2C%22Resource%22%3A%5B%22arn%3Aaws%3Ards-db%3Aus-east-2%3A1234567890%3Adbuser%3Adb-ABCDEFGHIJKL01234%2Fjane_doe%22%2C%22arn%3Aaws%3Ards-db%3Aus-east-2%3A1234567890%3Adbuser%3Adb-ABCDEFGHIJKL01234%2Fmary_roe%22%5D%7D%5D%7D&PolicyName=MyTestPolicy&Version=2010-05-08 + headers: + User-Agent: paws/0.6.1 (R4.3.3; darwin20; aarch64) + Accept: application/xml + Content-Type: application/x-www-form-urlencoded; charset=utf-8 + Content-Length: '433' + X-Amz-Date: 20240314T223539Z + Authorization: redacted + response: + status: + status_code: 200 + category: Success + reason: OK + message: 'Success: (200) OK' + headers: + date: Thu, 14 Mar 2024 22:35:39 GMT + x-amzn-requestid: 9658ed62-7912-4611-8019-b55ba1196714 + content-type: text/xml + content-length: '759' + body: + encoding: '' + file: no + string: | + + + + 0 + / + 2024-03-14T22:35:40Z + v1 + ANPA22PL7JXXQK37HQPA6 + true + MyTestPolicy + 0 + arn:aws:iam::744061095407:policy/MyTestPolicy + 2024-03-14T22:35:40Z + + + + 9658ed62-7912-4611-8019-b55ba1196714 + + + recorded_at: 2024-03-14 22:35:40 GMT + recorded_with: vcr/1.2.2.91, webmockr/0.9.0 diff --git a/tests/testthat/test-policies.R b/tests/testthat/test-policies.R index 53db310..7a2debb 100644 --- a/tests/testthat/test-policies.R +++ b/tests/testthat/test-policies.R @@ -74,3 +74,42 @@ test_that("aws_policy_detach", { expect_equal(NROW(user_before$attached_policies), 1) expect_equal(NROW(user_after$attached_policies), 0) }) + +test_that("aws_policy_document_create", { + doc1 <- aws_policy_document_create( + region = "us-east-2", + account_id = "1234567890", + resource_id = "*", + user = "*", + action = "rds-db:connect" + ) + doc1lst <- jsonlite::fromJSON(doc1, FALSE) + + expect_type(doc1, "character") + expect_s3_class(doc1, "json") + expect_named(doc1lst, c("Version", "Statement")) + expect_named(doc1lst$Statement[[1]], c("Effect", "Action", "Resource")) + expect_equal(doc1lst$Statement[[1]]$Effect, "Allow") + expect_equal(doc1lst$Statement[[1]]$Action, "rds-db:connect") +}) + +test_that("aws_policy_create", { + withr::local_envvar(c("TESTING64" = TRUE)) + + my_doc <- aws_policy_document_create( + region = "us-east-2", + account_id = "1234567890", + resource_id = "db-ABCDEFGHIJKL01234", + user = c("jane_doe", "mary_roe"), + action = "s3:ListAllMyBuckets" + ) + + vcr::use_cassette("aws_policy_create", { + polisee <- aws_policy_create("MyTestPolicy", document = my_doc) + }) + + expect_type(polisee, "list") + expect_named(polisee, "Policy") + expect_equal(polisee$Policy$PolicyName, "MyTestPolicy") + expect_match(polisee$Policy$PolicyName, "MyTestPolicy") +})