diff --git a/Makefile b/Makefile index e2627d9..f7a1641 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,13 @@ vign_auth: ${RSCRIPT} -e "Sys.setenv(NOT_CRAN='true'); knitr::knit('auth.Rmd.og', output = 'auth.Rmd')";\ cd .. +vign_s3iam: + cd vignettes;\ + ${RSCRIPT} -e "Sys.setenv(NOT_CRAN='true')" \ + -e "knitr::knit('s3iam.Rmd.og', output = 's3iam.Rmd')";\ + sed "s/${AWS_ACCOUNT_ID}/*****/g" s3iam.Rmd > tmp && mv tmp s3iam.Rmd;\ + cd .. + test: SIXTYFOUR_RUN_LOCAL_ONLY_TESTS=true ${RSCRIPT} -e "devtools::test()" diff --git a/vignettes/s3iam.Rmd b/vignettes/s3iam.Rmd new file mode 100644 index 0000000..bb5c4d6 --- /dev/null +++ b/vignettes/s3iam.Rmd @@ -0,0 +1,192 @@ +--- +title: "Managing buckets in a small group of users" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Managing buckets in a small group of users} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + + + +A common use case is to manage a small group of users and their access to AWS resources. This can be done using the `sixtyfour` package, which provides a set of high-level functions to make your life easier. + +## Installation + + +``` r +library(sixtyfour) +library(purrr) +``` + +## User groups + +First, create two user groups - `admin` and `users` - with the `six_admin_setup()` function. + + + + +``` r +six_admin_setup(users_group = "myusers", admin_group = "myadmin") +#> ℹ whoami: ***** +#> ℹ +#> ℹ myusers group created - add users to this group that do not require admin permissions +#> ℹ Added policies to the myusers group: AmazonRDSReadOnlyAccess, AmazonRedshiftReadOnlyAccess, AmazonS3ReadOnlyAccess, +#> AWSBillingReadOnlyAccess, and IAMReadOnlyAccess +#> ℹ +#> ℹ myadmin group created - add users to this group that require admin permissions +#> ℹ Added policies to the myadmin group: AdministratorAccess, Billing, CostOptimizationHubAdminAccess, AWSBillingReadOnlyAccess, and +#> AWSCostAndUsageReportAutomationPolicy +#> ℹ +#> ℹ Done! +``` + +## Users + +Next, create five users - one in the `admin` group, three in the `users` group, and one not in either group. + +The admin user: + + +``` r +name_admin <- random_user() +user_admin <- six_user_create(name_admin, copy_to_cb = FALSE) +#> ℹ Added policy UserInfo to UpstartCoding +#> ✔ Key pair created for UpstartCoding +#> ℹ AccessKeyId: ***** +#> ℹ SecretAccessKey: ***** +aws_user_add_to_group(name_admin, "myadmin") +``` + +The non-admin users: + + +``` r +users_non_admin <- map(1:3, \(x) { + six_user_create(random_user(), copy_to_cb = FALSE) +}) +#> ℹ Added policy UserInfo to FumingMatron +#> ✔ Key pair created for FumingMatron +#> ℹ AccessKeyId: ***** +#> ℹ SecretAccessKey: ***** +#> ℹ Added policy UserInfo to ForkedBounds +#> ✔ Key pair created for ForkedBounds +#> ℹ AccessKeyId: ***** +#> ℹ SecretAccessKey: ***** +#> ℹ Added policy UserInfo to MemorableLustre +#> ✔ Key pair created for MemorableLustre +#> ℹ AccessKeyId: ***** +#> ℹ SecretAccessKey: ***** +invisible(map(users_non_admin, \(x) { + aws_user_add_to_group(x$UserName, "myusers") +})) +names_users <- map_chr(users_non_admin, \(x) x$UserName) +``` + +And last, create a user that is not part of either of the above groups. + + +``` r +name_user_wo <- random_user() +user_wo_access <- six_user_create(name_user_wo, copy_to_cb = FALSE) +#> ℹ Added policy UserInfo to PedestrianDoorst +#> ✔ Key pair created for PedestrianDoorst +#> ℹ AccessKeyId: ***** +#> ℹ SecretAccessKey: ***** +``` + +## Buckets and files + +Create a bucket, and put a file in it. + + +``` r +bucket <- random_string("bucket") +demo_rds_file <- file.path(system.file(), "Meta/demo.rds") +six_bucket_upload(path = demo_rds_file, remote = bucket, force = TRUE) +#> [1] "s3://bucketqozlabhg/demo.rds" +``` + +## Check access + +Then check access for the user that should not have access + + + + +``` r +withr::with_envvar( + c("AWS_REGION" = user_wo_access$AwsRegion, + "AWS_ACCESS_KEY_ID" = user_wo_access$AccessKeyId, + "AWS_SECRET_ACCESS_KEY" = user_wo_access$SecretAccessKey + ), + aws_bucket_list_objects(bucket) +) +#> Error: AccessDenied (HTTP 403). User: arn:aws:iam::*****:user/PedestrianDoorst is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::bucketqozlabhg" because no identity-based policy allows the s3:ListBucket action +``` + +User PedestrianDoorst does not have access (read or write), as intended. + +Then check access for a user that should have read access: + + +``` r +withr::with_envvar( + c("AWS_REGION" = users_non_admin[[1]]$AwsRegion, + "AWS_ACCESS_KEY_ID" = users_non_admin[[1]]$AccessKeyId, + "AWS_SECRET_ACCESS_KEY" = users_non_admin[[1]]$SecretAccessKey + ), + { + # read + aws_bucket_list_objects(bucket) + + # write + links_rds_file <- file.path(system.file(), "Meta/links.rds") + six_bucket_upload(path = links_rds_file, remote = bucket, force = TRUE) + } +) +#> Error in `map()`: +#> ℹ In index: 1. +#> Caused by error: +#> ! AccessDenied (HTTP 403). User: arn:aws:iam::*****:user/FumingMatron is not authorized to perform: s3:PutObject on resource: "arn:aws:s3:::bucketqozlabhg/links.rds" because no identity-based policy allows the s3:PutObject action +``` + +User FumingMatron does have access for read and NOT for write, as intended. + +And make sure the admin has read and write access + + +``` r +withr::with_envvar( + c("AWS_REGION" = user_admin$AwsRegion, + "AWS_ACCESS_KEY_ID" = user_admin$AccessKeyId, + "AWS_SECRET_ACCESS_KEY" = user_admin$SecretAccessKey + ), { + # read + aws_bucket_list_objects(bucket) + + # write + links_rds_file <- file.path(system.file(), "Meta/links.rds") + six_bucket_upload(path = links_rds_file, remote = bucket, force = TRUE) + } +) +#> [1] "s3://bucketqozlabhg/links.rds" +``` + +User UpstartCoding does have access for read and write, as intended. + +## Cleanup + +Then cleanup the users, groups and buckets: + + +``` r +# Users +map(c(name_admin, names_users, name_user_wo), six_user_delete) + +# Groups +map(c("myadmin", "myusers"), six_group_delete) + +# Bucket +six_bucket_delete(bucket, force = TRUE) +``` diff --git a/vignettes/s3iam.Rmd.og b/vignettes/s3iam.Rmd.og new file mode 100644 index 0000000..b702ceb --- /dev/null +++ b/vignettes/s3iam.Rmd.og @@ -0,0 +1,156 @@ +--- +title: "Managing buckets in a small group of users" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Managing buckets in a small group of users} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +library(sixtyfour) +aws_configure(redacted = TRUE) +``` + +A common use case is to manage a small group of users and their access to AWS resources. This can be done using the `sixtyfour` package, which provides a set of high-level functions to make your life easier. + +## Installation + +```{r setup} +library(sixtyfour) +library(purrr) +``` + +## User groups + +First, create two user groups - `admin` and `users` - with the `six_admin_setup()` function. + +```{r delete-admin-user-groups, echo=FALSE} +if (aws_group_exists("myadmin")) without_verbose(six_group_delete("myadmin")) +if (aws_group_exists("myusers")) without_verbose(six_group_delete("myusers")) +``` + +```{r setup-admin-user-groups} +six_admin_setup(users_group = "myusers", admin_group = "myadmin") +``` + +## Users + +Next, create five users - one in the `admin` group, three in the `users` group, and one not in either group. + +The admin user: + +```{r create-admin-user, results='hide'} +name_admin <- random_user() +user_admin <- six_user_create(name_admin, copy_to_cb = FALSE) +aws_user_add_to_group(name_admin, "myadmin") +``` + +The non-admin users: + +```{r create-non-admin-users} +users_non_admin <- map(1:3, \(x) { + six_user_create(random_user(), copy_to_cb = FALSE) +}) +invisible(map(users_non_admin, \(x) { + aws_user_add_to_group(x$UserName, "myusers") +})) +names_users <- map_chr(users_non_admin, \(x) x$UserName) +``` + +And last, create a user that is not part of either of the above groups. + +```{r create-wo-access-user} +name_user_wo <- random_user() +user_wo_access <- six_user_create(name_user_wo, copy_to_cb = FALSE) +``` + +## Buckets and files + +Create a bucket, and put a file in it. + +```{r create-bucket} +bucket <- random_string("bucket") +demo_rds_file <- file.path(system.file(), "Meta/demo.rds") +six_bucket_upload(path = demo_rds_file, remote = bucket, force = TRUE) +``` + +## Check access + +Then check access for the user that should not have access + +```{r sleep-so-it-all-works, echo=FALSE, results="hide"} +Sys.sleep(15) +``` + +```{r verify-user-wo, error=TRUE} +withr::with_envvar( + c("AWS_REGION" = user_wo_access$AwsRegion, + "AWS_ACCESS_KEY_ID" = user_wo_access$AccessKeyId, + "AWS_SECRET_ACCESS_KEY" = user_wo_access$SecretAccessKey + ), + aws_bucket_list_objects(bucket) +) +``` + +User `r user_wo_access$UserName` does not have access (read or write), as intended. + +Then check access for a user that should have read access: + +```{r verify-user, error=TRUE} +withr::with_envvar( + c("AWS_REGION" = users_non_admin[[1]]$AwsRegion, + "AWS_ACCESS_KEY_ID" = users_non_admin[[1]]$AccessKeyId, + "AWS_SECRET_ACCESS_KEY" = users_non_admin[[1]]$SecretAccessKey + ), + { + # read + aws_bucket_list_objects(bucket) + + # write + links_rds_file <- file.path(system.file(), "Meta/links.rds") + six_bucket_upload(path = links_rds_file, remote = bucket, force = TRUE) + } +) +``` + +User `r users_non_admin[[1]]$UserName` does have access for read and NOT for write, as intended. + +And make sure the admin has read and write access + +```{r verify-admin} +withr::with_envvar( + c("AWS_REGION" = user_admin$AwsRegion, + "AWS_ACCESS_KEY_ID" = user_admin$AccessKeyId, + "AWS_SECRET_ACCESS_KEY" = user_admin$SecretAccessKey + ), { + # read + aws_bucket_list_objects(bucket) + + # write + links_rds_file <- file.path(system.file(), "Meta/links.rds") + six_bucket_upload(path = links_rds_file, remote = bucket, force = TRUE) + } +) +``` + +User `r user_admin$UserName` does have access for read and write, as intended. + +## Cleanup + +Then cleanup the users, groups and buckets: + +```{r cleanup, message=FALSE, results='hide'} +# Users +map(c(name_admin, names_users, name_user_wo), six_user_delete) + +# Groups +map(c("myadmin", "myusers"), six_group_delete) + +# Bucket +six_bucket_delete(bucket, force = TRUE) +```