r-lib / keyring

:closed_lock_with_key: Access the system credential store from R

Home Page:https://keyring.r-lib.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Knitr calls askForPassword() when running askForSecret()

nwstephens opened this issue · comments

For some reason hitting the knitr button in the RStudio IDE unexpectedly prompts the user for a password instead of a secret. Steps to reproduce:

  1. Create a simple R Markdown document
  2. Insert rstudioapi::askForSecret("test") into a code chunk
  3. Press the knitr button

Result: The IDE prompts for a password instead of a secrete. See here for a video.

I would expect the knit button to ask for a secret, not a password.

Note: Running the code chunk or rendering the code chunk with rmarkdown::render() still works. Only the knit button delivers a different experience.

I believe this is due to rstudioapi::askForSecret iself

rstudioapi::askForSecret <- function (name, message = paste(name, ":", sep = ""), title = paste(name, "Secret")) {
    if (hasFun("askForSecret")) {
        callFun("askForSecret", name, title, message)
    }
    else {
        askForPassword(message)
    }
}

As seen above, askForPassword is the fallback when hasFun("askForSecret") is FALSE.

And the latter is TRUE when use interactively in RStudio, which is why it works when you click the green arrow run chunk button, but FALSE in a non interactive R session, which is what happens when you click the Knit button

# in a non interactive session
callr::r(function() rstudioapi:::hasFun("askForSecret"))
#> [1] FALSE
# inside an interactive session
rstudioapi:::hasFun("askForSecret")
#> [1] TRUE

I don't think it a keyring or rmardown issue currently.

Thanks, @cderv . Any ideas why callr::r(function() rstudioapi:::hasFun("askForSecret")) returns FALSE? It would be better in this case if it returned TRUE.

You cannot use rstudioapi from a subprocess.

@nwstephens, as said just ☝️ , yes with callr, it returns FALSE because it is a subprocess and rstudioapi won't work because RStudio is not running. So it is expected that hasFun() returns FALSE as its first check will be rstudioapi::isAvailable(). You'll get the same with rstudioapi::askForPassword() if run explicitly in a subprocess

> callr::r(function() rstudioapi::askForPassword())
Error: callr subprocess failed: RStudio not running

I am just surprised that in you video, this works when Knitting a Rmd document in the IDE because the IDE will call rmarkdown::render() in a subprocess. So I had a closer look at the source of function in rstudioapi

So it seems rstudioapi has a special handling for child processes of RStudio IDE (when Knitting or when running Jobs maybe - I don't really know) and that is why rstudioapi::askForPassword() is working
See rstudioapi::callFun which is handling the call to all RStudio function: https://github.com/rstudio/rstudioapi/blob/8ed4d50a0a471737337c503069857fb217661cb1/R/code.R#L92-L95. It will do a special thing when child process of RStudio is detected.

So this would work as you expect in a Rmd file knitted in the IDE:

---
title: "rstudioapi in Rmd"
output: html_document
---

This will be TRUE when rendered with Knit button and FALSE if outside of RStudio


```{r}
rstudioapi:::isChildProcess()
```

but rstudio is not available

```{r}
rstudioapi::isAvailable()
```

unless explicitly asking for child process handling

```{r}
rstudioapi::isAvailable(child_ok = TRUE)
```

`askForSecret` would work as `askForPassword` if call this way

```{r}
rstudioapi::callFun("askForSecret", "test", "test:", "test Secret")
```


But this seems a bit hacky as this document would error is not knitted in RStudio IDE but in another interactive session.

but when you call rstudioapi::askForSecret(), it does not behave this way because of check with rstudioapi::hasFun()

Anyway, I think this is something to discuss with the IDE team about this usage and rstudioapi behavior.

This was the response from Kevin Ushey

This is because rstudioapi functions are only available in the RStudio main process, or in certain RStudio jobs. I don't think they're visible to the R process used for knit.

Which leads me to ask, "What is the intended behavior of askForSecret with knit -- throw an error?"

I don't think they're visible to the R process used for knit.

It seems that rstudioapi::askForPassword() is seen by R process used for knit. From your answer @kevinushey, I understand that this may not be expected ? As explain above, it seems callFun is working when knitting inside RStudio because it is seen as a child process of RStudio.

Which leads me to ask, "What is the intended behavior of askForSecret with knit -- throw an error?"

This is a discussion we should have in rstudioapi repo I guess: Are functions from rstudioapi supposed to work within an R Markdown document knitted in the IDE ? Maybe this discussion should move there ? (not sure it is possible between organization to move issues though)

I tend to say this is not a good idea as it will not be portable. The document would error if any function from rstudioapi is used in the document, but you render it outside of RStudio. If you render a document like in a background jobs also I am not sure this is supposed to open a user interface to enter a password.

However, I see the need for a window opening to provide secret informations. FWIW there is another feature I see that works already with Rmd only when inside RStudio which offers a user interface: it is parameterized report: https://bookdown.org/yihui/rmarkdown/params-knit.html#the-interactive-user-interface
Using a connection to DB inside a Rmd document interactively will also open the connection pane I believe.

Looking at this a bit more closely (with @cderv's helpful output) this is indeed an issue with the way that rstudioapi::askForSecret is implemented. It should work for documented run via Knit, but doesn't because hasFun() reports that the function doesn't exist.

I'll see if I can fix this -- I think this is ultimately an rstudioapi issue.

Should now be fixed -- install the development version of rstudioapi with remotes::install_github("rstudio/rstudioapi"). I see e.g.:

Screen Shot 2021-04-22 at 11 55 21 AM

Thanks @kevinushey. I verified that this issue has been resolved with implemented changes to the rstudioapi package.