fix(entra): check name and logic of entra_admin_users_have_mfa_enabled (#7230)

This commit is contained in:
Hugo Pereira Brito
2025-03-31 17:50:51 +02:00
committed by GitHub
parent c243110a49
commit b8ce09ec34
8 changed files with 207 additions and 54 deletions
@@ -1,7 +1,10 @@
{
"Provider": "microsoft365",
"CheckID": "entra_admin_portals_role_limited_access",
"CheckID": "entra_admin_portals_access_restriction",
"CheckTitle": "Ensure that only administrative roles have access to Microsoft Admin Portals",
"CheckAliases": [
"entra_admin_portals_role_limited_access"
],
"CheckType": [],
"ServiceName": "entra",
"SubServiceName": "",
@@ -7,7 +7,7 @@ from prowler.providers.microsoft365.services.entra.entra_service import (
)
class entra_admin_portals_role_limited_access(Check):
class entra_admin_portals_access_restriction(Check):
"""Check if Conditional Access policies deny access to the Microsoft 365 admin center for users with limited access roles.
This check ensures that Conditional Access policies are in place to deny access to the Microsoft 365 admin center for users with limited access roles.
@@ -1,7 +1,10 @@
{
"Provider": "microsoft365",
"CheckID": "entra_admin_mfa_enabled_for_administrative_roles",
"CheckID": "entra_admin_users_mfa_enabled",
"CheckTitle": "Ensure multifactor authentication is enabled for all users in administrative roles.",
"CheckAliases": [
"entra_admin_mfa_enabled_for_administrative_roles"
],
"CheckType": [],
"ServiceName": "entra",
"SubServiceName": "",
@@ -9,7 +9,7 @@ from prowler.providers.microsoft365.services.entra.entra_service import (
)
class entra_admin_mfa_enabled_for_administrative_roles(Check):
class entra_admin_users_mfa_enabled(Check):
"""
Ensure multifactor authentication is enabled for all users in administrative roles.
@@ -68,13 +68,13 @@ class entra_admin_mfa_enabled_for_administrative_roles(Check):
resource_name=policy.display_name,
resource_id=policy.id,
)
report.status = "PASS"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' enforces MFA for administrative roles."
if policy.state == ConditionalAccessPolicyState.ENABLED_FOR_REPORTING:
report.status = "FAIL"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' only reports MFA for administrative roles but does not enforce it."
break
else:
report.status = "PASS"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' enforces MFA for administrative roles."
break
findings.append(report)
return findings
@@ -20,7 +20,7 @@ from tests.providers.microsoft365.microsoft365_fixtures import (
)
class Test_entra_admin_portals_role_limited_access:
class Test_entra_admin_portals_access_restriction:
def test_entra_no_conditional_access_policies(self):
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
@@ -31,17 +31,17 @@ class Test_entra_admin_portals_role_limited_access:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_portals_access_restriction.entra_admin_portals_access_restriction.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access import (
entra_admin_portals_role_limited_access,
from prowler.providers.microsoft365.services.entra.entra_admin_portals_access_restriction.entra_admin_portals_access_restriction import (
entra_admin_portals_access_restriction,
)
entra_client.conditional_access_policies = {}
check = entra_admin_portals_role_limited_access()
check = entra_admin_portals_access_restriction()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
@@ -66,12 +66,12 @@ class Test_entra_admin_portals_role_limited_access:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_portals_access_restriction.entra_admin_portals_access_restriction.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access import (
entra_admin_portals_role_limited_access,
from prowler.providers.microsoft365.services.entra.entra_admin_portals_access_restriction.entra_admin_portals_access_restriction import (
entra_admin_portals_access_restriction,
)
from prowler.providers.microsoft365.services.entra.entra_service import (
ConditionalAccessPolicy,
@@ -114,7 +114,7 @@ class Test_entra_admin_portals_role_limited_access:
)
}
check = entra_admin_portals_role_limited_access()
check = entra_admin_portals_access_restriction()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
@@ -140,12 +140,12 @@ class Test_entra_admin_portals_role_limited_access:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_portals_access_restriction.entra_admin_portals_access_restriction.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access import (
entra_admin_portals_role_limited_access,
from prowler.providers.microsoft365.services.entra.entra_admin_portals_access_restriction.entra_admin_portals_access_restriction import (
entra_admin_portals_access_restriction,
)
from prowler.providers.microsoft365.services.entra.entra_service import (
ConditionalAccessPolicy,
@@ -189,7 +189,7 @@ class Test_entra_admin_portals_role_limited_access:
)
}
check = entra_admin_portals_role_limited_access()
check = entra_admin_portals_access_restriction()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
@@ -218,12 +218,12 @@ class Test_entra_admin_portals_role_limited_access:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_portals_access_restriction.entra_admin_portals_access_restriction.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_portals_role_limited_access.entra_admin_portals_role_limited_access import (
entra_admin_portals_role_limited_access,
from prowler.providers.microsoft365.services.entra.entra_admin_portals_access_restriction.entra_admin_portals_access_restriction import (
entra_admin_portals_access_restriction,
)
from prowler.providers.microsoft365.services.entra.entra_service import (
ConditionalAccessPolicy,
@@ -267,7 +267,7 @@ class Test_entra_admin_portals_role_limited_access:
)
}
check = entra_admin_portals_role_limited_access()
check = entra_admin_portals_access_restriction()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
@@ -21,7 +21,7 @@ from tests.providers.microsoft365.microsoft365_fixtures import (
)
class Test_entra_admin_mfa_enabled_for_administrative_roles:
class Test_entra_admin_users_mfa_enabled:
def test_no_conditional_access_policies(self):
"""No conditional access policies configured: expected FAIL."""
entra_client = mock.MagicMock
@@ -34,17 +34,17 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles import (
entra_admin_mfa_enabled_for_administrative_roles,
from prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled import (
entra_admin_users_mfa_enabled,
)
entra_client.conditional_access_policies = {}
check = entra_admin_mfa_enabled_for_administrative_roles()
check = entra_admin_users_mfa_enabled()
result = check.execute()
assert len(result) == 1
@@ -70,12 +70,12 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles import (
entra_admin_mfa_enabled_for_administrative_roles,
from prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled import (
entra_admin_users_mfa_enabled,
)
entra_client.conditional_access_policies = {
@@ -116,7 +116,7 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
)
}
check = entra_admin_mfa_enabled_for_administrative_roles()
check = entra_admin_users_mfa_enabled()
result = check.execute()
assert len(result) == 1
@@ -146,12 +146,12 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles import (
entra_admin_mfa_enabled_for_administrative_roles,
from prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled import (
entra_admin_users_mfa_enabled,
)
entra_client.conditional_access_policies = {
@@ -192,7 +192,7 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
)
}
check = entra_admin_mfa_enabled_for_administrative_roles()
check = entra_admin_users_mfa_enabled()
result = check.execute()
assert len(result) == 1
@@ -222,12 +222,12 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles import (
entra_admin_mfa_enabled_for_administrative_roles,
from prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled import (
entra_admin_users_mfa_enabled,
)
entra_client.conditional_access_policies = {
@@ -268,7 +268,7 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
)
}
check = entra_admin_mfa_enabled_for_administrative_roles()
check = entra_admin_users_mfa_enabled()
result = check.execute()
assert len(result) == 1
@@ -303,12 +303,12 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles import (
entra_admin_mfa_enabled_for_administrative_roles,
from prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled import (
entra_admin_users_mfa_enabled,
)
entra_client.conditional_access_policies = {
@@ -349,7 +349,7 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
)
}
check = entra_admin_mfa_enabled_for_administrative_roles()
check = entra_admin_users_mfa_enabled()
result = check.execute()
assert len(result) == 1
@@ -382,12 +382,12 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles import (
entra_admin_mfa_enabled_for_administrative_roles,
from prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled import (
entra_admin_users_mfa_enabled,
)
entra_client.conditional_access_policies = {
@@ -444,7 +444,7 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
)
}
check = entra_admin_mfa_enabled_for_administrative_roles()
check = entra_admin_users_mfa_enabled()
result = check.execute()
assert len(result) == 1
@@ -477,12 +477,12 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
return_value=set_mocked_microsoft365_provider(),
),
mock.patch(
"prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles.entra_client",
"prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_mfa_enabled_for_administrative_roles.entra_admin_mfa_enabled_for_administrative_roles import (
entra_admin_mfa_enabled_for_administrative_roles,
from prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled import (
entra_admin_users_mfa_enabled,
)
entra_client.conditional_access_policies = {
@@ -538,7 +538,7 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
)
}
check = entra_admin_mfa_enabled_for_administrative_roles()
check = entra_admin_users_mfa_enabled()
result = check.execute()
assert len(result) == 1
@@ -548,3 +548,150 @@ class Test_entra_admin_mfa_enabled_for_administrative_roles:
assert result[0].resource == {}
assert result[0].resource_name == "Conditional Access Policies"
assert result[0].resource_id == "conditionalAccessPolicies"
def test_policy_invalid_and_valid_policy(self):
"""
Valid policy:
- State enabled (ENABLED)
- Applies to administrative roles
- Application conditions include "All"
- MFA is configured in grant_controls
Expected PASS.
"""
policy_id = str(uuid4())
policy_id2 = str(uuid4())
display_name = "Valid MFA Policy"
entra_client = mock.MagicMock
entra_client.audited_tenant = "audited_tenant"
entra_client.audited_domain = DOMAIN
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_admin_users_mfa_enabled.entra_admin_users_mfa_enabled.entra_client",
new=entra_client,
),
):
from prowler.providers.microsoft365.services.entra.entra_admin_users_mfa_enabled.entra_admin_users_mfa_enabled import (
entra_admin_users_mfa_enabled,
)
entra_client.conditional_access_policies = {
policy_id: ConditionalAccessPolicy(
id=policy_id,
display_name=display_name,
conditions=Conditions(
application_conditions=ApplicationsConditions(
included_applications=["All"],
excluded_applications=[],
included_user_actions=[],
),
user_conditions=UsersConditions(
included_groups=[],
excluded_groups=[],
included_users=[],
excluded_users=[],
included_roles=[
"9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3",
"c4e39bd9-1100-46d3-8c65-fb160da0071f",
"b0f54661-2d74-4c50-afa3-1ec803f12efe",
"158c047a-c907-4556-b7ef-446551a6b5f7",
"b1be1c3e-b65d-4f19-8427-f6fa0d97feb9",
"29232cdf-9323-42fd-ade2-1d097af3e4de",
"62e90394-69f5-4237-9190-012177145e10",
"f2ef992c-3afb-46b9-b7cf-a126ee74c451",
"729827e3-9c14-49f7-bb1b-9608f156bbb8",
"966707d0-3269-4727-9be2-8c3a10f19b9d",
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13",
"e8611ab8-c189-46e8-94e1-60213ab1f814",
"194ae4cb-b126-40b2-bd5b-6091b380977d",
"f28a1f50-f6e7-4571-818b-6a12f2af6b6c",
"fe930be7-5e62-47db-91af-98c3a49a38b1",
],
excluded_roles=[],
),
),
grant_controls=GrantControls(
built_in_controls=[ConditionalAccessGrantControl.MFA],
operator=GrantControlOperator.AND,
),
session_controls=SessionControls(
persistent_browser=PersistentBrowser(
is_enabled=False, mode="always"
),
sign_in_frequency=SignInFrequency(
is_enabled=False,
frequency=None,
type=None,
interval=SignInFrequencyInterval.EVERY_TIME,
),
),
state=ConditionalAccessPolicyState.ENABLED_FOR_REPORTING,
),
policy_id2: ConditionalAccessPolicy(
id=policy_id,
display_name=display_name,
conditions=Conditions(
application_conditions=ApplicationsConditions(
included_applications=["All"],
excluded_applications=[],
included_user_actions=[],
),
user_conditions=UsersConditions(
included_groups=[],
excluded_groups=[],
included_users=[],
excluded_users=[],
included_roles=[
"9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3",
"c4e39bd9-1100-46d3-8c65-fb160da0071f",
"b0f54661-2d74-4c50-afa3-1ec803f12efe",
"158c047a-c907-4556-b7ef-446551a6b5f7",
"b1be1c3e-b65d-4f19-8427-f6fa0d97feb9",
"29232cdf-9323-42fd-ade2-1d097af3e4de",
"62e90394-69f5-4237-9190-012177145e10",
"f2ef992c-3afb-46b9-b7cf-a126ee74c451",
"729827e3-9c14-49f7-bb1b-9608f156bbb8",
"966707d0-3269-4727-9be2-8c3a10f19b9d",
"7be44c8a-adaf-4e2a-84d6-ab2649e08a13",
"e8611ab8-c189-46e8-94e1-60213ab1f814",
"194ae4cb-b126-40b2-bd5b-6091b380977d",
"f28a1f50-f6e7-4571-818b-6a12f2af6b6c",
"fe930be7-5e62-47db-91af-98c3a49a38b1",
],
excluded_roles=[],
),
),
grant_controls=GrantControls(
built_in_controls=[ConditionalAccessGrantControl.MFA],
operator=GrantControlOperator.AND,
),
session_controls=SessionControls(
persistent_browser=PersistentBrowser(
is_enabled=False, mode="always"
),
sign_in_frequency=SignInFrequency(
is_enabled=False,
frequency=None,
type=None,
interval=SignInFrequencyInterval.EVERY_TIME,
),
),
state=ConditionalAccessPolicyState.ENABLED,
),
}
check = entra_admin_users_mfa_enabled()
result = check.execute()
assert len(result) == 1
assert result[0].status == "PASS"
expected_status_extended = f"Conditional Access Policy '{display_name}' enforces MFA for administrative roles."
assert result[0].status_extended == expected_status_extended
assert result[0].resource == entra_client.conditional_access_policies
assert result[0].resource_name == display_name
assert result[0].resource_id == policy_id