This commit is contained in:
2025-08-22 14:06:20 -04:00
commit 46264cb556
36 changed files with 7185 additions and 0 deletions

View File

@ -0,0 +1,126 @@
package services
import (
"context"
"fmt"
"go.uber.org/zap"
"github.com/kms/api-key-service/internal/domain"
"github.com/kms/api-key-service/internal/repository"
)
// applicationService implements the ApplicationService interface
type applicationService struct {
appRepo repository.ApplicationRepository
logger *zap.Logger
}
// NewApplicationService creates a new application service
func NewApplicationService(appRepo repository.ApplicationRepository, logger *zap.Logger) ApplicationService {
return &applicationService{
appRepo: appRepo,
logger: logger,
}
}
// Create creates a new application
func (s *applicationService) Create(ctx context.Context, req *domain.CreateApplicationRequest, userID string) (*domain.Application, error) {
s.logger.Info("Creating application", zap.String("app_id", req.AppID), zap.String("user_id", userID))
// TODO: Add permission validation
// TODO: Add input validation using validator
app := &domain.Application{
AppID: req.AppID,
AppLink: req.AppLink,
Type: req.Type,
CallbackURL: req.CallbackURL,
HMACKey: generateHMACKey(), // TODO: Use proper key generation
TokenRenewalDuration: req.TokenRenewalDuration,
MaxTokenDuration: req.MaxTokenDuration,
Owner: req.Owner,
}
if err := s.appRepo.Create(ctx, app); err != nil {
s.logger.Error("Failed to create application", zap.Error(err), zap.String("app_id", req.AppID))
return nil, fmt.Errorf("failed to create application: %w", err)
}
s.logger.Info("Application created successfully", zap.String("app_id", app.AppID))
return app, nil
}
// GetByID retrieves an application by its ID
func (s *applicationService) GetByID(ctx context.Context, appID string) (*domain.Application, error) {
s.logger.Debug("Getting application by ID", zap.String("app_id", appID))
app, err := s.appRepo.GetByID(ctx, appID)
if err != nil {
s.logger.Error("Failed to get application", zap.Error(err), zap.String("app_id", appID))
return nil, fmt.Errorf("failed to get application: %w", err)
}
return app, nil
}
// List retrieves applications with pagination
func (s *applicationService) List(ctx context.Context, limit, offset int) ([]*domain.Application, error) {
s.logger.Debug("Listing applications", zap.Int("limit", limit), zap.Int("offset", offset))
if limit <= 0 {
limit = 50 // Default limit
}
if limit > 100 {
limit = 100 // Max limit
}
apps, err := s.appRepo.List(ctx, limit, offset)
if err != nil {
s.logger.Error("Failed to list applications", zap.Error(err))
return nil, fmt.Errorf("failed to list applications: %w", err)
}
s.logger.Debug("Listed applications", zap.Int("count", len(apps)))
return apps, nil
}
// Update updates an existing application
func (s *applicationService) Update(ctx context.Context, appID string, updates *domain.UpdateApplicationRequest, userID string) (*domain.Application, error) {
s.logger.Info("Updating application", zap.String("app_id", appID), zap.String("user_id", userID))
// TODO: Add permission validation
// TODO: Add input validation
app, err := s.appRepo.Update(ctx, appID, updates)
if err != nil {
s.logger.Error("Failed to update application", zap.Error(err), zap.String("app_id", appID))
return nil, fmt.Errorf("failed to update application: %w", err)
}
s.logger.Info("Application updated successfully", zap.String("app_id", appID))
return app, nil
}
// Delete deletes an application
func (s *applicationService) Delete(ctx context.Context, appID string, userID string) error {
s.logger.Info("Deleting application", zap.String("app_id", appID), zap.String("user_id", userID))
// TODO: Add permission validation
// TODO: Check for existing tokens and handle appropriately
if err := s.appRepo.Delete(ctx, appID); err != nil {
s.logger.Error("Failed to delete application", zap.Error(err), zap.String("app_id", appID))
return fmt.Errorf("failed to delete application: %w", err)
}
s.logger.Info("Application deleted successfully", zap.String("app_id", appID))
return nil
}
// generateHMACKey generates a secure HMAC key
// TODO: Replace with proper cryptographic key generation
func generateHMACKey() string {
// This is a placeholder - should use proper crypto/rand
return "generated-hmac-key-placeholder"
}

View File

