mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-10-05 03:12:14 +00:00
feat(api): add okta provider support (#11184)
This commit is contained in:
@@ -2,6 +2,14 @@
|
||||
|
||||
All notable changes to the **Prowler API** are documented in this file.
|
||||
|
||||
## [1.29.0] (Prowler UNRELEASED)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
- `okta` provider support [(#11184)](https://github.com/prowler-cloud/prowler/pull/11184)
|
||||
|
||||
---
|
||||
|
||||
## [1.28.0] (Prowler v5.27.0)
|
||||
|
||||
### 🚀 Added
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
from django.db import migrations
|
||||
|
||||
import api.db_utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("api", "0092_findings_arrays_gin_index_parent"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="provider",
|
||||
name="provider",
|
||||
field=api.db_utils.ProviderEnumField(
|
||||
choices=[
|
||||
("aws", "AWS"),
|
||||
("azure", "Azure"),
|
||||
("gcp", "GCP"),
|
||||
("kubernetes", "Kubernetes"),
|
||||
("m365", "M365"),
|
||||
("github", "GitHub"),
|
||||
("mongodbatlas", "MongoDB Atlas"),
|
||||
("iac", "IaC"),
|
||||
("oraclecloud", "Oracle Cloud Infrastructure"),
|
||||
("alibabacloud", "Alibaba Cloud"),
|
||||
("cloudflare", "Cloudflare"),
|
||||
("openstack", "OpenStack"),
|
||||
("image", "Image"),
|
||||
("googleworkspace", "Google Workspace"),
|
||||
("vercel", "Vercel"),
|
||||
("okta", "Okta"),
|
||||
],
|
||||
default="aws",
|
||||
),
|
||||
),
|
||||
migrations.RunSQL(
|
||||
"ALTER TYPE provider ADD VALUE IF NOT EXISTS 'okta';",
|
||||
reverse_sql=migrations.RunSQL.noop,
|
||||
),
|
||||
]
|
||||
@@ -296,6 +296,7 @@ class Provider(RowLevelSecurityProtectedModel):
|
||||
IMAGE = "image", _("Image")
|
||||
GOOGLEWORKSPACE = "googleworkspace", _("Google Workspace")
|
||||
VERCEL = "vercel", _("Vercel")
|
||||
OKTA = "okta", _("Okta")
|
||||
|
||||
@staticmethod
|
||||
def validate_aws_uid(value):
|
||||
@@ -354,6 +355,26 @@ class Provider(RowLevelSecurityProtectedModel):
|
||||
pointer="/data/attributes/uid",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def validate_okta_uid(value):
|
||||
if not re.match(
|
||||
r"^[a-z0-9][a-z0-9-]*\.("
|
||||
r"okta\.com|oktapreview\.com|okta-emea\.com|"
|
||||
r"okta-gov\.com|okta\.mil|okta-miltest\.com|trex-govcloud\.com"
|
||||
r")$",
|
||||
value,
|
||||
):
|
||||
raise ModelValidationError(
|
||||
detail=(
|
||||
"Okta provider ID must be a valid Okta-managed org domain "
|
||||
"(e.g., acme.okta.com, also .oktapreview.com / .okta-emea.com "
|
||||
"/ .okta-gov.com / .okta.mil / .okta-miltest.com / "
|
||||
".trex-govcloud.com), without scheme or path."
|
||||
),
|
||||
code="okta-uid",
|
||||
pointer="/data/attributes/uid",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def validate_kubernetes_uid(value):
|
||||
if not re.match(
|
||||
@@ -480,6 +501,12 @@ class Provider(RowLevelSecurityProtectedModel):
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
if self.provider == self.ProviderChoices.OKTA and self.uid:
|
||||
# Mirror the SDK, which lowercases the org domain before connecting.
|
||||
# Without this the API would reject Acme.okta.com even though the
|
||||
# SDK would accept it, and stored uids could disagree with the
|
||||
# authenticated org domain.
|
||||
self.uid = self.uid.strip().lower()
|
||||
getattr(self, f"validate_{self.provider}_uid")(self.uid)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
@@ -373,6 +373,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -389,6 +390,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -412,6 +414,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -430,6 +433,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -1453,6 +1457,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -1469,6 +1474,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -1491,6 +1497,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -1509,6 +1516,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -1997,6 +2005,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -2013,6 +2022,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -2035,6 +2045,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -2053,6 +2064,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -2584,6 +2596,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -2600,6 +2613,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -2622,6 +2636,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -2640,6 +2655,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -3134,6 +3150,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -3150,6 +3167,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -3173,6 +3191,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -3191,6 +3210,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -3740,6 +3760,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -3756,6 +3777,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -3779,6 +3801,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -3797,6 +3820,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -4254,6 +4278,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -4270,6 +4295,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -4293,6 +4319,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -4311,6 +4338,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -4766,6 +4794,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -4782,6 +4811,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -4805,6 +4835,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -4823,6 +4854,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -5266,6 +5298,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -5282,6 +5315,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -5305,6 +5339,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -5323,6 +5358,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -7156,6 +7192,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -7172,6 +7209,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -7195,6 +7233,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -7213,6 +7252,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- name: filter[search]
|
||||
@@ -7335,6 +7375,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -7351,6 +7392,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -7374,6 +7416,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -7392,6 +7435,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- name: filter[search]
|
||||
@@ -7503,6 +7547,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -7519,6 +7564,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -7541,6 +7587,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -7559,6 +7606,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- name: filter[search]
|
||||
@@ -7702,6 +7750,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -7718,6 +7767,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -7741,6 +7791,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -7759,6 +7810,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -7915,6 +7967,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -7931,6 +7984,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -7954,6 +8008,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -7972,6 +8027,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -8122,6 +8178,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -8138,6 +8195,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -8160,6 +8218,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -8178,6 +8237,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- name: filter[search]
|
||||
@@ -8370,6 +8430,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -8386,6 +8447,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -8409,6 +8471,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -8427,6 +8490,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -8548,6 +8612,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -8564,6 +8629,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -8587,6 +8653,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -8605,6 +8672,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -8750,6 +8818,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -8766,6 +8835,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -8789,6 +8859,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -8807,6 +8878,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -9593,6 +9665,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -9609,6 +9682,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider__in]
|
||||
schema:
|
||||
@@ -9632,6 +9706,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -9650,6 +9725,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -9673,6 +9749,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -9689,6 +9766,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -9712,6 +9790,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -9730,6 +9809,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- name: filter[search]
|
||||
@@ -10400,6 +10480,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -10416,6 +10497,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -10439,6 +10521,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -10457,6 +10540,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -10951,6 +11035,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -10967,6 +11052,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -10990,6 +11076,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -11008,6 +11095,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -11315,6 +11403,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -11331,6 +11420,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -11354,6 +11444,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -11372,6 +11463,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -11685,6 +11777,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -11701,6 +11794,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -11724,6 +11818,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -11742,6 +11837,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -12580,6 +12676,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
* `azure` - Azure
|
||||
@@ -12596,6 +12693,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
- in: query
|
||||
name: filter[provider_type__in]
|
||||
schema:
|
||||
@@ -12619,6 +12717,7 @@ paths:
|
||||
- openstack
|
||||
- oraclecloud
|
||||
- vercel
|
||||
- okta
|
||||
description: |-
|
||||
Multiple values may be separated by commas.
|
||||
|
||||
@@ -12637,6 +12736,7 @@ paths:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
explode: false
|
||||
style: form
|
||||
- in: query
|
||||
@@ -20115,6 +20215,23 @@ components:
|
||||
required:
|
||||
- clouds_yaml_content
|
||||
- clouds_yaml_cloud
|
||||
- type: object
|
||||
title: Okta OAuth Credentials
|
||||
properties:
|
||||
okta_client_id:
|
||||
type: string
|
||||
description: Client ID of the Okta API Services app used for OAuth 2.0 private-key JWT authentication.
|
||||
okta_private_key:
|
||||
type: string
|
||||
description: PEM-encoded private key whose matching public key (JWK) is registered on the Okta service app.
|
||||
okta_scopes:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: OAuth scopes to request. Optional; defaults to the minimum set required to run the currently enabled Okta checks.
|
||||
required:
|
||||
- okta_client_id
|
||||
- okta_private_key
|
||||
- type: object
|
||||
title: Vercel API Token
|
||||
properties:
|
||||
@@ -21127,6 +21244,7 @@ components:
|
||||
- image
|
||||
- googleworkspace
|
||||
- vercel
|
||||
- okta
|
||||
type: string
|
||||
description: |-
|
||||
* `aws` - AWS
|
||||
@@ -21144,6 +21262,7 @@ components:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
x-spec-enum-id: 91f917e0c3ab97e8
|
||||
uid:
|
||||
type: string
|
||||
@@ -21265,6 +21384,7 @@ components:
|
||||
- image
|
||||
- googleworkspace
|
||||
- vercel
|
||||
- okta
|
||||
type: string
|
||||
x-spec-enum-id: 91f917e0c3ab97e8
|
||||
description: |-
|
||||
@@ -21285,6 +21405,7 @@ components:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
uid:
|
||||
type: string
|
||||
title: Unique identifier for the provider, set by the provider
|
||||
@@ -21337,6 +21458,7 @@ components:
|
||||
- image
|
||||
- googleworkspace
|
||||
- vercel
|
||||
- okta
|
||||
type: string
|
||||
x-spec-enum-id: 91f917e0c3ab97e8
|
||||
description: |-
|
||||
@@ -21357,6 +21479,7 @@ components:
|
||||
* `image` - Image
|
||||
* `googleworkspace` - Google Workspace
|
||||
* `vercel` - Vercel
|
||||
* `okta` - Okta
|
||||
uid:
|
||||
type: string
|
||||
minLength: 3
|
||||
@@ -22206,6 +22329,23 @@ components:
|
||||
required:
|
||||
- clouds_yaml_content
|
||||
- clouds_yaml_cloud
|
||||
- type: object
|
||||
title: Okta OAuth Credentials
|
||||
properties:
|
||||
okta_client_id:
|
||||
type: string
|
||||
description: Client ID of the Okta API Services app used for OAuth 2.0 private-key JWT authentication.
|
||||
okta_private_key:
|
||||
type: string
|
||||
description: PEM-encoded private key whose matching public key (JWK) is registered on the Okta service app.
|
||||
okta_scopes:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: OAuth scopes to request. Optional; defaults to the minimum set required to run the currently enabled Okta checks.
|
||||
required:
|
||||
- okta_client_id
|
||||
- okta_private_key
|
||||
- type: object
|
||||
title: Vercel API Token
|
||||
properties:
|
||||
@@ -22631,6 +22771,23 @@ components:
|
||||
required:
|
||||
- clouds_yaml_content
|
||||
- clouds_yaml_cloud
|
||||
- type: object
|
||||
title: Okta OAuth Credentials
|
||||
properties:
|
||||
okta_client_id:
|
||||
type: string
|
||||
description: Client ID of the Okta API Services app used for OAuth 2.0 private-key JWT authentication.
|
||||
okta_private_key:
|
||||
type: string
|
||||
description: PEM-encoded private key whose matching public key (JWK) is registered on the Okta service app.
|
||||
okta_scopes:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: OAuth scopes to request. Optional; defaults to the minimum set required to run the currently enabled Okta checks.
|
||||
required:
|
||||
- okta_client_id
|
||||
- okta_private_key
|
||||
- type: object
|
||||
title: Vercel API Token
|
||||
properties:
|
||||
@@ -23066,6 +23223,23 @@ components:
|
||||
required:
|
||||
- clouds_yaml_content
|
||||
- clouds_yaml_cloud
|
||||
- type: object
|
||||
title: Okta OAuth Credentials
|
||||
properties:
|
||||
okta_client_id:
|
||||
type: string
|
||||
description: Client ID of the Okta API Services app used for OAuth 2.0 private-key JWT authentication.
|
||||
okta_private_key:
|
||||
type: string
|
||||
description: PEM-encoded private key whose matching public key (JWK) is registered on the Okta service app.
|
||||
okta_scopes:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: OAuth scopes to request. Optional; defaults to the minimum set required to run the currently enabled Okta checks.
|
||||
required:
|
||||
- okta_client_id
|
||||
- okta_private_key
|
||||
- type: object
|
||||
title: Vercel API Token
|
||||
properties:
|
||||
|
||||
@@ -31,6 +31,7 @@ from prowler.providers.image.image_provider import ImageProvider
|
||||
from prowler.providers.kubernetes.kubernetes_provider import KubernetesProvider
|
||||
from prowler.providers.m365.m365_provider import M365Provider
|
||||
from prowler.providers.mongodbatlas.mongodbatlas_provider import MongodbatlasProvider
|
||||
from prowler.providers.okta.okta_provider import OktaProvider
|
||||
from prowler.providers.openstack.openstack_provider import OpenstackProvider
|
||||
from prowler.providers.oraclecloud.oraclecloud_provider import OraclecloudProvider
|
||||
from prowler.providers.vercel.vercel_provider import VercelProvider
|
||||
@@ -130,6 +131,7 @@ class TestReturnProwlerProvider:
|
||||
(Provider.ProviderChoices.OPENSTACK.value, OpenstackProvider),
|
||||
(Provider.ProviderChoices.IMAGE.value, ImageProvider),
|
||||
(Provider.ProviderChoices.VERCEL.value, VercelProvider),
|
||||
(Provider.ProviderChoices.OKTA.value, OktaProvider),
|
||||
],
|
||||
)
|
||||
def test_return_prowler_provider(self, provider_type, expected_provider):
|
||||
@@ -238,6 +240,31 @@ class TestProwlerProviderConnectionTest:
|
||||
raise_on_exception=False,
|
||||
)
|
||||
|
||||
@patch("api.utils.return_prowler_provider")
|
||||
def test_prowler_provider_connection_test_okta_provider(
|
||||
self, mock_return_prowler_provider
|
||||
):
|
||||
"""Test connection test for Okta provider passes org domain and provider_id."""
|
||||
provider = MagicMock()
|
||||
provider.uid = "acme.okta.com"
|
||||
provider.provider = Provider.ProviderChoices.OKTA.value
|
||||
provider.secret.secret = {
|
||||
"okta_client_id": "0oa123456789abcdef",
|
||||
"okta_private_key": "-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----",
|
||||
"okta_scopes": ["okta.policies.read"],
|
||||
}
|
||||
mock_return_prowler_provider.return_value = MagicMock()
|
||||
|
||||
prowler_provider_connection_test(provider)
|
||||
mock_return_prowler_provider.return_value.test_connection.assert_called_once_with(
|
||||
okta_client_id="0oa123456789abcdef",
|
||||
okta_private_key="-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----",
|
||||
okta_scopes=["okta.policies.read"],
|
||||
okta_org_domain="acme.okta.com",
|
||||
provider_id="acme.okta.com",
|
||||
raise_on_exception=False,
|
||||
)
|
||||
|
||||
@patch("api.utils.return_prowler_provider")
|
||||
def test_prowler_provider_connection_test_image_provider_no_creds(
|
||||
self, mock_return_prowler_provider
|
||||
@@ -308,6 +335,10 @@ class TestGetProwlerProviderKwargs:
|
||||
Provider.ProviderChoices.VERCEL.value,
|
||||
{"team_id": "provider_uid"},
|
||||
),
|
||||
(
|
||||
Provider.ProviderChoices.OKTA.value,
|
||||
{"okta_org_domain": "provider_uid"},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_get_prowler_provider_kwargs(self, provider_type, expected_extra_kwargs):
|
||||
|
||||
@@ -1625,6 +1625,21 @@ class TestProviderViewSet:
|
||||
"uid": "C12",
|
||||
"alias": "Google Workspace Minimum Length",
|
||||
},
|
||||
{
|
||||
"provider": "okta",
|
||||
"uid": "acme.okta.com",
|
||||
"alias": "Okta Org",
|
||||
},
|
||||
{
|
||||
"provider": "okta",
|
||||
"uid": "agency.okta-gov.com",
|
||||
"alias": "Okta Gov Org",
|
||||
},
|
||||
{
|
||||
"provider": "okta",
|
||||
"uid": "agency.okta.mil",
|
||||
"alias": "Okta Mil Org",
|
||||
},
|
||||
]
|
||||
),
|
||||
)
|
||||
@@ -2143,6 +2158,24 @@ class TestProviderViewSet:
|
||||
"googleworkspace-uid",
|
||||
"uid",
|
||||
),
|
||||
(
|
||||
{
|
||||
"provider": "okta",
|
||||
"uid": "https://acme.okta.com",
|
||||
"alias": "test",
|
||||
},
|
||||
"okta-uid",
|
||||
"uid",
|
||||
),
|
||||
(
|
||||
{
|
||||
"provider": "okta",
|
||||
"uid": "acme.example.com",
|
||||
"alias": "test",
|
||||
},
|
||||
"okta-uid",
|
||||
"uid",
|
||||
),
|
||||
]
|
||||
),
|
||||
)
|
||||
@@ -2163,6 +2196,25 @@ class TestProviderViewSet:
|
||||
== f"/data/attributes/{error_pointer}"
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input_uid,stored_uid",
|
||||
[
|
||||
("Acme.okta.com", "acme.okta.com"),
|
||||
(" ACME.OKTA.COM ", "acme.okta.com"),
|
||||
("Agency.Okta-Gov.com", "agency.okta-gov.com"),
|
||||
],
|
||||
)
|
||||
def test_providers_create_okta_uid_normalized(
|
||||
self, authenticated_client, input_uid, stored_uid
|
||||
):
|
||||
response = authenticated_client.post(
|
||||
reverse("provider-list"),
|
||||
data={"provider": "okta", "uid": input_uid, "alias": "Okta"},
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert Provider.objects.get().uid == stored_uid
|
||||
|
||||
def test_providers_partial_update(self, authenticated_client, providers_fixture):
|
||||
provider1, *_ = providers_fixture
|
||||
new_alias = "This is the new name"
|
||||
@@ -2320,17 +2372,17 @@ class TestProviderViewSet:
|
||||
),
|
||||
("alias", "aws_testing_1", 1),
|
||||
("alias.icontains", "aws", 2),
|
||||
("inserted_at", TODAY, 13),
|
||||
("inserted_at", TODAY, 14),
|
||||
(
|
||||
"inserted_at.gte",
|
||||
"2024-01-01",
|
||||
13,
|
||||
14,
|
||||
),
|
||||
("inserted_at.lte", "2024-01-01", 0),
|
||||
(
|
||||
"updated_at.gte",
|
||||
"2024-01-01",
|
||||
13,
|
||||
14,
|
||||
),
|
||||
("updated_at.lte", "2024-01-01", 0),
|
||||
]
|
||||
@@ -2963,6 +3015,19 @@ class TestProviderSecretViewSet:
|
||||
"api_token": "fake-vercel-api-token-for-testing",
|
||||
},
|
||||
),
|
||||
# Okta with inline private key credentials
|
||||
(
|
||||
Provider.ProviderChoices.OKTA.value,
|
||||
ProviderSecret.TypeChoices.STATIC,
|
||||
{
|
||||
"okta_client_id": "0oa123456789abcdef",
|
||||
"okta_private_key": "-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----",
|
||||
"okta_scopes": [
|
||||
"okta.policies.read",
|
||||
"okta.groups.read",
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_provider_secrets_create_valid(
|
||||
@@ -3075,6 +3140,46 @@ class TestProviderSecretViewSet:
|
||||
== f"/data/attributes/{error_pointer}"
|
||||
)
|
||||
|
||||
def test_provider_secrets_invalid_create_okta_missing_private_key(
|
||||
self,
|
||||
providers_fixture,
|
||||
authenticated_client,
|
||||
):
|
||||
okta_provider = next(
|
||||
provider
|
||||
for provider in providers_fixture
|
||||
if provider.provider == Provider.ProviderChoices.OKTA.value
|
||||
)
|
||||
data = {
|
||||
"data": {
|
||||
"type": "provider-secrets",
|
||||
"attributes": {
|
||||
"name": "Okta Secret",
|
||||
"secret_type": ProviderSecret.TypeChoices.STATIC,
|
||||
"secret": {
|
||||
"okta_client_id": "0oa123456789abcdef",
|
||||
},
|
||||
},
|
||||
"relationships": {
|
||||
"provider": {
|
||||
"data": {"type": "providers", "id": str(okta_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 response.json()["errors"][0]["code"] == "required"
|
||||
assert response.json()["errors"][0]["source"]["pointer"] == (
|
||||
"/data/attributes/secret/okta_private_key"
|
||||
)
|
||||
|
||||
def test_provider_secrets_partial_update(
|
||||
self, authenticated_client, provider_secret_fixture
|
||||
):
|
||||
|
||||
@@ -37,6 +37,7 @@ if TYPE_CHECKING:
|
||||
from prowler.providers.mongodbatlas.mongodbatlas_provider import (
|
||||
MongodbatlasProvider,
|
||||
)
|
||||
from prowler.providers.okta.okta_provider import OktaProvider
|
||||
from prowler.providers.openstack.openstack_provider import OpenstackProvider
|
||||
from prowler.providers.oraclecloud.oraclecloud_provider import OraclecloudProvider
|
||||
from prowler.providers.vercel.vercel_provider import VercelProvider
|
||||
@@ -93,6 +94,7 @@ def return_prowler_provider(
|
||||
| KubernetesProvider
|
||||
| M365Provider
|
||||
| MongodbatlasProvider
|
||||
| OktaProvider
|
||||
| OpenstackProvider
|
||||
| OraclecloudProvider
|
||||
| VercelProvider
|
||||
@@ -181,6 +183,10 @@ def return_prowler_provider(
|
||||
from prowler.providers.vercel.vercel_provider import VercelProvider
|
||||
|
||||
prowler_provider = VercelProvider
|
||||
case Provider.ProviderChoices.OKTA.value:
|
||||
from prowler.providers.okta.okta_provider import OktaProvider
|
||||
|
||||
prowler_provider = OktaProvider
|
||||
case _:
|
||||
raise ValueError(f"Provider type {provider.provider} not supported")
|
||||
return prowler_provider
|
||||
@@ -246,6 +252,11 @@ def get_prowler_provider_kwargs(
|
||||
**prowler_provider_kwargs,
|
||||
"team_id": provider.uid,
|
||||
}
|
||||
elif provider.provider == Provider.ProviderChoices.OKTA.value:
|
||||
prowler_provider_kwargs = {
|
||||
**prowler_provider_kwargs,
|
||||
"okta_org_domain": provider.uid,
|
||||
}
|
||||
elif provider.provider == Provider.ProviderChoices.IMAGE.value:
|
||||
# Detect whether uid is a registry URL (e.g. "docker.io/andoniaf") or
|
||||
# a concrete image reference (e.g. "docker.io/andoniaf/myimage:latest").
|
||||
@@ -290,6 +301,7 @@ def initialize_prowler_provider(
|
||||
| KubernetesProvider
|
||||
| M365Provider
|
||||
| MongodbatlasProvider
|
||||
| OktaProvider
|
||||
| OpenstackProvider
|
||||
| OraclecloudProvider
|
||||
| VercelProvider
|
||||
@@ -351,6 +363,14 @@ def prowler_provider_connection_test(provider: Provider) -> Connection:
|
||||
"raise_on_exception": False,
|
||||
}
|
||||
return prowler_provider.test_connection(**vercel_kwargs)
|
||||
elif provider.provider == Provider.ProviderChoices.OKTA.value:
|
||||
okta_kwargs = {
|
||||
**prowler_provider_kwargs,
|
||||
"okta_org_domain": provider.uid,
|
||||
"provider_id": provider.uid,
|
||||
"raise_on_exception": False,
|
||||
}
|
||||
return prowler_provider.test_connection(**okta_kwargs)
|
||||
elif provider.provider == Provider.ProviderChoices.IMAGE.value:
|
||||
image_kwargs = {
|
||||
"image": provider.uid,
|
||||
|
||||
@@ -404,6 +404,26 @@ from rest_framework_json_api import serializers
|
||||
},
|
||||
"required": ["clouds_yaml_content", "clouds_yaml_cloud"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Okta OAuth Credentials",
|
||||
"properties": {
|
||||
"okta_client_id": {
|
||||
"type": "string",
|
||||
"description": "Client ID of the Okta API Services app used for OAuth 2.0 private-key JWT authentication.",
|
||||
},
|
||||
"okta_private_key": {
|
||||
"type": "string",
|
||||
"description": "PEM-encoded private key whose matching public key (JWK) is registered on the Okta service app.",
|
||||
},
|
||||
"okta_scopes": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "OAuth scopes to request. Optional; defaults to the minimum set required to run the currently enabled Okta checks.",
|
||||
},
|
||||
},
|
||||
"required": ["okta_client_id", "okta_private_key"],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Vercel API Token",
|
||||
|
||||
@@ -1543,6 +1543,8 @@ class BaseWriteProviderSecretSerializer(BaseWriteSerializer):
|
||||
serializer = GCPProviderSecret(data=secret)
|
||||
elif provider_type == Provider.ProviderChoices.GOOGLEWORKSPACE.value:
|
||||
serializer = GoogleWorkspaceProviderSecret(data=secret)
|
||||
elif provider_type == Provider.ProviderChoices.OKTA.value:
|
||||
serializer = OktaProviderSecret(data=secret)
|
||||
elif provider_type == Provider.ProviderChoices.GITHUB.value:
|
||||
serializer = GithubProviderSecret(data=secret)
|
||||
elif provider_type == Provider.ProviderChoices.IAC.value:
|
||||
@@ -1688,6 +1690,15 @@ class GoogleWorkspaceProviderSecret(serializers.Serializer):
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
class OktaProviderSecret(serializers.Serializer):
|
||||
okta_client_id = serializers.CharField()
|
||||
okta_private_key = serializers.CharField()
|
||||
okta_scopes = serializers.ListField(child=serializers.CharField(), required=False)
|
||||
|
||||
class Meta:
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
class MongoDBAtlasProviderSecret(serializers.Serializer):
|
||||
atlas_public_key = serializers.CharField()
|
||||
atlas_private_key = serializers.CharField()
|
||||
|
||||
@@ -571,6 +571,12 @@ def providers_fixture(tenants_fixture):
|
||||
alias="vercel_testing",
|
||||
tenant_id=tenant.id,
|
||||
)
|
||||
provider14 = Provider.objects.create(
|
||||
provider="okta",
|
||||
uid="acme.okta.com",
|
||||
alias="okta_testing",
|
||||
tenant_id=tenant.id,
|
||||
)
|
||||
|
||||
return (
|
||||
provider1,
|
||||
@@ -586,6 +592,7 @@ def providers_fixture(tenants_fixture):
|
||||
provider11,
|
||||
provider12,
|
||||
provider13,
|
||||
provider14,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ All notable changes to the **Prowler SDK** are documented in this file.
|
||||
- `entra_app_registration_client_secret_unused` check for M365 provider [(#11232)](https://github.com/prowler-cloud/prowler/pull/11232)
|
||||
- `cloudsql_instance_cmek_encryption_enabled` check for GCP provider [(#11023)](https://github.com/prowler-cloud/prowler/pull/11023)
|
||||
|
||||
### 🔄 Changed
|
||||
|
||||
- `OktaProvider.test_connection` accepts an optional `provider_id` (org domain) and raises `OktaInvalidProviderIdError` (14007) when it doesn't match the authenticated org — guards against stored UID drifting from the credentials' org [(#11184)](https://github.com/prowler-cloud/prowler/pull/11184)
|
||||
|
||||
---
|
||||
|
||||
## [5.27.1] (Prowler UNRELEASED)
|
||||
|
||||
@@ -34,6 +34,10 @@ class OktaBaseException(ProwlerException):
|
||||
"message": "Okta service app is missing required scopes",
|
||||
"remediation": "Have a Super Admin grant the required *.read scopes to the service app and assign the Read-Only Administrator role.",
|
||||
},
|
||||
(14007, "OktaInvalidProviderIdError"): {
|
||||
"message": "The provided provider_id does not match the credentials org domain",
|
||||
"remediation": "Check the provider_id (Okta org domain) and ensure it matches the org the service app credentials were issued for.",
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, code, file=None, original_exception=None, message=None):
|
||||
@@ -110,3 +114,10 @@ class OktaInsufficientPermissionsError(OktaCredentialsError):
|
||||
super().__init__(
|
||||
14006, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
|
||||
class OktaInvalidProviderIdError(OktaCredentialsError):
|
||||
def __init__(self, file=None, original_exception=None, message=None):
|
||||
super().__init__(
|
||||
14007, file=file, original_exception=original_exception, message=message
|
||||
)
|
||||
|
||||
@@ -22,6 +22,7 @@ from prowler.providers.okta.exceptions.exceptions import (
|
||||
OktaInsufficientPermissionsError,
|
||||
OktaInvalidCredentialsError,
|
||||
OktaInvalidOrgDomainError,
|
||||
OktaInvalidProviderIdError,
|
||||
OktaPrivateKeyFileError,
|
||||
OktaSetUpIdentityError,
|
||||
OktaSetUpSessionError,
|
||||
@@ -348,8 +349,17 @@ class OktaProvider(Provider):
|
||||
okta_private_key_file: str = "",
|
||||
okta_scopes: Optional[Union[str, list[str]]] = None,
|
||||
raise_on_exception: bool = True,
|
||||
provider_id: str = None,
|
||||
) -> Connection:
|
||||
"""Test the connection to Okta with the provided OAuth credentials."""
|
||||
"""Test the connection to Okta with the provided OAuth credentials.
|
||||
|
||||
Args:
|
||||
provider_id: The provider ID (Okta org domain). When supplied, the
|
||||
authenticated org domain must match it — guards against the
|
||||
stored provider UID drifting from the org the credentials were
|
||||
actually issued for. Compared case-insensitively, matching the
|
||||
normalization applied during session setup.
|
||||
"""
|
||||
try:
|
||||
OktaProvider.validate_arguments(
|
||||
okta_org_domain=okta_org_domain,
|
||||
@@ -364,7 +374,17 @@ class OktaProvider(Provider):
|
||||
private_key_file=okta_private_key_file,
|
||||
scopes=okta_scopes,
|
||||
)
|
||||
OktaProvider.setup_identity(session)
|
||||
identity = OktaProvider.setup_identity(session)
|
||||
|
||||
if provider_id and provider_id.strip().lower() != identity.org_domain:
|
||||
raise OktaInvalidProviderIdError(
|
||||
file=os.path.basename(__file__),
|
||||
message=(
|
||||
f"The provider ID '{provider_id}' does not match the "
|
||||
f"authenticated Okta org domain '{identity.org_domain}'."
|
||||
),
|
||||
)
|
||||
|
||||
return Connection(is_connected=True)
|
||||
except Exception as error:
|
||||
logger.critical(
|
||||
|
||||
@@ -7,6 +7,7 @@ from prowler.providers.okta.exceptions.exceptions import (
|
||||
OktaInsufficientPermissionsError,
|
||||
OktaInvalidCredentialsError,
|
||||
OktaInvalidOrgDomainError,
|
||||
OktaInvalidProviderIdError,
|
||||
OktaPrivateKeyFileError,
|
||||
OktaSetUpIdentityError,
|
||||
OktaSetUpSessionError,
|
||||
@@ -20,6 +21,7 @@ EXPECTED_CODES = {
|
||||
OktaInvalidOrgDomainError: 14004,
|
||||
OktaPrivateKeyFileError: 14005,
|
||||
OktaInsufficientPermissionsError: 14006,
|
||||
OktaInvalidProviderIdError: 14007,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from prowler.providers.okta.exceptions.exceptions import (
|
||||
OktaInsufficientPermissionsError,
|
||||
OktaInvalidCredentialsError,
|
||||
OktaInvalidOrgDomainError,
|
||||
OktaInvalidProviderIdError,
|
||||
OktaPrivateKeyFileError,
|
||||
OktaSetUpIdentityError,
|
||||
)
|
||||
@@ -396,6 +397,55 @@ class Test_OktaProvider_test_connection:
|
||||
with pytest.raises(OktaEnvironmentVariableError):
|
||||
OktaProvider.test_connection()
|
||||
|
||||
def test_provider_id_match_succeeds(self, _clear_okta_env, tmp_path):
|
||||
validate_p, session_p, identity_p = _mock_setup_paths()
|
||||
with validate_p, session_p, identity_p:
|
||||
connection = OktaProvider.test_connection(
|
||||
okta_org_domain=OKTA_ORG_DOMAIN,
|
||||
okta_client_id=OKTA_CLIENT_ID,
|
||||
okta_private_key_file="/tmp/key.pem",
|
||||
provider_id=OKTA_ORG_DOMAIN,
|
||||
)
|
||||
assert connection.is_connected is True
|
||||
assert connection.error is None
|
||||
|
||||
def test_provider_id_match_is_case_insensitive(self, _clear_okta_env, tmp_path):
|
||||
validate_p, session_p, identity_p = _mock_setup_paths()
|
||||
with validate_p, session_p, identity_p:
|
||||
connection = OktaProvider.test_connection(
|
||||
okta_org_domain=OKTA_ORG_DOMAIN,
|
||||
okta_client_id=OKTA_CLIENT_ID,
|
||||
okta_private_key_file="/tmp/key.pem",
|
||||
provider_id=OKTA_ORG_DOMAIN.upper(),
|
||||
)
|
||||
assert connection.is_connected is True
|
||||
|
||||
def test_provider_id_mismatch_raises(self, _clear_okta_env, tmp_path):
|
||||
validate_p, session_p, identity_p = _mock_setup_paths()
|
||||
with validate_p, session_p, identity_p:
|
||||
with pytest.raises(OktaInvalidProviderIdError):
|
||||
OktaProvider.test_connection(
|
||||
okta_org_domain=OKTA_ORG_DOMAIN,
|
||||
okta_client_id=OKTA_CLIENT_ID,
|
||||
okta_private_key_file="/tmp/key.pem",
|
||||
provider_id="other.okta.com",
|
||||
)
|
||||
|
||||
def test_provider_id_mismatch_returns_error_when_raise_disabled(
|
||||
self, _clear_okta_env, tmp_path
|
||||
):
|
||||
validate_p, session_p, identity_p = _mock_setup_paths()
|
||||
with validate_p, session_p, identity_p:
|
||||
connection = OktaProvider.test_connection(
|
||||
okta_org_domain=OKTA_ORG_DOMAIN,
|
||||
okta_client_id=OKTA_CLIENT_ID,
|
||||
okta_private_key_file="/tmp/key.pem",
|
||||
provider_id="other.okta.com",
|
||||
raise_on_exception=False,
|
||||
)
|
||||
assert connection.is_connected is False
|
||||
assert isinstance(connection.error, OktaInvalidProviderIdError)
|
||||
|
||||
|
||||
class Test_OktaProvider_print_credentials:
|
||||
def test_invokes_print_boxes_with_org_and_client(self, _clear_okta_env, tmp_path):
|
||||
|
||||
Reference in New Issue
Block a user