This commit is contained in:
hong-revo 2019-01-15 00:37:39 +11:00
Родитель 8c10eab8e8
Коммит a736649248
2 изменённых файлов: 64 добавлений и 24 удалений

Просмотреть файл

@ -7,6 +7,7 @@
#' - `refresh`: Refreshes the token. For expired Azure tokens using client credentials, refreshing really means requesting a new token. #' - `refresh`: Refreshes the token. For expired Azure tokens using client credentials, refreshing really means requesting a new token.
#' - `validate`: Checks if the token is still valid. For Azure tokens using client credentials, this just checks if the current time is less than the token's expiry time. #' - `validate`: Checks if the token is still valid. For Azure tokens using client credentials, this just checks if the current time is less than the token's expiry time.
#' - `hash`: Computes an MD5 hash on selected fields of the token. Used internally for identification purposes when caching. #' - `hash`: Computes an MD5 hash on selected fields of the token. Used internally for identification purposes when caching.
#' - `cache`: Stores the token on disk for use in future sessions.
#' #'
#' @seealso #' @seealso
#' [get_azure_token], [httr::Token] #' [get_azure_token], [httr::Token]
@ -239,7 +240,7 @@ private=list(
#' Similarly, since the authorization_code method opens a browser to load the AAD authorization page, your machine must have an Internet browser installed that can be run from inside R. In particular, if you are using a Linux [Data Science Virtual Machine](https://azure.microsoft.com/en-us/services/virtual-machines/data-science-virtual-machines/) in Azure, you may run into difficulties; use one of the other methods instead. #' Similarly, since the authorization_code method opens a browser to load the AAD authorization page, your machine must have an Internet browser installed that can be run from inside R. In particular, if you are using a Linux [Data Science Virtual Machine](https://azure.microsoft.com/en-us/services/virtual-machines/data-science-virtual-machines/) in Azure, you may run into difficulties; use one of the other methods instead.
#' #'
#' @section Caching: #' @section Caching:
#' AzureRMR differs from httr in its handling of token caching in a number of ways. First, caching is based on all the inputs to `get_azure_token` as listed above. Second, it defines its own directory for cached tokens, using the rappdirs package. On recent Windows versions, this will usually be in the location `C:\\Users\\(username)\\AppData\\Local\\AzureR\\AzureRMR`. On Linux, it will be in `~/.config/AzureRMR`, and on MacOS, it will be in `~/Library/Application Support/AzureRMR`. Note that a single directory is used for all tokens, and the working directory is not touched (which eliminates the risk of accidentally introducing cached tokens into source control). #' AzureRMR differs from httr in its handling of token caching in a number of ways. First, caching is based on all the inputs to `get_azure_token` as listed above. Second, it defines its own directory for cached tokens, using the rappdirs package. On recent Windows versions, this will usually be in the location `C:\\Users\\(username)\\AppData\\Local\\AzureR\\AzureRMR`. On Linux, it will be in `~/.config/AzureRMR`, and on MacOS, it will be in `~/Library/Application Support/AzureRMR`. Note that a single directory is used for all tokens, and the working directory is not touched (which significantly lessens the risk of accidentally introducing cached tokens into source control).
#' #'
#' To list all cached tokens on disk, use `list_azure_tokens`. This returns a list of token objects, named according to their MD5 hashes. #' To list all cached tokens on disk, use `list_azure_tokens`. This returns a list of token objects, named according to their MD5 hashes.
#' #'
@ -424,7 +425,7 @@ delete_azure_token <- function(resource_host, tenant, app, password=NULL, userna
endp <- httr::oauth_endpoint(base_url=base_url, endp <- httr::oauth_endpoint(base_url=base_url,
authorize="oauth2/authorize", authorize="oauth2/authorize",
access=if(use_device) "oauth2/devicecode" else NULL) access=if(use_device) "oauth2/devicecode" else "oauth2/token")
app <- httr::oauth_app("azure", app, app <- httr::oauth_app("azure", app,
secret=if(client_credentials) password else NULL, secret=if(client_credentials) password else NULL,
redirect_uri=if(client_credentials) NULL else httr::oauth_callback()) redirect_uri=if(client_credentials) NULL else httr::oauth_callback())

Просмотреть файл

@ -1,14 +1,5 @@
context("AzureToken") context("AzureToken")
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
if(tenant == "" || app == "" || password == "" || subscription == "")
skip("Authentication tests skipped: ARM credentials not set")
test_that("normalize_tenant, normalize_guid work", test_that("normalize_tenant, normalize_guid work",
{ {
guid <- "abcdefab-1234-5678-9012-abcdefabcdef" guid <- "abcdefab-1234-5678-9012-abcdefabcdef"
@ -22,6 +13,7 @@ test_that("normalize_tenant, normalize_guid work",
# improperly formatted GUID will be treated as a name # improperly formatted GUID will be treated as a name
guid5 <- paste0("(", guid) guid5 <- paste0("(", guid)
expect_false(is_guid(guid5))
expect_identical(normalize_tenant(guid5), paste0(guid5, ".onmicrosoft.com")) expect_identical(normalize_tenant(guid5), paste0(guid5, ".onmicrosoft.com"))
expect_identical(normalize_tenant("common"), "common") expect_identical(normalize_tenant("common"), "common")
@ -29,24 +21,71 @@ test_that("normalize_tenant, normalize_guid work",
expect_identical(normalize_tenant("mytenant.com"), "mytenant.com") expect_identical(normalize_tenant("mytenant.com"), "mytenant.com")
}) })
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
app <- Sys.getenv("AZ_TEST_APP_ID")
password <- Sys.getenv("AZ_TEST_PASSWORD")
subscription <- Sys.getenv("AZ_TEST_SUBSCRIPTION")
native_app <- Sys.getenv("AZ_TEST_NATIVE_APP_ID")
if(tenant == "" || app == "" || password == "" || subscription == "" || native_app == "")
skip("Authentication tests skipped: ARM credentials not set")
if(system.file(package="httpuv") == "")
skip("Authentication tests skipped: httpuv must be installed")
# not a perfect test: will fail to detect Linux DSVM issue
if(!interactive())
skip("Authentication tests skipped: must be an interactive session")
test_that("Authentication works", test_that("Authentication works",
{ {
suppressWarnings(file.remove(dir(AzureRMR:::config_dir(), full.names=TRUE))) suppressWarnings(file.remove(dir(AzureRMR:::config_dir(), full.names=TRUE)))
token <- get_azure_token("https://management.azure.com/", tenant, app, password) res <- "https://management.azure.com/"
expect_true(is_azure_token(token))
toklist <- list_azure_tokens() # obtain new tokens
hash <- AzureRMR:::token_hash( aut_tok <- get_azure_token(res, tenant, native_app, auth_type="authorization_code")
"https://management.azure.com/", tenant, app, password, username=NULL, expect_true(is_azure_token(aut_tok))
auth_type="client_credentials", expect_identical(aut_tok$hash(), "b29ef592fa435a4fd92672daf8726bae")
aad_host="https://login.microsoftonline.com/")
expect_true(length(toklist) > 0)
expect_true(hash %in% names(toklist))
expect_true(is_azure_token(token$refresh())) ccd_tok <- get_azure_token(res, tenant, app, password=password)
expect_null(delete_azure_token("https://management.azure.com/", tenant, app, password, confirm=FALSE)) expect_true(is_azure_token(ccd_tok))
expect_identical(ccd_tok$hash(), "c75c266d9c578af29e24d3f22013ebf6")
token <- get_azure_token("https://management.azure.com/", tenant, app, password) dev_tok <- get_azure_token(res, tenant, native_app, auth_type="device_code")
expect_null(delete_azure_token(hash=hash, confirm=FALSE)) expect_true(is_azure_token(dev_tok))
expect_identical(dev_tok$hash(), "37cbd9fec7c15b5a47edc1ea6f2f2747")
aut_expire <- as.numeric(aut_tok$credentials$expires_on)
ccd_expire <- as.numeric(ccd_tok$credentials$expires_on)
dev_expire <- as.numeric(dev_tok$credentials$expires_on)
Sys.sleep(5)
# refresh/reauthenticate
aut_tok$refresh()
ccd_tok$refresh()
dev_tok$refresh()
expect_true(as.numeric(aut_tok$credentials$expires_on) > aut_expire)
expect_true(as.numeric(ccd_tok$credentials$expires_on) > ccd_expire)
expect_true(as.numeric(dev_tok$credentials$expires_on) > dev_expire)
# load cached tokens: should not get repeated login prompts/screens
aut_tok2 <- get_azure_token(res, tenant, native_app, auth_type="authorization_code")
expect_true(is_azure_token(aut_tok2))
expect_identical(aut_tok2$hash(), "b29ef592fa435a4fd92672daf8726bae")
ccd_tok2 <- get_azure_token(res, tenant, app, password=password)
expect_true(is_azure_token(ccd_tok2))
expect_identical(ccd_tok2$hash(), "c75c266d9c578af29e24d3f22013ebf6")
dev_tok2 <- get_azure_token(res, tenant, native_app, auth_type="device_code")
expect_true(is_azure_token(dev_tok2))
expect_identical(dev_tok2$hash(), "37cbd9fec7c15b5a47edc1ea6f2f2747")
expect_null(delete_azure_token(res, tenant, native_app, auth_type="authorization_code", confirm=FALSE))
expect_null(delete_azure_token(res, tenant, app, password=password, confirm=FALSE))
expect_null(delete_azure_token(res, tenant, native_app, auth_type="device_code", confirm=FALSE))
}) })