Some checks failed
		
		
	
	Build Test Deploy / authority-container (push) Has been cancelled
				
			Build Test Deploy / docs (push) Has been cancelled
				
			Build Test Deploy / deploy (push) Has been cancelled
				
			Build Test Deploy / build-test (push) Has been cancelled
				
			Docs CI / lint-and-preview (push) Has been cancelled
				
			
		
			
				
	
	
		
			137 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# RFC: StellaOps.Authority.Plugin.Ldap
 | 
						||
 | 
						||
**Status:** Draft – for review by Auth Guild, Security Guild, DevEx (2025-10-10)  
 | 
						||
**Authors:** Plugin Team 4 (Auth Libraries & Identity Providers)  
 | 
						||
**Related initiatives:** PLG7 backlog, CORE5 event handlers, DOC4 developer guide
 | 
						||
 | 
						||
## 1. Problem Statement
 | 
						||
Many on-prem StellaOps deployments rely on existing LDAP/Active Directory domains for workforce identity. The current Standard Mongo-backed plugin requires duplicating users and secrets, which increases operational overhead and violates corporate policy in some regulated environments. We need a sovereign, offline-friendly LDAP plugin that:
 | 
						||
 | 
						||
- Supports password grant and bootstrap provisioning flows without storing credentials in Mongo.
 | 
						||
- Enforces StellaOps security policies (lockout, password policy hints, audit logging) while delegating credential validation to LDAP.
 | 
						||
- Operates deterministically in offline or partially connected environments by caching directory metadata when necessary.
 | 
						||
 | 
						||
## 2. Goals
 | 
						||
- Provide a first-party `StellaOps.Authority.Plugin.Ldap` plugin advertising `password` and optional `clientProvisioning` capabilities at launch.
 | 
						||
- Support username/password authentication against LDAP bind operations with configurable DN templates.
 | 
						||
- Allow optional bootstrap seeding of service accounts by writing into LDAP (guarded behind explicit configuration) or by mapping to pre-existing entries.
 | 
						||
- Surface directory-derived claims (groups, attributes) for downstream authorization via `IClaimsEnricher`.
 | 
						||
- Integrate with Authority lockout telemetry and structured logging without persisting secrets locally.
 | 
						||
 | 
						||
## 3. Non-Goals
 | 
						||
- Implement multi-factor authentication out of the box (future enhancement once TOTP/WebAuthn strategy is finalised).
 | 
						||
- Provide write-heavy directory management (e.g., user creation workflows) beyond optional bootstrap service account seeding.
 | 
						||
- Replace the Standard plugin; both must remain supported and selectable per environment.
 | 
						||
 | 
						||
## 4. Key Constraints & Assumptions
 | 
						||
- Offline-first posture: deployments may operate without outbound internet and with intermittent directory connectivity (e.g., read-only replicas). The plugin must tolerate transient LDAP connectivity failures and degrade gracefully.
 | 
						||
- Deterministic behaviour: identical configuration and directory state must yield identical token issuance results. Cached metadata (e.g., group lookups) must have defined expiration.
 | 
						||
- Security: No plaintext credential storage; TLS must be enforced for LDAP connections unless explicitly overridden for air-gapped lab environments.
 | 
						||
 | 
						||
## 5. High-Level Architecture
 | 
						||
1. **Configuration binding** (`ldap.yaml`): defines server endpoints, bind strategy, claim mapping, and optional bootstrap overrides.
 | 
						||
2. **Connection factory**: pooled LDAP connections using a resilient client (preferred dependency: `Novell.Directory.Ldap.NETStandard`).
 | 
						||
3. **Credential validator** (`IUserCredentialStore`): performs bind-as-user flow with optional fallback bind using service account when directories disallow anonymous search.
 | 
						||
4. **Claims enricher** (`IClaimsEnricher`): queries group membership/attributes and projects them into canonical roles/claims.
 | 
						||
5. **Optional client provisioning** (`IClientProvisioningStore`): maintains machine/service principals either in Mongo (metadata) or via LDAP `serviceConnectionPoint` entries based on configuration.
 | 
						||
6. **Health checks**: periodic LDAP `whoami` or `search` probes surfaced through `AuthorityPluginHealthResult`.
 | 
						||
 | 
						||
```
 | 
						||
Authority Host
 | 
						||
 ├── Plugin Manifest (ldap)
 | 
						||
 ├── Registrar → registers ConnectionFactory, LdapCredentialStore, LdapClaimsEnricher
 | 
						||
 ├── Password Grant Handler → CredentialStore.VerifyPasswordAsync → LDAP Bind
 | 
						||
 └── Claims Pipeline → ClaimsEnricher.EnrichAsync → LDAP group lookup
 | 
						||
```
 | 
						||
 | 
						||
## 6. Configuration Schema (Draft)
 | 
						||
