Skip to content

Commit 3f1b0aa

Browse files
authored
Merge pull request #848 from r-lib/feature/ppm-sso
PPM SSO
2 parents 142d450 + b74b223 commit 3f1b0aa

193 files changed

Lines changed: 41303 additions & 339 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.Rbuildignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
^src/library/curl/tools/option_table[.]txt$
4545
^src/library/jsonlite/src/yajl/libstatyajl[.]a$
4646
^src/library/pkgdepends/man/macros/eval2[.]Rd$
47+
^src/library/ts/man/macros/eval2[.]Rd$
48+
^src/library/tstoml/man/macros/eval2[.]Rd$
4749
^src/library/processx/src/supervisor/supervisor$
4850
^src/library/processx/src/tools/px$
4951
^src/library/processx/src/tools/sock$

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
/src/library/curl/tools/option_table.txt
3232
/src/library/jsonlite/src/yajl/libstatyajl.a
3333
/src/library/pkgdepends/man/macros/eval2.Rd
34+
/src/library/ts/man/macros/eval2.Rd
35+
/src/library/tstoml/man/macros/eval2.Rd
3436
/src/library/processx/src/supervisor/supervisor
3537
/src/library/processx/src/supervisor/supervisor.dSYM/
3638
/src/library/processx/src/tools/px

DESCRIPTION

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,13 @@ Config/needs/dependencies:
113113
keyring,
114114
lpSolve,
115115
pkgbuild,
116-
pkgcache,
117-
pkgdepends,
116+
r-lib/pkgcache@feature/ppm-sso,
117+
r-lib/pkgdepends,
118118
pkgsearch,
119119
processx,
120120
ps,
121+
r-lib/ts,
122+
gaborcsardi/tstoml,
121123
yaml
122124
Config/Needs/website:
123125
r-lib/asciicast,

NAMESPACE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ export(ppm_platforms)
5858
export(ppm_r_versions)
5959
export(ppm_repo_url)
6060
export(ppm_snapshots)
61+
export(ppm_sso_login)
62+
export(ppm_sso_logout)
63+
export(ppm_sso_status)
6164
export(repo_add)
6265
export(repo_auth)
6366
export(repo_auth_key_get)

R/auth.R

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#'
1010
#' ```{r child = "man/chunks/auth.Rmd"}
1111
#' ```
12+
#'
13+
#' @seealso [repo_auth()], [ppm_sso_login()].
1214
NULL
1315

1416
#' Query or set repository password in the system credential store

