зеркало из https://github.com/Azure/AzureGraph.git
1.2.0 update (#12)
* change org * test commit * Extensibility (#1) * sharepoint site/drive * use basename of src for download * download method for drive * add top-level get_sharepoint_site, get_drive * refactoring * fix glitch in test * add more is_* functions * start on list_files * list_files working, add get_drives methods * rationalise get_drive/document_library * fine-tune drive/file detection * add drive_item class * tweak for personal onedrive * add app for personal auth * file uploading * delete file * drive items * use AAD v2.0 as default * class registry * tweak login defaults * document everything * linting * update description * split ms_object and az_object * change default for init_list_objects * stub sharepoint list object * basic list functionality * break sharepoint stuff into new pkg * fixes * util fns from AzureRMR * update news * update docs * basic testing * add vignette * fix vignette * update news * change org back * also set default to aad v2 in ms_graph * update doc * bump version no * rework get_graph_login
This commit is contained in:
Родитель
c57654053e
Коммит
4d19dac5b0
|
@ -4,8 +4,6 @@
|
|||
\.Rproj$
|
||||
\.Rxproj$
|
||||
^\.Rproj\.user$
|
||||
.travis.yml
|
||||
CONTRIBUTING.md
|
||||
drat.sh
|
||||
^LICENSE\.md$
|
||||
^\.github$
|
||||
|
|
|
@ -6,7 +6,7 @@ name: R-CMD-check
|
|||
|
||||
jobs:
|
||||
R-CMD-check:
|
||||
if: github.repository_owner == 'Azure'
|
||||
if: github.repository_owner != 'cloudyr'
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
name: ${{ matrix.config.os }} (${{ matrix.config.r }})
|
||||
|
@ -29,7 +29,7 @@ jobs:
|
|||
fetch-depth: 0 # required for mirroring, see https://stackoverflow.com/a/64272409/474349
|
||||
|
||||
- name: Copy to Cloudyr
|
||||
if: runner.os == 'Linux' && github.ref == 'refs/heads/master'
|
||||
if: github.repository_owner == 'Azure' && runner.os == 'Linux' && github.ref == 'refs/heads/master'
|
||||
env:
|
||||
token: "${{ secrets.ghPat }}"
|
||||
# git config hack required, see https://stackoverflow.com/q/64270867/474349
|
||||
|
@ -91,7 +91,7 @@ jobs:
|
|||
path: check
|
||||
|
||||
- name: Update Cloudyr drat
|
||||
if: success() && runner.os == 'Linux' && github.ref == 'refs/heads/master'
|
||||
if: success() && github.repository_owner == 'Azure' && runner.os == 'Linux' && github.ref == 'refs/heads/master'
|
||||
env:
|
||||
token: "${{ secrets.ghPat }}"
|
||||
run: |
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Package: AzureGraph
|
||||
Title: Simple Interface to 'Microsoft Graph'
|
||||
Version: 1.1.2
|
||||
Version: 1.2.0
|
||||
Authors@R: c(
|
||||
person("Hong", "Ooi", , "hongooi73@gmail.com", role = c("aut", "cre")),
|
||||
person("Microsoft", role="cph")
|
||||
|
@ -18,11 +18,13 @@ Imports:
|
|||
httr (>= 1.3),
|
||||
jsonlite,
|
||||
openssl,
|
||||
curl,
|
||||
R6
|
||||
Suggests:
|
||||
AzureRMR,
|
||||
vctrs,
|
||||
knitr,
|
||||
rmarkdown,
|
||||
testthat
|
||||
Roxygen: list(markdown=TRUE)
|
||||
RoxygenNote: 6.1.1
|
||||
Roxygen: list(markdown=TRUE, r6=FALSE, old_usage=TRUE)
|
||||
RoxygenNote: 7.1.1
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,2 +1,2 @@
|
|||
YEAR: 2019
|
||||
YEAR: 2019-2021
|
||||
COPYRIGHT HOLDER: Microsoft
|
||||
|
|
11
NAMESPACE
11
NAMESPACE
|
@ -2,6 +2,7 @@
|
|||
|
||||
export(az_app)
|
||||
export(az_device)
|
||||
export(az_directory_role)
|
||||
export(az_group)
|
||||
export(az_object)
|
||||
export(az_service_principal)
|
||||
|
@ -10,13 +11,21 @@ export(call_graph_endpoint)
|
|||
export(call_graph_url)
|
||||
export(create_graph_login)
|
||||
export(delete_graph_login)
|
||||
export(format_public_fields)
|
||||
export(format_public_methods)
|
||||
export(get_graph_login)
|
||||
export(is_aad_object)
|
||||
export(is_app)
|
||||
export(is_directory_object)
|
||||
export(is_directory_role)
|
||||
export(is_empty)
|
||||
export(is_group)
|
||||
export(is_msgraph_object)
|
||||
export(is_service_principal)
|
||||
export(is_user)
|
||||
export(list_graph_logins)
|
||||
export(ms_graph)
|
||||
export(ms_object)
|
||||
export(named_list)
|
||||
export(register_graph_class)
|
||||
import(AzureAuth)
|
||||
importFrom(utils,modifyList)
|
||||
|
|
7
NEWS.md
7
NEWS.md
|
@ -1,3 +1,10 @@
|
|||
# AzureGraph 1.2.0
|
||||
|
||||
- Internal refactoring to support future extensibility, including transferring some utility functions from AzureRMR to here.
|
||||
- New "Extending AzureGraph" vignette, showing how to extend this package to represent other object types in Microsoft Graph.
|
||||
- Switch to AAD v2.0 as the default for authenticating.
|
||||
- Enhance `get_graph_login` to allow specifying scopes.
|
||||
|
||||
# AzureGraph 1.1.2
|
||||
|
||||
- Change maintainer email address.
|
||||
|
|
|
@ -14,3 +14,5 @@ utils::globalVariables(c("self", "private"))
|
|||
# default authentication app ID: leverage the az CLI
|
||||
.az_cli_app_id <- "04b07795-8ddb-461a-bbee-02f9e1bf7b46"
|
||||
|
||||
# authentication app ID for personal accounts
|
||||
.azurer_graph_app_id <- "5bb21e8a-06bf-4ac4-b613-110ac0e582c1"
|
||||
|
|
|
@ -89,6 +89,7 @@ public=list(
|
|||
initialize=function(token, tenant=NULL, properties=NULL, password=NULL)
|
||||
{
|
||||
self$type <- "application"
|
||||
private$api_type <- "applications"
|
||||
self$password <- password
|
||||
super$initialize(token, tenant, properties)
|
||||
},
|
||||
|
@ -161,7 +162,7 @@ public=list(
|
|||
list_owners=function(type=c("user", "group", "application", "servicePrincipal"))
|
||||
{
|
||||
res <- private$get_paged_list(self$do_operation("owners"))
|
||||
private$init_list_objects(private$filter_list(res, type))
|
||||
private$init_list_objects(res, type)
|
||||
},
|
||||
|
||||
create_service_principal=function(...)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#' Device in Azure Active Directory
|
||||
#'
|
||||
#' Base class representing a registered device.
|
||||
#' Class representing a registered device.
|
||||
#'
|
||||
#' @docType class
|
||||
#' @section Fields:
|
||||
|
@ -24,7 +24,7 @@
|
|||
#' [ms_graph], [az_user], [az_object]
|
||||
#'
|
||||
#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview),
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta)
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0)
|
||||
#'
|
||||
#' @format An R6 object of class `az_device`, inheriting from `az_object`.
|
||||
#' @export
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
#' Directory role
|
||||
#'
|
||||
#' Class representing a role in Azure Active Directory.
|
||||
#'
|
||||
#' @docType class
|
||||
#' @section Fields:
|
||||
#' - `token`: The token used to authenticate with the Graph host.
|
||||
#' - `tenant`: The Azure Active Directory tenant for this role.
|
||||
#' - `type`: always "directory role" for a directory role object.
|
||||
#' - `properties`: The item properties.
|
||||
#' @section Methods:
|
||||
#' - `new(...)`: Initialize a new object. Do not call this directly; see 'Initialization' below.
|
||||
#' - `delete(confirm=TRUE)`: Delete this item. By default, ask for confirmation first.
|
||||
#' - `update(...)`: Update the item's properties in Microsoft Graph.
|
||||
#' - `do_operation(...)`: Carry out an arbitrary operation on the item.
|
||||
#' - `sync_fields()`: Synchronise the R object with the item metadata in Microsoft Graph.
|
||||
#'
|
||||
#' @section Initialization:
|
||||
#' Currently support for directory roles is limited. Objects of this class should not be initialized directly.
|
||||
#'
|
||||
#' @seealso
|
||||
#' [ms_graph], [az_user]
|
||||
#'
|
||||
#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview),
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0)
|
||||
#'
|
||||
#' @format An R6 object of class `az_directory_role`, inheriting from `az_object`.
|
||||
#' @export
|
||||
az_directory_role <- R6::R6Class("az_directory_role", inherit=az_object,
|
||||
|
||||
public=list(
|
||||
|
||||
initialize=function(token, tenant=NULL, properties=NULL)
|
||||
{
|
||||
self$type <- "directory role"
|
||||
private$api_type <- "directoryRoles"
|
||||
super$initialize(token, tenant, properties)
|
||||
},
|
||||
|
||||
list_members=function()
|
||||
{
|
||||
res <- private$get_paged_list(self$do_operation("members"))
|
||||
private$init_list_objects(res)
|
||||
},
|
||||
|
||||
print=function(...)
|
||||
{
|
||||
cat("<Azure Active Directory role '", self$properties$displayName, "'>\n", sep="")
|
||||
cat(" directory id:", self$properties$id, "\n")
|
||||
cat(" description:", self$properties$description, "\n")
|
||||
cat("---\n")
|
||||
cat(format_public_methods(self))
|
||||
invisible(self)
|
||||
}
|
||||
))
|
18
R/az_group.R
18
R/az_group.R
|
@ -1,6 +1,6 @@
|
|||
#' Group in Azure Active Directory
|
||||
#'
|
||||
#' Base class representing an AAD group.
|
||||
#' Class representing an AAD group.
|
||||
#'
|
||||
#' @docType class
|
||||
#' @section Fields:
|
||||
|
@ -26,7 +26,7 @@
|
|||
#' [ms_graph], [az_app], [az_user], [az_object]
|
||||
#'
|
||||
#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview),
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta)
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0)
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
|
@ -50,24 +50,32 @@ public=list(
|
|||
initialize=function(token, tenant=NULL, properties=NULL)
|
||||
{
|
||||
self$type <- "group"
|
||||
private$api_type <- "groups"
|
||||
super$initialize(token, tenant, properties)
|
||||
},
|
||||
|
||||
list_members=function(type=c("user", "group", "application", "servicePrincipal"))
|
||||
{
|
||||
res <- private$get_paged_list(self$do_operation("members"))
|
||||
private$init_list_objects(private$filter_list(res, type))
|
||||
private$init_list_objects(res, type)
|
||||
},
|
||||
|
||||
list_owners=function(type=c("user", "group", "application", "servicePrincipal"))
|
||||
{
|
||||
res <- private$get_paged_list(self$do_operation("owners"))
|
||||
private$init_list_objects(private$filter_list(res, type))
|
||||
private$init_list_objects(res, type)
|
||||
},
|
||||
|
||||
print=function(...)
|
||||
{
|
||||
cat("<Graph group '", self$properties$displayName, "'>\n", sep="")
|
||||
group_type <- if("Unified" %in% self$properties$groupTypes)
|
||||
"Microsoft 365"
|
||||
else if(!self$properties$mailEnabled)
|
||||
"Security"
|
||||
else if(self$properties$securityEnabled)
|
||||
"Mail-enabled security"
|
||||
else "Distribution"
|
||||
cat("<", group_type, " group '", self$properties$displayName, "'>\n", sep="")
|
||||
cat(" directory id:", self$properties$id, "\n")
|
||||
cat(" description:", self$properties$description, "\n")
|
||||
cat("---\n")
|
||||
|
|
113
R/az_object.R
113
R/az_object.R
|
@ -1,6 +1,6 @@
|
|||
#' Azure Active Directory object
|
||||
#'
|
||||
#' Base class representing a directory object in Microsoft Graph.
|
||||
#' Base class representing an Azure Active Directory object in Microsoft Graph.
|
||||
#'
|
||||
#' @docType class
|
||||
#' @section Fields:
|
||||
|
@ -24,55 +24,14 @@
|
|||
#' [ms_graph], [az_app], [az_service_principal], [az_user], [az_group]
|
||||
#'
|
||||
#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview),
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta)
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0)
|
||||
#'
|
||||
#' @format An R6 object of class `az_object`.
|
||||
#' @format An R6 object of class `az_object`, inheriting from `ms_object`.
|
||||
#' @export
|
||||
az_object <- R6::R6Class("az_object",
|
||||
az_object <- R6::R6Class("az_object", inherit=ms_object,
|
||||
|
||||
public=list(
|
||||
|
||||
token=NULL,
|
||||
tenant=NULL,
|
||||
type=NULL,
|
||||
|
||||
# app data from server
|
||||
properties=NULL,
|
||||
|
||||
initialize=function(token, tenant=NULL, properties=NULL)
|
||||
{
|
||||
self$token <- token
|
||||
self$tenant <- tenant
|
||||
self$properties <- properties
|
||||
},
|
||||
|
||||
update=function(...)
|
||||
{
|
||||
self$do_operation(body=list(...), encode="json", http_verb="PATCH")
|
||||
self$properties <- self$do_operation()
|
||||
self
|
||||
},
|
||||
|
||||
sync_fields=function()
|
||||
{
|
||||
self$properties <- self$do_operation()
|
||||
invisible(self)
|
||||
},
|
||||
|
||||
delete=function(confirm=TRUE)
|
||||
{
|
||||
if(confirm && interactive())
|
||||
{
|
||||
msg <- sprintf("Do you really want to delete the %s '%s'?",
|
||||
self$type, self$properties$displayName)
|
||||
if(!get_confirmation(msg, FALSE))
|
||||
return(invisible(NULL))
|
||||
}
|
||||
|
||||
self$do_operation(http_verb="DELETE")
|
||||
invisible(NULL)
|
||||
},
|
||||
|
||||
list_object_memberships=function()
|
||||
{
|
||||
lst <- self$do_operation("getMemberObjects", body=list(securityEnabledOnly=TRUE),
|
||||
|
@ -89,75 +48,13 @@ public=list(
|
|||
unlist(private$get_paged_list(lst))
|
||||
},
|
||||
|
||||
do_operation=function(op="", ...)
|
||||
{
|
||||
op <- construct_path(private$get_endpoint(), self$properties$id, op)
|
||||
call_graph_endpoint(self$token, op, ...)
|
||||
},
|
||||
|
||||
print=function(...)
|
||||
{
|
||||
cat("<Graph directory object '", self$properties$displayName, "'>\n", sep="")
|
||||
cat("<Azure Active Directory object '", self$properties$displayName, "'>\n", sep="")
|
||||
cat(" directory id:", self$properties$id, "\n")
|
||||
cat("---\n")
|
||||
cat(format_public_methods(self))
|
||||
invisible(self)
|
||||
}
|
||||
),
|
||||
|
||||
private=list(
|
||||
|
||||
get_paged_list=function(lst, next_link_name="@odata.nextLink", value_name="value")
|
||||
{
|
||||
res <- lst[[value_name]]
|
||||
while(!is_empty(lst[[next_link_name]]))
|
||||
{
|
||||
lst <- call_graph_url(self$token, lst[[next_link_name]])
|
||||
res <- c(res, lst[[value_name]])
|
||||
}
|
||||
res
|
||||
},
|
||||
|
||||
filter_list=function(lst, type=c("user", "group", "application", "servicePrincipal", "device"))
|
||||
{
|
||||
type <- paste0("#microsoft.graph.", type)
|
||||
keep <- vapply(lst, function(obj) obj$`@odata.type` %in% type, FUN.VALUE=logical(1))
|
||||
lst[keep]
|
||||
},
|
||||
|
||||
init_list_objects=function(lst)
|
||||
{
|
||||
lapply(lst, function(obj)
|
||||
{
|
||||
switch(obj$`@odata.type`,
|
||||
"#microsoft.graph.user"=
|
||||
az_user$new(self$token, self$tenant, obj),
|
||||
"#microsoft.graph.group"=
|
||||
az_group$new(self$token, self$tenant, obj),
|
||||
"#microsoft.graph.application"=
|
||||
az_app$new(self$token, self$tenant, obj),
|
||||
"#microsoft.graph.servicePrincipal"=
|
||||
az_service_principal$new(self$token, self$tenant, obj),
|
||||
"#microsoft.graph.device"=
|
||||
az_device$new(self$token, self$tenant, obj),
|
||||
{
|
||||
warning("Unknown directory object type ", obj$`@odata.type`)
|
||||
obj
|
||||
}
|
||||
)
|
||||
})
|
||||
},
|
||||
|
||||
get_endpoint=function()
|
||||
{
|
||||
switch(self$type,
|
||||
"user"="users",
|
||||
"group"="groups",
|
||||
"application"="applications",
|
||||
"service principal"="servicePrincipals",
|
||||
"device"="devices",
|
||||
stop("Unknown directory object type"))
|
||||
}
|
||||
))
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#' Service principal in Azure Active Directory
|
||||
#'
|
||||
#' Base class representing an AAD service principal.
|
||||
#' Class representing an AAD service principal.
|
||||
#'
|
||||
#' @docType class
|
||||
#' @section Fields:
|
||||
|
@ -24,7 +24,7 @@
|
|||
#' [ms_graph], [az_app], [az_object]
|
||||
#'
|
||||
#' [Azure Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview),
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta)
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0)
|
||||
#'
|
||||
#' @format An R6 object of class `az_service_principal`, inheriting from `az_object`.
|
||||
#' @export
|
||||
|
@ -35,6 +35,7 @@ public=list(
|
|||
initialize=function(token, tenant=NULL, properties=NULL)
|
||||
{
|
||||
self$type <- "service principal"
|
||||
private$api_type <- "servicePrincipals"
|
||||
super$initialize(token, tenant, properties)
|
||||
},
|
||||
|
||||
|
|
19
R/az_user.R
19
R/az_user.R
|
@ -1,6 +1,6 @@
|
|||
#' User in Azure Active Directory
|
||||
#'
|
||||
#' Base class representing an AAD user account.
|
||||
#' Class representing an AAD user account.
|
||||
#'
|
||||
#' @docType class
|
||||
#' @section Fields:
|
||||
|
@ -16,7 +16,7 @@
|
|||
#' - `sync_fields()`: Synchronise the R object with the app data in Azure Active Directory.
|
||||
#' - `list_group_memberships()`: Return the IDs of all groups this user is a member of.
|
||||
#' - `list_object_memberships()`: Return the IDs of all groups, administrative units and directory roles this user is a member of.
|
||||
#' - `list_direct_memberships(id_only=TRUE)`: List the groups this user is a direct member of. Set `id_only=TRUE` to return only a vector of group IDs (the default), or `id_only=FALSE` to return a list of group objects.
|
||||
#' - `list_direct_memberships(id_only=TRUE)`: List the groups and directory roles this user is a direct member of. Set `id_only=TRUE` to return only a vector of IDs (the default), or `id_only=FALSE` to return a list of group objects.
|
||||
#' - `list_owned_objects(type=c("user", "group", "application", "servicePrincipal"))`: List directory objects (groups/apps/service principals) owned by this user. Specify the `type` argument to filter the result for specific object type(s).
|
||||
#' - `list_created_objects(type=c("user", "group", "application", "servicePrincipal"))`: List directory objects (groups/apps/service principals) created by this user. Specify the `type` argument to filter the result for specific object type(s).
|
||||
#' - `list_owned_devices()`: List the devices owned by this user.
|
||||
|
@ -30,7 +30,7 @@
|
|||
#' [ms_graph], [az_app], [az_group], [az_device], [az_object]
|
||||
#'
|
||||
#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview),
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta)
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0)
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
|
@ -64,6 +64,7 @@ public=list(
|
|||
initialize=function(token, tenant=NULL, properties=NULL, password=NULL)
|
||||
{
|
||||
self$type <- "user"
|
||||
private$api_type <- "users"
|
||||
self$password <- password
|
||||
super$initialize(token, tenant, properties)
|
||||
},
|
||||
|
@ -90,19 +91,19 @@ public=list(
|
|||
list_owned_objects=function(type=c("user", "group", "application", "servicePrincipal"))
|
||||
{
|
||||
res <- private$get_paged_list(self$do_operation("ownedObjects"))
|
||||
private$init_list_objects(private$filter_list(res, type))
|
||||
private$init_list_objects(res, type)
|
||||
},
|
||||
|
||||
list_created_objects=function(type=c("user", "group", "application", "servicePrincipal"))
|
||||
{
|
||||
res <- private$get_paged_list(self$do_operation("createdObjects"))
|
||||
private$init_list_objects(private$filter_list(res, type))
|
||||
private$init_list_objects(res, type)
|
||||
},
|
||||
|
||||
list_owned_devices=function()
|
||||
{
|
||||
res <- private$get_paged_list(self$do_operation("ownedDevices"))
|
||||
private$init_list_objects(private$filter_list(res))
|
||||
private$init_list_objects(res, "device")
|
||||
},
|
||||
|
||||
list_direct_memberships=function(id_only=TRUE)
|
||||
|
@ -111,11 +112,7 @@ public=list(
|
|||
|
||||
if(id_only)
|
||||
sapply(res, function(grp) grp$id)
|
||||
else
|
||||
{
|
||||
names(res) <- sapply(res, function(grp) grp$displayName)
|
||||
lapply(res, function(grp) az_group$new(self$token, self$tenant, grp))
|
||||
}
|
||||
else private$init_list_objects(res)
|
||||
},
|
||||
|
||||
print=function(...)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#' @param url A complete URL to send to the host.
|
||||
#' @param http_verb The HTTP verb as a string, one of `GET`, `PUT`, `POST`, `DELETE`, `HEAD` or `PATCH`.
|
||||
#' @param http_status_handler How to handle in R the HTTP status code of a response. `"stop"`, `"warn"` or `"message"` will call the appropriate handlers in httr, while `"pass"` ignores the status code.
|
||||
#' @param simplify Whether to turn arrays of objects in the JSON response into data frames. Set this to `TRUE` if you are expecting the endpoint to return tabular data and you want a tabular result, as opposed to a list of objects.
|
||||
#' @param auto_refresh Whether to refresh/renew the OAuth token if it is no longer valid.
|
||||
#' @param body The body of the request, for `PUT`/`POST`/`PATCH`.
|
||||
#' @param encode The encoding (really content-type) for the request body. The default value "json" means to serialize a list body into a JSON object. If you pass an already-serialized JSON object as the body, set `encode` to "raw".
|
||||
|
@ -15,6 +16,8 @@
|
|||
#' @details
|
||||
#' These functions form the low-level interface between R and Microsoft Graph. `call_graph_endpoint` forms a URL from its arguments and passes it to `call_graph_url`.
|
||||
#'
|
||||
#' If `simplify` is `TRUE`, `call_graph_url` will exploit the ability of `jsonlite::fromJSON` to convert arrays of objects into R data frames. This can be useful for REST calls that return tabular data. However, it can also cause problems for _paged_ lists, where each page will be turned into a separate data frame; as the individual objects may not have the same fields, the resulting data frames will also have differing columns. This will cause base R's `rbind` to fail when binding the pages together. When processing paged lists, AzureGraph will use `vctrs::vec_rbind` instead of `rbind` when the vctrs package is available; `vec_rbind` does not have this problem. For safety, you should only set `simplify=TRUE` when vctrs is installed.
|
||||
#'
|
||||
#' @return
|
||||
#' If `http_status_handler` is one of `"stop"`, `"warn"` or `"message"`, the status code of the response is checked. If an error is not thrown, the parsed content of the response is returned with the status code attached as the "status" attribute.
|
||||
#'
|
||||
|
@ -39,7 +42,7 @@ call_graph_endpoint <- function(token, operation, ..., options=list(),
|
|||
call_graph_url <- function(token, url, ..., body=NULL, encode="json",
|
||||
http_verb=c("GET", "DELETE", "PUT", "POST", "HEAD", "PATCH"),
|
||||
http_status_handler=c("stop", "warn", "message", "pass"),
|
||||
auto_refresh=TRUE)
|
||||
simplify=FALSE, auto_refresh=TRUE)
|
||||
{
|
||||
headers <- process_headers(token, url, auto_refresh)
|
||||
|
||||
|
@ -54,7 +57,7 @@ call_graph_url <- function(token, url, ..., body=NULL, encode="json",
|
|||
# do actual API call
|
||||
res <- httr::VERB(match.arg(http_verb), url, headers, ..., body=body, encode=encode)
|
||||
|
||||
process_response(res, match.arg(http_status_handler))
|
||||
process_response(res, match.arg(http_status_handler), simplify)
|
||||
}
|
||||
|
||||
|
||||
|
@ -79,11 +82,11 @@ process_headers <- function(token, host, auto_refresh)
|
|||
}
|
||||
|
||||
|
||||
process_response <- function(response, handler)
|
||||
process_response <- function(response, handler, simplify)
|
||||
{
|
||||
if(handler != "pass")
|
||||
{
|
||||
cont <- httr::content(response)
|
||||
cont <- httr::content(response, simplifyVector=simplify)
|
||||
handler <- get(paste0(handler, "_for_status"), getNamespace("httr"))
|
||||
handler(response, paste0("complete operation. Message:\n",
|
||||
sub("\\.$", "", error_message(cont))))
|
||||
|
@ -128,36 +131,6 @@ construct_path <- function(...)
|
|||
}
|
||||
|
||||
|
||||
# same as AzureRMR::named_list, do not export to avoid conflicts
|
||||
named_list <- function(lst=NULL, name_fields="name")
|
||||
{
|
||||
if(is_empty(lst))
|
||||
return(structure(list(), names=character(0)))
|
||||
|
||||
lst_names <- sapply(name_fields, function(n) sapply(lst, `[[`, n))
|
||||
if(length(name_fields) > 1)
|
||||
{
|
||||
dim(lst_names) <- c(length(lst_names) / length(name_fields), length(name_fields))
|
||||
lst_names <- apply(lst_names, 1, function(nn) paste(nn, collapse="/"))
|
||||
}
|
||||
names(lst) <- lst_names
|
||||
dups <- duplicated(tolower(names(lst)))
|
||||
if(any(dups))
|
||||
{
|
||||
duped_names <- names(lst)[dups]
|
||||
warning("Some names are duplicated: ", paste(unique(duped_names), collapse=" "), call.=FALSE)
|
||||
}
|
||||
lst
|
||||
}
|
||||
|
||||
|
||||
# same as AzureRMR::is_empty, do not export to avoid conflicts
|
||||
is_empty <- function(x)
|
||||
{
|
||||
length(x) == 0
|
||||
}
|
||||
|
||||
|
||||
# display confirmation prompt, return TRUE/FALSE (no NA)
|
||||
get_confirmation <- function(msg, default=TRUE)
|
||||
{
|
||||
|
|
22
R/format.R
22
R/format.R
|
@ -1,5 +1,15 @@
|
|||
# copied from AzureRMR:: do not export
|
||||
|
||||
#' Format a Microsoft Graph or Azure object
|
||||
#'
|
||||
#' Miscellaneous functions for printing Microsoft Graph and Azure R6 objects
|
||||
#'
|
||||
#' @param env An R6 object's environment for printing.
|
||||
#' @param exclude Objects in `env` to exclude from the printout.
|
||||
#'
|
||||
#' @details
|
||||
#' These are utilities to aid in printing R6 objects created by this package or its descendants. They are not meant to be called by the user.
|
||||
#'
|
||||
#' @rdname format
|
||||
#' @export
|
||||
format_public_fields <- function(env, exclude=character(0))
|
||||
{
|
||||
objnames <- ls(env)
|
||||
|
@ -7,7 +17,7 @@ format_public_fields <- function(env, exclude=character(0))
|
|||
objnames <- setdiff(objnames, c(exclude, std_fields))
|
||||
|
||||
maxwidth <- as.integer(0.8 * getOption("width"))
|
||||
|
||||
|
||||
objconts <- sapply(objnames, function(n)
|
||||
{
|
||||
x <- get(n, env)
|
||||
|
@ -21,13 +31,13 @@ format_public_fields <- function(env, exclude=character(0))
|
|||
if(nchar(x) > maxwidth - nchar(n) - 10)
|
||||
x <- paste0(substr(x, 1, maxwidth - nchar(n) - 10), " ...")
|
||||
x
|
||||
}
|
||||
}
|
||||
else deparse(x)[[1]]
|
||||
|
||||
paste0(strwrap(paste0(n, ": ", deparsed), width=maxwidth, indent=2, exdent=4),
|
||||
collapse="\n")
|
||||
}, simplify=FALSE)
|
||||
|
||||
|
||||
empty <- sapply(objconts, is.null)
|
||||
objconts <- objconts[!empty]
|
||||
|
||||
|
@ -39,6 +49,8 @@ format_public_fields <- function(env, exclude=character(0))
|
|||
}
|
||||
|
||||
|
||||
#' @rdname format
|
||||
#' @export
|
||||
format_public_methods <- function(env)
|
||||
{
|
||||
objnames <- ls(env)
|
||||
|
|
163
R/graph_login.R
163
R/graph_login.R
|
@ -6,8 +6,10 @@
|
|||
#' @param username If `auth_type == "resource_owner"`, your username.
|
||||
#' @param certificate If `auth_type == "client_credentials", a certificate to authenticate with. This is a more secure alternative to using an app secret.
|
||||
#' @param auth_type The OAuth authentication method to use, one of "client_credentials", "authorization_code", "device_code" or "resource_owner". If `NULL`, this is chosen based on the presence of the `username` and `password` arguments.
|
||||
#' @param version The Azure Active Directory version to use for authenticating.
|
||||
#' @param host Your Microsoft Graph host. Defaults to `https://graph.microsoft.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 scopes The Microsoft Graph scopes (permissions) to obtain for this Graph login. Only for `version=2`.
|
||||
#' @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 [AzureAuth::AzureToken]. This allows you to reuse the authentication details for an existing session. If supplied, all other arguments to `create_graph_login` will be ignored.
|
||||
#' @param refresh For `get_graph_login`, whether to refresh the authentication token on loading the client.
|
||||
|
@ -18,7 +20,7 @@
|
|||
#' @details
|
||||
#' `create_graph_login` creates a login client to authenticate with Microsoft Graph, using the supplied arguments. The authentication token is obtained using [get_azure_token], which automatically caches and reuses tokens for subsequent sessions. Note that credentials are only cached if you allowed AzureGraph to create a data directory at package startup.
|
||||
#'
|
||||
#' `get_graph_login` returns a login client by retrieving previously saved credentials. It searches for saved credentials according to the supplied tenant; if multiple logins are found, it will prompt for you to choose one.
|
||||
#' `get_graph_login` returns a previously created login client. If there are multiple existing clients, you can specify which client to return via the `selection`, `app`, `scopes` and `auth_type` arguments. If you don't specify which one to return, it will pop up a menu and ask you to choose one.
|
||||
#'
|
||||
#' One difference between `create_graph_login` and `get_graph_login` is the former will delete any previously saved credentials that match the arguments it was given. You can use this to force AzureGraph to remove obsolete tokens that may be lying around.
|
||||
#'
|
||||
|
@ -34,12 +36,12 @@
|
|||
#' [ms_graph], [AzureAuth::get_azure_token] for more details on authentication methods
|
||||
#'
|
||||
#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview),
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta)
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0)
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#'
|
||||
#' # without any arguments, this will create a client using your AAD credentials
|
||||
#' # without any arguments, this will create a client using your AAD organisational account
|
||||
#' az <- create_graph_login()
|
||||
#'
|
||||
#' # retrieve the login in subsequent sessions
|
||||
|
@ -47,18 +49,26 @@
|
|||
#'
|
||||
#' # this will create an Microsoft Graph client for the tenant 'microsoft.onmicrosoft.com',
|
||||
#' # using the client_credentials method
|
||||
#' az <- create_graph_login("microsoft", app="{app_id}", password="{password}")
|
||||
#' az <- create_graph_login("mytenant", app="{app_id}", password="{password}")
|
||||
#'
|
||||
#' # you can also login using credentials in a json file
|
||||
#' az <- create_graph_login(config_file="~/creds.json")
|
||||
#'
|
||||
#' # creating and obtaining a login with specific scopes
|
||||
#' create_graph_login("mytenant", scopes=c("User.Read", "Files.ReadWrite.All"))
|
||||
#' get_graph_login("mytenant", scopes=c("User.Read", "Files.ReadWrite.All"))
|
||||
#'
|
||||
#' # to use your personal account, set the tenant to one of the following
|
||||
#' create_graph_login("9188040d-6c67-4c5b-b112-36a304b66dad")
|
||||
#' create_graph_login("consumers") # requires AzureAuth 1.2.6
|
||||
#'
|
||||
#' }
|
||||
#' @rdname graph_login
|
||||
#' @export
|
||||
create_graph_login <- function(tenant="common", app=.az_cli_app_id,
|
||||
password=NULL, username=NULL, certificate=NULL, auth_type=NULL,
|
||||
create_graph_login <- function(tenant="common", app=NULL,
|
||||
password=NULL, username=NULL, certificate=NULL, auth_type=NULL, version=2,
|
||||
host="https://graph.microsoft.com/", aad_host="https://login.microsoftonline.com/",
|
||||
config_file=NULL, token=NULL, ...)
|
||||
scopes=".default", config_file=NULL, token=NULL, ...)
|
||||
{
|
||||
if(!is_azure_token(token))
|
||||
{
|
||||
|
@ -66,13 +76,22 @@ create_graph_login <- function(tenant="common", app=.az_cli_app_id,
|
|||
{
|
||||
conf <- jsonlite::fromJSON(config_file)
|
||||
call <- as.list(match.call())[-1]
|
||||
call$config_file <- NULL
|
||||
call$config_file <- call$token <- NULL
|
||||
call <- lapply(modifyList(call, conf), function(x) eval.parent(x))
|
||||
return(do.call(create_graph_login, call))
|
||||
}
|
||||
|
||||
tenant <- normalize_tenant(tenant)
|
||||
app <- normalize_guid(app)
|
||||
app <- if(is.null(app))
|
||||
{
|
||||
if(tenant %in% c("consumers", "9188040d-6c67-4c5b-b112-36a304b66dad"))
|
||||
.azurer_graph_app_id
|
||||
else .az_cli_app_id
|
||||
}
|
||||
else normalize_guid(app)
|
||||
|
||||
if(version == 2)
|
||||
host <- c(paste0(host, scopes), "openid", "offline_access")
|
||||
|
||||
token_args <- list(resource=host,
|
||||
tenant=tenant,
|
||||
|
@ -82,6 +101,7 @@ create_graph_login <- function(tenant="common", app=.az_cli_app_id,
|
|||
certificate=certificate,
|
||||
auth_type=auth_type,
|
||||
aad_host=aad_host,
|
||||
version=version,
|
||||
...)
|
||||
|
||||
hash <- do.call(token_hash, token_args)
|
||||
|
@ -110,7 +130,7 @@ create_graph_login <- function(tenant="common", app=.az_cli_app_id,
|
|||
|
||||
#' @rdname graph_login
|
||||
#' @export
|
||||
get_graph_login <- function(tenant="common", selection=NULL, refresh=TRUE)
|
||||
get_graph_login <- function(tenant="common", selection=NULL, app=NULL, scopes=NULL, auth_type=NULL, refresh=TRUE)
|
||||
{
|
||||
if(!dir.exists(AzureR_dir()))
|
||||
stop("AzureR data directory does not exist; cannot load saved logins")
|
||||
|
@ -126,43 +146,22 @@ get_graph_login <- function(tenant="common", selection=NULL, refresh=TRUE)
|
|||
stop(msg, call.=FALSE)
|
||||
}
|
||||
|
||||
if(length(this_login) == 1 && is.null(selection))
|
||||
selection <- 1
|
||||
else if(is.null(selection))
|
||||
{
|
||||
tokens <- lapply(this_login, function(f)
|
||||
readRDS(file.path(AzureR_dir(), f)))
|
||||
|
||||
choices <- sapply(tokens, function(token)
|
||||
{
|
||||
app <- token$client$client_id
|
||||
paste0("App ID: ", app, "\n Authentication method: ", token$auth_type)
|
||||
})
|
||||
|
||||
msg <- paste0("Choose a Microsoft Graph login for ", format_tenant(tenant))
|
||||
selection <- utils::menu(choices, title=msg)
|
||||
}
|
||||
|
||||
if(selection == 0)
|
||||
return(NULL)
|
||||
|
||||
file <- if(is.numeric(selection))
|
||||
this_login[selection]
|
||||
else if(is.character(selection))
|
||||
this_login[which(this_login == selection)] # force an error if supplied hash doesn't match available logins
|
||||
|
||||
file <- file.path(AzureR_dir(), file)
|
||||
if(is_empty(file) || !file.exists(file))
|
||||
stop("Azure Active Directory token not found for this login", call.=FALSE)
|
||||
|
||||
message("Loading Microsoft Graph login for ", format_tenant(tenant))
|
||||
|
||||
token <- readRDS(file)
|
||||
client <- ms_graph$new(token=token)
|
||||
# do we need to choose which login client to use?
|
||||
have_selection <- !is.null(selection)
|
||||
have_auth_spec <- any(!is.null(app), !is.null(scopes), !is.null(auth_type))
|
||||
|
||||
token <- if(length(this_login) > 1 || have_selection || have_auth_spec)
|
||||
choose_token(this_login, selection, app, scopes, auth_type)
|
||||
else load_azure_token(this_login)
|
||||
|
||||
if(is.null(token))
|
||||
return(NULL)
|
||||
|
||||
client <- ms_graph$new(token=token)
|
||||
if(refresh)
|
||||
client$token$refresh()
|
||||
|
||||
client
|
||||
}
|
||||
|
||||
|
@ -243,4 +242,82 @@ format_tenant <- function(tenant)
|
|||
if(tenant == "common")
|
||||
"default tenant"
|
||||
else paste0("tenant '", tenant, "'")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# algorithm for choosing a token:
|
||||
# if given a hash, choose it (error if no match)
|
||||
# otherwise if given any of app|scopes|auth_type, use those (error if no match)
|
||||
# otherwise if given a number, use it
|
||||
# otherwise ask
|
||||
choose_token <- function(hashes, selection, app, scopes, auth_type)
|
||||
{
|
||||
if(is.character(selection))
|
||||
{
|
||||
if(!(selection %in% hashes))
|
||||
stop("Token with selected hash not found", call.=FALSE)
|
||||
return(load_azure_token(selection))
|
||||
}
|
||||
|
||||
if(any(!is.null(app), !is.null(scopes), !is.null(auth_type)))
|
||||
{
|
||||
if(!is.null(scopes))
|
||||
scopes <- tolower(scopes)
|
||||
|
||||
# look for matching token
|
||||
for(hash in hashes)
|
||||
{
|
||||
app_match <- scope_match <- auth_match <- TRUE
|
||||
token <- load_azure_token(hash)
|
||||
if(!is.null(app) && token$client$client_id != hash)
|
||||
app_match <- FALSE
|
||||
if(!is.null(scopes))
|
||||
{
|
||||
# is this an AAD v1.0 token?
|
||||
if(is.null(token$scope))
|
||||
scope_match <- FALSE
|
||||
else
|
||||
{
|
||||
tok_scopes <- tolower(basename(grep("^.+://", token$scope, value=TRUE)))
|
||||
if(!setequal(scopes, tok_scopes))
|
||||
scope_match <- FALSE
|
||||
}
|
||||
}
|
||||
if(!is.null(auth_type) && token$auth_type != auth_type)
|
||||
auth_match <- FALSE
|
||||
|
||||
if(app_match && scope_match && auth_match)
|
||||
return(token)
|
||||
}
|
||||
# if we get here, no tokens match provided criteria
|
||||
stop("Token with selected authentication parameters not found", call.=FALSE)
|
||||
}
|
||||
|
||||
if(is.numeric(selection))
|
||||
{
|
||||
if(selection > length(hashes))
|
||||
stop("Invalid numeric selection", call.=FALSE)
|
||||
return(load_azure_token(hashes[selection]))
|
||||
}
|
||||
|
||||
# bring up a menu
|
||||
tokens <- lapply(hashes, load_azure_token)
|
||||
tenant <- tokens[[1]]$tenant
|
||||
choices <- sapply(tokens, function(token)
|
||||
{
|
||||
app <- token$client$client_id
|
||||
scopes <- if(!is.null(token$scope))
|
||||
paste(tolower(basename(grep("^.+://", token$scope, value=TRUE))), collapse=" ")
|
||||
else "<NA>"
|
||||
paste0("App ID: ", app,
|
||||
"\n Scopes: ", scopes,
|
||||
"\n Authentication method: ", token$auth_type,
|
||||
"\n MD5 Hash: ", token$hash())
|
||||
})
|
||||
msg <- paste0("Choose a Microsoft Graph login for ", format_tenant(tenant))
|
||||
selection <- utils::menu(choices, title=msg)
|
||||
if(selection == 0)
|
||||
NULL
|
||||
else tokens[[selection]]
|
||||
}
|
||||
|
||||
|
|
19
R/is.R
19
R/is.R
|
@ -40,7 +40,24 @@ is_group <- function(object)
|
|||
|
||||
#' @rdname info
|
||||
#' @export
|
||||
is_directory_object <- function(object)
|
||||
is_directory_role <- function(object)
|
||||
{
|
||||
R6::is.R6(object) && inherits(object, "az_directory_role")
|
||||
}
|
||||
|
||||
|
||||
#' @rdname info
|
||||
#' @export
|
||||
is_aad_object <- function(object)
|
||||
{
|
||||
R6::is.R6(object) && inherits(object, "az_object")
|
||||
}
|
||||
|
||||
|
||||
#' @rdname info
|
||||
#' @export
|
||||
is_msgraph_object <- function(object)
|
||||
{
|
||||
R6::is.R6(object) && inherits(object, "ms_object")
|
||||
}
|
||||
|
||||
|
|
14
R/ms_graph.R
14
R/ms_graph.R
|
@ -29,8 +29,10 @@
|
|||
#' - `username`: if `auth_type == "resource_owner"`, your username.
|
||||
#' - `certificate`: If `auth_type == "client_credentials", a certificate to authenticate with. This is a more secure alternative to using an app secret.
|
||||
#' - `auth_type`: The OAuth authentication method to use, one of "client_credentials", "authorization_code", "device_code" or "resource_owner". See [get_azure_token] for how the default method is chosen, along with some caveats.
|
||||
#' - `version`: The Azure Active Directory (AAD) version to use for authenticating.
|
||||
#' - `host`: your Microsoft Graph host. Defaults to `https://graph.microsoft.com/`.
|
||||
#' - `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.
|
||||
#' - `scopes`: The Microsoft Graph scopes (permissions) to obtain for this Graph login. Only for `version=2`.
|
||||
#' - `token`: Optionally, an OAuth 2.0 token, of class [AzureAuth::AzureToken]. This allows you to reuse the authentication details for an existing session. If supplied, all other arguments will be ignored.
|
||||
#'
|
||||
#' @section App creation:
|
||||
|
@ -44,7 +46,7 @@
|
|||
#' [create_graph_login], [get_graph_login]
|
||||
#'
|
||||
#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview),
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta)
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0)
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
|
@ -85,9 +87,9 @@ public=list(
|
|||
|
||||
# authenticate and get subscriptions
|
||||
initialize=function(tenant="common", app=.az_cli_app_id,
|
||||
password=NULL, username=NULL, certificate=NULL, auth_type=NULL,
|
||||
password=NULL, username=NULL, certificate=NULL, auth_type=NULL, version=2,
|
||||
host="https://graph.microsoft.com/", aad_host="https://login.microsoftonline.com/",
|
||||
token=NULL, ...)
|
||||
scopes=".default", token=NULL, ...)
|
||||
{
|
||||
if(is_azure_token(token))
|
||||
{
|
||||
|
@ -101,7 +103,10 @@ public=list(
|
|||
self$tenant <- normalize_tenant(tenant)
|
||||
app <- normalize_guid(app)
|
||||
|
||||
self$token <- get_azure_token(resource=self$host,
|
||||
if(version == 2)
|
||||
host <- c(paste0(host, scopes), "openid", "offline_access")
|
||||
|
||||
self$token <- get_azure_token(resource=host,
|
||||
tenant=self$tenant,
|
||||
app=app,
|
||||
password=password,
|
||||
|
@ -109,6 +114,7 @@ public=list(
|
|||
certificate=certificate,
|
||||
auth_type=auth_type,
|
||||
aad_host=aad_host,
|
||||
version=version,
|
||||
...)
|
||||
|
||||
NULL
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
#' Azure Active Directory object
|
||||
#'
|
||||
#' Base class representing a object in Microsoft Graph. All other Graph object classes ultimately inherit from this class.
|
||||
#'
|
||||
#' @docType class
|
||||
#' @section Fields:
|
||||
#' - `token`: The token used to authenticate with the Graph host.
|
||||
#' - `tenant`: The Azure Active Directory tenant for this object.
|
||||
#' - `type`: The type of object, in a human-readable format.
|
||||
#' - `properties`: The object properties.
|
||||
#' @section Methods:
|
||||
#' - `new(...)`: Initialize a new directory object. Do not call this directly; see 'Initialization' below.
|
||||
#' - `delete(confirm=TRUE)`: Delete an object. By default, ask for confirmation first.
|
||||
#' - `update(...)`: Update the object information in Azure Active Directory.
|
||||
#' - `do_operation(...)`: Carry out an arbitrary operation on the object.
|
||||
#' - `sync_fields()`: Synchronise the R object with the data in Azure Active Directory.
|
||||
#'
|
||||
#' @section Initialization:
|
||||
#' Objects of this class should not be created directly. Instead, create an object of the appropriate subclass.
|
||||
#'
|
||||
#' @seealso
|
||||
#' [ms_graph], [az_object]
|
||||
#'
|
||||
#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview),
|
||||
#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0)
|
||||
#'
|
||||
#' @format An R6 object of class `ms_object`.
|
||||
#' @export
|
||||
ms_object <- R6::R6Class("ms_object",
|
||||
|
||||
public=list(
|
||||
|
||||
token=NULL,
|
||||
tenant=NULL,
|
||||
|
||||
# user-readable object type
|
||||
type=NULL,
|
||||
|
||||
# object data from server
|
||||
properties=NULL,
|
||||
|
||||
initialize=function(token, tenant=NULL, properties=NULL)
|
||||
{
|
||||
self$token <- token
|
||||
self$tenant <- tenant
|
||||
self$properties <- properties
|
||||
},
|
||||
|
||||
update=function(...)
|
||||
{
|
||||
self$do_operation(body=list(...), encode="json", http_verb="PATCH")
|
||||
self$properties <- self$do_operation()
|
||||
self
|
||||
},
|
||||
|
||||
sync_fields=function()
|
||||
{
|
||||
self$properties <- self$do_operation()
|
||||
invisible(self)
|
||||
},
|
||||
|
||||
delete=function(confirm=TRUE)
|
||||
{
|
||||
if(confirm && interactive())
|
||||
{
|
||||
name <- self$properties$displayName
|
||||
if(is.null(name))
|
||||
name <- self$properties$name
|
||||
if(is.null(name))
|
||||
name <- self$properties$id
|
||||
msg <- sprintf("Do you really want to delete the %s '%s'?",
|
||||
self$type, name)
|
||||
if(!get_confirmation(msg, FALSE))
|
||||
return(invisible(NULL))
|
||||
}
|
||||
|
||||
self$do_operation(http_verb="DELETE")
|
||||
invisible(NULL)
|
||||
},
|
||||
|
||||
do_operation=function(op="", ...)
|
||||
{
|
||||
op <- construct_path(private$api_type, self$properties$id, op)
|
||||
call_graph_endpoint(self$token, op, ...)
|
||||
},
|
||||
|
||||
print=function(...)
|
||||
{
|
||||
cat("<Graph directory object '", self$properties$displayName, "'>\n", sep="")
|
||||
cat(" directory id:", self$properties$id, "\n")
|
||||
cat("---\n")
|
||||
cat(format_public_methods(self))
|
||||
invisible(self)
|
||||
}
|
||||
),
|
||||
|
||||
private=list(
|
||||
|
||||
# object type as it appears in REST API path
|
||||
api_type=NULL,
|
||||
|
||||
get_paged_list=function(lst, next_link_name="@odata.nextLink", value_name="value", simplify=FALSE)
|
||||
{
|
||||
bind_fn <- if(requireNamespace("vctrs"))
|
||||
vctrs::vec_rbind
|
||||
else base::rbind
|
||||
res <- lst[[value_name]]
|
||||
while(!is_empty(lst[[next_link_name]]))
|
||||
{
|
||||
lst <- call_graph_url(self$token, lst[[next_link_name]], simplify=simplify)
|
||||
res <- if(simplify)
|
||||
bind_fn(res, lst[[value_name]]) # this assumes all objects have the exact same fields
|
||||
else c(res, lst[[value_name]])
|
||||
}
|
||||
res
|
||||
},
|
||||
|
||||
init_list_objects=function(lst, type_filter=NULL, default_generator=ms_object)
|
||||
{
|
||||
lst <- lapply(lst, function(obj)
|
||||
{
|
||||
class_gen <- find_class_generator(obj, type_filter, default_generator)
|
||||
if(is.null(class_gen))
|
||||
NULL
|
||||
else class_gen$new(self$token, self$tenant, obj)
|
||||
})
|
||||
lst[!sapply(lst, is.null)]
|
||||
}
|
||||
))
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#' Miscellaneous utility functions
|
||||
#'
|
||||
#' @param lst A named list of objects.
|
||||
#' @param name_fields The components of the objects in `lst`, to be used as names.
|
||||
#' @param x For `is_empty`, An R object.
|
||||
#' @details
|
||||
#' `named_list` extracts from each object in `lst`, the components named by `name_fields`. It then constructs names for `lst` from these components, separated by a `"/"`.
|
||||
#'
|
||||
#' @return
|
||||
#' For `named_list`, the list that was passed in but with names. An empty input results in a _named list_ output: a list of length 0, with a `names` attribute.
|
||||
#'
|
||||
#' For `is_empty`, whether the length of the object is zero (this includes the special case of `NULL`).
|
||||
#'
|
||||
#' @rdname utils
|
||||
#' @export
|
||||
named_list <- function(lst=NULL, name_fields="name")
|
||||
{
|
||||
if(is_empty(lst))
|
||||
return(structure(list(), names=character(0)))
|
||||
|
||||
lst_names <- sapply(name_fields, function(n) sapply(lst, `[[`, n))
|
||||
if(length(name_fields) > 1)
|
||||
{
|
||||
dim(lst_names) <- c(length(lst_names) / length(name_fields), length(name_fields))
|
||||
lst_names <- apply(lst_names, 1, function(nn) paste(nn, collapse="/"))
|
||||
}
|
||||
names(lst) <- lst_names
|
||||
dups <- duplicated(tolower(names(lst)))
|
||||
if(any(dups))
|
||||
{
|
||||
duped_names <- names(lst)[dups]
|
||||
warning("Some names are duplicated: ", paste(unique(duped_names), collapse=" "), call.=FALSE)
|
||||
}
|
||||
lst
|
||||
}
|
||||
|
||||
|
||||
#' @rdname utils
|
||||
#' @export
|
||||
is_empty <- function(x)
|
||||
{
|
||||
length(x) == 0
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
#' Extensible registry of Microsoft Graph classes that AzureGraph supports
|
||||
#'
|
||||
#' @param name The name of the Graph class, eg "user", "servicePrincipal", etc.
|
||||
#' @param R6_generator An R6 class generator corresponding to this Graph class.
|
||||
#' @param check_function A boolean function that checks if a list of properties is for an object of this class.
|
||||
#' @details
|
||||
#' As written, AzureGraph knows about a subset of all the object classes contained in Microsoft Graph. These are mostly the classes originating from Azure Active Directory: users, groups, app registrations, service principals and registered devices.
|
||||
#'
|
||||
#' You can extend AzureGraph by writing your own R6 class that inherits from `ms_object`. If so, you should also _register_ your class by calling `register_graph_class` and providing the generator object, along with a check function. The latter should accept a list of object properties (as obtained from the Graph REST API), and return TRUE/FALSE based on whether the object is of your class.
|
||||
#'
|
||||
#' @return
|
||||
#' An invisible vector of registered class names.
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#'
|
||||
#' # built-in 'az_user' class, for an AAD user object
|
||||
#' register_graph_class("user", az_user,
|
||||
#' function(props) !is.null(props$userPrincipalName))
|
||||
#'
|
||||
#' }
|
||||
#' @export
|
||||
register_graph_class <- function(name, R6_generator, check_function)
|
||||
{
|
||||
if(!R6::is.R6Class(R6_generator))
|
||||
stop("R6_generator should be an R6 class generator object", call.=FALSE)
|
||||
if(!is.function(check_function))
|
||||
stop("check_function should be a function")
|
||||
|
||||
.graph_classes[[name]] <- list(
|
||||
generator=R6_generator,
|
||||
check=check_function
|
||||
)
|
||||
invisible(ls(.graph_classes))
|
||||
}
|
||||
|
||||
|
||||
.graph_classes <- new.env()
|
||||
|
||||
# classes supplied by AzureGraph
|
||||
register_graph_class("user", az_user,
|
||||
function(props) !is.null(props$userPrincipalName))
|
||||
|
||||
register_graph_class("group", az_group,
|
||||
function(props) !is.null(props$groupTypes))
|
||||
|
||||
register_graph_class("application", az_app,
|
||||
function(props) !is.null(props$appId) && is.null(props$servicePrincipalType))
|
||||
|
||||
register_graph_class("servicePrincipal", az_service_principal,
|
||||
function(props) !is.null(props$appId) && !is.null(props$servicePrincipalType))
|
||||
|
||||
register_graph_class("device", az_device,
|
||||
function(props) !is.null(props$publishingState))
|
||||
|
||||
register_graph_class("directoryRole", az_directory_role,
|
||||
function(props) !is.null(props$roleTemplateId))
|
||||
|
||||
find_class_generator <- function(props, type_filter, default_generator)
|
||||
{
|
||||
# use ODATA metadata if available
|
||||
if(!is.null(props$`@odata.type`))
|
||||
{
|
||||
type <- sub("^#microsoft.graph.", "", props$`@odata.type`)
|
||||
if(!(type %in% ls(.graph_classes)))
|
||||
type <- NA
|
||||
}
|
||||
else # check for each known type in turn
|
||||
{
|
||||
type <- NA
|
||||
for(n in ls(.graph_classes))
|
||||
{
|
||||
if(.graph_classes[[n]]$check(props))
|
||||
{
|
||||
type <- n
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# here, 'type' will be one of the known types or NA for unknown
|
||||
# always return the default class if unknown, even if a type filter is provided
|
||||
if(is.na(type))
|
||||
return(default_generator)
|
||||
|
||||
if(is.null(type_filter) || type %in% type_filter)
|
||||
.graph_classes[[type]]$generator
|
||||
else NULL
|
||||
}
|
||||
|
|
@ -4,9 +4,8 @@
|
|||
\name{az_app}
|
||||
\alias{az_app}
|
||||
\title{Registered app in Azure Active Directory}
|
||||
\format{An R6 object of class \code{az_app}, inheriting from \code{az_object}.}
|
||||
\usage{
|
||||
az_app
|
||||
\format{
|
||||
An R6 object of class \code{az_app}, inheriting from \code{az_object}.
|
||||
}
|
||||
\description{
|
||||
Base class representing an AAD app.
|
||||
|
@ -39,7 +38,7 @@ Base class representing an AAD app.
|
|||
\item \code{add_password(password_name=NULL, password_duration=NULL)}: Adds a strong password. \code{password_duration} is the length of time in years that the password remains valid, with default duration 2 years. Returns the ID of the generated password.
|
||||
\item \code{remove_password(password_id, confirm=TRUE)}: Removes the password with the given ID. By default, ask for confirmation first.
|
||||
\item \code{add_certificate(certificate)}: Adds a certificate for authentication. This can be specified as the name of a .pfx or .pem file, an \code{openssl::cert} object, an \code{AzureKeyVault::stored_cert} object, or a raw or character vector.
|
||||
\item \code{remove_certificate(certificate_id, confirm=TRUE}): Removes the certificate with the given ID. By default, ask for confirmation first.
|
||||
\item \verb{remove_certificate(certificate_id, confirm=TRUE}): Removes the certificate with the given ID. By default, ask for confirmation first.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,4 +97,3 @@ app$update(displayName="MyRenamedApp")
|
|||
\seealso{
|
||||
\link{ms_graph}, \link{az_service_principal}, \link{az_user}, \link{az_group}, \link{az_object}
|
||||
}
|
||||
\keyword{datasets}
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
\name{az_device}
|
||||
\alias{az_device}
|
||||
\title{Device in Azure Active Directory}
|
||||
\format{An R6 object of class \code{az_device}, inheriting from \code{az_object}.}
|
||||
\usage{
|
||||
az_device
|
||||
\format{
|
||||
An R6 object of class \code{az_device}, inheriting from \code{az_object}.
|
||||
}
|
||||
\description{
|
||||
Base class representing a registered device.
|
||||
Class representing a registered device.
|
||||
}
|
||||
\section{Fields}{
|
||||
|
||||
|
@ -43,6 +42,5 @@ Create objects of this class via the \code{list_registered_devices()} and \code{
|
|||
\link{ms_graph}, \link{az_user}, \link{az_object}
|
||||
|
||||
\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview},
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta}{REST API reference}
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference}
|
||||
}
|
||||
\keyword{datasets}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/az_dir_role.R
|
||||
\docType{class}
|
||||
\name{az_directory_role}
|
||||
\alias{az_directory_role}
|
||||
\title{Directory role}
|
||||
\format{
|
||||
An R6 object of class \code{az_directory_role}, inheriting from \code{az_object}.
|
||||
}
|
||||
\description{
|
||||
Class representing a role in Azure Active Directory.
|
||||
}
|
||||
\section{Fields}{
|
||||
|
||||
\itemize{
|
||||
\item \code{token}: The token used to authenticate with the Graph host.
|
||||
\item \code{tenant}: The Azure Active Directory tenant for this role.
|
||||
\item \code{type}: always "directory role" for a directory role object.
|
||||
\item \code{properties}: The item properties.
|
||||
}
|
||||
}
|
||||
|
||||
\section{Methods}{
|
||||
|
||||
\itemize{
|
||||
\item \code{new(...)}: Initialize a new object. Do not call this directly; see 'Initialization' below.
|
||||
\item \code{delete(confirm=TRUE)}: Delete this item. By default, ask for confirmation first.
|
||||
\item \code{update(...)}: Update the item's properties in Microsoft Graph.
|
||||
\item \code{do_operation(...)}: Carry out an arbitrary operation on the item.
|
||||
\item \code{sync_fields()}: Synchronise the R object with the item metadata in Microsoft Graph.
|
||||
}
|
||||
}
|
||||
|
||||
\section{Initialization}{
|
||||
|
||||
Currently support for directory roles is limited. Objects of this class should not be initialized directly.
|
||||
}
|
||||
|
||||
\seealso{
|
||||
\link{ms_graph}, \link{az_user}
|
||||
|
||||
\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview},
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference}
|
||||
}
|
|
@ -4,12 +4,11 @@
|
|||
\name{az_group}
|
||||
\alias{az_group}
|
||||
\title{Group in Azure Active Directory}
|
||||
\format{An R6 object of class \code{az_group}, inheriting from \code{az_object}.}
|
||||
\usage{
|
||||
az_group
|
||||
\format{
|
||||
An R6 object of class \code{az_group}, inheriting from \code{az_object}.
|
||||
}
|
||||
\description{
|
||||
Base class representing an AAD group.
|
||||
Class representing an AAD group.
|
||||
}
|
||||
\section{Fields}{
|
||||
|
||||
|
@ -59,6 +58,5 @@ grp$list_owners()
|
|||
\link{ms_graph}, \link{az_app}, \link{az_user}, \link{az_object}
|
||||
|
||||
\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview},
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta}{REST API reference}
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference}
|
||||
}
|
||||
\keyword{datasets}
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
\name{az_object}
|
||||
\alias{az_object}
|
||||
\title{Azure Active Directory object}
|
||||
\format{An R6 object of class \code{az_object}.}
|
||||
\usage{
|
||||
az_object
|
||||
\format{
|
||||
An R6 object of class \code{az_object}, inheriting from \code{ms_object}.
|
||||
}
|
||||
\description{
|
||||
Base class representing a directory object in Microsoft Graph.
|
||||
Base class representing an Azure Active Directory object in Microsoft Graph.
|
||||
}
|
||||
\section{Fields}{
|
||||
|
||||
|
@ -43,6 +42,5 @@ Objects of this class should not be created directly. Instead, create an object
|
|||
\link{ms_graph}, \link{az_app}, \link{az_service_principal}, \link{az_user}, \link{az_group}
|
||||
|
||||
\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview},
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta}{REST API reference}
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference}
|
||||
}
|
||||
\keyword{datasets}
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
\name{az_service_principal}
|
||||
\alias{az_service_principal}
|
||||
\title{Service principal in Azure Active Directory}
|
||||
\format{An R6 object of class \code{az_service_principal}, inheriting from \code{az_object}.}
|
||||
\usage{
|
||||
az_service_principal
|
||||
\format{
|
||||
An R6 object of class \code{az_service_principal}, inheriting from \code{az_object}.
|
||||
}
|
||||
\description{
|
||||
Base class representing an AAD service principal.
|
||||
Class representing an AAD service principal.
|
||||
}
|
||||
\section{Fields}{
|
||||
|
||||
|
@ -43,6 +42,5 @@ Creating new objects of this class should be done via the \code{create_service_p
|
|||
\link{ms_graph}, \link{az_app}, \link{az_object}
|
||||
|
||||
\href{https://docs.microsoft.com/en-us/graph/overview}{Azure Microsoft Graph overview},
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta}{REST API reference}
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference}
|
||||
}
|
||||
\keyword{datasets}
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
\name{az_user}
|
||||
\alias{az_user}
|
||||
\title{User in Azure Active Directory}
|
||||
\format{An R6 object of class \code{az_user}, inheriting from \code{az_object}.}
|
||||
\usage{
|
||||
az_user
|
||||
\format{
|
||||
An R6 object of class \code{az_user}, inheriting from \code{az_object}.
|
||||
}
|
||||
\description{
|
||||
Base class representing an AAD user account.
|
||||
Class representing an AAD user account.
|
||||
}
|
||||
\section{Fields}{
|
||||
|
||||
|
@ -31,7 +30,7 @@ Base class representing an AAD user account.
|
|||
\item \code{sync_fields()}: Synchronise the R object with the app data in Azure Active Directory.
|
||||
\item \code{list_group_memberships()}: Return the IDs of all groups this user is a member of.
|
||||
\item \code{list_object_memberships()}: Return the IDs of all groups, administrative units and directory roles this user is a member of.
|
||||
\item \code{list_direct_memberships(id_only=TRUE)}: List the groups this user is a direct member of. Set \code{id_only=TRUE} to return only a vector of group IDs (the default), or \code{id_only=FALSE} to return a list of group objects.
|
||||
\item \code{list_direct_memberships(id_only=TRUE)}: List the groups and directory roles this user is a direct member of. Set \code{id_only=TRUE} to return only a vector of IDs (the default), or \code{id_only=FALSE} to return a list of group objects.
|
||||
\item \code{list_owned_objects(type=c("user", "group", "application", "servicePrincipal"))}: List directory objects (groups/apps/service principals) owned by this user. Specify the \code{type} argument to filter the result for specific object type(s).
|
||||
\item \code{list_created_objects(type=c("user", "group", "application", "servicePrincipal"))}: List directory objects (groups/apps/service principals) created by this user. Specify the \code{type} argument to filter the result for specific object type(s).
|
||||
\item \code{list_owned_devices()}: List the devices owned by this user.
|
||||
|
@ -71,6 +70,5 @@ usr$list_owned_objects(type=c("application", "servicePrincipal"))
|
|||
\link{ms_graph}, \link{az_app}, \link{az_group}, \link{az_device}, \link{az_object}
|
||||
|
||||
\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview},
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta}{REST API reference}
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference}
|
||||
}
|
||||
\keyword{datasets}
|
||||
|
|
|
@ -11,7 +11,7 @@ call_graph_endpoint(token, operation, ..., options = list(),
|
|||
call_graph_url(token, url, ..., body = NULL, encode = "json",
|
||||
http_verb = c("GET", "DELETE", "PUT", "POST", "HEAD", "PATCH"),
|
||||
http_status_handler = c("stop", "warn", "message", "pass"),
|
||||
auto_refresh = TRUE)
|
||||
simplify = FALSE, auto_refresh = TRUE)
|
||||
}
|
||||
\arguments{
|
||||
\item{token}{An Azure OAuth token, of class \link{AzureToken}.}
|
||||
|
@ -34,6 +34,8 @@ call_graph_url(token, url, ..., body = NULL, encode = "json",
|
|||
|
||||
\item{http_status_handler}{How to handle in R the HTTP status code of a response. \code{"stop"}, \code{"warn"} or \code{"message"} will call the appropriate handlers in httr, while \code{"pass"} ignores the status code.}
|
||||
|
||||
\item{simplify}{Whether to turn arrays of objects in the JSON response into data frames. Set this to \code{TRUE} if you are expecting the endpoint to return tabular data and you want a tabular result, as opposed to a list of objects.}
|
||||
|
||||
\item{auto_refresh}{Whether to refresh/renew the OAuth token if it is no longer valid.}
|
||||
}
|
||||
\value{
|
||||
|
@ -46,6 +48,8 @@ Call the Microsoft Graph REST API
|
|||
}
|
||||
\details{
|
||||
These functions form the low-level interface between R and Microsoft Graph. \code{call_graph_endpoint} forms a URL from its arguments and passes it to \code{call_graph_url}.
|
||||
|
||||
If \code{simplify} is \code{TRUE}, \code{call_graph_url} will exploit the ability of \code{jsonlite::fromJSON} to convert arrays of objects into R data frames. This can be useful for REST calls that return tabular data. However, it can also cause problems for \emph{paged} lists, where each page will be turned into a separate data frame; as the individual objects may not have the same fields, the resulting data frames will also have differing columns. This will cause base R's \code{rbind} to fail when binding the pages together. When processing paged lists, AzureGraph will use \code{vctrs::vec_rbind} instead of \code{rbind} when the vctrs package is available; \code{vec_rbind} does not have this problem. For safety, you should only set \code{simplify=TRUE} when vctrs is installed.
|
||||
}
|
||||
\seealso{
|
||||
\link[httr:GET]{httr::GET}, \link[httr:PUT]{httr::PUT}, \link[httr:POST]{httr::POST}, \link[httr:DELETE]{httr::DELETE}, \link[httr:stop_for_status]{httr::stop_for_status}, \link[httr:content]{httr::content}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/format.R
|
||||
\name{format_public_fields}
|
||||
\alias{format_public_fields}
|
||||
\alias{format_public_methods}
|
||||
\title{Format a Microsoft Graph or Azure object}
|
||||
\usage{
|
||||
format_public_fields(env, exclude = character(0))
|
||||
|
||||
format_public_methods(env)
|
||||
}
|
||||
\arguments{
|
||||
\item{env}{An R6 object's environment for printing.}
|
||||
|
||||
\item{exclude}{Objects in \code{env} to exclude from the printout.}
|
||||
}
|
||||
\description{
|
||||
Miscellaneous functions for printing Microsoft Graph and Azure R6 objects
|
||||
}
|
||||
\details{
|
||||
These are utilities to aid in printing R6 objects created by this package or its descendants. They are not meant to be called by the user.
|
||||
}
|
|
@ -7,13 +7,14 @@
|
|||
\alias{list_graph_logins}
|
||||
\title{Login to Azure Active Directory Graph}
|
||||
\usage{
|
||||
create_graph_login(tenant = "common", app = .az_cli_app_id,
|
||||
password = NULL, username = NULL, certificate = NULL,
|
||||
auth_type = NULL, host = "https://graph.microsoft.com/",
|
||||
aad_host = "https://login.microsoftonline.com/", config_file = NULL,
|
||||
token = NULL, ...)
|
||||
create_graph_login(tenant = "common", app = NULL, password = NULL,
|
||||
username = NULL, certificate = NULL, auth_type = NULL, version = 2,
|
||||
host = "https://graph.microsoft.com/",
|
||||
aad_host = "https://login.microsoftonline.com/", scopes = ".default",
|
||||
config_file = NULL, token = NULL, ...)
|
||||
|
||||
get_graph_login(tenant = "common", selection = NULL, refresh = TRUE)
|
||||
get_graph_login(tenant = "common", selection = NULL, app = NULL,
|
||||
scopes = NULL, auth_type = NULL, refresh = TRUE)
|
||||
|
||||
delete_graph_login(tenant = "common", confirm = TRUE)
|
||||
|
||||
|
@ -32,11 +33,15 @@ list_graph_logins()
|
|||
|
||||
\item{auth_type}{The OAuth authentication method to use, one of "client_credentials", "authorization_code", "device_code" or "resource_owner". If \code{NULL}, this is chosen based on the presence of the \code{username} and \code{password} arguments.}
|
||||
|
||||
\item{host}{Your Microsoft Graph host. Defaults to \code{https://graph.microsoft.com/}. Change this if you are using a government or private cloud.}
|
||||
\item{version}{The Azure Active Directory version to use for authenticating.}
|
||||
|
||||
\item{aad_host}{Azure Active Directory host for authentication. Defaults to \code{https://login.microsoftonline.com/}. Change this if you are using a government or private cloud.}
|
||||
\item{host}{Your Microsoft Graph host. Defaults to \verb{https://graph.microsoft.com/}. Change this if you are using a government or private cloud.}
|
||||
|
||||
\item{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 \code{az ad sp create-for-rbac} command.}
|
||||
\item{aad_host}{Azure Active Directory host for authentication. Defaults to \verb{https://login.microsoftonline.com/}. Change this if you are using a government or private cloud.}
|
||||
|
||||
\item{scopes}{The Microsoft Graph scopes (permissions) to obtain for this Graph login. Only for \code{version=2}.}
|
||||
|
||||
\item{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 \verb{az ad sp create-for-rbac} command.}
|
||||
|
||||
\item{token}{Optionally, an OAuth 2.0 token, of class \link[AzureAuth:AzureToken]{AzureAuth::AzureToken}. This allows you to reuse the authentication details for an existing session. If supplied, all other arguments to \code{create_graph_login} will be ignored.}
|
||||
|
||||
|
@ -59,7 +64,7 @@ Login to Azure Active Directory Graph
|
|||
\details{
|
||||
\code{create_graph_login} creates a login client to authenticate with Microsoft Graph, using the supplied arguments. The authentication token is obtained using \link{get_azure_token}, which automatically caches and reuses tokens for subsequent sessions. Note that credentials are only cached if you allowed AzureGraph to create a data directory at package startup.
|
||||
|
||||
\code{get_graph_login} returns a login client by retrieving previously saved credentials. It searches for saved credentials according to the supplied tenant; if multiple logins are found, it will prompt for you to choose one.
|
||||
\code{get_graph_login} returns a previously created login client. If there are multiple existing clients, you can specify which client to return via the \code{selection}, \code{app}, \code{scopes} and \code{auth_type} arguments. If you don't specify which one to return, it will pop up a menu and ask you to choose one.
|
||||
|
||||
One difference between \code{create_graph_login} and \code{get_graph_login} is the former will delete any previously saved credentials that match the arguments it was given. You can use this to force AzureGraph to remove obsolete tokens that may be lying around.
|
||||
}
|
||||
|
@ -71,7 +76,7 @@ If you are using a Linux \href{https://azure.microsoft.com/en-us/services/virtua
|
|||
\examples{
|
||||
\dontrun{
|
||||
|
||||
# without any arguments, this will create a client using your AAD credentials
|
||||
# without any arguments, this will create a client using your AAD organisational account
|
||||
az <- create_graph_login()
|
||||
|
||||
# retrieve the login in subsequent sessions
|
||||
|
@ -79,16 +84,24 @@ az <- get_graph_login()
|
|||
|
||||
# this will create an Microsoft Graph client for the tenant 'microsoft.onmicrosoft.com',
|
||||
# using the client_credentials method
|
||||
az <- create_graph_login("microsoft", app="{app_id}", password="{password}")
|
||||
az <- create_graph_login("mytenant", app="{app_id}", password="{password}")
|
||||
|
||||
# you can also login using credentials in a json file
|
||||
az <- create_graph_login(config_file="~/creds.json")
|
||||
|
||||
# creating and obtaining a login with specific scopes
|
||||
create_graph_login("mytenant", scopes=c("User.Read", "Files.ReadWrite.All"))
|
||||
get_graph_login("mytenant", scopes=c("User.Read", "Files.ReadWrite.All"))
|
||||
|
||||
# to use your personal account, set the tenant to one of the following
|
||||
create_graph_login("9188040d-6c67-4c5b-b112-36a304b66dad")
|
||||
create_graph_login("consumers") # requires AzureAuth 1.2.6
|
||||
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
\link{ms_graph}, \link[AzureAuth:get_azure_token]{AzureAuth::get_azure_token} for more details on authentication methods
|
||||
|
||||
\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview},
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta}{REST API reference}
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference}
|
||||
}
|
||||
|
|
10
man/info.Rd
10
man/info.Rd
|
@ -5,7 +5,9 @@
|
|||
\alias{is_service_principal}
|
||||
\alias{is_user}
|
||||
\alias{is_group}
|
||||
\alias{is_directory_object}
|
||||
\alias{is_directory_role}
|
||||
\alias{is_aad_object}
|
||||
\alias{is_msgraph_object}
|
||||
\title{Informational functions}
|
||||
\usage{
|
||||
is_app(object)
|
||||
|
@ -16,7 +18,11 @@ is_user(object)
|
|||
|
||||
is_group(object)
|
||||
|
||||
is_directory_object(object)
|
||||
is_directory_role(object)
|
||||
|
||||
is_aad_object(object)
|
||||
|
||||
is_msgraph_object(object)
|
||||
}
|
||||
\arguments{
|
||||
\item{object}{An R object.}
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
\name{ms_graph}
|
||||
\alias{ms_graph}
|
||||
\title{Azure Active Directory Graph}
|
||||
\format{An R6 object of class \code{ms_graph}.}
|
||||
\usage{
|
||||
ms_graph
|
||||
\format{
|
||||
An R6 object of class \code{ms_graph}.
|
||||
}
|
||||
\description{
|
||||
Base class for interacting with Microsoft Graph API.
|
||||
|
@ -43,8 +42,10 @@ To authenticate with the \code{ms_graph} class directly, provide the following a
|
|||
\item \code{username}: if \code{auth_type == "resource_owner"}, your username.
|
||||
\item \code{certificate}: If `auth_type == "client_credentials", a certificate to authenticate with. This is a more secure alternative to using an app secret.
|
||||
\item \code{auth_type}: The OAuth authentication method to use, one of "client_credentials", "authorization_code", "device_code" or "resource_owner". See \link{get_azure_token} for how the default method is chosen, along with some caveats.
|
||||
\item \code{host}: your Microsoft Graph host. Defaults to \code{https://graph.microsoft.com/}.
|
||||
\item \code{aad_host}: Azure Active Directory host for authentication. Defaults to \code{https://login.microsoftonline.com/}. Change this if you are using a government or private cloud.
|
||||
\item \code{version}: The Azure Active Directory (AAD) version to use for authenticating.
|
||||
\item \code{host}: your Microsoft Graph host. Defaults to \verb{https://graph.microsoft.com/}.
|
||||
\item \code{aad_host}: Azure Active Directory host for authentication. Defaults to \verb{https://login.microsoftonline.com/}. Change this if you are using a government or private cloud.
|
||||
\item \code{scopes}: The Microsoft Graph scopes (permissions) to obtain for this Graph login. Only for \code{version=2}.
|
||||
\item \code{token}: Optionally, an OAuth 2.0 token, of class \link[AzureAuth:AzureToken]{AzureAuth::AzureToken}. This allows you to reuse the authentication details for an existing session. If supplied, all other arguments will be ignored.
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +92,5 @@ gr$create_app("mycertapp", password=FALSE, certificate=cert)
|
|||
\link{create_graph_login}, \link{get_graph_login}
|
||||
|
||||
\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview},
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-beta}{REST API reference}
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference}
|
||||
}
|
||||
\keyword{datasets}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/ms_object.R
|
||||
\docType{class}
|
||||
\name{ms_object}
|
||||
\alias{ms_object}
|
||||
\title{Azure Active Directory object}
|
||||
\format{
|
||||
An R6 object of class \code{ms_object}.
|
||||
}
|
||||
\description{
|
||||
Base class representing a object in Microsoft Graph. All other Graph object classes ultimately inherit from this class.
|
||||
}
|
||||
\section{Fields}{
|
||||
|
||||
\itemize{
|
||||
\item \code{token}: The token used to authenticate with the Graph host.
|
||||
\item \code{tenant}: The Azure Active Directory tenant for this object.
|
||||
\item \code{type}: The type of object, in a human-readable format.
|
||||
\item \code{properties}: The object properties.
|
||||
}
|
||||
}
|
||||
|
||||
\section{Methods}{
|
||||
|
||||
\itemize{
|
||||
\item \code{new(...)}: Initialize a new directory object. Do not call this directly; see 'Initialization' below.
|
||||
\item \code{delete(confirm=TRUE)}: Delete an object. By default, ask for confirmation first.
|
||||
\item \code{update(...)}: Update the object information in Azure Active Directory.
|
||||
\item \code{do_operation(...)}: Carry out an arbitrary operation on the object.
|
||||
\item \code{sync_fields()}: Synchronise the R object with the data in Azure Active Directory.
|
||||
}
|
||||
}
|
||||
|
||||
\section{Initialization}{
|
||||
|
||||
Objects of this class should not be created directly. Instead, create an object of the appropriate subclass.
|
||||
}
|
||||
|
||||
\seealso{
|
||||
\link{ms_graph}, \link{az_object}
|
||||
|
||||
\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview},
|
||||
\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/zzz_class_directory.R
|
||||
\name{register_graph_class}
|
||||
\alias{register_graph_class}
|
||||
\title{Extensible registry of Microsoft Graph classes that AzureGraph supports}
|
||||
\usage{
|
||||
register_graph_class(name, R6_generator, check_function)
|
||||
}
|
||||
\arguments{
|
||||
\item{name}{The name of the Graph class, eg "user", "servicePrincipal", etc.}
|
||||
|
||||
\item{R6_generator}{An R6 class generator corresponding to this Graph class.}
|
||||
|
||||
\item{check_function}{A boolean function that checks if a list of properties is for an object of this class.}
|
||||
}
|
||||
\value{
|
||||
An invisible vector of registered class names.
|
||||
}
|
||||
\description{
|
||||
Extensible registry of Microsoft Graph classes that AzureGraph supports
|
||||
}
|
||||
\details{
|
||||
As written, AzureGraph knows about a subset of all the object classes contained in Microsoft Graph. These are mostly the classes originating from Azure Active Directory: users, groups, app registrations, service principals and registered devices.
|
||||
|
||||
You can extend AzureGraph by writing your own R6 class that inherits from \code{ms_object}. If so, you should also \emph{register} your class by calling \code{register_graph_class} and providing the generator object, along with a check function. The latter should accept a list of object properties (as obtained from the Graph REST API), and return TRUE/FALSE based on whether the object is of your class.
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
|
||||
# built-in 'az_user' class, for an AAD user object
|
||||
register_graph_class("user", az_user,
|
||||
function(props) !is.null(props$userPrincipalName))
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/utils.R
|
||||
\name{named_list}
|
||||
\alias{named_list}
|
||||
\alias{is_empty}
|
||||
\title{Miscellaneous utility functions}
|
||||
\usage{
|
||||
named_list(lst = NULL, name_fields = "name")
|
||||
|
||||
is_empty(x)
|
||||
}
|
||||
\arguments{
|
||||
\item{lst}{A named list of objects.}
|
||||
|
||||
\item{name_fields}{The components of the objects in \code{lst}, to be used as names.}
|
||||
|
||||
\item{x}{For \code{is_empty}, An R object.}
|
||||
}
|
||||
\value{
|
||||
For \code{named_list}, the list that was passed in but with names. An empty input results in a \emph{named list} output: a list of length 0, with a \code{names} attribute.
|
||||
|
||||
For \code{is_empty}, whether the length of the object is zero (this includes the special case of \code{NULL}).
|
||||
}
|
||||
\description{
|
||||
Miscellaneous utility functions
|
||||
}
|
||||
\details{
|
||||
\code{named_list} extracts from each object in \code{lst}, the components named by \code{name_fields}. It then constructs names for \code{lst} from these components, separated by a \code{"/"}.
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
context("Class registration")
|
||||
|
||||
test_that("Class registration works",
|
||||
{
|
||||
newclass <- R6::R6Class("newclass", inherit=ms_object)
|
||||
expect_false("newclass" %in% ls(.graph_classes))
|
||||
expect_silent(register_graph_class("newclass", newclass, function(x) FALSE))
|
||||
expect_true("newclass" %in% ls(.graph_classes))
|
||||
expect_error(register_graph_class("badclass", "badclassname", FALSE))
|
||||
})
|
|
@ -9,6 +9,7 @@ if(tenant == "" || app == "")
|
|||
if(!interactive())
|
||||
skip("Authentication tests skipped: must be in interactive session")
|
||||
|
||||
scopes <- c("https://graph.microsoft.com/.default", "openid", "offline_access")
|
||||
|
||||
test_that("Graph authentication works",
|
||||
{
|
||||
|
@ -16,7 +17,7 @@ test_that("Graph authentication works",
|
|||
expect_is(gr, "ms_graph")
|
||||
expect_true(is_azure_token(gr$token))
|
||||
|
||||
token <- get_azure_token("https://graph.microsoft.com/", tenant, app)
|
||||
token <- get_azure_token(scopes, tenant, app, version=2)
|
||||
|
||||
gr2 <- ms_graph$new(token=token)
|
||||
expect_is(gr2, "ms_graph")
|
||||
|
|
|
@ -3,7 +3,7 @@ context("App creation/deletion")
|
|||
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
|
||||
pemfile <- Sys.getenv("AZ_TEST_CERT_FILE")
|
||||
|
||||
if(tenant == "" || cert_thumb == "")
|
||||
if(tenant == "" || pemfile == "")
|
||||
skip("App method tests skipped: login credentials not set")
|
||||
|
||||
if(!interactive())
|
||||
|
|
|
@ -2,6 +2,7 @@ context("Users/groups")
|
|||
|
||||
tenant <- Sys.getenv("AZ_TEST_TENANT_ID")
|
||||
user <- Sys.getenv("AZ_TEST_USERPRINCIPALNAME")
|
||||
admin_user <- Sys.getenv("AZ_TEST_ADMINUSERPRINCIPALNAME")
|
||||
|
||||
if(tenant == "" || user == "")
|
||||
skip("User method tests skipped: login credentials not set")
|
||||
|
@ -15,7 +16,7 @@ gr <- get_graph_login(tenant=tenant)
|
|||
test_that("User/group read functionality works",
|
||||
{
|
||||
me <- gr$get_user()
|
||||
expect_equal(me$properties$userPrincipalName, user)
|
||||
expect_equal(me$properties$userPrincipalName, admin_user)
|
||||
|
||||
me2 <- gr$get_user(user)
|
||||
expect_equal(me2$properties$userPrincipalName, user)
|
||||
|
@ -30,7 +31,7 @@ test_that("User/group read functionality works",
|
|||
expect_true(is.character(grps2))
|
||||
|
||||
grps3 <- me$list_direct_memberships(id_only=FALSE)
|
||||
expect_true(all(sapply(grps3, is_group)))
|
||||
expect_true(all(sapply(grps3, function(x) is_group(x) || is_directory_role(x))))
|
||||
expect_true(all(sapply(grps3, function(g) !is.null(g$properties$id))))
|
||||
|
||||
grp <- gr$get_group(grps1[1])
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
---
|
||||
title: "Extending AzureGraph"
|
||||
author: Hong Ooi
|
||||
output: rmarkdown::html_vignette
|
||||
vignette: >
|
||||
%\VignetteIndexEntry{Extending}
|
||||
%\VignetteEngine{knitr::rmarkdown}
|
||||
%\VignetteEncoding{utf8}
|
||||
---
|
||||
|
||||
As written, AzureGraph provides support for Microsoft Graph objects derived from Azure Active Directory (AAD): users, groups, app registrations and service principals. This vignette describes how to extend it to support other services.
|
||||
|
||||
## Extend the `ms_object` base class
|
||||
|
||||
AzureGraph provides the `ms_object` class to represent a generic object in Graph. You can extend this to support specific services by adding custom methods and fields.
|
||||
|
||||
For example, the [SharePointR](https://github.com/hongooi73/SharePointR) package extends AzureGraph to support SharePoint Online sites and OneDrive filesystems (both personal and business). This is the `ms_site` class from that package, which represents a SharePoint site. To save space, the actual code in the new methods has been elided.
|
||||
|
||||
```r
|
||||
ms_site <- R6::R6Class("ms_site", inherit=ms_object,
|
||||
|
||||
public=list(
|
||||
|
||||
initialize=function(token, tenant=NULL, properties=NULL)
|
||||
{
|
||||
self$type <- "site"
|
||||
private$api_type <- "sites"
|
||||
super$initialize(token, tenant, properties)
|
||||
},
|
||||
|
||||
list_drives=function() {}, # ...
|
||||
|
||||
get_drive=function(drive_id=NULL) {}, # ...
|
||||
|
||||
list_subsites=function() {}, # ...
|
||||
|
||||
get_list=function(list_name=NULL, list_id=NULL) {}, # ...
|
||||
|
||||
print=function(...)
|
||||
{
|
||||
cat("<Sharepoint site '", self$properties$displayName, "'>\n", sep="")
|
||||
cat(" directory id:", self$properties$id, "\n")
|
||||
cat(" web link:", self$properties$webUrl, "\n")
|
||||
cat(" description:", self$properties$description, "\n")
|
||||
cat("---\n")
|
||||
cat(format_public_methods(self))
|
||||
invisible(self)
|
||||
}
|
||||
))
|
||||
```
|
||||
|
||||
Note the following:
|
||||
|
||||
- The `initialize()` method of your class should take 3 arguments: the OAuth2 token for authenticating with Graph, the name of the AAD tenant, and the list of properties for this object as obtained from the Graph endpoint. It should set 2 fields: `self$type` contains a human-readable name for this type of object, and `private$api_type` contains the object type as it appears in the URL of a Graph API request. It should then call the superclass method to complete the initialisation. `initialize()` itself should not contact the Graph endpoint; it should merely create and populate the R6 object given the response from a previous request.
|
||||
|
||||
- The `print()` method is optional and should display any properties that can help identify this object to a human reader.
|
||||
|
||||
You can read the code of the existing classes such as `az_user`, `az_app` etc to see how to call the API. The `do_operation()` method should suffice for any regular communication with the Graph endpoint.
|
||||
|
||||
## Register the class with `register_graph_class`
|
||||
|
||||
Having defined your new class, call `register_graph_class` so that AzureGraph becomes aware of it and can automatically use it to populate object lists. If you are writing a new package, the `register_graph_class` call should go in your package's `.onLoad` startup function. For example, registering the `ms_site` SharePoint class looks like this.
|
||||
|
||||
```r
|
||||
.onLoad <- function(libname, pkgname)
|
||||
{
|
||||
register_graph_class("site", ms_site,
|
||||
function(props) grepl("sharepoint", props$id, fixed=TRUE))
|
||||
|
||||
# ... other startup code ...
|
||||
}
|
||||
```
|
||||
|
||||
`register_graph_class` takes 3 arguments:
|
||||
|
||||
- The name of the object class, as it appears in the [Microsoft Graph online documentation](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0).
|
||||
- The R6 class generator object, as defined in the previous section.
|
||||
- A check function which takes a list of properties (as returned by the Graph API) and returns TRUE/FALSE based on whether the properties are for an object of your class. This is necessary as some Graph calls that return lists of objects do not always include explicit metadata indicating the type of each object, hence the type must be inferred from the properties.
|
||||
|
||||
## Add getter and setter methods
|
||||
|
||||
Finally, so that people can use the same workflow with your class as with AzureGraph-supplied classes, you can add getter and setter methods to `ms_graph` and any other classes for which it's appropriate. Again, if you're writing a package, this should happen in the `.onLoad` function.
|
||||
|
||||
In the case of `ms_site`, it's appropriate to add a getter method not just to `ms_graph`, but also the `ms_group` class. This is because SharePoint sites have associated user groups, hence it's useful to be able to retrieve a site given the object for a group. The relevant code in the `.onLoad` function looks like this (slightly simplified):
|
||||
|
||||
```r
|
||||
.onLoad <- function(libname, pkgname)
|
||||
{
|
||||
# ...
|
||||
|
||||
ms_graph$set("public", "get_sharepoint_site", overwrite=TRUE,
|
||||
function(site_url=NULL, site_id=NULL)
|
||||
{
|
||||
op <- if(is.null(site_url) && !is.null(site_id))
|
||||
file.path("sites", site_id)
|
||||
else if(!is.null(site_url) && is.null(site_id))
|
||||
{
|
||||
site_url <- httr::parse_url(site_url)
|
||||
file.path("sites", paste0(site_url$hostname, ":"), site_url$path)
|
||||
}
|
||||
else stop("Must supply either site ID or URL")
|
||||
|
||||
ms_site$new(self$token, self$tenant, self$call_graph_endpoint(op))
|
||||
})
|
||||
|
||||
az_group$set("public", "get_sharepoint_site", overwrite=TRUE,
|
||||
function()
|
||||
{
|
||||
res <- self$do_operation("sites/root")
|
||||
ms_site$new(self$token, self$tenant, res)
|
||||
})
|
||||
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
Once this is done, the object for a SharePoint site can be instantiated as follows:
|
||||
|
||||
```r
|
||||
library(AzureGraph)
|
||||
library(SharePointR)
|
||||
|
||||
gr <- get_graph_login()
|
||||
|
||||
# directly from the Graph client
|
||||
mysite1 <- gr$get_sharepoint_site("https://mytenant.sharepoint.com/sites/my-site-name")
|
||||
|
||||
# or via a group
|
||||
mygroup <- gr$get_group("my-group-guid")
|
||||
mysite2 <- mygroup$get_sharepoint_site()
|
||||
```
|
|
@ -10,7 +10,7 @@ vignette: >
|
|||
|
||||
[Microsoft Graph](https://docs.microsoft.com/en-us/graph/overview) is a comprehensive framework for accessing data in various online Microsoft services, including Azure Active Directory (AAD), Office 365, OneDrive, Teams, and more. AzureGraph is a simple R6-based interface to the Graph REST API, and is the companion package to [AzureRMR](https://github.com/Azure/AzureRMR) and [AzureAuth](https://github.com/Azure/AzureAuth).
|
||||
|
||||
Currently, AzureGraph aims to provide an R interface only to the AAD part, with a view to supporting R interoperability with Azure: registered apps and service principals, users and groups. Like AzureRMR, it could potentially be extended to support other services.
|
||||
Currently, AzureGraph aims to provide an R interface only to the AAD part, with a view to supporting R interoperability with Azure: registered apps and service principals, users and groups. However, it can be extended to support other services; for more information, see the "Extending AzureGraph" vignette.
|
||||
|
||||
## Authentication
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче