web-dev-qa-db-fra.com

R Shiny: authentification utilisateur pour une seule application.R

Je développe une application R Shiny et souhaite ajouter un nom d'utilisateur et des identifiants de connexion. J'ai vérifié la démo de RStudio mais cela n'utilise que le ShinyServer Pro et j'utilise le paquet mongolite pour sauvegarder formData sur un Mongodb.

Est-il possible d’obliger les utilisateurs à ajouter des identifiants avant de générer l’UI de l’application?

9
Dante Smith

ShinyProxy , un serveur Shiny Open Source basé sur Docker et Spring Java, a été conçu pour résoudre ce problème. Il vous permet de coder en dur les utilisateurs dans le fichier de configuration de l'application, de vous connecter à un serveur LDAP, d'utiliser SSO/Keycloak ou de se connecter au réseau social.

8
John D

Voici un exemple d'utilisation des cookies pour l'authentification. Plus d'informations peuvent être trouvées dans mon blog ici .

Commencez par télécharger le cookie js dans le dossier www /:

if (!dir.exists('www/')) {
    dir.create('www')
}

download.file(
  url = 'https://raw.githubusercontent.com/js-cookie/js-cookie/master/src/js.cookie.js',
  destfile = 'www/js.cookies.js'
)

Installez les packages nécessaires:

install.packages(c('shiny', 'shinyjs', 'bcrypt'))

Enregistrez le code suivant sous app.R et cliquez sur le bouton "Exécuter l'application":

library(shiny)
library(shinyjs)
library(bcrypt)


# This would usually come from your user database.

# Never store passwords as clear text
password_hash <- hashpw('secret123') 

# Our not so random sessionid
# sessionid <- paste(
#   collapse = '', 
#   sample(x = c(letters, LETTERS, 0:9), size = 64, replace = TRUE)
# )
sessionid <- "OQGYIrpOvV3KnOpBSPgOhqGxz2dE5A9IpKhP6Dy2kd7xIQhLjwYzskn9mIhRAVHo" 


jsCode <- '
  shinyjs.getcookie = function(params) {
    var cookie = Cookies.get("id");
    if (typeof cookie !== "undefined") {
      Shiny.onInputChange("jscookie", cookie);
    } else {
      var cookie = "";
      Shiny.onInputChange("jscookie", cookie);
    }
  }
  shinyjs.setcookie = function(params) {
    Cookies.set("id", escape(params), { expires: 0.5 });  
    Shiny.onInputChange("jscookie", params);
  }
  shinyjs.rmcookie = function(params) {
    Cookies.remove("id");
    Shiny.onInputChange("jscookie", "");
  }
'

server <- function(input, output) {

  status <- reactiveVal(value = NULL)
  # check if a cookie is present and matching our super random sessionid  
  observe({
    js$getcookie()
    if (!is.null(input$jscookie) && 
        input$jscookie == sessionid) {
          status(paste0('in with sessionid ', input$jscookie))
    }
    else {
      status('out')
    }
  })

  observeEvent(input$login, {
    if (input$username == 'admin' & 
        checkpw(input$password, hash = password_hash)) {
      # generate a sessionid and store it in your database,
      # sessionid <- paste(
      #   collapse = '', 
      #   sample(x = c(letters, LETTERS, 0:9), size = 64, replace = TRUE)
      # )
      # but we keep it simple in this example...
      js$setcookie(sessionid)
    } else {
      status('out, cause you don\'t know the password secret123 for user admin.')
    }
  })

  observeEvent(input$logout, {
    status('out')
    js$rmcookie()
  })

  output$output <- renderText({
    paste0('You are logged ', status())}
  )
}

ui <- fluidPage(
  tags$head(
    tags$script(src = "js.cookies.js")
  ),
  useShinyjs(),
  extendShinyjs(text = jsCode),
  sidebarLayout(
    sidebarPanel(
      textInput('username', 'User', placeholder = 'admin'),
      passwordInput('password', 'Password', placeholder = 'secret123'),
      actionButton('login', 'Login'),
      actionButton('logout', 'Logout')
    ),
    mainPanel(
      verbatimTextOutput('output')
    )
  )
)

shinyApp(ui = ui, server = server)
8
Calli Gross

Eh bien, vous pouvez le faire via du code en utilisant renderUI et en changeant l'interface utilisateur à la volée. Voici un exemple de la façon de le faire:

library(shiny)
library(ggplot2)

u <- shinyUI(fluidPage(
  titlePanel("Shiny Password"),

  sidebarLayout(position = "left",
                sidebarPanel( h3("sidebar panel"),
                              uiOutput("in.pss"),
                              uiOutput("in.clr"),
                              uiOutput("in.titl"),
                              uiOutput("in.cnt"),
                              uiOutput("in.seed")

                ),
                mainPanel(h3("main panel"),
                          textOutput('echo'),
                          plotOutput('stdplot')
                )
  )
))

pok <- F

