Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,22 @@ CYBERARK_ENABLED=false
# Discovered automatically via tenant discovery in the Settings UI
# CYBERARK_UAP_BASE_URL=https://your-tenant.uap.cyberark.cloud/api

# CyberArk tenant name (for auto-discovery of URLs)
# CYBERARK_TENANT_NAME=your-tenant-name

# CyberArk API credentials (service account with read permissions)
# IMPORTANT: Use secrets manager or vault in production
# CYBERARK_CLIENT_ID=
# CYBERARK_CLIENT_SECRET=

# CyberArk SCIM Integration (optional - for Identity user/role collection)
# Requires CyberArk Identity URL to be configured above
# CYBERARK_SCIM_ENABLED=false
# CYBERARK_SCIM_APP_ID=
# CYBERARK_SCIM_SCOPE=
# CYBERARK_SCIM_CLIENT_ID=
# CYBERARK_SCIM_CLIENT_SECRET=

# Terraform resource type overrides for the idsec provider
# Override these if your idsec provider uses different resource type names
# CYBERARK_TF_SAFE_TYPE=idsec_safe
Expand Down
13 changes: 13 additions & 0 deletions backend/app/api/routes/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,12 +766,25 @@ async def _get_or_create_cyberark_settings(
return settings

settings = CyberArkSettings(
tenant_name=env_settings.cyberark_tenant_name,
enabled=env_settings.cyberark_enabled,
base_url=env_settings.cyberark_base_url,
identity_url=env_settings.cyberark_identity_url,
uap_base_url=env_settings.cyberark_uap_base_url,
client_id=env_settings.cyberark_client_id,
client_secret=env_settings.cyberark_client_secret,
scim_enabled=env_settings.cyberark_scim_enabled,
scim_app_id=env_settings.cyberark_scim_app_id,
scim_scope=env_settings.cyberark_scim_scope,
scim_client_id=env_settings.cyberark_scim_client_id,
scim_client_secret=env_settings.cyberark_scim_client_secret,
)
# Auto-derive scim_oauth2_url if both identity_url and scim_app_id are set
if settings.identity_url and settings.scim_app_id:
identity_base = settings.identity_url.rstrip("/")
settings.scim_oauth2_url = (
f"{identity_base}/oauth2/token/{settings.scim_app_id}"
)
db.add(settings)
await db.commit()
await db.refresh(settings)
Expand Down
22 changes: 22 additions & 0 deletions backend/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,28 @@ class Settings(BaseSettings):
description="Terraform resource type for SIA DB policy",
)

# CyberArk tenant name (seeds CyberArkSettings in database on startup)
cyberark_tenant_name: Optional[str] = Field(
default=None, description="CyberArk tenant name for auto-discovery"
)

# SCIM integration (seeds CyberArkSettings in database on startup)
cyberark_scim_enabled: bool = Field(
default=False, description="Enable CyberArk SCIM user collection"
)
cyberark_scim_app_id: Optional[str] = Field(
default=None, description="CyberArk Identity SCIM application ID"
)
cyberark_scim_scope: Optional[str] = Field(
default=None, description="OAuth2 scope for SCIM token request"
)
cyberark_scim_client_id: Optional[str] = Field(
default=None, description="SCIM OAuth2 client ID"
)
cyberark_scim_client_secret: Optional[str] = Field(
default=None, description="SCIM OAuth2 client secret"
)

@property
def cors_origins_list(self) -> List[str]:
"""Parse CORS origins string into a list."""
Expand Down
7 changes: 5 additions & 2 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@

from app.api.deps import get_current_user
from app.api.routes import (
access_mapping,
auth,
cyberark,
ec2,
ecs,
eip,
Expand All @@ -23,7 +25,6 @@
rds,
resources,
)
from app.api.routes import access_mapping, cyberark
from app.api.routes import settings as settings_routes
from app.api.routes import subnet, terraform, topology, users, vpc
from app.config import get_settings
Expand All @@ -44,15 +45,17 @@ async def lifespan(app: FastAPI):
"""Application lifespan handler for startup and shutdown events."""
from app.models.database import async_session_maker
from app.services.auth import ensure_admin_user
from app.services.cyberark_settings import ensure_cyberark_settings

# Startup
logger.info("Starting AWS Infrastructure Visualizer...")
await init_db()
logger.info("Database initialized")

