package auth import ( "context" "fmt" "time" "github.com/orchard9/rdev/internal/domain" "github.com/orchard9/rdev/internal/service" ) // APIKey is an alias for domain.APIKey. // All API key behavior (IsExpired, IsRevoked, etc.) lives in domain/apikey.go. type APIKey = domain.APIKey // Error sentinels — delegate to domain errors. // Consumers should migrate to domain.ErrXxx over time. var ( ErrKeyNotFound = domain.ErrKeyNotFound ErrKeyRevoked = domain.ErrKeyRevoked ErrKeyExpired = domain.ErrKeyExpired ErrIPNotAllowed = domain.ErrIPNotAllowed ) // CreateKeyRequest is the input for creating a new key. type CreateKeyRequest struct { Name string Scopes []Scope ProjectIDs []string // nil = all projects AllowedIPs []string // CIDR notation; nil = no restriction ExpiresIn time.Duration // 0 = never CreatedBy string } // CreateKeyResponse is the output of creating a new key. type CreateKeyResponse struct { Key *APIKey Secret string // Full key, shown only once } // Service handles API key operations. // It wraps service.APIKeyService to provide the same interface as before // while delegating to the hexagonal service layer. type Service struct { svc *service.APIKeyService adminKey string } // NewService creates a new auth service. // Accepts a service.APIKeyService (hexagonal) instead of raw *sql.DB. func NewService(svc *service.APIKeyService, adminKey string) *Service { return &Service{ svc: svc, adminKey: adminKey, } } // IsAdminKey checks if the provided key is the super admin key. func (s *Service) IsAdminKey(key string) bool { return s.adminKey != "" && key == s.adminKey } // Create generates a new API key. func (s *Service) Create(ctx context.Context, req CreateKeyRequest) (*CreateKeyResponse, error) { // Validate scopes if !ValidateScopes(req.Scopes) { return nil, fmt.Errorf("invalid scopes") } // Convert []string ProjectIDs to []domain.ProjectID var projectIDs []domain.ProjectID if req.ProjectIDs != nil { projectIDs = make([]domain.ProjectID, len(req.ProjectIDs)) for i, p := range req.ProjectIDs { projectIDs[i] = domain.ProjectID(p) } } result, err := s.svc.Create(ctx, service.CreateKeyRequest{ Name: req.Name, Scopes: req.Scopes, ProjectIDs: projectIDs, AllowedIPs: req.AllowedIPs, ExpiresIn: req.ExpiresIn, CreatedBy: req.CreatedBy, }) if err != nil { return nil, err } return &CreateKeyResponse{ Key: result.Key, Secret: result.Secret, }, nil } // Validate checks if a key is valid and returns the key details. func (s *Service) Validate(ctx context.Context, key string) (*APIKey, error) { return s.svc.Validate(ctx, key) } // List returns all API keys (without secrets). func (s *Service) List(ctx context.Context) ([]*APIKey, error) { return s.svc.List(ctx) } // Get returns a single API key by ID. func (s *Service) Get(ctx context.Context, id string) (*APIKey, error) { return s.svc.Get(ctx, domain.APIKeyID(id)) } // Revoke marks an API key as revoked. func (s *Service) Revoke(ctx context.Context, id string) error { return s.svc.Revoke(ctx, domain.APIKeyID(id)) }