mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-15 03:18:07 +00:00
Add a registration command to allow an application registration to connect to the Onefuzz (#2571)
This commit is contained in:
@ -383,7 +383,7 @@ class Client:
|
||||
},
|
||||
{
|
||||
"allowedMemberTypes": ["Application"],
|
||||
"description": "Allow access from a lab machine.",
|
||||
"description": "Allow access from a managed node.",
|
||||
"displayName": OnefuzzAppRole.ManagedNode.value,
|
||||
"id": str(uuid.uuid4()),
|
||||
"isEnabled": True,
|
||||
@ -397,6 +397,14 @@ class Client:
|
||||
"isEnabled": True,
|
||||
"value": OnefuzzAppRole.UserAssignment.value,
|
||||
},
|
||||
{
|
||||
"allowedMemberTypes": ["Application"],
|
||||
"description": "Allow access from an unmanaged node.",
|
||||
"displayName": OnefuzzAppRole.UnmanagedNode.value,
|
||||
"id": str(uuid.uuid4()),
|
||||
"isEnabled": True,
|
||||
"value": OnefuzzAppRole.UnmanagedNode.value,
|
||||
},
|
||||
]
|
||||
|
||||
if not app:
|
||||
|
@ -10,7 +10,17 @@ import time
|
||||
import urllib.parse
|
||||
from datetime import datetime, timedelta
|
||||
from enum import Enum
|
||||
from typing import Any, Callable, Dict, List, NamedTuple, Optional, Tuple, TypeVar
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
List,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
from uuid import UUID
|
||||
|
||||
import requests
|
||||
@ -24,6 +34,8 @@ logger = logging.getLogger("deploy")
|
||||
GRAPH_RESOURCE = "https://graph.microsoft.com"
|
||||
GRAPH_RESOURCE_ENDPOINT = "https://graph.microsoft.com/v1.0"
|
||||
|
||||
NameOrAppId = Union[str, UUID]
|
||||
|
||||
|
||||
class GraphQueryError(Exception):
|
||||
def __init__(self, message: str, status_code: Optional[int]) -> None:
|
||||
@ -117,6 +129,7 @@ def retry(
|
||||
data: Any = None,
|
||||
) -> OperationResult:
|
||||
count = 0
|
||||
error = None
|
||||
while True:
|
||||
try:
|
||||
return operation(data)
|
||||
@ -160,6 +173,7 @@ class OnefuzzAppRole(Enum):
|
||||
ManagedNode = "ManagedNode"
|
||||
CliClient = "CliClient"
|
||||
UserAssignment = "UserAssignment"
|
||||
UnmanagedNode = "UnmanagedNode"
|
||||
|
||||
|
||||
def register_application(
|
||||
@ -309,22 +323,32 @@ def create_application_registration(
|
||||
registered_app_id = registered_app["appId"]
|
||||
app_id = app["appId"]
|
||||
|
||||
authorize_and_assign_role(app_id, registered_app_id, approle, subscription_id)
|
||||
return registered_app
|
||||
|
||||
|
||||
def authorize_and_assign_role(
|
||||
onfuzz_app_id: UUID,
|
||||
registered_app_id: UUID,
|
||||
role: OnefuzzAppRole,
|
||||
subscription_id: str,
|
||||
) -> None:
|
||||
def try_authorize_application(data: Any) -> None:
|
||||
authorize_application(
|
||||
UUID(registered_app_id),
|
||||
UUID(app_id),
|
||||
registered_app_id,
|
||||
onfuzz_app_id,
|
||||
subscription_id=subscription_id,
|
||||
)
|
||||
|
||||
retry(try_authorize_application, "authorize application")
|
||||
|
||||
def try_assign_instance_role(data: Any) -> None:
|
||||
assign_instance_app_role(onefuzz_instance_name, name, subscription_id, approle)
|
||||
assign_instance_app_role(
|
||||
onfuzz_app_id, registered_app_id, subscription_id, role
|
||||
)
|
||||
|
||||
retry(try_assign_instance_role, "assingn role")
|
||||
|
||||
return registered_app
|
||||
|
||||
|
||||
def add_application_password(
|
||||
password_name: str, app_object_id: UUID, subscription_id: str
|
||||
@ -422,6 +446,10 @@ def authorize_application(
|
||||
permissions: List[str] = ["user_impersonation"],
|
||||
subscription_id: Optional[str] = None,
|
||||
) -> None:
|
||||
logger.info(
|
||||
f"authorizing registration {registration_app_id} to access application {onefuzz_app_id} with the permissions '{', '.join(permissions)}'"
|
||||
)
|
||||
|
||||
onefuzz_app = get_application(
|
||||
app_id=onefuzz_app_id, subscription_id=subscription_id
|
||||
)
|
||||
@ -575,8 +603,8 @@ def assign_app_role(
|
||||
|
||||
|
||||
def assign_instance_app_role(
|
||||
onefuzz_instance_name: str,
|
||||
application_name: str,
|
||||
onefuzz_instance: NameOrAppId,
|
||||
application_name: NameOrAppId,
|
||||
subscription_id: str,
|
||||
app_role: OnefuzzAppRole,
|
||||
) -> None:
|
||||
@ -585,11 +613,15 @@ def assign_instance_app_role(
|
||||
their managed identity to the provided App Role
|
||||
"""
|
||||
|
||||
logger.info(
|
||||
f"Assigning app role {app_role} from {onefuzz_instance} to {application_name}"
|
||||
)
|
||||
if isinstance(onefuzz_instance, str):
|
||||
onefuzz_service_appIds = query_microsoft_graph_list(
|
||||
method="GET",
|
||||
resource="applications",
|
||||
params={
|
||||
"$filter": "displayName eq '%s'" % onefuzz_instance_name,
|
||||
"$filter": "displayName eq '%s'" % onefuzz_instance,
|
||||
"$select": "appId",
|
||||
},
|
||||
subscription=subscription_id,
|
||||
@ -597,6 +629,9 @@ def assign_instance_app_role(
|
||||
if len(onefuzz_service_appIds) == 0:
|
||||
raise Exception("onefuzz app registration not found")
|
||||
appId = onefuzz_service_appIds[0]["appId"]
|
||||
else:
|
||||
appId = onefuzz_instance
|
||||
|
||||
onefuzz_service_principals = query_microsoft_graph_list(
|
||||
method="GET",
|
||||
resource="servicePrincipals",
|
||||
@ -607,15 +642,27 @@ def assign_instance_app_role(
|
||||
if len(onefuzz_service_principals) == 0:
|
||||
raise Exception("onefuzz app service principal not found")
|
||||
onefuzz_service_principal = onefuzz_service_principals[0]
|
||||
|
||||
if isinstance(application_name, str):
|
||||
application_service_principals = query_microsoft_graph_list(
|
||||
method="GET",
|
||||
resource="servicePrincipals",
|
||||
params={"$filter": "displayName eq '%s'" % application_name},
|
||||
subscription=subscription_id,
|
||||
)
|
||||
else:
|
||||
application_service_principals = query_microsoft_graph_list(
|
||||
method="GET",
|
||||
resource="servicePrincipals",
|
||||
params={"$filter": "appId eq '%s'" % application_name},
|
||||
subscription=subscription_id,
|
||||
)
|
||||
|
||||
if len(application_service_principals) == 0:
|
||||
raise Exception(f"application '{application_name}' service principal not found")
|
||||
|
||||
application_service_principal = application_service_principals[0]
|
||||
|
||||
managed_node_role = (
|
||||
seq(onefuzz_service_principal["appRoles"])
|
||||
.filter(lambda x: x["value"] == app_role.value)
|
||||
@ -799,6 +846,12 @@ def main() -> None:
|
||||
cli_registration_parser.add_argument(
|
||||
"--registration_name", help="the name of the cli registration"
|
||||
)
|
||||
register_app_parser = subparsers.add_parser("register_app", parents=[parent_parser])
|
||||
register_app_parser.add_argument("--app_id", help="the application id to register")
|
||||
register_app_parser.add_argument(
|
||||
"--role",
|
||||
help=f"the role of the application to register. Valid values: {', '.join([member.value for member in OnefuzzAppRole])}",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.verbose:
|
||||
@ -821,6 +874,17 @@ def main() -> None:
|
||||
args.subscription_id,
|
||||
display_secret=True,
|
||||
)
|
||||
elif args.command == "register_app":
|
||||
onefuzz_app_id = get_application(display_name=onefuzz_instance_name)
|
||||
if not onefuzz_app_id:
|
||||
raise Exception("could not find onefuzz application")
|
||||
|
||||
authorize_and_assign_role(
|
||||
UUID(onefuzz_app_id["appId"]),
|
||||
UUID(args.app_id),
|
||||
OnefuzzAppRole(args.role),
|
||||
args.subscription_id,
|
||||
)
|
||||
elif args.command == "assign_scaleset_role":
|
||||
assign_instance_app_role(
|
||||
onefuzz_instance_name,
|
||||
|
Reference in New Issue
Block a user