Major refactoring to hexagonal (ports & adapters) architecture: - Add service layer (apikey_service, project_service) for business logic - Add webhook system with dispatcher and delivery tracking - Add command queue with priority-based processing - Add rate limiting with sliding window algorithm - Add audit logging for command execution - Add OpenTelemetry integration (traces, metrics, spans) - Add circuit breaker for fault tolerance - Add cached repository wrapper for performance - Add comprehensive validation package - Add Kubernetes client integration for pod management - Add database migrations (allowed_ips, audit_log, rate_limiting, queue, webhooks) - Add network policy and PodDisruptionBudget for k8s - Remove legacy executor and projects/registry packages - Untrack secrets.yaml (now managed via envault) - Add coverage.out to .gitignore - Add e2e test infrastructure with docker-compose - Add comprehensive documentation (API, architecture, operations, plans) - Add golangci-lint config and pre-commit hook Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
118 lines
2.8 KiB
Go
118 lines
2.8 KiB
Go
package domain
|
|
|
|
import (
|
|
"net"
|
|
"time"
|
|
)
|
|
|
|
// APIKeyID is a strongly-typed identifier for API keys.
|
|
type APIKeyID string
|
|
|
|
// Scope represents a permission scope for API keys.
|
|
type Scope string
|
|
|
|
const (
|
|
ScopeAdmin Scope = "admin"
|
|
ScopeProjectsRead Scope = "projects:read"
|
|
ScopeProjectsExecute Scope = "projects:execute"
|
|
ScopeKeysManage Scope = "keys:manage"
|
|
)
|
|
|
|
// APIKey represents an API key for authentication.
|
|
type APIKey struct {
|
|
ID APIKeyID
|
|
Name string
|
|
KeyPrefix string // First 8 chars of key for identification
|
|
Scopes []Scope
|
|
ProjectIDs []ProjectID // nil = access to all projects
|
|
AllowedIPs []string // CIDR notation, e.g., ["192.168.1.0/24", "10.0.0.0/8"]; nil = no restriction
|
|
CreatedAt time.Time
|
|
ExpiresAt *time.Time
|
|
LastUsedAt *time.Time
|
|
RevokedAt *time.Time
|
|
CreatedBy string
|
|
}
|
|
|
|
// IsExpired returns true if the key has expired.
|
|
func (k *APIKey) IsExpired() bool {
|
|
if k.ExpiresAt == nil {
|
|
return false
|
|
}
|
|
return time.Now().After(*k.ExpiresAt)
|
|
}
|
|
|
|
// IsRevoked returns true if the key has been revoked.
|
|
func (k *APIKey) IsRevoked() bool {
|
|
return k.RevokedAt != nil
|
|
}
|
|
|
|
// IsActive returns true if the key is valid for use.
|
|
func (k *APIKey) IsActive() bool {
|
|
return !k.IsRevoked() && !k.IsExpired()
|
|
}
|
|
|
|
// HasScope returns true if the key has the specified scope.
|
|
func (k *APIKey) HasScope(scope Scope) bool {
|
|
// Admin scope grants all permissions
|
|
for _, s := range k.Scopes {
|
|
if s == ScopeAdmin || s == scope {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// HasAnyScope returns true if the key has any of the specified scopes.
|
|
func (k *APIKey) HasAnyScope(scopes ...Scope) bool {
|
|
for _, scope := range scopes {
|
|
if k.HasScope(scope) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// HasProjectAccess returns true if the key can access the given project.
|
|
func (k *APIKey) HasProjectAccess(projectID ProjectID) bool {
|
|
// Admin or nil project list means access to all projects
|
|
if k.HasScope(ScopeAdmin) || k.ProjectIDs == nil {
|
|
return true
|
|
}
|
|
for _, pid := range k.ProjectIDs {
|
|
if pid == projectID {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsIPAllowed checks if the given IP address is allowed by the key's IP restrictions.
|
|
// Returns true if no IP restrictions are set or if the IP matches any allowed CIDR.
|
|
func (k *APIKey) IsIPAllowed(clientIP string) bool {
|
|
// No restrictions means all IPs are allowed
|
|
if len(k.AllowedIPs) == 0 {
|
|
return true
|
|
}
|
|
|
|
ip := net.ParseIP(clientIP)
|
|
if ip == nil {
|
|
return false
|
|
}
|
|
|
|
for _, cidr := range k.AllowedIPs {
|
|
_, network, err := net.ParseCIDR(cidr)
|
|
if err != nil {
|
|
// If not a CIDR, try parsing as single IP
|
|
allowedIP := net.ParseIP(cidr)
|
|
if allowedIP != nil && allowedIP.Equal(ip) {
|
|
return true
|
|
}
|
|
continue
|
|
}
|
|
if network.Contains(ip) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|