```yaml
 | 
						||
connection:
 | 
						||
  host: "ldaps://ldap.example.internal"
 | 
						||
  port: 636
 | 
						||
  useStartTls: false
 | 
						||
  validateCertificates: true
 | 
						||
  bindDn: "cn=stellaops-bind,ou=service,dc=example,dc=internal"
 | 
						||
  bindPasswordSecret: "file:/etc/stellaops/secrets/ldap-bind.txt"
 | 
						||
  searchBase: "dc=example,dc=internal"
 | 
						||
  usernameAttribute: "uid"
 | 
						||
  userDnFormat: "uid={username},ou=people,dc=example,dc=internal"  # optional template
 | 
						||
security:
 | 
						||
  requireTls: true
 | 
						||
  allowedCipherSuites: []          # optional allow-list
 | 
						||
  referralChasing: false
 | 
						||
lockout:
 | 
						||
  useAuthorityPolicies: true       # reuse Authority lockout counters
 | 
						||
  directoryLockoutAttribute: "pwdAccountLockedTime"
 | 
						||
claims:
 | 
						||
  groupAttribute: "memberOf"
 | 
						||
  groupToRoleMap:
 | 
						||
    "cn=stellaops-admins,ou=groups,dc=example,dc=internal": "operators"
 | 
						||
    "cn=stellaops-read,ou=groups,dc=example,dc=internal": "auditors"
 | 
						||
  extraAttributes:
 | 
						||
    displayName: "displayName"
 | 
						||
    email: "mail"
 | 
						||
clientProvisioning:
 | 
						||
  enabled: false
 | 
						||
  containerDn: "ou=service,dc=example,dc=internal"
 | 
						||
  secretAttribute: "userPassword"
 | 
						||
health:
 | 
						||
  probeIntervalSeconds: 60
 | 
						||
  timeoutSeconds: 5
 | 
						||
```
 | 
						||
 | 
						||
## 7. Capability Mapping
 | 
						||
| Capability | Implementation Notes |
 | 
						||
|------------|---------------------|
 | 
						||
| `password` | Bind-as-user validation with Authority lockout integration. Mandatory. |
 | 
						||
| `clientProvisioning` | Optional; when enabled, creates/updates LDAP entries for machine clients or stores metadata in Mongo if directory writes are disabled. |
 | 
						||
| `bootstrap` | Exposed only when bootstrap manifest provides service account credentials AND directory write permissions are confirmed during startup. |
 | 
						||
| `mfa` | Not supported in MVP. Future iteration may integrate TOTP attributes or external MFA providers. |
 | 
						||
 | 
						||
## 8. Operational Considerations
 | 
						||
- **Offline cache:** provide optional Mongo cache for group membership to keep `/ready` responsive if LDAP is temporarily unreachable. Cache entries must include TTL and invalidation hooks.
 | 
						||
- **Secrets management:** accept `file:` and environment variable references; integrate with existing `StellaOps.Configuration` secret providers.
 | 
						||
- **Observability:** emit structured logs with event IDs (`LDAP_BIND_START`, `LDAP_BIND_FAILURE`, `LDAP_GROUP_LOOKUP`), counters for success/failure, and latency histograms.
 | 
						||
- **Throttling:** reuse Authority rate-limiting middleware; add per-connection throttles to avoid saturating directory servers during brute-force attacks.
 | 
						||
 | 
						||
## 9. Security & Compliance
 | 
						||
- Enforce TLS (`ldaps://` or STARTTLS) by default. Provide explicit `allowInsecure` flag gated behind environment variable for lab/testing only.
 | 
						||
- Support password hash migration by detecting directory lockout attributes and surfacing `RequiresPasswordReset` when policies demand changes.
 | 
						||
- Log distinguished names only at `Debug` level to avoid leaking sensitive structure in default logs.
 | 
						||
- Coordinate with Security Guild for penetration testing before GA; incorporate audit log entries for bind attempts and provisioning changes.
 | 
						||
 | 
						||
## 10. Testing Strategy
 | 
						||
- **Unit tests:** mock LDAP connections to validate DN formatting, error mapping, and capability negotiation.
 | 
						||
- **Integration tests:** run against an ephemeral OpenLDAP container (seeded via LDIF fixtures) within CI. Include offline cache regression (disconnect LDAP mid-test).
 | 
						||
- **Determinism tests:** feed identical LDIF snapshots and configuration to ensure output tokens/claims remain stable across runs.
 | 
						||
- **Smoke tests:** `dotnet test` harness plus manual `dotnet run` scenario verifying `/token` password grants and `/internal/users` bootstrap with LDAP-backed store.
 | 
						||
 | 
						||
## 11. Implementation Plan
 | 
						||
1. Scaffold `StellaOps.Authority.Plugin.Ldap` project + tests (net10.0, `<IsAuthorityPlugin>` true).
 | 
						||
2. Implement configuration options + validation (mirroring Standard plugin guardrails).
 | 
						||
3. Build connection factory + credential store with bind logic.
 | 
						||
4. Implement claims enricher and optional cache layer.
 | 
						||
5. Add client provisioning store (optional) with toggles for read-only deployments.
 | 
						||
6. Wire bootstrapper to validate connectivity/permissions and record findings in startup logs.
 | 
						||
7. Extend developer guide with LDAP specifics (post-RFC acceptance).
 | 
						||
8. Update Docs and TODO trackers; produce release notes entry once merged.
 | 
						||
 | 
						||
## 12. Open Questions
 | 
						||
- Should client provisioning default to storing metadata in Mongo even when LDAP writes succeed (to preserve audit history)?
 | 
						||
- Do we require LDAPS mutual TLS support (client certificates) for regulated environments? If yes, need to extend configuration schema.
 | 
						||
- How will we map LDAP groups to Authority scopes/roles when names differ significantly? Consider supporting regex or mapping scripts.
 | 
						||
 | 
						||
## 13. Timeline (Tentative)
 | 
						||
- **Week 1:** RFC review & sign-off.
 | 
						||
- **Week 2-3:** Implementation & unit tests.
 | 
						||
- **Week 4:** Integration tests + documentation updates.
 | 
						||
- **Week 5:** Security review, release candidate packaging.
 | 
						||
 | 
						||
## 14. Approval
 | 
						||
- **Auth Guild Lead:** _TBD_
 | 
						||
- **Security Guild Representative:** _TBD_
 | 
						||
- **DevEx Docs:** _TBD_
 | 
						||
 | 
						||
---
 | 
						||
Please add comments inline or via PR review. Once approved, track execution under PLG7.
 |