R/embed.R

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,15 @@ embed <- local({
225225
)
226226

227227
lib <- lib_dir()
228+
229+
if (grepl("@", pkg)) {
230+
pkgsplit <- strsplit(pkg, "@")[[1]]
231+
pkg <- pkgsplit[1]
232+
ref <- pkgsplit[2]
233+
} else {
234+
ref <- "main"
235+
}
236+
228237
pkg_name <- sub("^.*/", "", pkg)
229238
if (mode == "add") {
230239
if (file.exists(file.path(lib, pkg_name))) {
@@ -236,8 +245,9 @@ embed <- local({
236245
on.exit(unlink(tmp, recursive = TRUE), add = TRUE)
237246
if (grepl("/", pkg)) {
238247
url <- sprintf(
239-
"https://github.com/%s/archive/refs/heads/main.tar.gz",
240-
pkg
248+
"https://github.com/%s/archive/refs/heads/%s.tar.gz",
249+
pkg,
250+
ref
241251
)
242252
path1 <- file.path(tmp, paste0(pkg_name, ".tar.gz"))
243253
download.file(url, path1)
@@ -303,6 +313,7 @@ embed <- local({
303313
rimraf(file.path(lib, pkg, "inst", "CITATION"))
304314
rimraf(file.path(lib, pkg, "MD5"))
305315
rimraf(file.path(lib, pkg, "README.md"))
316+
rimraf(file.path(lib, pkg, "inst", "tsdocs"))
306317
}
307318
}
308319

R/onload.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pkg_data <- new.env(parent = emptyenv())
1919
# We don't use the env vars that cli supports, on purpose, because
2020
# they are inherited in the subprocess of the subprocess
2121
options(
22+
pak.is_worker = TRUE,
2223
cli.num_colors = as.numeric(Sys.getenv("R_PKG_NUM_COLORS", "1")),
2324
rlib_interactive = (Sys.getenv("R_PKG_INTERACTIVE") == "TRUE"),
2425
cli.dynamic = (Sys.getenv("R_PKG_DYNAMIC_TTY") == "TRUE")

R/ppm-sso.R

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#' Posit Package Manager single sign-on (SSO) authentication
2+
#'
3+
#' @details
4+
#' ## Set up SSO authentication:
5+
#' - Set the `PACKAGEMANAGER_ADDRESS` environment variable to the URL of
6+
#' your RStudio Package Manager instance. For example, add this line to
7+
#' your `.Renviron` file:
8+
#' ```
9+
#' PACKAGEMANAGER_ADDRESS=https://<ppm-url>
10+
#' ```
11+
#' Alternatively, you can also set it in your shell profile on Unix,
12+
#' or in the System or User environment variables on Windows.
13+
#' - Set `options(repos)` to include a repository from your Package Manager
14+
#' instance. Include `__token__` as the username in the URL. For example:
15+
#' ```
16+
#' options(repos = c(
17+
#' PPM = "https://__token__@<ppm-url>/<repo-path>",
18+
#' getOption("repos")
19+
#' ))
20+
#' ```
21+
#' You probably want to add this to your `.Rprofile` file, so that it is
22+
#' set in every R session.
23+
#' - Call [repo_get()] to trigger authentication and caching of the token.
24+
#' You should be prompted to log in via your browser, and the obtained
25+
#' token will be cached for future use. Call `ppm_sso_status()` to check
26+
#' the status of your authentication, including the path of the cached
27+
#' token and its expiration time.
28+
#' - Alternatively, you can call `ppm_sso_login()` directly to trigger
29+
#' the login process directly.
30+
#'
31+
#' `ppm_sso_login()` initiates the SSO login process. You should be
32+
#' prompted to log in via your browser, and the obtained token will be
33+
#' cached for future use.
34+
#'
35+
#' @return `ppm_sso_login()` returns the obtained token invisibly.
36+
#'
37+
#' @seealso [Authenticated repositories],
38+
#' <https://docs.posit.co/rspm/admin/authentication/>
39+
#' @export
40+
#' @examplesIf FALSE
41+
#' Sys.setenv(PACKAGEMANAGER_ADDRESS = "https://<ppm-url>")
42+
#' options(repos = c(
43+
#' PPM = "https://__token__@<ppm-url>/<repo-path>",
44+
#' getOption("repos")
45+
#' ))
46+
#' ppm_sso_login()
47+
#' ppm_sso_status()
48+
#' ppm_sso_status(connect = TRUE)
49+
#' ppm_sso_logout()
50+
51+
ppm_sso_login <- function() {
52+
res <- remote(
53+
function() {
54+
asNamespace("pkgcache")$ppm_sso_login()
55+
},
56+
list()
57+
)
58+
invisible(res)
59+
}
60+
61+
#' @rdname ppm_sso_login
62+
#' @details
63+
#' `ppm_sso_logout()` removes the cached token, effectively logging you
64+
#' out. If there is no cached token, it does nothing.
65+
#' @return `ppm_sso_logout()` does not return anything.
66+
#' @export
67+
68+
ppm_sso_logout <- function() {
69+
res <- remote(
70+
function() {
71+
asNamespace("pkgcache")$ppm_sso_logout()
72+
},
73+
list()
74+
)
75+
invisible(res)
76+
}
77+
78+
#' @rdname ppm_sso_login
79+
#' @param connect If `TRUE`, also checks if the token is valid by making a test
80+
#' request to the Package Manager instance. This requires an active internet
81+
#' connection and may take a few seconds. If `FALSE`, only checks if a
82+
#' token is cached and not expired.
83+
#' @details
84+
#' `ppm_sso_status()` checks the status of your authentication, including
85+
#' the path of the cached token and its expiration time.
86+
#' @return `ppm_sso_status()` returns a list with the following components:
87+
#' - `ppm_url`: The URL of the Package Manager instance.
88+
#' - `token_file`: The path of the cached token file.
89+
#' - `token`: The cached token (partially masked for display) or `NA` if
90+
#' no token is found locally.
91+
#' - `valid`: `TRUE` if the token is valid (only if `connect = TRUE`),
92+
#' `FALSE` if invalid, or `NA` if not checked.
93+
#' - `issuer`: The issuer of the token, or `NA` if not available.
94+
#' - `subject`: The subject of the token, or `NA` if not available.
95+
#' - `audience`: The audience of the token, or `NA` if not available.
96+
#' - `issued_at`: The issue time of the token as a POSIXct object, or `NA`
97+
#' if not available.
98+
#' - `expires_at`: The expiration time of the token as a POSIXct object,
99+
#' or `NA` if not available.
100+
#' - `expired`: `TRUE` if the token is expired, `FALSE` if not expired,
101+
#' or `NA` if expiration time is not available.
102+
#' - `expires_in`: The time until expiration as a difftime object, or
103+
#' `NA` if expiration time is not available or the token is already
104+
#' expired.
105+
#' @export
106+
ppm_sso_status <- function(connect = FALSE) {
107+
remote(
108+
function(connect) {
109+
ret <- asNamespace("pkgcache")$ppm_sso_status(connect)
110+
asNamespace("pak")$pak_preformat(ret)
111+
},
112+
list(connect)
113+
)
114+
}

R/subprocess.R

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,12 @@ remote <- function(func, args = list()) {
7474
opts <- options()
7575
extraopts <- c("Ncpus", "BioC_mirror")
7676
pkg_options <- opts[
77-
grepl("^pkg[.]", names(opts)) | grepl("^async_http_", names(opts)) | names(opts) %in% extraopts
77+
grepl("^pkg[.]", names(opts)) |
78+
grepl("^async_http_", names(opts)) |
79+
names(opts) %in% extraopts
7880
]
7981
envs <- Sys.getenv()
80-
extraenvs <- c("R_BIOC_VERSION", "PATH")
82+
extraenvs <- c("R_BIOC_VERSION", "PATH", "PACKAGEMANAGER_ADDRESS")
8183
if (any(grepl("@", subst_args[["__repos__"]]))) {
8284
extraenvs <- c(extraenvs, envs[grep("^https?://", names(envs))])
8385
}

_pkgdown.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ reference:
9393
- ppm_r_versions
9494
- ppm_repo_url
9595
- ppm_snapshots
96+
- ppm_sso_login
97+
- ppm_sso_logout
98+
- ppm_sso_status
9699
- repo_add
97100
- repo_auth
98101
- repo_auth_key_get

0 commit comments

Comments
 (0)