Files
RenewCMS/internal/infrastructure/useCases/AuthenticateAPIKey.go
T

68 lines
1.8 KiB
Go

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)
}