# Managing buckets in a small group of users

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

    library(sixtyfour)
    library(purrr)

## User groups

First, create two user groups - `admin` and `users` - with the `six_admin_setup()` function.

    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:

    name_admin <- random_user()
    user_admin <- six_user_create(name_admin, copy_to_cb = FALSE)
    #> ℹ Added policy UserInfo to SleepySubsidy
    #> ✔ Key pair created for SleepySubsidy
    #> ℹ AccessKeyId: *****
    #> ℹ SecretAccessKey: *****
    aws_user_add_to_group(name_admin, "myadmin")

The non-admin users:

    users_non_admin <- map(1:3, \(x) {
      six_user_create(random_user(), copy_to_cb = FALSE)
    })
    #> ℹ Added policy UserInfo to NoncommercialPub
    #> ✔ Key pair created for NoncommercialPub
    #> ℹ AccessKeyId: *****
    #> ℹ SecretAccessKey: *****
    #> ℹ Added policy UserInfo to BridgedSomewhere
    #> ✔ Key pair created for BridgedSomewhere
    #> ℹ AccessKeyId: *****
    #> ℹ SecretAccessKey: *****
    #> ℹ Added policy UserInfo to DoctrinalSeating
    #> ✔ Key pair created for DoctrinalSeating
    #> ℹ 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.

    name_user_wo <- random_user()
    user_wo_access <- six_user_create(name_user_wo, copy_to_cb = FALSE)
    #> ℹ Added policy UserInfo to BrimmingAviation
    #> ✔ Key pair created for BrimmingAviation
    #> ℹ AccessKeyId: *****
    #> ℹ SecretAccessKey: *****

## Buckets and files

Create a bucket, and put a file in it.

    bucket <- random_bucket()
    demo_rds_file <- file.path(system.file(), "Meta/demo.rds")
    six_bucket_upload(path = demo_rds_file, remote = bucket, force = TRUE)
    #> [1] "s3://bucket-cuwgyafqtleomkhd/demo.rds"

## Check access

Then check access for the user that should not have access

    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/BrimmingAviation is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::bucket-cuwgyafqtleomkhd" because no identity-based policy allows the s3:ListBucket action

User BrimmingAviation does not have access (read or write), as intended.

Then check access for a user that should have read access and NOT write access:

    # Read
    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
      ),
      {
        aws_bucket_list_objects(bucket)
      }
    )
    #> # A tibble: 1 × 8
    #>   bucket          key   uri    size type  etag  lastmodified        storageclass
    #>   <glue>          <chr> <glu> <fs:> <chr> <chr> <dttm>              <chr>
    #> 1 bucket-cuwgyaf… demo… s3:/…   256 file  "\"7… 2025-03-12 22:51:04 STANDARD

    # Write
    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
      ),
      {
        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/NoncommercialPub is not authorized to perform: s3:PutObject on resource: "arn:aws:s3:::bucket-cuwgyafqtleomkhd/links.rds" because no identity-based policy allows the s3:PutObject action

User NoncommercialPub does have access for read and NOT for write, as intended.

And make sure the admin has read and write access

    # Read
    withr::with_envvar(
      c("AWS_REGION" = user_admin$AwsRegion,
        "AWS_ACCESS_KEY_ID" = user_admin$AccessKeyId,
        "AWS_SECRET_ACCESS_KEY" = user_admin$SecretAccessKey
      ), {
        aws_bucket_list_objects(bucket)
      }
    )
    #> # A tibble: 1 × 8
    #>   bucket          key   uri    size type  etag  lastmodified        storageclass
    #>   <glue>          <chr> <glu> <fs:> <chr> <chr> <dttm>              <chr>
    #> 1 bucket-cuwgyaf… demo… s3:/…   256 file  "\"7… 2025-03-12 22:51:04 STANDARD

    # Write
    withr::with_envvar(
      c("AWS_REGION" = user_admin$AwsRegion,
        "AWS_ACCESS_KEY_ID" = user_admin$AccessKeyId,
        "AWS_SECRET_ACCESS_KEY" = user_admin$SecretAccessKey
      ), {
        links_rds_file <- file.path(system.file(), "Meta/links.rds")
        six_bucket_upload(path = links_rds_file, remote = bucket, force = TRUE)
      }
    )
    #> [1] "s3://bucket-cuwgyafqtleomkhd/links.rds"

User SleepySubsidy DOES have access for read and write, as intended.

## Cleanup

Then cleanup the users, groups and buckets:

    # 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)