## Summary Automated scanning of Entra ID directory roles to surface high-privilege role assignments as trackable findings with alerting support. ## What's included ### Core Services - **EntraAdminRolesReportService** — Fetches role definitions + assignments via Graph API, builds payload with fingerprint deduplication - **EntraAdminRolesFindingGenerator** — Creates/resolves/reopens findings based on high-privilege role catalog - **HighPrivilegeRoleCatalog** — Curated list of high-privilege Entra roles (Global Admin, Privileged Auth Admin, etc.) - **ScanEntraAdminRolesJob** — Queued job orchestrating scan → report → findings → alerts pipeline ### UI - **AdminRolesSummaryWidget** — Tenant dashboard card showing last scan time, high-privilege assignment count, scan trigger button - RBAC-gated: `ENTRA_ROLES_VIEW` for viewing, `ENTRA_ROLES_MANAGE` for scan trigger ### Infrastructure - Graph contracts for `entraRoleDefinitions` + `entraRoleAssignments` - `config/entra_permissions.php` — Entra permission registry - `StoredReport.fingerprint` migration (deduplication support) - `OperationCatalog` label + duration for `entra.admin_roles.scan` - Artisan command `entra:scan-admin-roles` for CLI/scheduled use ### Global UX improvement - **SummaryCountsNormalizer**: Zero values filtered, snake_case keys humanized (e.g. `report_deduped: 1` → `Report deduped: 1`). Affects all operation notifications. ## Test Coverage - **12 test files**, **79+ tests**, **307+ assertions** - Report service, finding generator, job orchestration, widget rendering, alert integration, RBAC enforcement, badge mapping ## Spec artifacts - `specs/105-entra-admin-roles-evidence-findings/tasks.md` — Full task breakdown (38 tasks, all complete) - `specs/105-entra-admin-roles-evidence-findings/checklists/requirements.md` — All items checked ## Files changed 46 files changed, 3641 insertions(+), 15 deletions(-) Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #128
133 lines
4.2 KiB
PHP
133 lines
4.2 KiB
PHP
<?php
|
|
|
|
namespace App\Support\Auth;
|
|
|
|
/**
|
|
* Canonical Capability Registry
|
|
*
|
|
* This is the single source of truth for all capability strings in the system.
|
|
* All role-to-capability mappings must reference only these constants.
|
|
*/
|
|
class Capabilities
|
|
{
|
|
/**
|
|
* @var array<string>|null
|
|
*/
|
|
private static ?array $all = null;
|
|
|
|
// Workspaces
|
|
public const WORKSPACE_VIEW = 'workspace.view';
|
|
|
|
public const WORKSPACE_MANAGE = 'workspace.manage';
|
|
|
|
public const WORKSPACE_ARCHIVE = 'workspace.archive';
|
|
|
|
// Workspace memberships
|
|
public const WORKSPACE_MEMBERSHIP_VIEW = 'workspace_membership.view';
|
|
|
|
public const WORKSPACE_MEMBERSHIP_MANAGE = 'workspace_membership.manage';
|
|
|
|
// Managed tenant onboarding
|
|
public const WORKSPACE_MANAGED_TENANT_ONBOARD = 'workspace_managed_tenant.onboard';
|
|
|
|
public const WORKSPACE_MANAGED_TENANT_ONBOARD_IDENTIFY = 'workspace_managed_tenant.onboard.identify';
|
|
|
|
public const WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_VIEW = 'workspace_managed_tenant.onboard.connection.view';
|
|
|
|
public const WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE = 'workspace_managed_tenant.onboard.connection.manage';
|
|
|
|
public const WORKSPACE_MANAGED_TENANT_ONBOARD_VERIFICATION_START = 'workspace_managed_tenant.onboard.verification.start';
|
|
|
|
public const WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC = 'workspace_managed_tenant.onboard.bootstrap.inventory_sync';
|
|
|
|
public const WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_POLICY_SYNC = 'workspace_managed_tenant.onboard.bootstrap.policy_sync';
|
|
|
|
public const WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP = 'workspace_managed_tenant.onboard.bootstrap.backup_bootstrap';
|
|
|
|
public const WORKSPACE_MANAGED_TENANT_ONBOARD_ACTIVATE = 'workspace_managed_tenant.onboard.activate';
|
|
|
|
// Workspace settings
|
|
public const WORKSPACE_SETTINGS_VIEW = 'workspace_settings.view';
|
|
|
|
public const WORKSPACE_SETTINGS_MANAGE = 'workspace_settings.manage';
|
|
|
|
// Workspace alerts
|
|
public const ALERTS_VIEW = 'workspace_alerts.view';
|
|
|
|
public const ALERTS_MANAGE = 'workspace_alerts.manage';
|
|
|
|
// Tenants
|
|
public const TENANT_VIEW = 'tenant.view';
|
|
|
|
public const TENANT_MANAGE = 'tenant.manage';
|
|
|
|
public const TENANT_DELETE = 'tenant.delete';
|
|
|
|
public const TENANT_SYNC = 'tenant.sync';
|
|
|
|
// Inventory
|
|
public const TENANT_INVENTORY_SYNC_RUN = 'tenant_inventory_sync.run';
|
|
|
|
// Findings
|
|
public const TENANT_FINDINGS_ACKNOWLEDGE = 'tenant_findings.acknowledge';
|
|
|
|
// Verification
|
|
public const TENANT_VERIFICATION_ACKNOWLEDGE = 'tenant_verification.acknowledge';
|
|
|
|
// Tenant memberships
|
|
public const TENANT_MEMBERSHIP_VIEW = 'tenant_membership.view';
|
|
|
|
public const TENANT_MEMBERSHIP_MANAGE = 'tenant_membership.manage';
|
|
|
|
// Optional mappings (no Graph resolution in v1)
|
|
public const TENANT_ROLE_MAPPING_VIEW = 'tenant_role_mapping.view';
|
|
|
|
public const TENANT_ROLE_MAPPING_MANAGE = 'tenant_role_mapping.manage';
|
|
|
|
// Backup schedules
|
|
public const TENANT_BACKUP_SCHEDULES_MANAGE = 'tenant_backup_schedules.manage';
|
|
|
|
public const TENANT_BACKUP_SCHEDULES_RUN = 'tenant_backup_schedules.run';
|
|
|
|
// Providers (existing gate names used throughout the app)
|
|
public const PROVIDER_VIEW = 'provider.view';
|
|
|
|
public const PROVIDER_MANAGE = 'provider.manage';
|
|
|
|
public const PROVIDER_RUN = 'provider.run';
|
|
|
|
// Workspace baselines (Golden Master governance)
|
|
public const WORKSPACE_BASELINES_VIEW = 'workspace_baselines.view';
|
|
|
|
public const WORKSPACE_BASELINES_MANAGE = 'workspace_baselines.manage';
|
|
|
|
// Audit
|
|
public const AUDIT_VIEW = 'audit.view';
|
|
|
|
// Entra admin roles
|
|
public const ENTRA_ROLES_VIEW = 'entra_roles.view';
|
|
|
|
public const ENTRA_ROLES_MANAGE = 'entra_roles.manage';
|
|
|
|
/**
|
|
* Get all capability constants
|
|
*
|
|
* @return array<string>
|
|
*/
|
|
public static function all(): array
|
|
{
|
|
if (self::$all !== null) {
|
|
return self::$all;
|
|
}
|
|
|
|
$reflection = new \ReflectionClass(self::class);
|
|
|
|
return self::$all = array_values($reflection->getConstants());
|
|
}
|
|
|
|
public static function isKnown(string $capability): bool
|
|
{
|
|
return in_array($capability, self::all(), true);
|
|
}
|
|
}
|