feat(auth): add Microsoft Azure Service Principal and Entra ID SSO credential templates - #207
feat(auth): add Microsoft Azure Service Principal and Entra ID SSO credential templates#207JLCode-tech wants to merge 35 commits into
Conversation
…O routes, doc SSO/terraform split F1: normalize naive azure_sso_token_expiry to UTC before comparing in _test_azure_template — matches get_sso_status / credential_refresh_service guards; fixes TypeError on SQLite/dev naive round-trip. Adds SQLite regression test (mutation-verified: fails with the exact TypeError without the guard). F2: remove the unwired standalone Azure SSO routes (/azure/sso/initiate, /azure/sso/poll, /azure/subscriptions), their request models, and the unused client methods (initiateAzureSSO/pollAzureSSO/listAzureSubscriptions). The frontend uses the server-side template flow (authenticate-sso/poll-sso, returns only has_credentials); these paths leaked long-lived access/refresh tokens in the response body. Regenerated openapi.json + api-generated.ts. F4: document at the terraform credential-injection site that SSO Azure templates deliberately inject no credential (SSO is validation/console; terraform provisioning uses the service-principal secret). Claude-Session: https://claude.ai/code/session_01UCsZXDxBsWV2s4kT47DwDW
Self-review (cold, adversarial) + fixes appliedIndependent cold audit, executed. No blockers — secret-at-rest is correct ( F1 (MEDIUM — tz-naive datetime crash) — FIXED. F2 (LOW — token-leaking unused routes) — FIXED (removed). F3 (description) — FIXED: corrected the "MSAL" claim (it's raw OAuth2 Verified: 74 passed, ruff clean, contract fresh. Note for merge coordination: #206/#208 also touch |
Review —
|
- Convert AWS/IBM region validators from hardcoded-list rejection to pattern-based acceptance so new or private regions are selectable. - Add Azure and GCP region validators using the same pattern-based approach; wire them into project, credential-template, and cluster schemas/routes. - Update frontend region selectors (AWS, Cloud, SystemDefaults) to free-form inputs with datalist suggestions instead of restrictive dropdowns. - Add KubernetesCluster.account_id and discovery_status columns plus fleet-health response fields for cloud context. - Update unit tests for validators, project schemas, k8s schemas, and frontend selectors.
…ctor - Extract shared is_operator_live_connected() helper and use it in the operator list, fleet health, and BNK health context. - Reuse services.scanner.nodes.parse_node() in BNK fetch instead of duplicating the zone/instance-type label fallback logic. - Add an optional label prop to CloudRegionSelector and reuse it in SystemDefaults to remove four near-identical region input blocks.
- Add connectivity and integration sections to BnkHealthResponse. - Reuse the cluster's persisted status for connectivity and the shared operator live-connection helper for integration. - Display ConnectivityBadge and IntegrationBadge in the dashboard banner. - Add backend unit tests and frontend dashboard tests for the new fields.
Add /detect-credentials endpoint that discovers existing Kubernetes clusters from a project's credential template for AWS, IBM Cloud, Azure, and GCP. Each provider lists accessible clusters, builds a kubeconfig from the template credentials, and registers the cluster in BNK-Forge. - New ClusterDiscoveryService orchestrates detection and registration. - Provider helpers: EKS, ROKS, AKS, GKE. - Frontend auto-detect switched to api.detectClustersFromCredentials(). - Backend + frontend tests updated; openapi.json and api-generated.ts regenerated.
- Move BNK Resources tab from System page to Fleet page - Make GET /api/system/bnk-consumption viewer-accessible - Move MCP Server from standalone sidebar page to System page tab - Move Benchmarks sidebar item from OPERATE to OBSERVE section - Update affected tests and regenerate OpenAPI types
- Add services/bnk/traffic_stats.py with analyze_traffic_stats() and fetch_tmm_traffic_stats() wrapping existing TMM debug helpers. - Add Pydantic schemas for listener/egress/firewall-rule traffic stats. - Wire trafficStats into the unified /f5bnk/data response. - Surface hit/connection badges on F5BNKTopologyViewer listener/egress nodes. - Add hits column to F5BNKPolicyViewer firewall-rule tables. - Add total-connections summary chips in TrafficFlowOverview. - Regenerate openapi.json and TypeScript generated types. - Add backend unit tests and frontend component/hook tests.
- Enrich BNK topology with gateway/listener/route accepted/programmed conditions - Add policy resolved/programmed status to topology and policy associations - Add response models for gateway topology and policy associations endpoints - Surface inline status badges in topology, traffic flow, and policy views - Visualize cross-namespace ReferenceGrants in topology and traffic flow - Extract shared ConditionsList component for Gateway/HTTPRoute/Service details - Add lightweight Service detail fallback and register it in resource registry - Regenerate OpenAPI spec and TypeScript generated types
…urce with settings Module Library sync failed for official-bnk-forge-modules because the clone used source.branch and then tried git checkout <git_ref>. A shallow branch clone does not fetch tags, so checking out a tag ref (v2.2.0) failed with 'pathspec did not match any file(s) known to git'. Use source.git_ref (falling back to branch) directly in git clone --branch, which accepts branch and tag names and already checks out the requested ref. Also reconcile the canonical official module source with the current module_library.git_* settings before a direct source sync, so a stale branch/git_ref on the source row does not override the configured ref. Validated: /api/module-sources/3/sync now succeeds, discovers 24 pack modules, and updates the source row to branch=git_ref=release/2.2.
…figview probes when no VS rows
…d CNE available state - Update has_condition() and get_condition_message() to inspect direct conditions arrays on parent_status dicts as well as standard K8s status.conditions. - Add get_policy_operational_status() to evaluate status.ancestors and status.descendants condition refs for BNKNetPolicy and BNKSecPolicy in BNK 2.3. - Update _build_cne_instance() to recognize Available/Reconciled condition states and populate default phase when healthy. - Update _match_routes_to_listener() to check parent_status condition acceptance.
Stop per-request ThreadPoolExecutors from spawning 20 workers each, which exploded backend PID count to 100+ under concurrent BNK page loads. Use module-level shared executors with small caps for BNK CRD fetches and TMM configview probes. Add Redis-backed short-term caches for: - EKS/GCP bearer tokens (10 min TTL) - fetch_all_bnk_data results (30 s TTL) - TMM traffic stats + configview uuid mappings (30 s / 5 min TTL) - CWC license status (30 s) and report (60 s) Each cache supports force=true to bypass when the UI explicitly refreshes. License activation invalidates the cached status/report so the new state is reflected immediately.
Add account_id, discovery_status, connectivity_status, integration_status,
zones, access_method, and node_count to the KubernetesCluster model, cluster
response schemas, serializers, and detail endpoints. Populate account_id from
credential-template discovery paths (AWS account, Azure subscription, GCP
project) and persist version/node_count/zones/last_synced_at from the scanner.
Includes migration v2_157 and a new GET /api/projects/{project_id}/connectivity
route backed by probe_project_clusters.
…uster The _build_bnk_context helper added in the health refactor queries ConnectedOperator by cluster.id. Tests that patched KubernetesService returned a MagicMock cluster, causing a SQLite bind error. Configure the mock to return the real test cluster so the endpoint can build its connectivity/integration context.
…se endpoints; fix project-switch refresh
…hes across tab navigation
…zure api_server (M3)
M1 (coverage gap): the discovered-cluster kubeconfig is encrypted at rest via
encrypt_value(), but no test asserted it — a mutation to plaintext passed all 8
tests. Add test_persisted_kubeconfig_is_encrypted_at_rest, which reads the
persisted kubeconfig_encrypted column and asserts it is NOT plaintext
(no "apiVersion" in the stored value; stored != decrypt_value(stored)) yet
decrypts back to the real kubeconfig. Reverting encrypt_value to plaintext reds
this test (decrypt raises / stored equals its plaintext).
M3 (defensive): _detect_azure_clusters composed api_server as
f"https://{creds['server']}:443", which double-prefixes into a malformed URL if
creds['server'] ever carries a scheme or an explicit port. Add
_normalize_api_server_host() to strip a leading http(s):// scheme and a trailing
:port so a bare host, scheme-prefixed host, or host:port all yield one correct
https://host:443, with a 3-shape parametrized test.
Scoped to M1 + M3; region validators (M2) untouched. No API/model change.
Claude-Session: https://claude.ai/code/session_01UCsZXDxBsWV2s4kT47DwDW
…edential templates
…gn alembic docstring
…O routes, doc SSO/terraform split F1: normalize naive azure_sso_token_expiry to UTC before comparing in _test_azure_template — matches get_sso_status / credential_refresh_service guards; fixes TypeError on SQLite/dev naive round-trip. Adds SQLite regression test (mutation-verified: fails with the exact TypeError without the guard). F2: remove the unwired standalone Azure SSO routes (/azure/sso/initiate, /azure/sso/poll, /azure/subscriptions), their request models, and the unused client methods (initiateAzureSSO/pollAzureSSO/listAzureSubscriptions). The frontend uses the server-side template flow (authenticate-sso/poll-sso, returns only has_credentials); these paths leaked long-lived access/refresh tokens in the response body. Regenerated openapi.json + api-generated.ts. F4: document at the terraform credential-injection site that SSO Azure templates deliberately inject no credential (SSO is validation/console; terraform provisioning uses the service-principal secret). Claude-Session: https://claude.ai/code/session_01UCsZXDxBsWV2s4kT47DwDW
- Rebase onto work/v4-localhost (incorporating staging and #205) - Renumber Alembic migration to v2_158 with down_revision v2_157 - Restrict azure_auth_method typing with Literal["service_principal", "sso"] - Persist refreshed Azure SSO tokens on test connection by committing DB session - Ensure SSO action menu and config completeness checks gate strictly on azure_auth_method == 'sso' - Add regression component tests for refresh token DB persistence and config validation
f7cf118 to
025330c
Compare
Review Findings Resolution & Rebase UpdateAll review items have been addressed, verified locally, and pushed:
Local Verification:
|
CI 100% Green & Re-Review RequestAll review items (M1, M2, m1, m2) have been addressed and OpenAPI spec / generated TypeScript types synced. Ready for re-review. |
Re-Review Request: Azure Auth Templates Audit VerifiedAll review action items from the
Branch is fully ready for re-review and merge. |
Summary
Adds support for Microsoft Azure Service Principal credentials and Entra ID SSO credential templates, including OAuth token refresh lifecycle management and region auto-discovery.
Key Changes
v2_156_add_azure_credential_template_fields.py) and updatedSystemCredentialTemplatemodels.AzureAuthService, which performs token acquisition and validation via raw OAuth2 requests tologin.microsoftonline.com(using therequestslibrary — nomsaldependency).CredentialTemplateServiceandCredentialRefreshServiceto handle Azure Service Principal and Entra ID secrets/certificates.test_azure_auth_service.pyandtest_credential_template_service.py.CredentialTemplates.tsx,SSOAuthDialog.tsx, andresolveCredStatus.ts.CloudRegionSelector.tsx.https://claude.ai/code/session_01UCsZXDxBsWV2s4kT47DwDW