From 0f414e451e4ef2435501f4bb3dff539950ee6a84 Mon Sep 17 00:00:00 2001 From: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Date: Thu, 27 Feb 2025 10:31:02 +0100 Subject: [PATCH] feat(microsoft365): add new check `entra_policy_ensure_default_user_cannot_create_tenants` (#6918) Co-authored-by: MrCloudSec --- .../__init__.py | 0 ...t_user_cannot_create_tenants.metadata.json | 30 ++++ ...sure_default_user_cannot_create_tenants.py | 50 ++++++ ...default_user_cannot_create_tenants_test.py | 149 ++++++++++++++++++ .../entra/microsoft365_entra_service_test.py | 48 +++--- 5 files changed, 259 insertions(+), 18 deletions(-) create mode 100644 prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/__init__.py create mode 100644 prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/entra_policy_ensure_default_user_cannot_create_tenants.metadata.json create mode 100644 prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/entra_policy_ensure_default_user_cannot_create_tenants.py create mode 100644 tests/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/microsoft365_entra_policy_ensure_default_user_cannot_create_tenants_test.py diff --git a/prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/__init__.py b/prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/entra_policy_ensure_default_user_cannot_create_tenants.metadata.json b/prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/entra_policy_ensure_default_user_cannot_create_tenants.metadata.json new file mode 100644 index 0000000000..12a0da5cf9 --- /dev/null +++ b/prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/entra_policy_ensure_default_user_cannot_create_tenants.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "microsoft365", + "CheckID": "entra_policy_ensure_default_user_cannot_create_tenants", + "CheckTitle": "Ensure that 'Restrict non-admin users from creating tenants' is set to 'Yes'", + "CheckType": [], + "ServiceName": "entra", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "#microsoft.graph.authorizationPolicy", + "Description": "Require administrators or appropriately delegated users to create new tenants.", + "Risk": "It is recommended to only allow an administrator to create new tenants. This prevent users from creating new Azure AD or Azure AD B2C tenants and ensures that only authorized users are able to do so.", + "RelatedUrl": "https://learn.microsoft.com/en-us/entra/fundamentals/users-default-permissions", + "Remediation": { + "Code": { + "CLI": "Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{ AllowedToCreateTenants = $false }", + "NativeIaC": "", + "Other": "1. Navigate to Microsoft Entra admin center https://entra.microsoft.com 2. Click to expand Identity > Users > User settings 3. Set 'Restrict non-admin users from creating tenants' to 'Yes' then 'Save'", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enforcing this setting will ensure that only authorized users are able to create new tenants.", + "Url": "https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#tenant-creator" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/entra_policy_ensure_default_user_cannot_create_tenants.py b/prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/entra_policy_ensure_default_user_cannot_create_tenants.py new file mode 100644 index 0000000000..de2c2112eb --- /dev/null +++ b/prowler/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/entra_policy_ensure_default_user_cannot_create_tenants.py @@ -0,0 +1,50 @@ +from typing import List + +from prowler.lib.check.models import Check, CheckReportMicrosoft365 +from prowler.providers.microsoft365.services.entra.entra_client import entra_client + + +class entra_policy_ensure_default_user_cannot_create_tenants(Check): + """Check if default users are restricted from creating tenants. + + This check verifies whether the authorization policy prevents non-admin users + from creating new tenants in Microsoft Entra ID. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + """ + + def execute(self) -> List[CheckReportMicrosoft365]: + """Execute the check for tenant creation restrictions. + + This method examines the authorization policy settings to determine if + non-admin users are allowed to create new tenants. If tenant creation is + restricted, the check passes. + + Returns: + List[Check_Report_Microsoft365]: A list containing the result of the check. + """ + findings = [] + auth_policy = entra_client.authorization_policy + + report = CheckReportMicrosoft365( + metadata=self.metadata(), + resource=auth_policy if auth_policy else {}, + resource_name=auth_policy.name if auth_policy else "Authorization Policy", + resource_id=auth_policy.id if auth_policy else "authorizationPolicy", + ) + report.status = "FAIL" + report.status_extended = "Tenant creation is not disabled for non-admin users." + + if getattr( + entra_client.authorization_policy, "default_user_role_permissions", None + ) and not getattr( + entra_client.authorization_policy.default_user_role_permissions, + "allowed_to_create_tenants", + True, + ): + report.status = "PASS" + report.status_extended = "Tenant creation is disabled for non-admin users." + + findings.append(report) + return findings diff --git a/tests/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/microsoft365_entra_policy_ensure_default_user_cannot_create_tenants_test.py b/tests/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/microsoft365_entra_policy_ensure_default_user_cannot_create_tenants_test.py new file mode 100644 index 0000000000..5adf54c01e --- /dev/null +++ b/tests/providers/microsoft365/services/entra/entra_policy_ensure_default_user_cannot_create_tenants/microsoft365_entra_policy_ensure_default_user_cannot_create_tenants_test.py @@ -0,0 +1,149 @@ +from unittest import mock +from uuid import uuid4 + +from prowler.providers.microsoft365.services.entra.entra_service import ( + AuthorizationPolicy, + DefaultUserRolePermissions, +) +from tests.providers.microsoft365.microsoft365_fixtures import ( + set_mocked_microsoft365_provider, +) + + +class Test_entra_policy_ensure_default_user_cannot_create_tenants: + def test_entra_empty_tenant(self): + entra_client = mock.MagicMock + entra_client.authorization_policy = {} + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_microsoft365_provider(), + ), + mock.patch( + "prowler.providers.microsoft365.services.entra.entra_policy_ensure_default_user_cannot_create_tenants.entra_policy_ensure_default_user_cannot_create_tenants.entra_client", + new=entra_client, + ), + ): + from prowler.providers.microsoft365.services.entra.entra_policy_ensure_default_user_cannot_create_tenants.entra_policy_ensure_default_user_cannot_create_tenants import ( + entra_policy_ensure_default_user_cannot_create_tenants, + ) + + check = entra_policy_ensure_default_user_cannot_create_tenants() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Tenant creation is not disabled for non-admin users." + ) + assert result[0].resource == {} + assert result[0].resource_name == "Authorization Policy" + assert result[0].resource_id == "authorizationPolicy" + assert result[0].location == "global" + + def test_entra_default_user_role_permissions_allowed_to_create_tenants(self): + id = str(uuid4()) + entra_client = mock.MagicMock + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_microsoft365_provider(), + ), + mock.patch( + "prowler.providers.microsoft365.services.entra.entra_policy_ensure_default_user_cannot_create_tenants.entra_policy_ensure_default_user_cannot_create_tenants.entra_client", + new=entra_client, + ), + ): + from prowler.providers.microsoft365.services.entra.entra_policy_ensure_default_user_cannot_create_tenants.entra_policy_ensure_default_user_cannot_create_tenants import ( + entra_policy_ensure_default_user_cannot_create_tenants, + ) + + entra_client.authorization_policy = AuthorizationPolicy( + id=id, + name="Test", + description="Test", + default_user_role_permissions=DefaultUserRolePermissions( + allowed_to_create_tenants=True + ), + ) + + check = entra_policy_ensure_default_user_cannot_create_tenants() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "Tenant creation is not disabled for non-admin users." + ) + assert result[0].resource == { + "id": id, + "name": "Test", + "description": "Test", + "default_user_role_permissions": { + "allowed_to_create_apps": None, + "allowed_to_create_security_groups": None, + "allowed_to_create_tenants": True, + "allowed_to_read_bitlocker_keys_for_owned_device": None, + "allowed_to_read_other_users": None, + "odata_type": None, + "permission_grant_policies_assigned": None, + }, + } + assert result[0].resource_name == "Test" + assert result[0].resource_id == id + assert result[0].location == "global" + + def test_entra_default_user_role_permissions_not_allowed_to_create_tenants(self): + id = str(uuid4()) + entra_client = mock.MagicMock + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_microsoft365_provider(), + ), + mock.patch( + "prowler.providers.microsoft365.services.entra.entra_policy_ensure_default_user_cannot_create_tenants.entra_policy_ensure_default_user_cannot_create_tenants.entra_client", + new=entra_client, + ), + ): + from prowler.providers.microsoft365.services.entra.entra_policy_ensure_default_user_cannot_create_tenants.entra_policy_ensure_default_user_cannot_create_tenants import ( + entra_policy_ensure_default_user_cannot_create_tenants, + ) + + entra_client.authorization_policy = AuthorizationPolicy( + id=id, + name="Test", + description="Test", + default_user_role_permissions=DefaultUserRolePermissions( + allowed_to_create_tenants=False + ), + ) + + check = entra_policy_ensure_default_user_cannot_create_tenants() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "Tenant creation is disabled for non-admin users." + ) + assert result[0].resource == { + "id": id, + "name": "Test", + "description": "Test", + "default_user_role_permissions": { + "allowed_to_create_apps": None, + "allowed_to_create_security_groups": None, + "allowed_to_create_tenants": False, + "allowed_to_read_bitlocker_keys_for_owned_device": None, + "allowed_to_read_other_users": None, + "odata_type": None, + "permission_grant_policies_assigned": None, + }, + } + assert result[0].resource_name == "Test" + assert result[0].resource_id == id + assert result[0].location == "global" diff --git a/tests/providers/microsoft365/services/entra/microsoft365_entra_service_test.py b/tests/providers/microsoft365/services/entra/microsoft365_entra_service_test.py index 86109478e2..caf9fdec74 100644 --- a/tests/providers/microsoft365/services/entra/microsoft365_entra_service_test.py +++ b/tests/providers/microsoft365/services/entra/microsoft365_entra_service_test.py @@ -3,6 +3,7 @@ from unittest.mock import patch from prowler.providers.microsoft365.models import Microsoft365IdentityInfo from prowler.providers.microsoft365.services.entra.entra_service import ( AuthorizationPolicy, + DefaultUserRolePermissions, Entra, ) from tests.providers.microsoft365.microsoft365_fixtures import ( @@ -12,20 +13,20 @@ from tests.providers.microsoft365.microsoft365_fixtures import ( async def mock_entra_get_authorization_policy(_): - return { - "id-1": AuthorizationPolicy( - id="id-1", - name="Name 1", - description="Description 1", - default_user_role_permissions=None, - ) - } + return AuthorizationPolicy( + id="id-1", + name="Name 1", + description="Description 1", + default_user_role_permissions=DefaultUserRolePermissions( + allowed_to_create_apps=True, + allowed_to_create_security_groups=True, + allowed_to_create_tenants=True, + allowed_to_read_bitlocker_keys_for_owned_device=True, + allowed_to_read_other_users=True, + ), + ) -@patch( - "prowler.providers.microsoft365.services.entra.entra_service.Entra._get_authorization_policy", - new=mock_entra_get_authorization_policy, -) class Test_Entra_Service: def test_get_client(self): admincenter_client = Entra( @@ -35,11 +36,22 @@ class Test_Entra_Service: ) assert admincenter_client.client.__class__.__name__ == "GraphServiceClient" + @patch( + "prowler.providers.microsoft365.services.entra.entra_service.Entra._get_authorization_policy", + new=mock_entra_get_authorization_policy, + ) def test_get_authorization_policy(self): entra_client = Entra(set_mocked_microsoft365_provider()) - assert entra_client.authorization_policy["id-1"].id == "id-1" - assert entra_client.authorization_policy["id-1"].name == "Name 1" - assert entra_client.authorization_policy["id-1"].description == "Description 1" - assert not entra_client.authorization_policy[ - "id-1" - ].default_user_role_permissions + assert entra_client.authorization_policy.id == "id-1" + assert entra_client.authorization_policy.name == "Name 1" + assert entra_client.authorization_policy.description == "Description 1" + assert ( + entra_client.authorization_policy.default_user_role_permissions + == DefaultUserRolePermissions( + allowed_to_create_apps=True, + allowed_to_create_security_groups=True, + allowed_to_create_tenants=True, + allowed_to_read_bitlocker_keys_for_owned_device=True, + allowed_to_read_other_users=True, + ) + )