From a731bb5a63e20999359b6574e4cf1a49aaa00110 Mon Sep 17 00:00:00 2001 From: merlinoa Date: Tue, 29 Sep 2020 16:38:21 -0400 Subject: [PATCH] new "is_auth_required" option to allow users to access your app with or without being signed in --- NEWS.md | 2 +- R/Sessions.R | 9 ++++- R/global_sessions_config.R | 10 ++++- R/secure_server.R | 35 +++++++++++------ R/secure_ui.R | 26 +++++++++---- inst/examples/polished_example_03/global.R | 13 +++++++ inst/examples/polished_example_03/server.R | 44 ++++++++++++++++++++++ inst/examples/polished_example_03/ui.R | 34 +++++++++++++++++ 8 files changed, 150 insertions(+), 23 deletions(-) create mode 100644 inst/examples/polished_example_03/global.R create mode 100644 inst/examples/polished_example_03/server.R create mode 100644 inst/examples/polished_example_03/ui.R diff --git a/NEWS.md b/NEWS.md index d438010a..b7b8a801 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # v0.2.0.9000 - +- new "is_auth_required option" to `global_sessions_config()` which (when set to FALSE) allows users to access your app without being signed in. By default this argument is set to TRUE. # v0.2.0 diff --git a/R/Sessions.R b/R/Sessions.R index 4fb05ecf..adf3a446 100644 --- a/R/Sessions.R +++ b/R/Sessions.R @@ -76,6 +76,7 @@ Sessions <- R6::R6Class( is_invite_required = TRUE, sign_in_providers = character(0), is_email_verification_required = TRUE, + is_auth_required = TRUE, #' @description #' polished Sessions configuration function #' @@ -90,7 +91,8 @@ Sessions <- R6::R6Class( admin_mode = FALSE, is_invite_required = TRUE, sign_in_providers = "email", - is_email_verification_required = FALSE + is_email_verification_required = TRUE, + is_auth_required = TRUE ) { if (!(length(sign_in_providers) >= 1 && is.character(sign_in_providers))) { @@ -118,11 +120,16 @@ Sessions <- R6::R6Class( if (!(length(is_email_verification_required) == 1 && is.logical(is_email_verification_required))) { stop("invalid `is_email_verification_required` argument passed to `global_sessions_config()`", call. = FALSE) } + if (!(length(is_auth_required) == 1 && is.logical(is_auth_required))) { + stop("invalid `is_auth_required` argument passed to `global_sessions_config()`", call. = FALSE) + } + private$admin_mode <- admin_mode self$is_invite_required <- is_invite_required self$is_email_verification_required <- is_email_verification_required + self$is_auth_required <- is_auth_required private$refresh_jwt_pub_key() diff --git a/R/global_sessions_config.R b/R/global_sessions_config.R index 4772a0c2..a28ffc39 100644 --- a/R/global_sessions_config.R +++ b/R/global_sessions_config.R @@ -25,6 +25,10 @@ #' "email", "microsoft", and/or "facebook". Defaults to \code{"email"}. #' @param is_email_verification_required TRUE by default. Whether or not to require the user to #' verify their email before accessing your Shiny app. +#' @param is_auth_required TRUE by default. Whether or not to require users to be signed +#' in to access the app. It can be useful to set this argument to FASLE if you want to +#' allow user to do certain actions (such as viewing charts and tables) without signing in, +#' and only require users to sign in if they want to save data to your database. #' #' @export #' @@ -51,7 +55,8 @@ global_sessions_config <- function( is_invite_required = TRUE, api_url = "https://api.polished.tech", sign_in_providers = "email", - is_email_verification_required = TRUE + is_email_verification_required = TRUE, + is_auth_required = TRUE ) { if (!(length(api_key) == 1 && is.character(api_key))) { @@ -104,7 +109,8 @@ global_sessions_config <- function( admin_mode = admin_mode, is_invite_required = is_invite_required, sign_in_providers = sign_in_providers, - is_email_verification_required = is_email_verification_required + is_email_verification_required = is_email_verification_required, + is_auth_required = is_auth_required ) } \ No newline at end of file diff --git a/R/secure_server.R b/R/secure_server.R index 9bc41d2b..1ca84160 100644 --- a/R/secure_server.R +++ b/R/secure_server.R @@ -74,8 +74,9 @@ secure_server <- function( # user is not signed in # if the user is not on the sign in page, redirect to sign in and reload - if (!identical(page, "sign_in") || (is.null(splash_module) && is.null(page)) - ) { + if ((!identical(page, "sign_in") || (is.null(splash_module) && is.null(page))) && + isTRUE(.global_sessions$is_auth_required)) { + shiny::updateQueryString( queryString = paste0("?page=sign_in"), session = session, @@ -197,7 +198,10 @@ secure_server <- function( } else if (is.null(query_list$page)) { # go to the custom app - server(input, output, session) + if (isTRUE(.global_sessions$is_auth_required)) { + server(input, output, session) + } + if (isTRUE(hold_user$is_admin)) { # go to admin panel button @@ -242,6 +246,10 @@ secure_server <- function( }, once = TRUE) + if (isFALSE(.global_sessions$is_auth_required)) { + server(input, output, session) + } + # load up the sign in module server logic if the user in on "sign_in" page observeEvent(session$userData$user(), { @@ -272,17 +280,20 @@ secure_server <- function( } } - } else if (is.null(page) && !is.null(splash_module)) { + } else if (is.null(page)) { - if (names(formals(splash_module))[[1]] == "id") { - splash_module("splash") - } else { - callModule( - splash_module, - "splash" - ) - } + if (!is.null(splash_module) && isTRUE(isFALSE(.global_sessions$is_auth_required))) { + + if (names(formals(splash_module))[[1]] == "id") { + splash_module("splash") + } else { + callModule( + splash_module, + "splash" + ) + } + } } }, ignoreNULL = FALSE, once = TRUE) diff --git a/R/secure_ui.R b/R/secure_ui.R index 71e9d8e1..07cdaf2b 100644 --- a/R/secure_ui.R +++ b/R/secure_ui.R @@ -135,15 +135,26 @@ secure_ui <- function( } else { + if (isFALSE(.global_sessions$is_auth_required)) { + # auth is not required, so allow the user to go directly to the custom shiny app + # go to Shiny app without admin button. User is not an admin + page_out <- tagList( + ui, + tags$script(src = "polish/js/router.js?version=1"), + tags$script(src = "polish/js/polished_session.js?version=2"), + tags$script(paste0("polished_session('", user$hashed_cookie, "')")) + ) + } else { + # send a random uuid as the polished_session. This will trigger a session + # reload and a redirect to the sign in page + page_out <- tagList( + tags$script(src = "polish/js/router.js?version=1"), + tags$script(src = "polish/js/polished_session.js?version=2"), + tags$script(paste0("polished_session('", uuid::UUIDgenerate(), "')")) + ) + } - # send a random uuid as the polished_session. This will trigger a session - # reload and a redirect to the sign in page - page_out <- tagList( - tags$script(src = "polish/js/router.js?version=1"), - tags$script(src = "polish/js/polished_session.js?version=2"), - tags$script(paste0("polished_session('", uuid::UUIDgenerate(), "')")) - ) } @@ -161,6 +172,7 @@ secure_ui <- function( } else if (isTRUE(user$email_verified) || isFALSE(.global_sessions$is_email_verification_required)) { + if (identical(page_query, "account")) { # server the payments module UI diff --git a/inst/examples/polished_example_03/global.R b/inst/examples/polished_example_03/global.R new file mode 100644 index 00000000..f50ce82a --- /dev/null +++ b/inst/examples/polished_example_03/global.R @@ -0,0 +1,13 @@ +library(shiny) +library(polished) +library(config) +library(shinyjs) + +app_config <- config::get() + +# configure polished +global_sessions_config( + app_name = "polished_example_03", + api_key = app_config$api_key, + is_auth_required = FALSE +) diff --git a/inst/examples/polished_example_03/server.R b/inst/examples/polished_example_03/server.R new file mode 100644 index 00000000..274ecbff --- /dev/null +++ b/inst/examples/polished_example_03/server.R @@ -0,0 +1,44 @@ +server <- function(input, output, session) { + + output$secure_content <- renderPrint({ + session$userData$user() + }) + + observe({ + + if (is.null(session$userData$user())) { + + hideElement("sign_out") + showElement("go_to_sign_in") + + } else { + + hideElement("go_to_sign_in") + showElement("sign_out") + + } + + }) + + observeEvent(input$sign_out, { + req(session$userData$user()) + + sign_out_from_shiny(session) + session$reload() + + }) + + observeEvent(input$go_to_sign_in, { + + # set query string to sign in page + shiny::updateQueryString( + queryString = paste0("?page=sign_in"), + session = session, + mode = "push" + ) + session$reload() + }) + +} + +secure_server(server) diff --git a/inst/examples/polished_example_03/ui.R b/inst/examples/polished_example_03/ui.R new file mode 100644 index 00000000..2ad6c9d9 --- /dev/null +++ b/inst/examples/polished_example_03/ui.R @@ -0,0 +1,34 @@ + + +ui <- fluidPage( + shinyjs::useShinyjs(), + fluidRow( + column( + 6, + h1("Polished Example 03"), + br() + ), + column( + 6, + br(), + shinyjs::hidden(actionButton( + "sign_out", + "Sign Out", + icon = icon("sign-out-alt"), + class = "pull-right" + )), + shinyjs::hidden(actionButton( + "go_to_sign_in", + "Sign In", + icon = icon("sign-in-alt"), + class = "pull-right" + )) + ), + column( + 12, + verbatimTextOutput("secure_content") + ) + ) +) + +secure_ui(ui)