mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-16 11:58:09 +00:00
migrate to msgraph (#966)
* migrate to msgraph * add subscription id to query_microsoft_graph * migrating remaingin references * formatting * adding missing dependencies * flake fix * fix get_tenant_id * cleanup * formatting * migrate application creation in deploy.py * foramt * mypy fix * isort * isort * format * bug fixes * specify the correct signInAudience * fix backing service principal creation fix preauthorized application * remove remaining references to graphrbac * fix ms graph authentication * formatting * fix typo * format * deployment fix * set implicitGrantSettings in the deployment * format * fix deployment * fix graph authentication on the server * use the current cli logged in account to retrive the backend token cache * assign the the msgraph app role permissions to the web app during the deployment * formatting * fix build * build fix * fix bandit issue * mypy fix * isort * deploy fixes * formatting * remove assign_app_permissions * mypy fix * build fix * mypy fix * format * formatting * flake fix * remove webapp identity permission assignment * remove unused reference to assign_app_role * remove manual registration message * fixing name and logging * address PR coments * address PR comments * build fix * lint * lint * mypy fix * mypy fix * formatting * address PR comments * linting * lint * remove ONEFUZZ_AAD_GROUP_ID check * regenerate webhook_events.md * change return type of query_microsoft_graph_list * fix tenant_id Co-authored-by: Marc Greisen <marc@greisen.org> Co-authored-by: Stas <stishkin@live.com>
This commit is contained in:
@ -6,12 +6,12 @@
|
||||
import functools
|
||||
import logging
|
||||
import os
|
||||
from typing import Any, Callable, List, TypeVar, cast
|
||||
import urllib.parse
|
||||
from typing import Any, Callable, Dict, List, Optional, TypeVar, cast
|
||||
from uuid import UUID
|
||||
|
||||
import requests
|
||||
from azure.core.exceptions import ClientAuthenticationError
|
||||
from azure.graphrbac import GraphRbacManagementClient
|
||||
from azure.graphrbac.models import CheckGroupMembershipParameters
|
||||
from azure.identity import DefaultAzureCredential
|
||||
from azure.keyvault.secrets import SecretClient
|
||||
from azure.mgmt.resource import ResourceManagementClient
|
||||
@ -23,6 +23,10 @@ from onefuzztypes.primitives import Container, Region
|
||||
|
||||
from .monkeypatch import allow_more_workers, reduce_logging
|
||||
|
||||
# https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0
|
||||
GRAPH_RESOURCE = "https://graph.microsoft.com"
|
||||
GRAPH_RESOURCE_ENDPOINT = "https://graph.microsoft.com/v1.0"
|
||||
|
||||
|
||||
@cached
|
||||
def get_msi() -> MSIAuthentication:
|
||||
@ -99,18 +103,77 @@ def get_regions() -> List[Region]:
|
||||
return sorted([Region(x.name) for x in locations])
|
||||
|
||||
|
||||
@cached
|
||||
def get_graph_client() -> GraphRbacManagementClient:
|
||||
return GraphRbacManagementClient(get_msi(), get_subscription())
|
||||
class GraphQueryError(Exception):
|
||||
def __init__(self, message: str, status_code: Optional[int]) -> None:
|
||||
super(GraphQueryError, self).__init__(message)
|
||||
self.message = message
|
||||
self.status_code = status_code
|
||||
|
||||
|
||||
def query_microsoft_graph(
|
||||
method: str,
|
||||
resource: str,
|
||||
params: Optional[Dict] = None,
|
||||
body: Optional[Dict] = None,
|
||||
) -> Dict:
|
||||
cred = get_identity()
|
||||
access_token = cred.get_token(f"{GRAPH_RESOURCE}/.default")
|
||||
|
||||
url = urllib.parse.urljoin(f"{GRAPH_RESOURCE_ENDPOINT}/", resource)
|
||||
headers = {
|
||||
"Authorization": "Bearer %s" % access_token.token,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
response = requests.request(
|
||||
method=method, url=url, headers=headers, params=params, json=body
|
||||
)
|
||||
|
||||
if 200 <= response.status_code < 300:
|
||||
if response.content and response.content.strip():
|
||||
json = response.json()
|
||||
if isinstance(json, Dict):
|
||||
return json
|
||||
else:
|
||||
raise GraphQueryError(
|
||||
"invalid data expected a json object: HTTP"
|
||||
f" {response.status_code} - {json}",
|
||||
response.status_code,
|
||||
)
|
||||
else:
|
||||
return {}
|
||||
else:
|
||||
error_text = str(response.content, encoding="utf-8", errors="backslashreplace")
|
||||
raise GraphQueryError(
|
||||
f"request did not succeed: HTTP {response.status_code} - {error_text}",
|
||||
response.status_code,
|
||||
)
|
||||
|
||||
|
||||
def query_microsoft_graph_list(
|
||||
method: str,
|
||||
resource: str,
|
||||
params: Optional[Dict] = None,
|
||||
body: Optional[Dict] = None,
|
||||
) -> List[Any]:
|
||||
result = query_microsoft_graph(
|
||||
method,
|
||||
resource,
|
||||
params,
|
||||
body,
|
||||
)
|
||||
value = result.get("value")
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
else:
|
||||
raise GraphQueryError("Expected data containing a list of values", None)
|
||||
|
||||
|
||||
def is_member_of(group_id: str, member_id: str) -> bool:
|
||||
client = get_graph_client()
|
||||
return bool(
|
||||
client.groups.is_member_of(
|
||||
CheckGroupMembershipParameters(group_id=group_id, member_id=member_id)
|
||||
).value
|
||||
body = {"groupIds": [group_id]}
|
||||
response = query_microsoft_graph_list(
|
||||
method="POST", resource=f"users/{member_id}/checkMemberGroups", body=body
|
||||
)
|
||||
return group_id in response
|
||||
|
||||
|
||||
@cached
|
||||
|
Reference in New Issue
Block a user