Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
114
cloud/maplepress-backend/internal/service/site/rotate_apikey.go
Normal file
114
cloud/maplepress-backend/internal/service/site/rotate_apikey.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
package site
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
"go.uber.org/zap"
|
||||
|
||||
siteusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/site"
|
||||
)
|
||||
|
||||
// RotateAPIKeyService handles API key rotation operations
|
||||
type RotateAPIKeyService interface {
|
||||
RotateAPIKey(ctx context.Context, tenantID gocql.UUID, input *siteusecase.RotateAPIKeyInput) (*siteusecase.RotateAPIKeyOutput, error)
|
||||
}
|
||||
|
||||
type rotateAPIKeyService struct {
|
||||
// Focused usecases
|
||||
getSiteUC *siteusecase.GetSiteUseCase
|
||||
generateAPIKeyUC *siteusecase.GenerateAPIKeyUseCase
|
||||
updateSiteAPIKeyUC *siteusecase.UpdateSiteAPIKeyUseCase
|
||||
updateSiteAPIKeyToRepoUC *siteusecase.UpdateSiteAPIKeyToRepoUseCase
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewRotateAPIKeyService creates a new RotateAPIKeyService
|
||||
func NewRotateAPIKeyService(
|
||||
getSiteUC *siteusecase.GetSiteUseCase,
|
||||
generateAPIKeyUC *siteusecase.GenerateAPIKeyUseCase,
|
||||
updateSiteAPIKeyUC *siteusecase.UpdateSiteAPIKeyUseCase,
|
||||
updateSiteAPIKeyToRepoUC *siteusecase.UpdateSiteAPIKeyToRepoUseCase,
|
||||
logger *zap.Logger,
|
||||
) RotateAPIKeyService {
|
||||
return &rotateAPIKeyService{
|
||||
getSiteUC: getSiteUC,
|
||||
generateAPIKeyUC: generateAPIKeyUC,
|
||||
updateSiteAPIKeyUC: updateSiteAPIKeyUC,
|
||||
updateSiteAPIKeyToRepoUC: updateSiteAPIKeyToRepoUC,
|
||||
logger: logger.Named("rotate-apikey-service"),
|
||||
}
|
||||
}
|
||||
|
||||
// RotateAPIKey orchestrates the API key rotation workflow
|
||||
func (s *rotateAPIKeyService) RotateAPIKey(ctx context.Context, tenantID gocql.UUID, input *siteusecase.RotateAPIKeyInput) (*siteusecase.RotateAPIKeyOutput, error) {
|
||||
s.logger.Info("rotating API key",
|
||||
zap.String("tenant_id", tenantID.String()),
|
||||
zap.String("site_id", input.SiteID))
|
||||
|
||||
// Step 1: Get current site
|
||||
siteOutput, err := s.getSiteUC.Execute(ctx, tenantID, &siteusecase.GetSiteInput{
|
||||
ID: input.SiteID,
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Error("failed to get site",
|
||||
zap.String("site_id", input.SiteID),
|
||||
zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
site := siteOutput.Site
|
||||
|
||||
// Step 2: Store old key info for response and rotation
|
||||
oldKeyLastFour := site.APIKeyLastFour
|
||||
oldAPIKeyHash := site.APIKeyHash
|
||||
|
||||
// Step 3: Determine test mode from existing API key prefix
|
||||
// If current key starts with "test_", generate a test key; otherwise generate live key
|
||||
testMode := len(site.APIKeyPrefix) >= 5 && site.APIKeyPrefix[:5] == "test_"
|
||||
|
||||
s.logger.Info("generating new API key",
|
||||
zap.Bool("test_mode", testMode),
|
||||
zap.String("current_key_prefix", site.APIKeyPrefix),
|
||||
zap.String("site_id", input.SiteID))
|
||||
apiKeyResult, err := s.generateAPIKeyUC.Execute(testMode)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to generate new API key", zap.Error(err))
|
||||
return nil, fmt.Errorf("failed to generate API key: %w", err)
|
||||
}
|
||||
|
||||
// Step 4: Update site entity with new key details
|
||||
s.updateSiteAPIKeyUC.Execute(&siteusecase.UpdateSiteAPIKeyInput{
|
||||
Site: site,
|
||||
NewAPIKeyHash: apiKeyResult.HashedKey,
|
||||
NewKeyPrefix: apiKeyResult.Prefix,
|
||||
NewKeyLastFour: apiKeyResult.LastFour,
|
||||
})
|
||||
|
||||
// Step 5: Update site API key in repository (all tables)
|
||||
// Use UpdateSiteAPIKeyToRepoUC to properly handle sites_by_apikey table (delete old + insert new)
|
||||
if err := s.updateSiteAPIKeyToRepoUC.Execute(ctx, &siteusecase.UpdateSiteAPIKeyToRepoInput{
|
||||
Site: site,
|
||||
OldAPIKeyHash: oldAPIKeyHash,
|
||||
}); err != nil {
|
||||
s.logger.Error("failed to update site with new API key", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Step 6: Build output
|
||||
rotatedAt := time.Now()
|
||||
|
||||
s.logger.Info("API key rotated successfully",
|
||||
zap.String("site_id", input.SiteID),
|
||||
zap.String("old_key_last_four", oldKeyLastFour),
|
||||
zap.String("new_key_prefix", apiKeyResult.Prefix),
|
||||
zap.String("new_key_last_four", apiKeyResult.LastFour))
|
||||
|
||||
return &siteusecase.RotateAPIKeyOutput{
|
||||
NewAPIKey: apiKeyResult.PlaintextKey, // PLAINTEXT - only shown once!
|
||||
OldKeyLastFour: oldKeyLastFour,
|
||||
RotatedAt: rotatedAt,
|
||||
}, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue