Auditing Group Access to Content

Problem

You need to know what content a group is able to access. You might use this to audit access control lists, or validate that groups you manage have access to all the content they should.

Solution

Search Connect for content that is accessible to a group, and generate a data frame summarizing the content.

You need the group’s GUID. See Finding Groups and Viewing Group Information for details.

Note

Administrator permissions are needed in order to perform this action.

from posit import connect
import polars as pl

GROUP_GUID = "01c24f7d-700a-47d9-a695-2d25d3c14da5"

client = connect.Client()

results = client.get(f"v1/experimental/groups/{GROUP_GUID}/content")
results_df = pl.DataFrame(results.json())

results_out = (
  # extract the access_type (viewer or publisher) from the
  # nested struct and rename that column to access_type
  results_df
  .explode('permissions')
  .unnest('permissions')
  .filter(pl.col('principal_guid') == GROUP_GUID)
  .select("content_guid", "content_title", "principal_role")
  .rename({"principal_role": "access_type"})
)

The resulting DataFrame contains the content GUID, the title, and the type of access the group has to the content.

>>> results_out
shape: (3, 3)
┌─────────────────────────────────┬──────────────────────────┬─────────────┐
│ content_guid                    ┆ content_title            │ access_type │
---------
strstrstr
╞═════════════════════════════════╪══════════════════════════╡═════════════╡
0bef0ba7-1470-458f-95b3-6e93c3… ┆ quarto-stock-report-r    │ viewer      │
2aa38512-46e4-4be7-9bb9-0b32a9… ┆ Stock Report             │ viewer      │
│ f33285e4-6916-4241-b241-858f03… ┆ top-5-income-share-shiny │ viewer      │
└─────────────────────────────────┴──────────────────────────┘─────────────┘

Use the get_group_content() function to get a data frame of content permissions. The function takes one or more GUIDs of groups.

library(connectapi)
library(dplyr)

client <- connect()

groups <- get_groups(client)
target_group <- dplyr::filter(groups, name == "research")
results <- get_group_content(client, target_group$guid)

The resulting DataFrame contains the content GUID, the title, and the type of access the group has to the content.

> results
# A tibble: 4 x 7
  group_guid group_name  content_guid content_name     content_title access_type role
  <chr>      <chr>       <chr>        <chr>            <chr>         <chr>       <chr>
1 a6fb5cea…  research    8b57f54e…    app-1197-9825-t~ app-1197-982~ acl         publisher
2 a6fb5cea…  research    8bf70c85…    quarto-email-de~ quarto-email~ acl         viewer
3 a6fb5cea…  research    fcad1958…    top-queries      top-queries   logged_in   publisher

Pass in multiple GUIDs to see content access data for multiple groups at once.

> get_group_content(client, groups$guid)
# A tibble: 4 x 7
  group_guid group_name  content_guid content_name     content_title access_type role
  <chr>      <chr>       <chr>        <chr>            <chr>         <chr>       <chr>
1 a6fb5cea…  research    8b57f54e…    app-1197-9825-t~ app-1197-982~ acl         publisher
2 a6fb5cea…  research    8bf70c85…    quarto-email-de~ quarto-email~ acl         viewer
3 a6fb5cea…  research    fcad1958…    top-queries      top-queries   logged_in   publisher
4 ae5c3b2c…  devops      46fb83eb…    forecast-email-~ forecast-ema~ logged_in   publisher
# ℹ 16 more rows
# ℹ Use `print(n = ...)` to see more rows

Discussion

Now that you have the content that a group can see, you can use the content GUID in other recipes like Revoking Access from Groups to prevent the group (and others) from being able to access that that content.