Skip to content

Commit

Permalink
Merge pull request #38 from inbo/20-pass-credentials-via-sys-env
Browse files Browse the repository at this point in the history
my attempt at allowing storage of password in Sys env
  • Loading branch information
SanderDevisscher authored Apr 4, 2024
2 parents a501cb1 + f504bd3 commit d1afc38
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 10 deletions.
5 changes: 3 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: iassetR
Title: Use the iAsset API in R
Version: 0.0.0.9000
Version: 0.0.1.9000
Authors@R: c(
person("Pieter", "Huybrechts", , "[email protected]", role = "aut",
comment = c(ORCID = "0000-0002-6658-6062")),
Expand All @@ -19,12 +19,13 @@ Imports:
glue,
httr2,
janitor,
keyring,
magrittr,
openssl,
purrr,
rlang,
stringr
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
RoxygenNote: 7.3.1
BugReports: https://github.com/inbo/iassetR/issues
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export("%>%")
export(get_access_token)
export(get_fields)
export(get_records)
export(get_username)
importFrom(magrittr,"%>%")
importFrom(rlang,.data)
70 changes: 64 additions & 6 deletions R/get_access_token.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,88 @@
#' @return Invisibily, an access token upon succes
#' @export
#'
#' @examples \dontrun{get_access_token("my_username")}
#' @details
#' This function uses keyring to retrieve the password. If no password has been
#' set using keyring you'll be prompted to enter your password using askpass.
#' This password - username combination will then be stored in the system
#' credential store.
#' Keyring uses secret environment variables on GitHub Actions.
#'
#' @examples \dontrun{
#' get_access_token("my_username")
#' }
get_access_token <-
function(username, quiet = FALSE) {
function(username = get_username(), quiet = FALSE) {
# check input params
assertthat::assert_that(assertthat::is.string(username))

assertthat::assert_that(assertthat::is.flag(quiet))

# check for keyring support
assertthat::assert_that(keyring::has_keyring_support())

# check if a keyring exists
iasset_keyring_exists <-
"iasset_password" %in% dplyr::pull(keyring::key_list(), "service")

## check if a username is set
iasset_username_missing <- !rlang::is_string(get_username())

# check that only one keyring is set
number_of_keyrings <- nrow(keyring::key_list(service = "iasset_password"))
assertthat::assert_that(number_of_keyrings <= 1,
msg = paste(
"iassetR currently only supports storing one iAsset account at a time.",
"Delete any other accounts using",
'keyring::key_list(service = "iasset_password")$username',
"to get a list of linked usernames &",
'keyring::key_delete(service = "iasset_password",',
'username = "username_to_delete")',
"to delete the unneeded username"
)
)

# prompt user for credentials if password or username is missing
if(!iasset_keyring_exists | iasset_username_missing){
message(
paste(
"iasset credentials are missing, please enter your credentials or",
"if you don't have any contact your domain admin."
)
)
keyring::key_set(
service = "iasset_password",
username =
askpass::askpass(prompt = "Please enter your iasset username: "),
prompt = "Please enter your iasset password: "
)
}

# fetch the username, we'll fetch the password in line to avoid storing it
username <- get_username()

# check that the fetched username is a string
assertthat::assert_that(assertthat::is.string(username))

# build a request and perform it
login_request <-
httr2::request(base_url = "https://api.iasset.nl/login/")
hash <- askpass::askpass() %>%
openssl::md5()

login_response <- login_request %>%
httr2::req_body_form(
username = username,
password = hash,
password = openssl::md5(keyring::key_get("iasset_password",
username = username)),
domain = "riparias",
version = "9.7"
) %>%
httr2::req_perform() %>%
httr2::resp_body_json(check_type = FALSE)

# print success
if (!quiet) {
message(login_response$returndata[[1]]$success_msg)
}

# return access token
invisible(purrr::chuck(login_response, "returndata", 1, "access_token"))
}
25 changes: 25 additions & 0 deletions R/get_username.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#' Get the username associated with the iAsset credentials in the system credentials store.
#'
#' @param service Character. The keyring service to fetch the stored username for.
#'
#' @return A character vector of stored usernames for the service. NA if none.
#'
#' @export
#'
#' @examples
#' \dontrun{get_username("iasset_password")}
get_username <- function(service = "iasset_password") {
# check input params
assertthat::assert_that(assertthat::is.string(service))

# check that the system supports keyring
assertthat::assert_that(keyring::has_keyring_support())

# check that the queried service is in the keyring
assertthat::validate_that(service %in% keyring::key_list()$service,
msg = glue::glue("{service} not found in keyring")
)

# fetch the associated usernames
keyring::key_list(service = service)$username
}
13 changes: 11 additions & 2 deletions man/get_access_token.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions man/get_username.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d1afc38

Please sign in to comment.