@ -0,0 +1,65 @@
package services
import (
"context"
"fmt"
"go.uber.org/zap"
"github.com/kms/api-key-service/internal/config"
)
// authenticationService implements the AuthenticationService interface
type authenticationService struct {
config config.ConfigProvider
logger *zap.Logger
}
// NewAuthenticationService creates a new authentication service
func NewAuthenticationService(config config.ConfigProvider, logger *zap.Logger) AuthenticationService {
return &authenticationService{
config: config,
logger: logger,
}
}
// GetUserID extracts user ID from context
func (s *authenticationService) GetUserID(ctx context.Context) (string, error) {
// For now, this is a simple implementation
// In a real implementation, this would extract from JWT tokens, session, etc.
if userID, ok := ctx.Value("user_id").(string); ok {
return userID, nil
}
return "", fmt.Errorf("user ID not found in context")
}
// ValidatePermissions checks if user has required permissions
func (s *authenticationService) ValidatePermissions(ctx context.Context, userID string, appID string, requiredPermissions []string) error {
s.logger.Debug("Validating permissions",
zap.String("user_id", userID),
zap.String("app_id", appID),
zap.Strings("required_permissions", requiredPermissions))
// TODO: Implement actual permission validation
// For now, we'll just allow all requests
return nil
}
// GetUserClaims retrieves user claims
func (s *authenticationService) GetUserClaims(ctx context.Context, userID string) (map[string]string, error) {
s.logger.Debug("Getting user claims", zap.String("user_id", userID))
// TODO: Implement actual claims retrieval
// For now, return basic claims
claims := map[string]string{
"user_id": userID,
"email": userID, // Assuming user_id is email for now
"name": "Test User",
}
return claims, nil
}

View File

@ -0,0 +1,59 @@
package services
import (
"context"
"github.com/google/uuid"
"github.com/kms/api-key-service/internal/domain"
)
// ApplicationService defines the interface for application business logic
type ApplicationService interface {
// Create creates a new application
Create(ctx context.Context, req *domain.CreateApplicationRequest, userID string) (*domain.Application, error)
// GetByID retrieves an application by its ID
GetByID(ctx context.Context, appID string) (*domain.Application, error)
// List retrieves applications with pagination
List(ctx context.Context, limit, offset int) ([]*domain.Application, error)
// Update updates an existing application
Update(ctx context.Context, appID string, updates *domain.UpdateApplicationRequest, userID string) (*domain.Application, error)
// Delete deletes an application
Delete(ctx context.Context, appID string, userID string) error
}
// TokenService defines the interface for token business logic
type TokenService interface {
// CreateStaticToken creates a new static token
CreateStaticToken(ctx context.Context, req *domain.CreateStaticTokenRequest, userID string) (*domain.CreateStaticTokenResponse, error)
// ListByApp lists all tokens for an application
ListByApp(ctx context.Context, appID string, limit, offset int) ([]*domain.StaticToken, error)
// Delete deletes a token
Delete(ctx context.Context, tokenID uuid.UUID, userID string) error
// GenerateUserToken generates a user token
GenerateUserToken(ctx context.Context, appID, userID string, permissions []string) (string, error)
// VerifyToken verifies a token and returns verification response
VerifyToken(ctx context.Context, req *domain.VerifyRequest) (*domain.VerifyResponse, error)
// RenewUserToken renews a user token
RenewUserToken(ctx context.Context, req *domain.RenewRequest) (*domain.RenewResponse, error)
}
// AuthenticationService defines the interface for authentication business logic
type AuthenticationService interface {
// GetUserID extracts user ID from context
GetUserID(ctx context.Context) (string, error)
// ValidatePermissions checks if user has required permissions
ValidatePermissions(ctx context.Context, userID string, appID string, requiredPermissions []string) error
// GetUserClaims retrieves user claims
GetUserClaims(ctx context.Context, userID string) (map[string]string, error)
}

View File