# Ensure admin user exists if configured
# Ensure admin user and CyberArk settings exist if configured
async with async_session_maker() as session:
await ensure_admin_user(session)
await ensure_cyberark_settings(session)

yield

Expand Down
85 changes: 85 additions & 0 deletions backend/app/services/cyberark_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""
CyberArk settings startup seeding service.

Seeds CyberArkSettings in the database from environment variables on startup,
similar to how ensure_admin_user auto-provisions the admin account.
"""

import logging

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import get_settings
from app.models.cyberark import CyberArkSettings

logger = logging.getLogger(__name__)


async def ensure_cyberark_settings(db: AsyncSession) -> None:
"""
Seed or sync CyberArk settings from environment variables on startup.

If no CyberArkSettings row exists and CyberArk env vars are configured,
creates one seeded from env vars. If a row exists and env vars provide
secrets, syncs secrets so Secrets Manager values stay current.
"""
settings = get_settings()

# Only proceed if at least some CyberArk config is provided
if not (settings.cyberark_enabled or settings.cyberark_scim_enabled):
logger.debug("No CyberArk env vars configured; skipping settings seed")
return

result = await db.execute(select(CyberArkSettings).limit(1))
existing = result.scalar_one_or_none()

if existing:
# Sync secrets from env vars if provided (Secrets Manager may have
# rotated values that should override what is in the DB)
changed = False
if (
settings.cyberark_client_secret
and settings.cyberark_client_secret != existing.client_secret
):
existing.client_secret = settings.cyberark_client_secret
changed = True
if (
settings.cyberark_scim_client_secret
and settings.cyberark_scim_client_secret != existing.scim_client_secret
):
existing.scim_client_secret = settings.cyberark_scim_client_secret
changed = True
if changed:
await db.commit()
logger.info("CyberArk settings secrets synced from environment")
else:
logger.debug("CyberArk settings already exist; no env sync needed")
return

# No row exists — create one seeded from env vars
new_settings = CyberArkSettings(
tenant_name=settings.cyberark_tenant_name,
enabled=settings.cyberark_enabled,
base_url=settings.cyberark_base_url,
identity_url=settings.cyberark_identity_url,
uap_base_url=settings.cyberark_uap_base_url,
client_id=settings.cyberark_client_id,
client_secret=settings.cyberark_client_secret,
scim_enabled=settings.cyberark_scim_enabled,
scim_app_id=settings.cyberark_scim_app_id,
scim_scope=settings.cyberark_scim_scope,
scim_client_id=settings.cyberark_scim_client_id,
scim_client_secret=settings.cyberark_scim_client_secret,
)

# Auto-derive scim_oauth2_url if both identity_url and scim_app_id are set
if new_settings.identity_url and new_settings.scim_app_id:
identity_base = new_settings.identity_url.rstrip("/")
new_settings.scim_oauth2_url = (
f"{identity_base}/oauth2/token/{new_settings.scim_app_id}"
)

db.add(new_settings)
await db.commit()
logger.info("CyberArk settings seeded from environment variables")
37 changes: 35 additions & 2 deletions infrastructure/environments/dev/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ locals {
OIDC_ISSUER = var.oidc_issuer
OIDC_CLIENT_ID = var.oidc_client_id
ADMIN_USERNAME = var.admin_username

# CyberArk
CYBERARK_ENABLED = tostring(var.cyberark_enabled)
CYBERARK_TENANT_NAME = var.cyberark_tenant_name
CYBERARK_BASE_URL = var.cyberark_base_url
CYBERARK_IDENTITY_URL = var.cyberark_identity_url
CYBERARK_CLIENT_ID = var.cyberark_client_id
CYBERARK_UAP_BASE_URL = var.cyberark_uap_base_url

# CyberArk SCIM
CYBERARK_SCIM_ENABLED = tostring(var.cyberark_scim_enabled)
CYBERARK_SCIM_APP_ID = var.cyberark_scim_app_id
CYBERARK_SCIM_SCOPE = var.cyberark_scim_scope
CYBERARK_SCIM_CLIENT_ID = var.cyberark_scim_client_id
}

# Secrets mapping (env var name -> Secret ARN)
Expand All @@ -97,7 +111,19 @@ locals {
_admin_secrets = var.admin_password != "" ? {
ADMIN_PASSWORD = module.secrets.admin_password_arn
} : {}
secrets = merge(local._base_secrets, local._oidc_secrets, local._admin_secrets)
_cyberark_secrets = var.cyberark_client_secret != "" ? {
CYBERARK_CLIENT_SECRET = module.secrets.cyberark_client_secret_arn
} : {}
_scim_secrets = var.cyberark_scim_client_secret != "" ? {
CYBERARK_SCIM_CLIENT_SECRET = module.secrets.scim_client_secret_arn
} : {}
secrets = merge(
local._base_secrets,
local._oidc_secrets,
local._admin_secrets,
local._cyberark_secrets,
local._scim_secrets,
)
}

# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -148,7 +174,14 @@ module "secrets" {
oidc_client_secret = var.oidc_client_secret
create_admin_secret = var.admin_password != ""
admin_password = var.admin_password
tags = local.common_tags

# CyberArk secrets
create_cyberark_secret = var.cyberark_client_secret != ""
cyberark_client_secret = var.cyberark_client_secret
create_scim_secret = var.cyberark_scim_client_secret != ""
cyberark_scim_client_secret = var.cyberark_scim_client_secret

tags = local.common_tags
}

# -----------------------------------------------------------------------------
Expand Down
16 changes: 16 additions & 0 deletions infrastructure/environments/dev/terraform.tfvars.example
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ tf_state_bucket = "your-terraform-state-bucket"
# admin_username = "admin"
# admin_password = "YourStr0ng!Password"

# CyberArk integration (optional - leave commented to disable)
# cyberark_enabled = true
# cyberark_tenant_name = "your-tenant-name"
# cyberark_base_url = "https://your-tenant.privilegecloud.cyberark.cloud"
# cyberark_identity_url = "https://your-tenant.id.cyberark.cloud"
# cyberark_client_id = "your-api-client-id"
# cyberark_client_secret = "your-api-client-secret"
# cyberark_uap_base_url = "https://your-tenant.uap.cyberark.cloud/api"

# CyberArk SCIM integration (optional - requires CyberArk identity URL above)
# cyberark_scim_enabled = true
# cyberark_scim_app_id = "your-scim-app-id"
# cyberark_scim_scope = "scim"
# cyberark_scim_client_id = "your-scim-client-id"
# cyberark_scim_client_secret = "your-scim-client-secret"

# Application settings
log_level = "DEBUG"
debug = true
82 changes: 82 additions & 0 deletions infrastructure/environments/dev/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,85 @@ variable "debug" {
type = bool
default = true
}

# -----------------------------------------------------------------------------
# CyberArk Integration (Optional)
# -----------------------------------------------------------------------------

variable "cyberark_enabled" {
description = "Enable CyberArk integration"
type = bool
default = false
}

variable "cyberark_tenant_name" {
description = "CyberArk tenant name for auto-discovery of URLs"
type = string
default = ""
}

variable "cyberark_base_url" {
description = "CyberArk Privilege Cloud base URL"
type = string
default = ""
}

variable "cyberark_identity_url" {
description = "CyberArk Identity tenant URL"
type = string
default = ""
}

variable "cyberark_client_id" {
description = "CyberArk API client ID"
type = string
default = ""
}

variable "cyberark_client_secret" {
description = "CyberArk API client secret (stored in Secrets Manager)"
type = string
default = ""
sensitive = true
}

variable "cyberark_uap_base_url" {
description = "CyberArk UAP base URL for SIA policy collection"
type = string
default = ""
}

# -----------------------------------------------------------------------------
# CyberArk SCIM Integration (Optional)
# -----------------------------------------------------------------------------

variable "cyberark_scim_enabled" {
description = "Enable CyberArk SCIM user collection"
type = bool
default = false
}

variable "cyberark_scim_app_id" {
description = "CyberArk Identity SCIM application ID"
type = string
default = ""
}

variable "cyberark_scim_scope" {
description = "OAuth2 scope for SCIM token request"
type = string
default = ""
}

variable "cyberark_scim_client_id" {
description = "CyberArk SCIM OAuth2 client ID"
type = string
default = ""
}

variable "cyberark_scim_client_secret" {
description = "CyberArk SCIM OAuth2 client secret (stored in Secrets Manager)"
type = string
default = ""
sensitive = true
}
Loading
Loading