diff --git a/DESCRIPTION b/DESCRIPTION index 57e0cc5..86db39f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: rAccess Title: Access Control Module for 'shiny' Applications -Version: 0.1.1.299 +Version: 0.1.3 Authors@R: c(person(given = "Peyman", family = "Eshghi", diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..6c0c077 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,7 @@ +# rAccess 0.1.1.3 + +### Bug fixes + * Fixed a bug that prevented searching for users in deployed apps. + +# rAccess 0.1.1.2 + * November 03, 2025 - Initial Release. \ No newline at end of file diff --git a/R/functions.R b/R/functions.R index 3835af6..1fd878a 100644 --- a/R/functions.R +++ b/R/functions.R @@ -310,6 +310,7 @@ rAccess <- R6Class( self$s3_bucket <- s3_bucket self$s3_access_key <- s3_access_key self$s3_secret_key <- s3_secret_key + self$use_rconnect_users <- use_rconnect_users self$verbose <- verbose self$user <- user self$secure_mode <- secure_mode diff --git a/R/module_sub_iam.R b/R/module_sub_iam.R index 676f2fb..5456844 100644 --- a/R/module_sub_iam.R +++ b/R/module_sub_iam.R @@ -287,30 +287,26 @@ module_sub_iam_server <- function(id, access_panel_id, rAccess_obj) { username_ <- input$txtUsername if (nchar(trimws(username_)) >= 1) { withProgress(message = "Fetching list of users...", { - if (Sys.getenv("SHINY_PORT") == "") { - if (!is.null(rAccess_obj$user_df)) { - choices_ <- rAccess_obj$matched_users(username_) - } else { - choices_ <- NULL - } + if (!is.null(rAccess_obj$user_df)) { + choices_ <- rAccess_obj$matched_users(username_) } else { - rconnect_choices_ <- rAccess::get_user_api( - contact_info = username_, - url = session$clientData$url_hostname, - api_key = NULL - ) - if (!is.null(rAccess_obj$user_df)) { - userdf_choices_ <- rAccess_obj$matched_users(username_) - } else { - userdf_choices_ <- NULL - } + choices_ <- NULL + } + userdf_choices_ <- choices_ + if (Sys.getenv("SHINY_PORT") != "") { if (rAccess_obj$use_rconnect_users) { - choices_ <- unique(rbind(rconnect_choices_, userdf_choices_)) + rconnect_choices_ <- rAccess::get_user_api( + contact_info = username_, + url = session$clientData$url_hostname, + api_key = NULL + ) + choices_ <- rbind(rconnect_choices_, userdf_choices_) } else { choices_ <- userdf_choices_ } } if (!is.null(choices_)) { + choices_ <- choices_[!duplicated(tolower(choices_$userid)), ] choices_temp <- choices_$userid names(choices_temp) <- paste0(choices_$userid, " (", choices_$username, ")") diff --git a/README.md b/README.md index 89a9221..220291d 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,81 @@ -# rAccess +# rAccess -[![Test coverage](https://raw.githubusercontent.com/johnsonandjohnson/rAccess/coverage/badges/coverage.svg)](https://github.com/johnsonandjohnson/rAccess/actions/workflows/test-coverage.yaml) + +[![Test +coverage](https://raw.githubusercontent.com/johnsonandjohnson/rAccess/coverage/badges/coverage.svg)](https://github.com/johnsonandjohnson/rAccess/actions/workflows/test-coverage.yaml) [![R-CMD-check](https://github.com/johnsonandjohnson/rAccess/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/johnsonandjohnson/rAccess/actions/workflows/R-CMD-check.yaml) - - -[![CRAN Version](https://www.r-pkg.org/badges/version/rAccess?color=green)](https://cran.r-project.org/package=rAccess) - - -`rAccess` is an R package that offers a flexible framework for -in-app access control, allowing local and/or remote storage, organization, and -retrieval of access lists. It features a pluggable shiny module to create and -manage access lists for individual Shiny applications. It is built on top of the -Posit Connect Access Management, which means no credentials are collected or stored. +[![CRAN +Version](https://www.r-pkg.org/badges/version/rAccess?color=green)](https://cran.r-project.org/package=rAccess) -A friendly user interface enables the app Admin to easily manage user access -permission for respective access units. + -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. +`rAccess` is an R package that offers a flexible framework for in-app +access control, allowing local and/or remote storage, organization, and +retrieval of access lists. It features a pluggable shiny module to +create and manage access lists for individual Shiny applications. It is +built on top of the Posit Connect Access Management, which means no +credentials are collected or stored. + +A friendly user interface enables the app Admin to easily manage user +access permission for respective access units. + +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. + +## Core Concepts + +**Access Panels** serve as organizational containers that group related *Access +Units* together, enabling you to implement hierarchical access control +structures. In the rAccess user interface, each *Access Panel* appears as a +separate tab where administrators can navigate to configure permissions. This +tabbed structure allows admins to logically organize access controls by +functional areas - for example, having separate panels for "Data Visualization", + "Report Generation", and "Administrative Functions". Each panel can contain + multiple related units that share similar access requirements or belong to +the same feature set. + +**Access Units** represent the most granular level of access control in your +application - these are the specific elements that users can be granted or denied +access to. They could be individual shiny modules, specific UI components (like +download buttons or input panels), particular data views, or any discrete +functionality within your application. In the rAccess user interface, *Access +Units* appear as individual toggles or dropdown selections within their +respective *Access Panel* tabs, allowing administrators to precisely control which + users can access each specific feature or component of the application. ## Installation -``` +``` #install.packages("pak") pak::pak("johnsonandjohnson/rAccess") ``` ## Usage -`rAccess` includes server and ui modules for access management tab which could -be used within a main Shiny web application. +`rAccess` includes server and ui modules for access management tab which +could be used within a main Shiny web application. -``` +``` library(rAccess) ``` ## Adding rAccess config file -The package includes a template configuration file, making it simple for users -to get started. It contains all the necessary parameters for rAccess and can be -customized to suit individual needs. +The package includes a template configuration file, making it simple for +users to get started. It contains all the necessary parameters for +rAccess and can be customized to suit individual needs. 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 * @@ -87,19 +114,24 @@ panel_str: # Panel structure to be defined by the develop data: # datapath associated with access unit ``` -**For detailed description on the config file components refer vignette : Tutorial.** +**For detailed description on the config file components refer vignette +: Tutorial.** ### Creating a new instance of rAccess -Once the configuration file is ready the user can create a new instance of -rAccess as below: -``` +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") ``` + ### Creating a new instance of rAccess without a config file -If there is no config file in place, user can also pass the rAccess parameters as -arguments to the new instance of the `rAccess` object. -``` + +If there is no config file in place, user can also pass the rAccess +parameters as arguments to the new instance of the `rAccess` object. + +``` access_panels <- list( `ADMIN` = NULL, `Access Panel 1` = c("Unit 1", "Unit 2"), @@ -126,13 +158,17 @@ newIAM$access_panels newIAM$access_units newIAM$access_mode ``` + ### board_type options in rAccess -There three pin board options available for the users: "local", "s3", and "rconnect". -This can be specified with the parameter `board_type` of the rAccess object. -* `"local"` : Local folder will be used as a pin_board. The user must also specify the "local_board_path". +There three pin board options available for the users: "local", "s3", +and "rconnect". This can be specified with the parameter `board_type` of +the rAccess object. -``` +- `"local"` : Local folder will be used as a pin_board. The user must + also specify the "local_board_path". + +``` newIAM <- rAccess$new(user = "", app_name = "testApp", board_type = "local", @@ -143,9 +179,10 @@ newIAM <- rAccess$new(user = "", user_df = user_df) ``` -* `"s3"` : S3 bucket will be used as a pin_board. When board_type is "s3", the user must give the s3 credentials. +- `"s3"` : S3 bucket will be used as a pin_board. When board_type is + "s3", the user must give the s3 credentials. -``` +``` newIAM <- rAccess$new(user = "", app_name = "testApp", board_type = "s3", @@ -157,10 +194,11 @@ newIAM <- rAccess$new(user = "", user_df = user_df) ``` -* `"rconnect"` : Posit Connect pin_board will be utilized. If the pin_board does not already exist, -it will be created and deployed on the same Posit Connect server where the app is hosted. +- `"rconnect"` : Posit Connect pin_board will be utilized. If the + pin_board does not already exist, it will be created and deployed on + the same Posit Connect server where the app is hosted. -``` +``` newIAM <- rAccess$new(user = "", app_name = "testApp", board_type = "rconnect", @@ -170,10 +208,14 @@ newIAM <- rAccess$new(user = "", ``` ### Creating user list -The user list for rAccess could either be supplied as the `user_df` argument of the `rAccess` object or could be fetched from the rconnect user list. -**Creating user_df*** -``` +The user list for rAccess could either be supplied as the `user_df` +argument of the `rAccess` object or could be fetched from the rconnect +user list. + +**Creating user_df**\* + +``` user_df <- tibble::tribble( ~userid, ~username, "UserId1", "User Name 1", @@ -190,15 +232,13 @@ newIAM <- rAccess$new(user = "", access_panels = access_panels, access_mode = "default", user_df = user_df) - ``` -**Using API to fetch user data** -If you have access to your organization’s user directory via an API, -you might want to first fetch the data and then prepare the user_df, -similar to the example below +**Using API to fetch user data** If you have access to your +organization’s user directory via an API, you might want to first fetch +the data and then prepare the user_df, similar to the example below -``` +``` api_url <- "" users <- jsonlite::fromJSON(api_url) user_df <- tibble::tibble(userid = users$USERID, username = users$USERNAME) @@ -210,15 +250,15 @@ newIAM <- rAccess$new(user = "", access_panels = access_panels, access_mode = "default", user_df = user_df) - ``` **Using User list from rconnect** -User list will be automatically fetched from the Posit Connect servers when -deployed. Users must make sure that `use_rconnect_users` parameter is set as -`TRUE` to get users from Posit Connect. -``` +User list will be automatically fetched from the Posit Connect servers +when deployed. Users must make sure that `use_rconnect_users` parameter +is set as `TRUE` to get users from Posit Connect. + +``` # When deployed newIAM <- rAccess$new(user = "", app_name = "testApp", @@ -228,9 +268,9 @@ newIAM <- rAccess$new(user = "", use_rconnect_users = TRUE) ``` -## Example +## Example -``` +``` library(DT) library(pins) library(shiny) @@ -322,3 +362,5 @@ server <- function(input, output, session) { shinyApp(ui, server) ``` + +![App recording](man/figures/readme_rAccess.gif) diff --git a/man/figures/readme_rAccess.gif b/man/figures/readme_rAccess.gif new file mode 100644 index 0000000..d261095 Binary files /dev/null and b/man/figures/readme_rAccess.gif differ