Skip to content

Commit

Permalink
chore!: Finalizes migration to tower
Browse files Browse the repository at this point in the history
  • Loading branch information
andyquinterom committed Aug 27, 2024
1 parent 493f95a commit d6fa4e8
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 258 deletions.
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Suggests:
knitr,
rmarkdown,
shiny,
tower,
testthat (>= 3.0.0)
VignetteBuilder:
knitr
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.PHONY: install

install:
Rscript -e "devtools::install()"


4 changes: 3 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Generated by roxygen2: do not edit by hand

S3method(internal_add_auth_layers,entra_id_config)
S3method(internal_add_auth_layers,google_config)
S3method(print,access_token)
export(add_auth_layers)
export(expires_at)
export(expires_in)
export(get_token_field)
Expand All @@ -10,6 +13,5 @@ export(new_auth0_config)
export(new_entra_id_config)
export(new_google_config)
export(new_openid_config)
export(sso_shiny_app)
export(token)
export(use_futures)
117 changes: 50 additions & 67 deletions R/auth0.R
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,9 @@ get_client_id.auth0_config <- function(config) {

#' @keywords internal
#' @noRd
shiny_app.auth0_config <- function(config, app) {
app_handler <- app$httpHandler
login_handler <- function(req) {

# If the user sends a POST request to /login, we'll get a code
# and exchange it for an access token. We'll then redirect the
# user to the root path, setting a cookie with the access token.
if (req$PATH_INFO == "/login") {
internal_add_auth_layers.auth0_config <- function(config, tower) {
tower |>
tower::add_get_route("/login", \(req) {
query <- shiny::parseQueryString(req$QUERY_STRING)
token <- promises::future_promise({
request_token(config, query[["code"]])
Expand Down Expand Up @@ -140,9 +135,8 @@ shiny_app.auth0_config <- function(config, app) {
}
)
)
}

if (req$PATH_INFO == "/logout") {
}) |>
tower::add_get_route("/logout", \(req) {
return(
shiny::httpResponse(
status = 302,
Expand All @@ -152,72 +146,61 @@ shiny_app.auth0_config <- function(config, app) {
)
)
)
}

# Get eh HTTP cookies from the request
cookies <- parse_cookies(req$HTTP_COOKIE)

# If the user requests the root path, we'll check if they have
# an access token. If they don't, we'll redirect them to the
# login page.
if (req$PATH_INFO == "/") {
}) |>
tower::add_http_layer(\(req) {
# Get the HTTP cookies from the request
cookies <- parse_cookies(req$HTTP_COOKIE)
req$PARSED_COOKIES <- cookies

# If the user requests the root path, we'll check if they have
# an access token. If they don't, we'll redirect them to the
# login page.
if (req$PATH_INFO == "/") {
token <- tryCatch(
expr = access_token(config, remove_bearer(cookies$access_token)),
error = function(e) {
return(NULL)
}
)
if (is.null(token)) {
return(
shiny::httpResponse(
status = 302,
headers = list(
Location = get_login_url(config)
)
)
)
}
}
}) |>
tower::add_http_layer(function(req) {
# If the user requests any other path, we'll check if they have
# an access token. If they don't, we'll return a 403 Forbidden
# response.
token <- tryCatch(
expr = access_token(config, remove_bearer(cookies$access_token)),
expr = access_token(
config,
remove_bearer(req$PARSED_COOKIES$access_token)
),
error = function(e) {
return(NULL)
}
)

if (is.null(token)) {
return(
shiny::httpResponse(
status = 302,
headers = list(
Location = get_login_url(config)
)
status = 403,
content_type = "text/plain",
content = "Forbidden"
)
)
}
}

# If the user requests any other path, we'll check if they have
# an access token. If they don't, we'll return a 403 Forbidden
# response.
token <- tryCatch(
expr = access_token(config, remove_bearer(cookies$access_token)),
error = function(e) {
return(NULL)
}
)

if (is.null(token)) {
return(
shiny::httpResponse(
status = 403,
content_type = "text/plain",
content = "Forbidden"
)
)
}

# If we have reached this point, the user has a valid access
# token and therefore we can return NULL, which will cause the
# app handler to be called.
return(NULL)
}

handlers <- list(
login_handler,
app_handler
)

app$httpHandler <- function(req) {
for (handler in handlers) {
response <- handler(req)
if (!is.null(response)) {
return(response)
}
}
}

return(app)
# If we have reached this point, the user has a valid access
# token and therefore we can return NULL, which will cause the
# app handler to be called.
return(NULL)
})
}
123 changes: 55 additions & 68 deletions R/entra_id.R
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ decode_token.entra_id_config <- function(config, token) {
jose::jwt_decode_sig(token, jwk),
error = function(e) {
NULL
},
warning = function(w) {
NULL
}
)
}) |>
Expand All @@ -102,15 +105,10 @@ get_client_id.entra_id_config <- function(config) {
config$client_id
}

