mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
feat(m365): add M365 certificate auth to API (#8538)
This commit is contained in:
committed by
GitHub
parent
e80eed6baf
commit
b74744b135
@@ -7,6 +7,7 @@ All notable changes to the **Prowler API** are documented in this file.
|
||||
### Added
|
||||
- Default JWT keys are generated and stored if they are missing from configuration [(#8655)](https://github.com/prowler-cloud/prowler/pull/8655)
|
||||
- `compliance_name` for each compliance [(#7920)](https://github.com/prowler-cloud/prowler/pull/7920)
|
||||
- Support for M365 Certificate authentication [(#8538)](https://github.com/prowler-cloud/prowler/pull/8538)
|
||||
- API Key support [(#8805)](https://github.com/prowler-cloud/prowler/pull/8805)
|
||||
- SAML role mapping protection for single-admin tenants to prevent accidental lockout [(#8882)](https://github.com/prowler-cloud/prowler/pull/8882)
|
||||
- Support for `passed_findings` and `total_findings` fields in compliance requirement overview for accurate Prowler ThreatScore calculation [(#8582)](https://github.com/prowler-cloud/prowler/pull/8582)
|
||||
|
||||
@@ -12018,6 +12018,33 @@ components:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory
|
||||
where the application is registered.
|
||||
user:
|
||||
type: email
|
||||
description: 'Deprecated: User microsoft email address.'
|
||||
password:
|
||||
type: string
|
||||
description: 'Deprecated: User password.'
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- user
|
||||
- password
|
||||
- type: object
|
||||
title: M365 Certificate Credentials
|
||||
properties:
|
||||
client_id:
|
||||
type: string
|
||||
description: The Azure application (client) ID for authentication
|
||||
in Azure AD.
|
||||
tenant_id:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory
|
||||
where the application is registered.
|
||||
certificate_content:
|
||||
type: string
|
||||
description: The certificate content in base64 format for
|
||||
certificate-based authentication.
|
||||
user:
|
||||
type: email
|
||||
description: User microsoft email address.
|
||||
@@ -12026,8 +12053,8 @@ components:
|
||||
description: User password.
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- certificate_content
|
||||
- user
|
||||
- password
|
||||
- type: object
|
||||
@@ -13882,6 +13909,33 @@ components:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory where
|
||||
the application is registered.
|
||||
user:
|
||||
type: email
|
||||
description: 'Deprecated: User microsoft email address.'
|
||||
password:
|
||||
type: string
|
||||
description: 'Deprecated: User password.'
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- user
|
||||
- password
|
||||
- type: object
|
||||
title: M365 Certificate Credentials
|
||||
properties:
|
||||
client_id:
|
||||
type: string
|
||||
description: The Azure application (client) ID for authentication
|
||||
in Azure AD.
|
||||
tenant_id:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory where
|
||||
the application is registered.
|
||||
certificate_content:
|
||||
type: string
|
||||
description: The certificate content in base64 format for certificate-based
|
||||
authentication.
|
||||
user:
|
||||
type: email
|
||||
description: User microsoft email address.
|
||||
@@ -13890,8 +13944,8 @@ components:
|
||||
description: User password.
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- certificate_content
|
||||
- user
|
||||
- password
|
||||
- type: object
|
||||
@@ -14130,6 +14184,33 @@ components:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory
|
||||
where the application is registered.
|
||||
user:
|
||||
type: email
|
||||
description: 'Deprecated: User microsoft email address.'
|
||||
password:
|
||||
type: string
|
||||
description: 'Deprecated: User password.'
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- user
|
||||
- password
|
||||
- type: object
|
||||
title: M365 Certificate Credentials
|
||||
properties:
|
||||
client_id:
|
||||
type: string
|
||||
description: The Azure application (client) ID for authentication
|
||||
in Azure AD.
|
||||
tenant_id:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory
|
||||
where the application is registered.
|
||||
certificate_content:
|
||||
type: string
|
||||
description: The certificate content in base64 format for
|
||||
certificate-based authentication.
|
||||
user:
|
||||
type: email
|
||||
description: User microsoft email address.
|
||||
@@ -14138,8 +14219,8 @@ components:
|
||||
description: User password.
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- certificate_content
|
||||
- user
|
||||
- password
|
||||
- type: object
|
||||
@@ -14394,6 +14475,33 @@ components:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory where
|
||||
the application is registered.
|
||||
user:
|
||||
type: email
|
||||
description: 'Deprecated: User microsoft email address.'
|
||||
password:
|
||||
type: string
|
||||
description: 'Deprecated: User password.'
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- user
|
||||
- password
|
||||
- type: object
|
||||
title: M365 Certificate Credentials
|
||||
properties:
|
||||
client_id:
|
||||
type: string
|
||||
description: The Azure application (client) ID for authentication
|
||||
in Azure AD.
|
||||
tenant_id:
|
||||
type: string
|
||||
description: The Azure tenant ID, representing the directory where
|
||||
the application is registered.
|
||||
certificate_content:
|
||||
type: string
|
||||
description: The certificate content in base64 format for certificate-based
|
||||
authentication.
|
||||
user:
|
||||
type: email
|
||||
description: User microsoft email address.
|
||||
@@ -14402,8 +14510,8 @@ components:
|
||||
description: User password.
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- tenant_id
|
||||
- certificate_content
|
||||
- user
|
||||
- password
|
||||
- type: object
|
||||
|
||||
@@ -1870,17 +1870,7 @@ class TestProviderSecretViewSet:
|
||||
"kubeconfig_content": "kubeconfig-content",
|
||||
},
|
||||
),
|
||||
# M365 with STATIC secret - no user or password
|
||||
(
|
||||
Provider.ProviderChoices.M365.value,
|
||||
ProviderSecret.TypeChoices.STATIC,
|
||||
{
|
||||
"client_id": "client-id",
|
||||
"client_secret": "client-secret",
|
||||
"tenant_id": "tenant-id",
|
||||
},
|
||||
),
|
||||
# M365 with user only
|
||||
# M365 client secret credentials
|
||||
(
|
||||
Provider.ProviderChoices.M365.value,
|
||||
ProviderSecret.TypeChoices.STATIC,
|
||||
@@ -1889,27 +1879,17 @@ class TestProviderSecretViewSet:
|
||||
"client_secret": "client-secret",
|
||||
"tenant_id": "tenant-id",
|
||||
"user": "test@domain.com",
|
||||
},
|
||||
),
|
||||
# M365 with password only
|
||||
(
|
||||
Provider.ProviderChoices.M365.value,
|
||||
ProviderSecret.TypeChoices.STATIC,
|
||||
{
|
||||
"client_id": "client-id",
|
||||
"client_secret": "client-secret",
|
||||
"tenant_id": "tenant-id",
|
||||
"password": "supersecret",
|
||||
},
|
||||
),
|
||||
# M365 with user and password
|
||||
# M365 certificate credentials (valid base64)
|
||||
(
|
||||
Provider.ProviderChoices.M365.value,
|
||||
ProviderSecret.TypeChoices.STATIC,
|
||||
{
|
||||
"client_id": "client-id",
|
||||
"client_secret": "client-secret",
|
||||
"tenant_id": "tenant-id",
|
||||
"certificate_content": "VGVzdCBjZXJ0aWZpY2F0ZSBjb250ZW50",
|
||||
"user": "test@domain.com",
|
||||
"password": "supersecret",
|
||||
},
|
||||
@@ -2279,6 +2259,50 @@ class TestProviderSecretViewSet:
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
def test_m365_provider_secrets_invalid_certificate_base64(
|
||||
self, authenticated_client, providers_fixture
|
||||
):
|
||||
"""Test M365 provider secret creation with invalid base64 certificate content"""
|
||||
# Find M365 provider from fixture
|
||||
m365_provider = None
|
||||
for provider in providers_fixture:
|
||||
if provider.provider == Provider.ProviderChoices.M365.value:
|
||||
m365_provider = provider
|
||||
break
|
||||
|
||||
assert m365_provider is not None, "M365 provider not found in fixture"
|
||||
|
||||
data = {
|
||||
"data": {
|
||||
"type": "provider-secrets",
|
||||
"attributes": {
|
||||
"name": "M365 Certificate Invalid Base64",
|
||||
"secret_type": "static",
|
||||
"secret": {
|
||||
"client_id": "client-id",
|
||||
"tenant_id": "tenant-id",
|
||||
"certificate_content": "invalid-base64-content!@#$%",
|
||||
"user": "test@domain.com",
|
||||
"password": "supersecret",
|
||||
},
|
||||
},
|
||||
"relationships": {
|
||||
"provider": {
|
||||
"data": {"type": "providers", "id": str(m365_provider.id)}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
response = authenticated_client.post(
|
||||
reverse("providersecret-list"),
|
||||
data=json.dumps(data),
|
||||
content_type="application/vnd.api+json",
|
||||
)
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
assert "certificate content is not valid base64 encoded data" in str(
|
||||
response.json()
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestScanViewSet:
|
||||
@@ -5781,10 +5805,12 @@ class TestScheduleViewSet:
|
||||
)
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
@patch("tasks.beat.perform_scheduled_scan_task.apply_async")
|
||||
@patch("api.v1.views.Task.objects.get")
|
||||
def test_schedule_daily_already_scheduled(
|
||||
self,
|
||||
mock_task_get,
|
||||
mock_apply_async,
|
||||
authenticated_client,
|
||||
providers_fixture,
|
||||
tasks_fixture,
|
||||
@@ -5792,6 +5818,7 @@ class TestScheduleViewSet:
|
||||
provider, *_ = providers_fixture
|
||||
prowler_task = tasks_fixture[0]
|
||||
mock_task_get.return_value = prowler_task
|
||||
mock_apply_async.return_value.id = prowler_task.id
|
||||
json_payload = {
|
||||
"provider_id": str(provider.id),
|
||||
}
|
||||
|
||||
@@ -115,6 +115,40 @@ from rest_framework_json_api import serializers
|
||||
"description": "The Azure tenant ID, representing the directory where the application is "
|
||||
"registered.",
|
||||
},
|
||||
"user": {
|
||||
"type": "email",
|
||||
"description": "Deprecated: User microsoft email address.",
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"description": "Deprecated: User password.",
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"client_id",
|
||||
"client_secret",
|
||||
"tenant_id",
|
||||
"user",
|
||||
"password",
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "M365 Certificate Credentials",
|
||||
"properties": {
|
||||
"client_id": {
|
||||
"type": "string",
|
||||
"description": "The Azure application (client) ID for authentication in Azure AD.",
|
||||
},
|
||||
"tenant_id": {
|
||||
"type": "string",
|
||||
"description": "The Azure tenant ID, representing the directory where the application is "
|
||||
"registered.",
|
||||
},
|
||||
"certificate_content": {
|
||||
"type": "string",
|
||||
"description": "The certificate content in base64 format for certificate-based authentication.",
|
||||
},
|
||||
"user": {
|
||||
"type": "email",
|
||||
"description": "User microsoft email address.",
|
||||
@@ -126,8 +160,8 @@ from rest_framework_json_api import serializers
|
||||
},
|
||||
"required": [
|
||||
"client_id",
|
||||
"client_secret",
|
||||
"tenant_id",
|
||||
"certificate_content",
|
||||
"user",
|
||||
"password",
|
||||
],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import base64
|
||||
import json
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
@@ -1395,10 +1396,38 @@ class AzureProviderSecret(serializers.Serializer):
|
||||
|
||||
class M365ProviderSecret(serializers.Serializer):
|
||||
client_id = serializers.CharField()
|
||||
client_secret = serializers.CharField()
|
||||
client_secret = serializers.CharField(required=False)
|
||||
tenant_id = serializers.CharField()
|
||||
user = serializers.EmailField(required=False)
|
||||
password = serializers.CharField(required=False)
|
||||
certificate_content = serializers.CharField(required=False)
|
||||
|
||||
def validate(self, attrs):
|
||||
if attrs.get("client_secret") and attrs.get("certificate_content"):
|
||||
raise serializers.ValidationError(
|
||||
"You cannot provide both client_secret and certificate_content."
|
||||
)
|
||||
if not attrs.get("client_secret") and not attrs.get("certificate_content"):
|
||||
raise serializers.ValidationError(
|
||||
"You must provide either client_secret or certificate_content."
|
||||
)
|
||||
return super().validate(attrs)
|
||||
|
||||
def validate_certificate_content(self, certificate_content):
|
||||
"""Validate that M365 certificate content is valid base64 encoded data."""
|
||||
if certificate_content:
|
||||
try:
|
||||
base64.b64decode(certificate_content, validate=True)
|
||||
except Exception as e:
|
||||
raise ValidationError(
|
||||
{
|
||||
"certificate_content": [
|
||||
f"The provided certificate content is not valid base64 encoded data: {str(e)}"
|
||||
]
|
||||
},
|
||||
code="m365-certificate-content",
|
||||
)
|
||||
return certificate_content
|
||||
|
||||
class Meta:
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
Reference in New Issue
Block a user