s <- shinyServer(function(input, output) 
{
  output$in.pss   <- renderUI({ input$pss; if (pok) return(NULL) else return(textInput("pss","Password:","")) })
  output$in.clr   <- renderUI({ input$pss; if (pok) return(selectInput("clr","Color:",c("red","blue"))) else return(NULL) })
  output$in.titl  <- renderUI({ input$pss; if (pok) return(textInput("titl","Title:","Data")) else return(NULL) })
  output$in.cnt   <- renderUI({ input$pss; if (pok) return(sliderInput("cnt","Count:",100,1000,500,5)) else return(NULL) })
  output$in.seed  <- renderUI({ input$pss; if (pok) return(numericInput("seed","Seed:",1234,1,10000,1)) else return(NULL) })
  histdata <- reactive(
    {
      input$pss;
      validate(need(input$cnt,"Need count"),need(input$seed,"Need seed"))
      set.seed(input$seed)
      df <- data.frame(x=rnorm(input$cnt))
    }
  )
  observe({
     if (!pok) {
       password <- input$pss
       if (!is.null(password) && password == "pass") {
         pok <<- TRUE
       }
     }
   }
  )
  output$echo = renderText(
    {
      if (pok) {
        s <- sprintf("the %s is %s and has %d rows and uses the %d seed",
           input$ent,input$clr,nrow(histdata()),input$seed)
      } else {
        s <- ""
      }
      return(s)
    }
  )
  output$stdplot = renderPlot(
    {
      input$pss
      if (pok) {
        return(qplot(data = histdata(),x,fill = I(input$clr),binwidth = 0.2,main=input$titl))
      } else {
        return(NULL)
      }
    }
  )
}
)
shinyApp(ui=u,server=s)

Les rendements

ceci à la connexion:

 enter image description here

Et ceci une fois que vous avez entré le mot de passe codé en dur "pass".

 enter image description here

Bien sûr, la programmation de cette manière est un peu délicate, mais vous pouvez utiliser des tabulations et les masquer en utilisant une logique similaire. 

Ou si vous utilisez shinyServer, vous pouvez probablement mettre un filtre devant le site. Mais c’est comme ça que je l’approcherais dans Shiny.

6
Mike Wise

Vous pouvez ajouter un proxy d'authentification en amont de votre application Shiny, comme suit: https://www.datascienceriot.com/add-authentication-to-shiny-server-with-nginx/kris/

Il s'agit d'une configuration squelette de Nginx qui redirige du port HTTPS 443 vers votre serveur Shiny s'exécutant sur le port 8000.

server {
    listen       443;
    server_name  shinyservername;

    ssl                  on;
    ssl_certificate      ...
    ssl_certificate_key  ...
    ssl_dhparam ...

    location / {
        proxy_pass http://yourdestinationIP:8000;
        proxy_set_header        X-Forwarded-Proto $scheme;
        add_header              Front-End-Https   on;
        proxy_set_header        Accept-Encoding   "";
        proxy_set_header        Host            $Host;
        proxy_set_header        X-Real-IP       $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/htpasswd;
    }
}

Configurez le pare-feu de votre hôte pour qu'il ouvre le port 443 et n'autorise que les connexions localhost au serveur Shiny sur le port 8000: 

iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp -s localhost --dport 8000 -j ACCEPT
iptables -A INPUT -p tcp --dport 8000 -j DROP

Ajoutez des informations d'identification statiques pour un ou plusieurs utilisateurs dans /etc/nginx/htpasswd:

htpasswd –c /etc/nginx/htpasswd myshinyuser

Un des inconvénients (sur de nombreux autres) est que cela authentifiera et autorisera, mais ne transmettra pas les informations d'utilisateur authentifiées à votre application. Pour cela, vous aurez besoin de l’intégration d’authentification de Shiny Server Pro qui vous transmet l’utilisateur de la session.

3
Christopher Gentle

J'ai récemment écrit un package R qui fournit des modules de connexion/déconnexion que vous pouvez intégrer à n'importe quel framework d'interface utilisateur de bootstrap.

Blogpost avec exemple d'utilisation de shinydashboard

Repo de colis

le répertoire inst/ dans le référentiel de package contient le code de l'exemple d'application.

3
Paul Campbell

J'utilise shinyAppLogin au lieu de shinApp:

# R code
shinyAppLogin <- function( ui, server, title="Restricted Area", accounts = list(admin="admin"), ...) {
    ui_with_login <- bootstrapPage(theme = "login_style.css",
        div(class="login-page",
            div(class="form",
                h1(title), br(),
                tags$form(class="login-form",
                    textInput(inputId = "user", label = NULL, placeholder="Username"),
                    passwordInput(inputId = "pass", label = "", placeholder = "Password" ),
                    actionButton(inputId = "login", label = "Login")
            ) ) ) )

    server_with_login <- function(input, output, session) {

        observeEvent(input$login, ignoreNULL = T, {

        if ( input$user %in% names(accounts) && input$pass == accounts[[input$user]] ) {

            removeUI(selector = "body", multiple = T, immediate = T, session = session)
            insertUI(selector = "html", where = "beforeEnd", ui = ui, immediate = T, session = session )
            server(session$input, session$output, session = session)
        }
    } ) }

    shinyApp(ui = ui_with_login, server = server_with_login, ...)
}

alors mon code devient: shinyAppLogin (my_ui, my_server)

Ecran de connexion lorsque styles

puis j’ai utilisé les fichiers css de entrez la description du lien ici , enregistrez simplement votre fichier css dans www/login_style.css

1
Battmanux