@ -0,0 +1,162 @@
package services
import (
"context"
"fmt"
"time"
"github.com/google/uuid"
"go.uber.org/zap"
"github.com/kms/api-key-service/internal/domain"
"github.com/kms/api-key-service/internal/repository"
)
// tokenService implements the TokenService interface
type tokenService struct {
tokenRepo repository.StaticTokenRepository
appRepo repository.ApplicationRepository
permRepo repository.PermissionRepository
grantRepo repository.GrantedPermissionRepository
logger *zap.Logger
}
// NewTokenService creates a new token service
func NewTokenService(
tokenRepo repository.StaticTokenRepository,
appRepo repository.ApplicationRepository,
permRepo repository.PermissionRepository,
grantRepo repository.GrantedPermissionRepository,
logger *zap.Logger,
) TokenService {
return &tokenService{
tokenRepo: tokenRepo,
appRepo: appRepo,
permRepo: permRepo,
grantRepo: grantRepo,
logger: logger,
}
}
// CreateStaticToken creates a new static token
func (s *tokenService) CreateStaticToken(ctx context.Context, req *domain.CreateStaticTokenRequest, userID string) (*domain.CreateStaticTokenResponse, error) {
s.logger.Info("Creating static token", zap.String("app_id", req.AppID), zap.String("user_id", userID))
// TODO: Validate permissions
// TODO: Validate application exists
// TODO: Generate secure token
// TODO: Grant permissions
tokenID := uuid.New()
now := time.Now()
// Create the token entity
token := &domain.StaticToken{
ID: tokenID,
AppID: req.AppID,
Owner: domain.Owner{
Type: domain.OwnerTypeIndividual,
Name: userID,
Owner: userID,
},
KeyHash: "placeholder-hash-" + tokenID.String(),
Type: "hmac",
CreatedAt: now,
UpdatedAt: now,
}
// Save the token to the database
err := s.tokenRepo.Create(ctx, token)
if err != nil {
s.logger.Error("Failed to create token in database", zap.Error(err), zap.String("token_id", tokenID.String()))
return nil, fmt.Errorf("failed to create token: %w", err)
}
response := &domain.CreateStaticTokenResponse{
ID: tokenID,
Token: "static-token-placeholder-" + tokenID.String(),
Permissions: req.Permissions,
CreatedAt: now,
}
s.logger.Info("Static token created successfully", zap.String("token_id", tokenID.String()))
return response, nil
}
// ListByApp lists all tokens for an application
func (s *tokenService) ListByApp(ctx context.Context, appID string, limit, offset int) ([]*domain.StaticToken, error) {
s.logger.Debug("Listing tokens for application", zap.String("app_id", appID))
// TODO: Implement actual token listing
return []*domain.StaticToken{}, nil
}
// Delete deletes a token
func (s *tokenService) Delete(ctx context.Context, tokenID uuid.UUID, userID string) error {
s.logger.Info("Deleting token", zap.String("token_id", tokenID.String()), zap.String("user_id", userID))
// Check if token exists
exists, err := s.tokenRepo.Exists(ctx, tokenID)
if err != nil {
s.logger.Error("Failed to check token existence", zap.Error(err), zap.String("token_id", tokenID.String()))
return err
}
if !exists {
s.logger.Error("Token not found", zap.String("token_id", tokenID.String()))
return fmt.Errorf("token with ID '%s' not found", tokenID.String())
}
// Delete the token
err = s.tokenRepo.Delete(ctx, tokenID)
if err != nil {
s.logger.Error("Failed to delete token", zap.Error(err), zap.String("token_id", tokenID.String()))
return err
}
// TODO: Revoke associated permissions
return nil
}
// GenerateUserToken generates a user token
func (s *tokenService) GenerateUserToken(ctx context.Context, appID, userID string, permissions []string) (string, error) {
s.logger.Info("Generating user token", zap.String("app_id", appID), zap.String("user_id", userID))
// TODO: Validate application
// TODO: Validate permissions
// TODO: Generate JWT token
return "user-token-placeholder-" + userID, nil
}
// VerifyToken verifies a token and returns verification response
func (s *tokenService) VerifyToken(ctx context.Context, req *domain.VerifyRequest) (*domain.VerifyResponse, error) {
s.logger.Debug("Verifying token", zap.String("app_id", req.AppID), zap.String("type", string(req.Type)))
// TODO: Implement actual token verification logic
response := &domain.VerifyResponse{
Valid: true,
UserID: req.UserID,
Permissions: []string{"basic"},
TokenType: req.Type,
}
return response, nil
}
// RenewUserToken renews a user token
func (s *tokenService) RenewUserToken(ctx context.Context, req *domain.RenewRequest) (*domain.RenewResponse, error) {
s.logger.Info("Renewing user token", zap.String("app_id", req.AppID), zap.String("user_id", req.UserID))
// TODO: Validate current token
// TODO: Generate new token with extended expiry but same max valid date
response := &domain.RenewResponse{
Token: "renewed-token-placeholder",
ExpiresAt: time.Now().Add(7 * 24 * time.Hour),
MaxValidAt: time.Now().Add(30 * 24 * time.Hour),
}
return response, nil
}