package useCases import ( "RenewCMS/internal/domain/apikey" "RenewCMS/internal/domain/user" "errors" "strings" "time" ) const lastUsedWriteInterval = time.Minute type AuthenticateAPIKeyUseCase struct { apiKeyRepository apikey.Repository keyGenerator apikey.KeyGenerator userRepository user.Repository now func() time.Time } type AuthenticatedAPIKey struct { Key apikey.APIKey User user.User } func NewAuthenticateAPIKeyUseCase(apiKeyRepository apikey.Repository, keyGenerator apikey.KeyGenerator, userRepository user.Repository, now func() time.Time) *AuthenticateAPIKeyUseCase { return &AuthenticateAPIKeyUseCase{apiKeyRepository, keyGenerator, userRepository, now} } func (a *AuthenticateAPIKeyUseCase) AuthenticateAPIKey(rawKey string) (AuthenticatedAPIKey, error) { if !strings.HasPrefix(rawKey, apikey.RawKeyPrefix) { return AuthenticatedAPIKey{}, apikey.ErrAPIKeyInvalid } key, err := a.apiKeyRepository.GetByHashedKey(a.keyGenerator.Hash(rawKey)) if err != nil { if errors.Is(err, apikey.ErrAPIKeyNotFound) { return AuthenticatedAPIKey{}, apikey.ErrAPIKeyInvalid } return AuthenticatedAPIKey{}, err } now := a.now() if key.StatusAt(now) != apikey.StatusActive { return AuthenticatedAPIKey{}, apikey.ErrAPIKeyInvalid } owner, err := a.userRepository.Get(key.UserID) if err != nil { if errors.Is(err, user.ErrUserNotFound) { return AuthenticatedAPIKey{}, apikey.ErrAPIKeyInvalid } return AuthenticatedAPIKey{}, err } if !owner.IsVerified { return AuthenticatedAPIKey{}, apikey.ErrAPIKeyInvalid } a.recordUse(key, now) return AuthenticatedAPIKey{Key: key, User: owner}, nil } func (a *AuthenticateAPIKeyUseCase) recordUse(key apikey.APIKey, now time.Time) { if key.LastUsedAt != nil && now.Sub(*key.LastUsedAt) < lastUsedWriteInterval { return } _ = a.apiKeyRepository.TouchLastUsed(key.ID, now) }