--- title: "Module Access Management with `rAccess`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Module Access Management with `rAccess`} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` This vignette demonstrates how to implement module-level access control in `teal` applications using the `rAccess` package integration provided by uteals. ## Overview The `rAccess` package enables role-based access control for `teal` modules, allowing you to: - Restrict module access based on user permissions - Implement admin-only functionality - Dynamically filter modules at runtime - Provide graceful handling for unauthorized access Users will only see and interact with modules they are authorized to use. ## Library Calls ```{r setup} library(uteals) library(teal) library(teal.modules.general) library(rAccess) library(shiny) library(bslib) ``` ## Quick Start Here's a minimal example to get started: ```r # 1. Create modules mods <- modules( tm_data_table("Data Table"), tm_variable_browser("Variable Browser") ) # 2. Extract module structure extract_modules_to_yaml(mods, "panel_str.yml") # 3. Filter modules by user permissions filtered_mods <- keep_by_label(mods, c("Data Table")) ``` ## Configuration ### Step 1: Generate Module Structure Use the `extract_modules_to_yaml()` function to automatically generate the module structure: ```r # Define your teal modules mods <- modules( tm_data_table("Data Table"), tm_variable_browser("Variable Browser"), tm_g_scatterplot("Scatterplot", x = ..., y = ...), tm_outliers("Summary Statistics", outlier_var = ...) ) # Generate YAML structure extract_modules_to_yaml(mods, "panel_str.yml") ``` This creates: ```yaml # panel_str.yml panel_str: - access_panel: ADMIN - access_panel: Modules access_units: - unit: Data Table - unit: Variable Browser - unit: Scatterplot - unit: Summary Statistics ``` ### Step 2: Create `rAccess` Configuration Create your main configuration file (`rAccess_config.yml`): ```yaml # rAccess_config.yml module: rAccess parameters: app_name: TealAccessDemo board_type: local access_mode: default unit_display: 'dropdown' user_df: !expr tibble::tribble( ~userid, ~username, "user1", "Regular User", "user2", "Limited User") use_rconnect_users: FALSE verbose: FALSE secure_mode: FALSE # Copy the panel_str section from generated file panel_str: - access_panel: ADMIN - access_panel: Modules access_units: - unit: Data Table - unit: Variable Browser - unit: Scatterplot - unit: Summary Statistics ``` ## Implementation ### Pattern 1: Basic Module Filtering ```r # Filter modules based on user access user_modules <- c("Data Table", "Variable Browser") filtered_modules <- keep_by_label(all_modules, user_modules) # Remove specific modules restricted_modules <- remove_by_label(all_modules, c("Admin Panel")) ``` ### Pattern 2: Dynamic Access Control ```r server <- function(input, output, session) { # Get user identity user_id <- ifelse(interactive(), Sys.getenv("USER"), session$user) # Initialize rAccess iam <- rAccess$new(user = user_id, config = "rAccess_config.yml") # Get user permissions user_access <- iam$get_user_accesslist() allowed_modules <- unlist(user_access, use.names = FALSE) # Filter modules filtered_modules <- keep_by_label(all_modules, allowed_modules) # Handle edge cases if (is.null(filtered_modules)) { filtered_modules <- create_no_access_module() } } ``` ## Complete Application Example ### Configuration ```yaml # rAccess_config.yml module: rAccess parameters: app_name: TealAccessDemo board_type: local access_mode: default unit_display: 'dropdown' user_df: !expr tibble::tribble( ~userid, ~username, "user1", "Regular User", "user2", "Limited User") use_rconnect_users: FALSE verbose: FALSE secure_mode: FALSE ``` ```r ### Data Setup # Prepare sample data data <- teal.data::teal_data( IRIS = iris, MTCARS = mtcars, code = c("IRIS <- iris", "MTCARS <- mtcars") ) ### Module Definition # Define all available modules all_modules <- modules( tm_data_table("Data Table"), tm_variable_browser("Variable Browser"), tm_g_scatterplot( label = "Scatterplot", x = data_extract_spec( dataname = "IRIS", select = select_spec(choices = variable_choices(iris)) ), y = data_extract_spec( dataname = "IRIS", select = select_spec(choices = variable_choices(iris)) ) ), tm_outliers( label = "Summary Statistics", outlier_var = data_extract_spec( dataname = "MTCARS", select = select_spec(choices = variable_choices(mtcars, c("mpg", "hp", "wt"))) ) ) ) ### UI Implementation ui <- bslib::page_navbar( title = "Teal Access Demo", # Main application tab bslib::nav_panel( "Analytics Modules", uiOutput("teal_ui") ), # Admin panel (conditionally visible) bslib::nav_panel( "Access Control", rAccess::module_iam_ui("access_control"), tags$div( class = "alert alert-info", tags$h5("Administrator Panel"), tags$p("Manage user access and permissions here.") ) ) ) ### Server Implementation server <- function(input, output, session) { # User authentication user_id <- ifelse(interactive(), "user1", session$user) # Initialize access control iam <- rAccess$new(user = user_id, config = "rAccess_config.yml") # Admin module server (conditional) if (iam$no_admin() || iam$is_admin()) { rAccess::module_iam_server("access_control", iam) } # Filter modules based on permissions user_access_list <- iam$get_user_accesslist() users_modules <- unlist(user_access_list, use.names = FALSE) filtered_modules <- keep_by_label(all_modules, users_modules) # Handle no access if (is.null(filtered_modules)) { filtered_modules <- modules( module( label = "No Access", ui = function(id) tags$div( class = "alert alert-warning", h4("Access Restricted"), p("You do not have permission to access any modules."), p("Please contact your administrator.") ) ) ) } # Render teal UI output$teal_ui <- renderUI({ tagList( ui_teal("teal_app", filtered_modules), ui_session_info("session_info") ) }) # Teal server srv_teal("teal_app", data = data, modules = filtered_modules) srv_session_info("session_info") } ### Application Launch # Create and run the application app <- shinyApp(ui, server) # For development if (interactive()) { runApp(app) } ``` ### Running the Example The app will show different modules based on the user: - `admin`: Access to admin panel - `other users`: Access based on `rAccess` permissions - Unauthorized users: "No Access" message **Note**: Until you define and add at least one admin, the admin tab will be accessible by everyone. Once there is at least one admin defined, access control becomes enforced. ### Access-boards Storage Setup In this example, with `board_type: local` configured in `rAccess_config.yml`, access lists are stored locally in the `./data/` directory within your application. Alternative storage options include `AWS S3` or `Posit Connect` pins - refer to the `rAccess` documentation for configuration details. ### Debugging Tips ```r # Ensure the rAccess configuration (`rAccess_config.yml` in the example) is accessible by your app. # Ensure module labels match exactly between configuration and code # Enable verbose logging iam <- rAccess$new(user = user_id, config = "config.yml", verbose = TRUE) # Check user access list print(iam$get_user_accesslist()) # Verify module labels print(sapply(all_modules$children, function(x) x$label)) ``` ## Summary The uteals package provides essential utilities for implementing access control in teal applications: - `extract_modules_to_yaml()`: Automates configuration generation - `keep_by_label()`: Filters modules by user permissions - `remove_by_label()`: Excludes restricted modules This integration enables secure, role-based access control while maintaining the flexibility and power of the teal framework. ## Further Reading - [`rAccess` Documentation](https://johnsonandjohnson.github.io/rAccess/) - [`Teal` as `Shiny` Module](https://insightsengineering.github.io/teal/latest-tag/articles/teal-as-a-shiny-module.html)