r if(Sys.getenv('CONNECT_SERVER') == '') { "<h4>ERROR: You must set the CONNECT_SERVER environment variable</h4>\n" } r if(Sys.getenv('CONTENT_INFO_PIN') == '') { "<h4>ERROR: You must set the CONTENT_INFO_PIN environment variable ex. 'username/content-info'</h4>\n" } r if(Sys.getenv('USER_INFO_PIN') == '') { "<h4>ERROR: You must set the USER_INFO_PIN environment variable ex. 'username/user-info'</h4>\n" } r if(Sys.getenv('GROUP_INFO_PIN') == '') { "<h4>ERROR: You must set the GROUP_INFO_PIN environment variable ex. 'username/group-info'</h4>\n" } r if(Sys.getenv('CONNECT_API_KEY') == '') { "<h4>ERROR: You must set the CONNECT_API_KEY environment variable</h4>\n" } r if(Sys.getenv('CONNECT_API_KEY') == '' || Sys.getenv('CONNECT_SERVER') == '') { knitr::knit_exit() }

Required Pins

This report depends on the existence of the following pins:

Get the Content Info Pin

The content-info pin can be generated by running pin-content-list.Rmd (intended to be run on a schedule).

# Register RStudio Connect
library(pins)
board_register("rsconnect", server = Sys.getenv('CONNECT_SERVER'))

# Retrieve Pin
content <- pin_get(name = Sys.getenv('CONTENT_INFO_PIN'), board = "rsconnect")

Create a summary table of the number of content items under each access type.

library(dplyr)
library(gt)

df <- content %>%
  select(guid, name, title, owner_guid, access_type, created_time, last_deployed_time, content_url, dashboard_url)

df %>% count(access_type, sort = TRUE) %>%
  gt() %>%
  tab_header(
    title = "Sharing Settings",
    subtitle = "RStudio Connect Server Content Audit"
  ) %>%
  cols_label(
    access_type = "Access Type",
    n = "Count"
  )

Filter the content list to examine only content items with access_type == 'acl'. This will contain two sub-types of content relevant for auditing:

# Filter content list by 'acl' access_type
acl_only <- df %>%
  filter(access_type == 'acl')

Permissions API

library(httr)

# Function for initializing an empty permissions tibble
empty_permissions <- function() {
  cols <- c('id','content_guid','principal_guid','principal_type','role')
  cols %>% purrr::map_dfc(setNames, object = list(character()))
}

# Function for calling the content `/permissions` API endpoint
get_permissions <- function(guid) {
  permissions <- GET(paste0(Sys.getenv("CONNECT_SERVER"),"__api__/v1/content/",guid,"/permissions"),
    add_headers(Authorization = paste("Key", Sys.getenv("CONNECT_API_KEY"))))

  if (status_code(permissions) == 200){
    result <- tidyr::unnest_wider(tibble::tibble(dat = content(permissions)), dat)
    if (dim(result)[1] == 0){
      empty_permissions() 
    } else {
      result
    }
  } else {
    empty_permissions()
  }

}

# Get permissions for all ACL-type content items
# Content that is accessible only by the publisher-owner (no additional users or groups) will return no records

acl_info <- purrr::map(acl_only$guid, get_permissions)
acl_tibble <- tidyr::unnest(tibble::tibble(dat = acl_info), dat)

# Join acl_tibble with acl_only
acl_result <- acl_tibble %>%
  left_join(acl_only, by=c("content_guid" = "guid")) %>%
  rename(content_owner = owner_guid)

# View the table result
library(reactable)

acl_result %>%
  select(content_guid, principal_guid, principal_type, role, name, content_owner) %>%
  reactable(searchable = TRUE, highlight = TRUE, columns=list(
  content_guid = colDef(name = "Content GUID"),
  name = colDef(name = "Content Name"),
  content_owner = colDef(name = "Content Owner"),
  principal_guid = colDef(name = "Principal GUID"),
  principal_type = colDef(name = "Principal Type"),
  role = colDef(name = "Share Access Role")
))

Split by principal type (user/group) and create pins

For each content item shared with specific users or groups, report the user/group and list whether they have viewer or collaborator access.

Note: The user-info and group-info pins can be generated by running pin-users-groups.Rmd (intended to be run on a schedule).

library(pins)
library(dplyr)
# Get the user info Pin
user_list <- pin_get(name = Sys.getenv("USER_INFO_PIN"), board = "rsconnect")
# Get the group info Pin
group_list <- pin_get(name = Sys.getenv("GROUP_INFO_PIN"), board = "rsconnect")

# Add user names to the permission info
pr_users <- acl_result %>% 
  filter(principal_type == 'user') %>%
  left_join(user_list, by=c("principal_guid" = "guid"))

# Add group names to the permission info
pr_groups <- acl_result %>%
  filter(principal_type == 'group') %>%
  left_join(group_list, by=c("principal_guid" = "guid"))
# Create a pin for all content permissions that have been explicitly shared with other (specific) users.
pins::pin(pr_users, name = "acl-users-table", description = "Results pulled from the content /permissions API, where the content has been shared with a user.", board = "rsconnect")

# Create a pin for all content items that have been explicitly shared with specific groups.
pins::pin(pr_groups, name = "acl-groups-table", description = "Results pulled from the content /permissions API, where the content has been shared with a group.", board = "rsconnect")

Download the full content permissions result table

# Write the content permissions result table response out to a CSV file for download
write.csv(acl_result, "rsc-acl-permissions.csv", row.names=FALSE)


kmasiello/rscview documentation built on Jan. 3, 2023, 2:58 p.m.