Skip to content

Commit

Permalink
Graph login (#5)
Browse files Browse the repository at this point in the history
* create graph login with ARM login

* normalize tenant

* require dev AzureAuth

* fix

* document

* test interop

* require quietly

* catch errors when making graph login

* put graph login checks in one place

* bump required AzureAuth ver

* AzureAuth 1.2.1 on CRAN

* doc/news update
  • Loading branch information
hongooi73 authored Sep 9, 2019
1 parent 1175eaf commit 03aedd5
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 10 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ VignetteBuilder: knitr
Depends:
R (>= 3.3)
Imports:
AzureAuth (>= 1.0.1),
AzureAuth (>= 1.2.1),
utils,
httr (>= 1.3),
jsonlite,
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# AzureRMR 2.1.3.9000

- If the AzureGraph package is installed and the `graph_host` argument is not `NULL`, `create_azure_login` will also create a login client for Microsoft Graph with the same credentials. This is to facilitate working with registered apps and service principals, eg when managing roles and permissions. Some Azure services also require creating service principals as part of creating a resource (eg Azure Kubernetes Service), and keeping the Graph credentials consistent with ARM helps ensure nothing breaks.
- Fix a bug where `create_azure_login` still required the `tenant` argument when a token was supplied.
- Fixes to allow use of Azure Active Directory v2.0 tokens for authenticating. Note that AAD v1.0 is still the default and recommended version.

Expand Down
12 changes: 9 additions & 3 deletions R/az_login.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
#' @param host Your ARM host. Defaults to `https://management.azure.com/`. Change this if you are using a government or private cloud.
#' @param aad_host Azure Active Directory host for authentication. Defaults to `https://login.microsoftonline.com/`. Change this if you are using a government or private cloud.
#' @param config_file Optionally, a JSON file containing any of the arguments listed above. Arguments supplied in this file take priority over those supplied on the command line. You can also use the output from the Azure CLI `az ad sp create-for-rbac` command.
#' @param token Optionally, an OAuth 2.0 token, of class [AzureToken]. This allows you to reuse the authentication details for an existing session. If supplied, all other arguments to `create_azure_login` will be ignored.
#' @param token Optionally, an OAuth 2.0 token, of class [AzureToken]. This allows you to reuse the authentication details for an existing session. If supplied, the other arguments above to `create_azure_login` will be ignored.
#' @param graph_host The Microsoft Graph endpoint. See 'Microsoft Graph integration' below.
#' @param refresh For `get_azure_login`, whether to refresh the authentication token on loading the client.
#' @param selection For `get_azure_login`, if you have multiple logins for a given tenant, which one to use. This can be a number, or the input MD5 hash of the token used for the login. If not supplied, `get_azure_login` will print a menu and ask you to choose a login.
#' @param confirm For `delete_azure_login`, whether to ask for confirmation before deleting.
Expand All @@ -24,6 +25,9 @@
#'
#' One difference between `create_azure_login` and `get_azure_login` is the former will delete any previously saved credentials that match the arguments it was given. You can use this to force AzureRMR to remove obsolete tokens that may be lying around.
#'
#' @section Microsoft Graph integration:
#' If the AzureGraph package is installed and the `graph_host` argument is not `NULL`, `create_azure_login` will also create a login client for Microsoft Graph with the same credentials. This is to facilitate working with registered apps and service principals, eg when managing roles and permissions. Some Azure services also require creating service principals as part of creating a resource (eg Azure Kubernetes Service), and keeping the Graph credentials consistent with ARM helps ensure nothing breaks.
#'
#' @section Linux DSVM note:
#' 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 have problems running `create_azure_login()` (ie, without any arguments). In this case, try `create_azure_login(auth_type="device_code")`.
#'
Expand All @@ -33,7 +37,7 @@
#' If the AzureRMR data directory for saving credentials does not exist, `get_azure_login` will throw an error.
#'
#' @seealso
#' [az_rm], [AzureAuth::get_azure_token] for more details on authentication methods
#' [az_rm], [AzureAuth::get_azure_token] for more details on authentication methods, [AzureGraph::create_graph_login] for the corresponding function to create a Microsoft Graph login client
#'
#' [Azure Resource Manager overview](https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview),
#' [REST API reference](https://docs.microsoft.com/en-us/rest/api/resources/)
Expand Down Expand Up @@ -64,7 +68,7 @@
create_azure_login <- function(tenant="common", app=.az_cli_app_id,
password=NULL, username=NULL, certificate=NULL, auth_type=NULL,
host="https://management.azure.com/", aad_host="https://login.microsoftonline.com/",
config_file=NULL, token=NULL, ...)
config_file=NULL, token=NULL, graph_host="https://graph.microsoft.com/", ...)
{
if(!is_azure_token(token))
{
Expand Down Expand Up @@ -110,6 +114,8 @@ create_azure_login <- function(tenant="common", app=.az_cli_app_id,
arm_logins[[tenant]] <- sort(unique(c(arm_logins[[tenant]], client$token$hash())))
save_arm_logins(arm_logins)

make_graph_login_from_token(token, aad_host, graph_host)

client
}

Expand Down
17 changes: 17 additions & 0 deletions R/make_graph_login.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
make_graph_login_from_token <- function(token, aad_host, graph_host)
{
if(is_empty(graph_host) || !requireNamespace("AzureGraph", quietly=TRUE))
return()

message("Also creating Microsoft Graph login for ", format_tenant(token$tenant))
newtoken <- token$clone()
if(is_azure_v1_token(newtoken))
newtoken$resource <- graph_host
else newtoken$scope <- sub(aad_host, graph_host, newtoken$scope, fixed=TRUE)

newtoken$refresh()

res <- try(AzureGraph::create_graph_login(tenant=token$tenant, token=newtoken))
if(inherits(res, "try-error"))
warning("Unable to create Microsoft Graph login", call.=FALSE)
}
13 changes: 10 additions & 3 deletions man/azure_login.Rd

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

20 changes: 17 additions & 3 deletions tests/testthat/test01_auth.R
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,35 @@ test_that("Login interface works",
lst <- list_azure_logins()
expect_true(is.list(lst))

az3 <- create_azure_login(tenant=tenant, app=app, password=password)
az3 <- create_azure_login(tenant=tenant, app=app, password=password, graph_host=NULL)
expect_is(az3, "az_rm")

creds <- tempfile(fileext=".json")
writeLines(jsonlite::toJSON(list(tenant=tenant, app=app, password=password)), creds)

az4 <- create_azure_login(config_file=creds)
az4 <- create_azure_login(config_file=creds, graph_host=NULL)
expect_identical(normalize_tenant(tenant), az4$tenant)
expect_is(az4, "az_rm")

az5 <- get_azure_login(tenant)
expect_is(az5, "az_rm")

tok <- get_azure_token("https://management.azure.com/", tenant, app, password)
az6 <- create_azure_login(token=tok)
az6 <- create_azure_login(token=tok, graph_host=NULL)
expect_is(az6, "az_rm")
})

test_that("Graph interop works",
{
if(!requireNamespace("AzureGraph"))
skip("Graph interop tests skipped: AzureGraph not installed")

graph_logins <- file.path(AzureR_dir(), "graph_logins.json")
suppressWarnings(file.remove(graph_logins))

az <- create_azure_login(tenant=tenant, app=app, password=password)
expect_true(file.exists(graph_logins))

gr <- AzureGraph::get_graph_login(tenant)
expect_is(gr, "ms_graph")
})

0 comments on commit 03aedd5

Please sign in to comment.