mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
fix(m365): avoid user requests in setup_identity app context and user auth log enhancement (#8055)
Co-authored-by: Hugo Pereira Brito <101209179+HugoPBrito@users.noreply.github.com> Co-authored-by: HugoPBrito <hugopbrit@gmail.com>
This commit is contained in:
@@ -168,7 +168,9 @@ export M365_PASSWORD="6500780061006d0070006c006500700061007300730077006f00720064
|
||||
|
||||
These two new environment variables are **required** to execute the PowerShell modules needed to retrieve information from M365 services. Prowler uses Service Principal authentication to access Microsoft Graph and user credentials to authenticate to Microsoft PowerShell modules.
|
||||
|
||||
- `M365_USER` should be your Microsoft account email using the default domain. This means it must look like `example@YourCompany.onmicrosoft.com`.
|
||||
- `M365_USER` should be your Microsoft account email using the **assigned domain in the tenant**. This means it must look like `example@YourCompany.onmicrosoft.com` or `example@YourCompany.com`, but it must be the exact domain assigned to that user in the tenant.
|
||||
???+ warning
|
||||
If the user is newly created, you need to sign in with that account first, as Microsoft will prompt you to change the password. If you don’t complete this step, user authentication will fail because Microsoft marks the initial password as expired.
|
||||
|
||||
To ensure that you are using the default domain you can see how to verify it [here](../tutorials/microsoft365/getting-started-m365.md#step-1-obtain-your-domain).
|
||||
|
||||
|
||||
@@ -172,6 +172,9 @@ Follow these steps to assign the role:
|
||||
|
||||

|
||||
|
||||
???+ warning
|
||||
Remember that if the user is newly created, you need to sign in with that account first, as Microsoft will prompt you to change the password. If you don’t complete this step, user authentication will fail because Microsoft marks the initial password as expired.
|
||||
|
||||
---
|
||||
|
||||
### Get your encrypted password
|
||||
|
||||
@@ -161,10 +161,12 @@ class M365PowerShell(PowerShellSession):
|
||||
)
|
||||
|
||||
if result is None:
|
||||
return False
|
||||
raise Exception(
|
||||
"Unexpected error: Acquiring token in behalf of user did not return a result."
|
||||
)
|
||||
|
||||
if "access_token" not in result:
|
||||
return False
|
||||
raise Exception(f"MsGraph Error {result.get('error_description')}")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -196,6 +196,8 @@ class M365Provider(Provider):
|
||||
self._identity = self.setup_identity(
|
||||
sp_env_auth,
|
||||
env_auth,
|
||||
browser_auth,
|
||||
az_cli_auth,
|
||||
self._session,
|
||||
)
|
||||
|
||||
@@ -505,6 +507,7 @@ class M365Provider(Provider):
|
||||
Exception: If failed to retrieve M365 credentials.
|
||||
|
||||
"""
|
||||
logger.info("M365 provider: Setting up session...")
|
||||
if not browser_auth:
|
||||
if sp_env_auth or env_auth:
|
||||
try:
|
||||
@@ -717,6 +720,8 @@ class M365Provider(Provider):
|
||||
identity = M365Provider.setup_identity(
|
||||
sp_env_auth,
|
||||
env_auth,
|
||||
browser_auth,
|
||||
az_cli_auth,
|
||||
session,
|
||||
)
|
||||
|
||||
@@ -732,6 +737,8 @@ class M365Provider(Provider):
|
||||
message=f"The provider ID {provider_id} does not match any of the service principal tenant domains: {', '.join(identity.tenant_domains)}",
|
||||
)
|
||||
|
||||
logger.info("M365 provider: Identity retrieved successfully")
|
||||
|
||||
# Set up PowerShell credentials
|
||||
if user and password:
|
||||
M365Provider.setup_powershell(
|
||||
@@ -739,13 +746,12 @@ class M365Provider(Provider):
|
||||
m365_credentials,
|
||||
identity,
|
||||
)
|
||||
logger.info("M365 provider: Connection to PowerShell successful")
|
||||
else:
|
||||
logger.info(
|
||||
"M365 provider: Connection to PowerShell has not been requested"
|
||||
)
|
||||
|
||||
logger.info("M365 provider: Connection to PowerShell successful")
|
||||
|
||||
return Connection(is_connected=True)
|
||||
|
||||
# Exceptions from setup_region_config
|
||||
@@ -876,6 +882,8 @@ class M365Provider(Provider):
|
||||
def setup_identity(
|
||||
sp_env_auth,
|
||||
env_auth,
|
||||
browser_auth,
|
||||
az_cli_auth,
|
||||
session,
|
||||
):
|
||||
"""
|
||||
@@ -891,7 +899,7 @@ class M365Provider(Provider):
|
||||
Returns:
|
||||
M365IdentityInfo: An instance of M365IdentityInfo containing the identity information.
|
||||
"""
|
||||
logger.info("M365 provider: Setting up identity ...")
|
||||
logger.info("M365 provider: Setting up identity...")
|
||||
# TODO: fill this object with real values not default and set to none
|
||||
identity = M365IdentityInfo()
|
||||
|
||||
@@ -943,7 +951,7 @@ class M365Provider(Provider):
|
||||
identity.identity_type = "Service Principal"
|
||||
elif env_auth:
|
||||
identity.identity_type = "Service Principal and User Credentials"
|
||||
else:
|
||||
elif browser_auth or az_cli_auth:
|
||||
identity.identity_type = "User"
|
||||
try:
|
||||
logger.info(
|
||||
|
||||
@@ -257,7 +257,70 @@ class Testm365PowerShell:
|
||||
session.process.stdin.write = MagicMock()
|
||||
session.read_output = MagicMock(return_value="decrypted_password")
|
||||
|
||||
assert session.test_credentials(credentials) is False
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
session.test_credentials(credentials)
|
||||
assert (
|
||||
"Unexpected error: Acquiring token in behalf of user did not return a result."
|
||||
in str(exc_info.value)
|
||||
)
|
||||
|
||||
mock_msal.assert_called_once_with(
|
||||
client_id="test_client_id",
|
||||
client_credential="test_client_secret",
|
||||
authority="https://login.microsoftonline.com/test_tenant_id",
|
||||
)
|
||||
mock_msal_instance.acquire_token_by_username_password.assert_called_once_with(
|
||||
username="test@contoso.onmicrosoft.com",
|
||||
password="test_password",
|
||||
scopes=["https://graph.microsoft.com/.default"],
|
||||
)
|
||||
|
||||
session.close()
|
||||
|
||||
@patch("subprocess.Popen")
|
||||
@patch("msal.ConfidentialClientApplication")
|
||||
def test_test_credentials_auth_failure_no_access_token(self, mock_msal, mock_popen):
|
||||
mock_process = MagicMock()
|
||||
mock_popen.return_value = mock_process
|
||||
mock_msal_instance = MagicMock()
|
||||
mock_msal.return_value = mock_msal_instance
|
||||
mock_msal_instance.acquire_token_by_username_password.return_value = {
|
||||
"error_description": "invalid_grant: authentication failed"
|
||||
}
|
||||
|
||||
credentials = M365Credentials(
|
||||
user="test@contoso.onmicrosoft.com",
|
||||
passwd="test_password",
|
||||
encrypted_passwd="test_encrypted_password",
|
||||
client_id="test_client_id",
|
||||
client_secret="test_client_secret",
|
||||
tenant_id="test_tenant_id",
|
||||
)
|
||||
identity = M365IdentityInfo(
|
||||
identity_id="test_id",
|
||||
identity_type="User",
|
||||
tenant_id="test_tenant",
|
||||
tenant_domain="contoso.onmicrosoft.com",
|
||||
tenant_domains=["contoso.onmicrosoft.com"],
|
||||
location="test_location",
|
||||
)
|
||||
session = M365PowerShell(credentials, identity)
|
||||
|
||||
# Mock the execute method to return the decrypted password
|
||||
def mock_execute(command, *args, **kwargs):
|
||||
if "Write-Output" in command:
|
||||
return "decrypted_password"
|
||||
return None
|
||||
|
||||
session.execute = MagicMock(side_effect=mock_execute)
|
||||
session.process.stdin.write = MagicMock()
|
||||
session.read_output = MagicMock(return_value="decrypted_password")
|
||||
|
||||
with pytest.raises(Exception) as exc_info:
|
||||
session.test_credentials(credentials)
|
||||
assert "MsGraph Error invalid_grant: authentication failed" in str(
|
||||
exc_info.value
|
||||
)
|
||||
|
||||
mock_msal.assert_called_once_with(
|
||||
client_id="test_client_id",
|
||||
|
||||
Reference in New Issue
Block a user