package useCases import ( "RenewCMS/internal/domain/audit" "RenewCMS/internal/domain/hash" "RenewCMS/internal/domain/user" "context" "errors" ) type AuthenticateUserUseCase struct { userRepository user.Repository hasher hash.Hasher recorder audit.Recorder } func NewAuthenticateUserUseCase(userRepository user.Repository, hasher hash.Hasher, recorder audit.Recorder) *AuthenticateUserUseCase { return &AuthenticateUserUseCase{userRepository, hasher, recorder} } func (a *AuthenticateUserUseCase) Authenticate(ctx context.Context, username string, password string) (user.User, error) { dbUser, err := a.verifyCredentials(username, password) if err != nil { a.recordRejectedLogin(ctx, username, err) return user.User{}, err } event := audit.NewEvent(audit.ActionLogin, audit.CategoryAuth) event.Actor = audit.UserActor(dbUser) a.recorder.Record(ctx, event) return dbUser, nil } func (a *AuthenticateUserUseCase) verifyCredentials(username string, password string) (user.User, error) { dbUser, err := a.userRepository.GetByUsername(username) if err != nil { return user.User{}, err } if err := a.hasher.Compare(dbUser.Password, password); err != nil { return user.User{}, user.ErrInvalidCredentials } return dbUser, nil } func (a *AuthenticateUserUseCase) recordRejectedLogin(ctx context.Context, username string, cause error) { if !errors.Is(cause, user.ErrUserNotFound) && !errors.Is(cause, user.ErrInvalidCredentials) { return } reason := audit.ReasonWrongLogin event := audit.NewEvent(audit.ActionLoginFailed, audit.CategoryAuth) event.Actor = audit.AnonymousActor(username) event.Outcome = audit.OutcomeFailed event.FailureReason = &reason event.Security = true a.recorder.Record(ctx, event) } func (a *AuthenticateUserUseCase) Logout(ctx context.Context) { origin, ok := audit.OriginFrom(ctx) if !ok || origin.Actor.Type != audit.ActorUser { return } a.recorder.Record(ctx, audit.NewEvent(audit.ActionLogout, audit.CategoryAuth)) }