Repository navigation
Fix/azure tenancy - #96
Merged
Merged
Conversation
One AuthSec multi-tenant app registration, and the customer only sees Microsoft's own screens: admin consent creates AuthSec's service principal and grants its Graph application permissions; a delegated sign-in (PKCE, no refresh token) gives the administrator's ARM token, parked in Redis for at most 15 minutes. Connect then checks the service principal and its Graph permissions (granted, then usable), checks the signed-in user may bind the tenant, creates Reader on each selected subscription WITH THE ADMINISTRATOR'S TOKEN -- AuthSec's principal never holds roleAssignments/write -- and proves each one with AuthSec's own app-only token, polling while Azure applies it. A tenant is one cloud_connector row (scope_kind 'tenant', migration 042) and each subscription one row under it. Verify re-proves either without the customer; Revoke stops using a tenant and its subscriptions. No credential is stored: auth_ref is "azure-app:<client id>" and the secret stays in the deployment environment. Resource Manager and app-only tokens go through the official Azure SDK (azcore, azidentity, armsubscriptions, armauthorization), Graph through the Microsoft Graph SDK. Discovery is not part of this change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… their access Tenant and subscription discovery passes: Entra objects, Microsoft Entra role assignments and definitions (built-in, custom, scoped), app and delegated permissions, Azure RBAC, Key Vault, federation, sign-ins and activity, Foundry, workloads. Agents from Microsoft Entra Agent ID, the Microsoft 365 agent registry, Foundry agent applications and project agents, Azure Bot Service, Logic Apps agent loops and the Teams app catalog, plus inference, each listed once and closed when its source no longer lists it. Scheduled rescans, custom-app onboarding with credentials in Vault, and projection into the shared iga_* graph behind IGA_GRAPH_PROJECTION.
Azure migrations renumbered after staging's (042-044 -> 138-140); the duplicate sortedKeys helper dropped in favour of staging's.
138-140 now leave a constraint that already admits their value as it is, so on a database that ran them under their old numbers (042-044), or whose bootstrap already lists every value, they never narrow it under rows a later source wrote.
The Azure projection states each row's scope in provider_attrs as Kubernetes and GitHub rows do (the tenant for Entra objects; the subscription, with its resource group or region, for resources and workloads), so /api/iga/v1/inventory lists provider azure with scope, native id and as_of from the Azure discovery run. /authsec/discovery/connections lists Azure tenants and subscriptions, with their latest run, coverage and a live graph state.
The shared connections list gives an Azure tenant as the connection and counts its subscriptions in its scope summary instead of listing each one. Every Azure inventory row is scoped to its tenant, with a resource's or workload's subscription as its sub-scope and its subscription id and resource group in its attrs, so filtering by the tenant connection includes them.
Own app: the client secret is stored only after every read it enables is proven, and destroyed on revoke or when the tenant moves to the AuthSec app. Its failures are the customer's (400, credential_invalid, unknown tenant, app not in tenant, named missing permissions), never AuthSec's deployment. Warnings for Graph permissions that can change data, Azure roles beyond Reader, and no readable subscription. A tenant switching between apps moves its other subscriptions to the new credentials. Verify checks revoked first; a passing Microsoft failure no longer stops scheduled discovery; discovery fails closed when the own app's credentials cannot be read; unexpected errors no longer return internals.
…ing took 138-140) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d joins, isolation tests Brings feat/azure-multitenant-onboarding (PR #92) in line with the tenancy rules on authsec-staging (CLAUDE.md §4, §7 AS-108): - Every Azure statement runs in a transaction under RLS for one workspace (tenancy.InWorkspace / InWorkspaceRead): the Azure repositories, the graph reads, the twelve Azure services, and the graph projection (one transaction per pass). The 18 files are listed in scripts/tenant-rls-files.txt. Exempt, with reasons: the scheduler's scan of due tenants across workspaces, the run lease renew/finish fenced by run id and owner, and the controller's service constructor. - Tenant ratchet back to 72 (was 104). RLS guard: no violations. - Every join on an id now also matches the workspace (projection, graph reads, repository lists). - IGA isolation check passes: cloud_permission, cloud_secret and cloud_resource are read-only seams of the Azure collected model the projection reads (ALLOWED_SHARED, with the reason; an ARM resource id names its subscription, so the AWS cloud_resource collision does not arise). - Tests (tests/integration/flows/isolation_azure_test.go): Test_Isolation_AzureDiscovery -- workspace A gets 404 on every /azure/connectors/:id route of B, cannot revoke B's connector, and never sees B's rows in any list or graph read; B sees its own (control). Test_AzureProjectionUnderRLS -- a tenant and a subscription pass project under RLS as authsec_tenant and write only their own workspace's graph. Behaviour notes: agent registration, inference, retirement and sign-in origin attribution now commit per step (all or nothing) instead of row by row; evidence is written in batches of 50 per transaction. Schema parity: identical except idx_cloud_scan_schedule_tenancy_ws, which plain authsec-staging misses too (staging's 139, not this branch). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An access policy on an Azure resource deploys as role assignments (migration 144, iga_gov_azure_access, job azure_access): - allow: AuthSec creates a built-in role assignment for the principal on the resource and deletes it at the end; - block: AuthSec records and removes the principal's own assignments on the resource that grant the access, and restores them on End/Pause/expiry. Access from a broader scope is a preview blocker (Azure has no deny). Same routes, views and statuses as AWS; RLS on the new table; the file is in the RLS list. TestAccessPolicyAzure covers block, allow and inherited. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Changes
Testing
go test -short ./tests/unit/)go vet ./...cleanrun-integrationto this PR to trigger integration tests in CI (requires a live DB)Checklist