SP Guest Account Access locked down by default and deploying user added as admin. (#1425)

* Service Principal locked down by default and deploying user added.

* Fixing call to client.

* Adding multiple users.

* Retrieving SP id.

* Refactorin gcall.

* Getting closer.

* Can retrieve object_id from create

* New retrieve sp functionality.

* mypy fixes.

* Separating functionality into new function.

* mypy errors.

* Logic for updating appRoleAssignemntRequired param.

* Changing to patch.

* Updating to Patch.

* Fixing bug.

* Fixing bad assignment.

* Responding to comments.

* Removing functionality for updating setting.

* Update src/deployment/deploy.py

Co-authored-by: Cheick Keita <kcheick@gmail.com>

* UPdating error message.

* Retriggering?

* Trying to emulate new file structure.

* Fixing lint issues.

* Fixing create sp.

Co-authored-by: nharper285 <nharper285@gmail.com>
Co-authored-by: Cheick Keita <kcheick@gmail.com>
This commit is contained in:
Noah McGregor Harper
2021-11-05 16:29:08 -07:00
committed by GitHub
parent 6b64b60da7
commit de2b7cabd0
3 changed files with 125 additions and 5 deletions

View File

@ -60,9 +60,12 @@ from deploylib.registration import (
GraphQueryError, GraphQueryError,
OnefuzzAppRole, OnefuzzAppRole,
add_application_password, add_application_password,
add_user,
assign_instance_app_role, assign_instance_app_role,
authorize_application, authorize_application,
get_application, get_application,
get_service_principal,
get_signed_in_user,
get_tenant_id, get_tenant_id,
query_microsoft_graph, query_microsoft_graph,
register_application, register_application,
@ -300,7 +303,6 @@ class Client:
display_name=self.application_name, display_name=self.application_name,
subscription_id=self.get_subscription_id(), subscription_id=self.get_subscription_id(),
) )
app_roles = [ app_roles = [
{ {
"allowedMemberTypes": ["Application"], "allowedMemberTypes": ["Application"],
@ -318,6 +320,14 @@ class Client:
"isEnabled": True, "isEnabled": True,
"value": OnefuzzAppRole.ManagedNode.value, "value": OnefuzzAppRole.ManagedNode.value,
}, },
{
"allowedMemberTypes": ["User"],
"description": "Allows user access from the CLI.",
"displayName": OnefuzzAppRole.UserAssignment.value,
"id": str(uuid.uuid4()),
"isEnabled": True,
"value": OnefuzzAppRole.UserAssignment.value,
},
] ]
if not app: if not app:
@ -372,7 +382,7 @@ class Client:
service_principal_params = { service_principal_params = {
"accountEnabled": True, "accountEnabled": True,
"appRoleAssignmentRequired": False, "appRoleAssignmentRequired": True,
"servicePrincipalType": "Application", "servicePrincipalType": "Application",
"appId": app["appId"], "appId": app["appId"],
} }
@ -431,11 +441,10 @@ class Client:
# this is a requirement to update the application roles # this is a requirement to update the application roles
for role in app["appRoles"]: for role in app["appRoles"]:
role["isEnabled"] = False role["isEnabled"] = False
query_microsoft_graph( query_microsoft_graph(
method="PATCH", method="PATCH",
resource=f"applications/{app['id']}", resource=f"applications/{app['id']}",
body={"appRoles": app["AppRoles"]}, body={"appRoles": app["appRoles"]},
subscription=self.get_subscription_id(), subscription=self.get_subscription_id(),
) )
@ -603,6 +612,37 @@ class Client:
OnefuzzAppRole.ManagedNode, OnefuzzAppRole.ManagedNode,
) )
def assign_user_access(self) -> None:
logger.info("assinging user access to service principal")
app = get_application(
display_name=self.application_name,
subscription_id=self.get_subscription_id(),
)
user = get_signed_in_user(self.subscription_id)
if app:
sp = get_service_principal(app["appId"], self.subscription_id)
# Update appRoleAssignmentRequired if necessary
if not sp["appRoleAssignmentRequired"]:
logger.warning(
"The service is not currently configured to require a role assignment to access it."
+ " This means that any authenticated user can access the service. "
+ " To change this behavior enable 'Assignment Required?' on the service principal in the AAD Portal."
)
# Assign Roles and Add Users
roles = [
x["id"]
for x in app["appRoles"]
if x["displayName"] == OnefuzzAppRole.UserAssignment.value
]
users = [user["id"]]
if self.admins:
admins_str = [str(x) for x in self.admins]
users += admins_str
for user_id in users:
add_user(sp["id"], user_id, roles[0])
def apply_migrations(self) -> None: def apply_migrations(self) -> None:
logger.info("applying database migrations") logger.info("applying database migrations")
name = self.results["deploy"]["func-name"]["value"] name = self.results["deploy"]["func-name"]["value"]
@ -983,6 +1023,7 @@ def main() -> None:
("rbac", Client.setup_rbac), ("rbac", Client.setup_rbac),
("arm", Client.deploy_template), ("arm", Client.deploy_template),
("assign_scaleset_identity_role", Client.assign_scaleset_identity_role), ("assign_scaleset_identity_role", Client.assign_scaleset_identity_role),
("assign_user_access", Client.assign_user_access),
] ]
full_deployment_states = rbac_only_states + [ full_deployment_states = rbac_only_states + [

View File

@ -154,6 +154,7 @@ class ApplicationInfo(NamedTuple):
class OnefuzzAppRole(Enum): class OnefuzzAppRole(Enum):
ManagedNode = "ManagedNode" ManagedNode = "ManagedNode"
CliClient = "CliClient" CliClient = "CliClient"
UserAssignment = "UserAssignment"
def register_application( def register_application(
@ -646,6 +647,84 @@ def set_app_audience(
raise Exception(err_str) raise Exception(err_str)
def get_signed_in_user(subscription_id: Optional[str]) -> Any:
# Get principalId by retrieving owner for SP
try:
app = query_microsoft_graph(
method="GET",
resource="me/",
subscription=subscription_id,
)
return app
except GraphQueryError:
query = (
"az rest --method post --url "
"https://graph.microsoft.com/v1.0/me "
'--headers "Content-Type"=application/json'
)
logger.warning(
"execute the following query in the azure portal bash shell and "
"run deploy.py again : \n%s",
query,
)
err_str = "Unable to retrieve signed-in user via Microsoft Graph Query API. \n"
raise Exception(err_str)
def get_service_principal(app_id: str, subscription_id: Optional[str]) -> Any:
try:
service_principals = query_microsoft_graph_list(
method="GET",
resource="servicePrincipals",
params={"$filter": f"appId eq '{app_id}'"},
subscription=subscription_id,
)
if len(service_principals) != 0:
return service_principals[0]
else:
raise GraphQueryError(
f"Could not retrieve any service principals for App Id: {app_id}", 400
)
except GraphQueryError:
err_str = "Unable to add retrieve SP using Microsoft Graph Query API. \n"
raise Exception(err_str)
def add_user(object_id: str, principal_id: str, role_id: str) -> None:
# Get principalId by retrieving owner for SP
# need to add users with proper role assignment
http_body = {
"principalId": principal_id,
"resourceId": object_id,
"appRoleId": role_id,
}
try:
query_microsoft_graph(
method="POST",
resource="users/%s/appRoleAssignments" % principal_id,
body=http_body,
)
except GraphQueryError as ex:
if "Permission being assigned already exists" not in ex.message:
query = (
"az rest --method post --url "
"https://graph.microsoft.com/v1.0/users/%s/appRoleAssignments "
"--body '%s' --headers \"Content-Type\"=application/json"
% (principal_id, http_body)
)
logger.warning(
"execute the following query in the azure portal bash shell and "
"run deploy.py again : \n%s",
query,
)
err_str = "Unable to add user to SP using Microsoft Graph Query API. \n"
raise Exception(err_str)
else:
logger.info("User already assigned to application.")
def main() -> None: def main() -> None:
formatter = argparse.ArgumentDefaultsHelpFormatter formatter = argparse.ArgumentDefaultsHelpFormatter

View File

@ -4,7 +4,7 @@
# Licensed under the MIT License. # Licensed under the MIT License.
import unittest import unittest
from typing import Any, List from typing import Any
from deploylib.configuration import NetworkSecurityConfig from deploylib.configuration import NetworkSecurityConfig