release orchestrator pivot, architecture and planning

This commit is contained in:
2026-01-10 22:37:22 +02:00
parent c84f421e2f
commit d509c44411
130 changed files with 70292 additions and 721 deletions

View File

@@ -0,0 +1,305 @@
# Authentication & Authorization
## Authentication Methods
### OAuth 2.0 for Human Users
```
┌──────────────────────────────────────────────────────────────────────────────┐
│ OAUTH 2.0 AUTHORIZATION CODE FLOW │
│ │
│ ┌──────────┐ ┌──────────────┐ │
│ │ Browser │ │ Authority │ │
│ └────┬─────┘ └──────┬───────┘ │
│ │ │ │
│ │ 1. Login request │ │
│ │ ────────────────────────────────────► │ │
│ │ │ │
│ │ 2. Redirect to IdP │ │
│ │ ◄──────────────────────────────────── │ │
│ │ │ │
│ │ 3. User authenticates at IdP │ │
│ │ ─────────────────────────────────► │ │
│ │ │ │
│ │ 4. IdP callback with code │ │
│ │ ◄──────────────────────────────────── │ │
│ │ │ │
│ │ 5. Exchange code for tokens │ │
│ │ ────────────────────────────────────► │ │
│ │ │ │
│ │ 6. Access token + refresh token │ │
│ │ ◄──────────────────────────────────── │ │
│ │ │ │
└──────────────────────────────────────────────────────────────────────────────┘
```
### mTLS for Agents
Agents authenticate using mutual TLS with certificates issued by Stella's internal CA.
**Registration Flow:**
1. Admin generates one-time registration token
2. Agent starts with registration token
3. Agent submits CSR (Certificate Signing Request)
4. Authority issues certificate signed by Stella CA
5. Agent uses certificate for all subsequent requests
### API Keys for Service-to-Service
External services can use API keys for programmatic access:
- Keys are tenant-scoped
- Keys can have restricted permissions
- Keys can have expiration dates
- Key usage is audited
## JWT Token Structure
### Access Token Claims
```typescript
interface AccessTokenClaims {
// Standard claims
iss: string; // "https://authority.stella.local"
sub: string; // User ID
aud: string[]; // ["stella-api"]
exp: number; // Expiration timestamp
iat: number; // Issued at timestamp
jti: string; // Unique token ID
// Custom claims
tenant_id: string;
roles: string[];
permissions: Permission[];
email?: string;
name?: string;
}
```
### Token Lifetimes
| Token Type | Lifetime | Refresh |
|------------|----------|---------|
| Access Token | 15 minutes | Via refresh token |
| Refresh Token | 7 days | Rotated on use |
| Agent Token | 1 hour | Via mTLS connection |
| API Key | Configurable | Not refreshed |
## Authorization Model
### Resource Types
```typescript
type ResourceType =
| "environment"
| "release"
| "promotion"
| "target"
| "agent"
| "workflow"
| "plugin"
| "integration"
| "evidence";
```
### Action Types
```typescript
type ActionType =
| "create"
| "read"
| "update"
| "delete"
| "execute"
| "approve"
| "deploy"
| "rollback";
```
### Permission Structure
```typescript
interface Permission {
resource: ResourceType;
action: ActionType;
scope?: PermissionScope;
conditions?: Condition[];
}
type PermissionScope =
| "*" // All resources
| { environmentId: UUID } // Specific environment
| { labels: Record<string, string> }; // Label-based
```
### Built-in Roles
| Role | Description | Key Permissions |
|------|-------------|-----------------|
| `admin` | Full access | All permissions |
| `release_manager` | Manage releases and promotions | Create releases, request promotions |
| `deployer` | Execute deployments | Approve promotions (where allowed), view releases |
| `approver` | Approve promotions | Approve promotions (SoD respected) |
| `viewer` | Read-only access | Read all resources |
| `agent` | Agent service account | Execute deployment tasks |
### Role Definitions
```typescript
const roles = {
admin: {
permissions: [
{ resource: "*", action: "*" }
]
},
release_manager: {
permissions: [
{ resource: "release", action: "create" },
{ resource: "release", action: "read" },
{ resource: "release", action: "update" },
{ resource: "promotion", action: "create" },
{ resource: "promotion", action: "read" },
{ resource: "environment", action: "read" },
{ resource: "workflow", action: "read" },
{ resource: "workflow", action: "execute" }
]
},
deployer: {
permissions: [
{ resource: "release", action: "read" },
{ resource: "promotion", action: "read" },
{ resource: "promotion", action: "approve" },
{ resource: "environment", action: "read" },
{ resource: "target", action: "read" },
{ resource: "agent", action: "read" }
]
},
approver: {
permissions: [
{ resource: "promotion", action: "read" },
{ resource: "promotion", action: "approve" },
{ resource: "release", action: "read" },
{ resource: "environment", action: "read" }
]
},
viewer: {
permissions: [
{ resource: "*", action: "read" }
]
}
};
```
## Environment-Scoped Permissions
Permissions can be scoped to specific environments:
```typescript
// User can approve promotions only to staging
{
resource: "promotion",
action: "approve",
scope: { environmentId: "staging-env-id" }
}
// User can deploy only to targets with specific labels
{
resource: "target",
action: "deploy",
scope: { labels: { "tier": "frontend" } }
}
```
## Separation of Duties (SoD)
When SoD is enabled for an environment:
- The user who requested a promotion cannot approve it
- The user who created a release cannot be the sole approver
- Approval records include SoD verification status
```typescript
interface ApprovalValidation {
promotionId: UUID;
approverId: UUID;
requesterId: UUID;
sodRequired: boolean;
sodSatisfied: boolean;
validationResult: "valid" | "self_approval_denied" | "sod_violation";
}
```
## Permission Checking Algorithm
```typescript
async function checkPermission(
userId: UUID,
resource: ResourceType,
action: ActionType,
resourceId?: UUID
): Promise<boolean> {
// 1. Get user's roles and direct permissions
const userRoles = await getUserRoles(userId);
const userPermissions = await getUserPermissions(userId);
// 2. Expand role permissions
const rolePermissions = userRoles.flatMap(r => roles[r].permissions);
const allPermissions = [...rolePermissions, ...userPermissions];
// 3. Check for matching permission
for (const perm of allPermissions) {
if (matchesResource(perm.resource, resource) &&
matchesAction(perm.action, action) &&
matchesScope(perm.scope, resourceId) &&
evaluateConditions(perm.conditions)) {
return true;
}
}
return false;
}
function matchesResource(pattern: string, resource: string): boolean {
return pattern === "*" || pattern === resource;
}
function matchesAction(pattern: string, action: string): boolean {
return pattern === "*" || pattern === action;
}
```
## API Authorization Headers
All API requests require:
```http
Authorization: Bearer <access_token>
```
For agent requests (over mTLS):
```http
X-Agent-Id: <agent_id>
Authorization: Bearer <agent_token>
```
## Permission Denied Response
```json
{
"success": false,
"error": {
"code": "PERMISSION_DENIED",
"message": "User does not have permission to approve promotions to production",
"details": {
"resource": "promotion",
"action": "approve",
"scope": { "environmentId": "prod-env-id" },
"requiredRoles": ["admin", "approver"],
"userRoles": ["viewer"]
}
}
}
```
## References
- [Security Overview](overview.md)
- [Agent Security](agent-security.md)
- [Authority Module](../../../authority/architecture.md)