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 }