rAccess terminology
Roles and responibilities
- App Developer
- App Admin
- App Publisher
App Developer is responsible to find an App Admin
to provide Access units and panels.
App Developer will configure rAccess accordingly and implement
the controlling logic in the app.
App Publisher will work with App Developer to deploy
the app with correct configuration and add the identified App
Admin through the rAccess interface.
App Admin is responsible to manage users assignments in rAccess
interface.
Workflow

To use a rAccess security model, there are three distinct sets of actions that will be carried out by different roles.
Design phase :
- App developer should identify Access Panels and a number of Access Units within each Access Panel.
Development phase :
- App developer lists the Access Panels and Access units in a config
Yaml file.
- App developer will implement the required server side controlling logic.
Publishing phase :
- App publisher will publish the app with the sharing settings
of:
All users - login required
- App publisher will assign the identified App Admin as an admin to the deployed app, using the rAccess interface.
Post-deployment :
App admin controls and maintains access lists within the app. A nice shiny GUI is provided to facilitate this.
-
Audit trails are available for Access lists of all applications that utilize rAccess
- All past versions of Access lists per application are
available
- For any particular application, one can obtain the effective access lists at a certain time in history (e.g. effective access list for application A at 10:52 on 10 April 2022.
- All past versions of Access lists per application are
available
Implementation
The rAccess package includes the ui and server module
for the access control tab:
- module_iam_ui()
- module_iam_server()
and these could be plugged into the ui and server of the main application.
module_iam_server() takes the arguments:
-
id- Module id -
rAccess_obj- An instance of the R6 class objectrAccess
Once the access panels and access units are
identified, the developer can create a new instance of the R6 object
rAccess.
The parameters to the rAccess object can either be
passed directly as arguments to the new instance of rAccess
or could be defined within a configuration yaml file.
Adding rAccess config file
To add a config file template to your project directory use:
rAccess::use_config(file_name = "rAccess.yml")
Config file structure
module: rAccess
parameters:
app_name: # application name *
board_type: # local/rconnect/s3 *
access_mode: # default/single unit
unit_display: # dropdown/switch
switch_size: # default/mini/small/normal/large
user_df: !expr tibble::tribble(
~userid, ~username,
"UserID", "User Name") # Sample user_df
use_rconnect_users: TRUE # TRUE - combine rconnect userlist with user_df
secure_mode: FALSE # TRUE - Access will be denied to users with no access to atleast one panel/unit
local_board_path: # Path to save local pin_board when board_type = "local"
s3_bucket: # s3 bucket name when board_type = "s3"
s3_access_key: # s3 access key when board_type = "s3"
s3_secret_key: # s3 secret key when board_type = "s3"
verbose: FALSE # If TRUE Prints all changes in the log
data: # Study data path if available
datapath1:
datapath2:
panel_str: # Panel structure to be defined by the developer
- access_panel: ADMIN # Every App should have an ADMIN panel.(mandatory)
- access_panel: # Access Panel name *
access_units:
- unit: # Access unit name *
- unit: # Access unit name
- access_panel: # Access Panel name *
access_units:
- unit:
data: # datapath associated with access unit
- unit:
data: # datapath associated with access unit
Parameters
app_name Application Name. The same will be used as pin_name.
-
board_type Takes values:
-
"local"for a local pin board. -
"s3"for using s3 bucket to save pin board. -
"rconnect"for rconnect pin board.
-
Pin_board is created using the pins package to store the
access data. It could be a local folder when running the app
interactively. When deployed to connect servers, the S3 bucket or
rsconnect pin_board must be used.
-
access_mode - Enables user to select access modes. Available access modes are :
-
default- Allows access to multiple access panels and multiple access units -
single unit- Allows access to single access unit within an access panel.
-
unit_display - Defines the display type for access units in the ui. Takes values : ‘switch’, ‘dropdown’. It defaults to toggle switches.
switch_size - Defines the size of toggle switches used in access control ui. It takes values : ‘default’, ‘mini’, ‘small’, ‘normal’, ‘large’.
user_df - A data frame with userid and username or NULL. If the
user_dfis provided, then the access module will search for potential users from within the given data frame in interactive mode.
user_df <- tibble::tribble(
~userid, username,
"UserId1", "User Name 1",
"UserId2", "User Name 2",
"UserId3", "User Name 3",
"UserId4", "User Name 4",
"UserId5", "User Name 5"
)
use_rconnect_users Takes
TRUE/FALSE. If TRUE the rconnect user list will be combined with the user_df. This will only affect the deployed apps in Posit Connect servers.safe_mode Takes
TRUE/FALSEIf TRUE then access to the app will be denied to users with no access to at least one panel/unit.local_board_path - Path to the local folder to save pin board when board_type = “local”. Local pin_boards are mainly useful when the app running interactively, but you can also deploy the app with this option. However, you will lose the saved access lists if you re-deploy the app. By default the local path will be data folder in the root directory.
s3_bucket, s3_access_key, s3_secret_key - S3 bucket credentials to be used when board_type = “s3”.
verbose If TRUE Prints all data base changes in log. To be used for debugging.
data
The study data path associated to the access units (if any) should be
added in this section as dathapath1,
datapath2, …
data:
datapath1: "testpath/data/path1"
datapath2: "testpath/data/path2"
The datapaths will not be displayed anywhere in the access control UI
but will be saved as part of the panel structure and developers can
access them using panel_config.
e.g. newIAM$panel_config
Panel Structure
The panel structure for each access panel is to maintained as given in the sample config file. Every access control module must have an ‘ADMIN’ panel and it has no units. This will be defined under panel_str as:
panel_str:
- access_panel: ADMIN
All other access panels should follow the structure:
- access_panel: "Access Panel 1"
access_units:
- unit: "Unit 1"
data: datapath1
- unit: "Unit 2"
data: datapath2
If there are multiple datapaths associated with an access unit then, it could be defined as:
- access_panel: "Access Panel 1"
access_units:
- unit: "Unit 1"
data: !expr c('datapath1', ''datapath2`)
Creating a new instance of rAccess with Config file
Once the configuration file is ready the user can create a new
instance of rAccess as below:
newIAM <- rAccess$new(user = "UserID", config = "rAccess.yml")
When deploying apps, you should always use session$user
as the user to ensure the app recognizes who is currently
logged in.
Demo
library(shiny)
ui <- function() {
navbarPage(
"Demo!",
tabPanel(
"Plot",
sidebarLayout(
sidebarPanel(
radioButtons(
"plotType", "Plot type",
c("Scatter" = "p", "Line" = "l")
)
),
mainPanel(
plotOutput("plot")
)
)
),
tabPanel(
"Summary",
verbatimTextOutput("summary")
),
navbarMenu(
"More",
tabPanel(
"Table",
DT::dataTableOutput("table")
),
tabPanel(
"About",
fluidRow(
column(
6,
h1("this is a sample app")
)
)
)
)
)
}
server <- function(input, output, session) {
output$plot <- renderPlot({
plot(cars, type = input$plotType)
})
output$summary <- renderPrint({
summary(cars)
})
output$table <- DT::renderDataTable({
DT::datatable(cars)
})
}
shinyApp(ui, server)
library(DT)
#>
#> Attaching package: 'DT'
#> The following objects are masked from 'package:shiny':
#>
#> dataTableOutput, renderDataTable
library(pins)
library(shiny)
library(rAccess)
ui <- navbarPage(
id = "mainpage",
title = "Demo!",
tabPanel(
"Plot",
sidebarLayout(
sidebarPanel(
radioButtons(
"plotType", "Plot type",
c("Scatter" = "p", "Line" = "l")
)
),
mainPanel(
plotOutput("plot")
)
)
),
tabPanel(
"Summary",
verbatimTextOutput("summary")
),
navbarMenu(
"More",
tabPanel(
"Table",
DT::dataTableOutput("table")
),
tabPanel(
"About",
fluidRow(
column(
6,
h1("this is a sample app")
)
)
)
),
tabPanel(
"Access Control",
rAccess::module_iam_ui("iam")
)
)
server <- function(input, output, session) {
# Add sample rAccess config file to the root directory
if (!file.exists("sample_rAccess.yml")) {
rAccess::use_config("sample_rAccess.yml")
}
# Create new instance of rAccess
user_id <- ifelse(!exists("session$user"), "UserID", session$user)
newIAM <- rAccess$new(
user = user_id,
config = "sample_rAccess.yml"
)
if (newIAM$no_admin() || newIAM$is_admin()) {
showTab("mainpage", target = "Access Control")
} else {
hideTab("mainpage", target = "Access Control")
}
rAccess::module_iam_server("iam", newIAM)
# Get panels with access
user_access_list <- newIAM$get_user_accesslist()
# Show/Hide: Access Control Panel
if (newIAM$no_admin() || newIAM$is_admin()) {
showTab("mainpage", target = "Access Control")
} else {
hideTab("mainpage", target = "Access Control")
print("YOU DO NOT HAVE ADMIN ACCESS")
}
# Show/Hide: plot tab
if (!"plot" %in% user_access_list[["sum"]]) {
hideTab("mainpage", target = "Plot")
} else {
showTab("mainpage", target = "Plot")
}
# Show/hide the Summary Tab
if (!"summary" %in% user_access_list[["sum"]]) {
hideTab("mainpage", target = "Summary")
} else {
showTab("mainpage", target = "Summary")
}
# Show/hide the Table tab
if (!"view" %in% user_access_list[["data"]]) {
hideTab("mainpage", target = "Table")
} else {
showTab("mainpage", target = "Table")
}
# ----------------------------------------------------------------------------
output$plot <- renderPlot({
plot(cars, type = input$plotType)
})
output$summary <- renderPrint({
summary(cars)
})
output$table <- DT::renderDataTable({
DT::datatable(cars)
})
}
shinyApp(ui, server)module: rAccess
parameters:
app_name: demoApp
board_type: local
access_mode: default
unit_display: 'dropdown'
switch_size:
user_df: !expr tibble::tribble(
~userid, ~username,
"UserID", "User Name 1",
"UserID2", "User Name 2",
use_rconnect_users: TRUE
local_board_path:
s3_bucket:
s3_access_key:
s3_secret_key:
verbose: FALSE
data:
datapath1:
datapath2:
panel_str:
- access_panel: ADMIN
- access_panel: sum
access_units:
- unit: plot
- unit: summary
- access_panel: data
access_units:
- unit: view
Audit trail functionalities
rAccess package also offers additional functions for
performing audit trail activities.
- get_accesshistory() - Takes pin_board and pin_name as arguments and returns a list of access data created at different time points.
pin_board <- pins::board_folder(path = "./data/")
get_accesshistory(pin_board, "demo")
#> $`2023-06-29 12:44:20 Edited by NHarida1`
#> AccessPanel UserName UserID plot summary view ADMIN sum
#> 1 ADMIN Nandukrishnan Haridas NHarida1 FALSE FALSE FALSE TRUE TRUE
#> 2 ADMIN Peyman Eshghi PEshghi FALSE FALSE FALSE TRUE TRUE
#> 3 data Nandukrishnan Haridas NHarida1 TRUE FALSE FALSE FALSE FALSE
#> 4 data Peyman Eshghi PEshghi TRUE FALSE FALSE FALSE FALSE
#> 5 sum Peyman Eshghi PEshghi TRUE TRUE FALSE FALSE FALSE
#> 6 sum Nandukrishnan Haridas NHarida1 TRUE TRUE FALSE FALSE FALSE
#> data
#> 1 TRUE
#> 2 TRUE
#> 3 FALSE
#> 4 FALSE
#> 5 FALSE
#> 6 FALSE- get_accesslist() - Takes pin_board, pin_name, datemin and datemax as arguments and returns access list at a given date or in a specific time period.
get_accesslist(pin_board, "demo", datemin = "2023-06-29")
#> $`2023-06-29 12:44:20`
#> AccessPanel UserName UserID plot summary view ADMIN sum
#> 1 ADMIN Nandukrishnan Haridas NHarida1 FALSE FALSE FALSE TRUE TRUE
#> 2 ADMIN Peyman Eshghi PEshghi FALSE FALSE FALSE TRUE TRUE
#> 3 data Nandukrishnan Haridas NHarida1 TRUE FALSE FALSE FALSE FALSE
#> 4 data Peyman Eshghi PEshghi TRUE FALSE FALSE FALSE FALSE
#> 5 sum Peyman Eshghi PEshghi TRUE TRUE FALSE FALSE FALSE
#> 6 sum Nandukrishnan Haridas NHarida1 TRUE TRUE FALSE FALSE FALSE
#> data
#> 1 TRUE
#> 2 TRUE
#> 3 FALSE
#> 4 FALSE
#> 5 FALSE
#> 6 FALSE- get_admins() - Takes pin_board, pin_name and admin_panel as arguments and returns the list of admins.
get_admins(pin_board, "demo", "ADMIN")
#> NHarida1 - Nandukrishnan Haridas PEshghi - Peyman Eshghi
#> "NHarida1" "PEshghi"- get_board() - Takes pin_board and pin_name as arguments and gather all pin_board elements.
get_board(pin_board, "demo")
#> $access_panels
#> [1] "ADMIN" "data" "sum"
#>
#> $access_units
#> [1] "plot" "summary" "view"
#>
#> $access_df
#> AccessPanel UserName UserID plot summary view ADMIN sum
#> 1 ADMIN Nandukrishnan Haridas NHarida1 FALSE FALSE FALSE TRUE TRUE
#> 2 ADMIN Peyman Eshghi PEshghi FALSE FALSE FALSE TRUE TRUE
#> 3 data Nandukrishnan Haridas NHarida1 TRUE FALSE FALSE FALSE FALSE
#> 4 data Peyman Eshghi PEshghi TRUE FALSE FALSE FALSE FALSE
#> 5 sum Peyman Eshghi PEshghi TRUE TRUE FALSE FALSE FALSE
#> 6 sum Nandukrishnan Haridas NHarida1 TRUE TRUE FALSE FALSE FALSE
#> data
#> 1 TRUE
#> 2 TRUE
#> 3 FALSE
#> 4 FALSE
#> 5 FALSE
#> 6 FALSE
#>
#> $access_list
#> list()