#' @keywords internal
shiny_app.entra_id_config <- function(config, app) {
app_handler <- app$httpHandler
login_handler <- function(req) {

# If the user sends a POST request to /login, we'll get a code
# and exchange it for an access token. We'll then redirect the
# user to the root path, setting a cookie with the access token.
if (req$REQUEST_METHOD == "POST" && req$PATH_INFO == "/login") {
#' @export
internal_add_auth_layers.entra_id_config <- function(config, tower) {
tower |>
tower::add_post_route("/login", function(req) {
form <- shiny::parseQueryString(req[["rook.input"]]$read_lines())
token <- promises::future_promise({
request_token(config, form[["code"]])
Expand Down Expand Up @@ -138,9 +136,8 @@ shiny_app.entra_id_config <- function(config, app) {
}
)
)
}

if (req$PATH_INFO == "/logout") {
}) |>
tower::add_get_route("/logout", function(req) {
return(
shiny::httpResponse(
status = 302,
Expand All @@ -150,72 +147,62 @@ shiny_app.entra_id_config <- function(config, app) {
)
)
)
}

# Get eh HTTP cookies from the request
cookies <- parse_cookies(req$HTTP_COOKIE)

# If the user requests the root path, we'll check if they have
# an access token. If they don't, we'll redirect them to the
# login page.
if (req$PATH_INFO == "/") {
}) |>
tower::add_http_layer(function(req) {
# Get the HTTP cookies from the request
cookies <- parse_cookies(req$HTTP_COOKIE)
req$PARSED_COOKIES <- cookies

# If the user requests the root path, we'll check if they have
# an access token. If they don't, we'll redirect them to the
# login page.
if (req$PATH_INFO == "/") {
token <- tryCatch(
expr = access_token(config, remove_bearer(cookies$access_token)),
error = function(e) {
return(NULL)
}
)
print(token)
if (is.null(token)) {
return(
shiny::httpResponse(
status = 302,
headers = list(
Location = get_login_url(config)
)
)
)
}
}
}) |>
tower::add_http_layer(function(req) {
# If the user requests any other path, we'll check if they have
# an access token. If they don't, we'll return a 403 Forbidden
# response.
token <- tryCatch(
expr = access_token(config, remove_bearer(cookies$access_token)),
expr = access_token(
config,
remove_bearer(req$PARSED_COOKIES$access_token)
),
error = function(e) {
return(NULL)
}
)

if (is.null(token)) {
return(
shiny::httpResponse(
status = 302,
headers = list(
Location = get_login_url(config)
)
status = 403,
content_type = "text/plain",
content = "Forbidden"
)
)
}
}

# If the user requests any other path, we'll check if they have
# an access token. If they don't, we'll return a 403 Forbidden
# response.
token <- tryCatch(
expr = access_token(config, remove_bearer(cookies$access_token)),
error = function(e) {
return(NULL)
}
)

if (is.null(token)) {
return(
shiny::httpResponse(
status = 403,
content_type = "text/plain",
content = "Forbidden"
)
)
}

# If we have reached this point, the user has a valid access
# token and therefore we can return NULL, which will cause the
# app handler to be called.
return(NULL)
}

handlers <- list(
login_handler,
app_handler
)

app$httpHandler <- function(req) {
for (handler in handlers) {
response <- handler(req)
if (!is.null(response)) {
return(response)
}
}
}

return(app)
# If we have reached this point, the user has a valid access
# token and therefore we can return NULL, which will cause the
# app handler to be called.
return(NULL)
})
}
1 change: 0 additions & 1 deletion R/futures.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#' @title Use futures for asynchronous computations
#' @description Enable a future plan for asynchronous computations.
#' Since tapLock needs to do calls to external APIs, it can be a good idea
Expand Down
Loading

0 comments on commit d6fa4e8

Please sign in to comment.