package service import ( "context" "errors" "testing" "time" "veyra/services/api/internal/identity/domain" "veyra/services/api/internal/platform/apperror" ) type testRepository struct { user domain.User credential domain.Credential session domain.Session createdSessions int } func (r *testRepository) CreateUser(_ context.Context, u domain.User, c domain.Credential) (domain.User, error) { r.user = u r.credential = c return u, nil } func (r *testRepository) FindCredential(_ context.Context, login string) (domain.User, domain.Credential, error) { if login != r.credential.LoginName { return domain.User{}, domain.Credential{}, apperror.ErrNotFound } return r.user, r.credential, nil } func (r *testRepository) CreateSession(_ context.Context, s domain.Session) error { r.session = s r.createdSessions++ return nil } func (r *testRepository) FindSessionUser(_ context.Context, _, _ string, _ time.Time) (domain.User, error) { return r.user, nil } func (r *testRepository) RevokeSession(_ context.Context, _, _ string, _ time.Time) error { return nil } func (r *testRepository) ListUsers(context.Context, int) ([]domain.User, error) { return []domain.User{r.user}, nil } func TestPasswordAndSessionSecretsAreHashed(t *testing.T) { ctx := context.Background() repo := &testRepository{} auth, err := NewAuthService(repo) if err != nil { t.Fatal(err) } password := "a-development-password" if _, err := auth.ProvisionUser(ctx, " ADMIN ", password, "Admin", "ADMIN"); err != nil { t.Fatal(err) } if repo.credential.PasswordHash == password || repo.credential.LoginName != "admin" { t.Fatal("credential handling failed") } result, err := auth.Login(ctx, "admin", password, "admin") if err != nil { t.Fatal(err) } if len(result.Token) != 43 || repo.session.TokenHash == result.Token || repo.session.TokenHash != TokenHash(result.Token) { t.Fatal("raw session secret persisted") } if _, err := auth.Login(ctx, "admin", "incorrect-password", "admin"); !errors.Is(err, apperror.ErrUnauthenticated) { t.Fatal("wrong password accepted") } if _, err := auth.Login(ctx, "admin", password, "app"); !errors.Is(err, apperror.ErrUnauthenticated) { t.Fatal("admin credential used for app login") } if _, err := auth.Login(ctx, "missing", password, "admin"); !errors.Is(err, apperror.ErrUnauthenticated) { t.Fatal("missing identity was disclosed") } if repo.createdSessions != 1 { t.Fatal("failed login created a session") } repo.user.Status = "DISABLED" if _, err := auth.Authenticate(ctx, result.Token, "admin"); !errors.Is(err, apperror.ErrUnauthenticated) { t.Fatal("disabled user remained authenticated") } } func TestUserCannotAuthenticateAsAdmin(t *testing.T) { repo := &testRepository{user: domain.User{Status: "ACTIVE", Role: "USER"}} auth, err := NewAuthService(repo) if err != nil { t.Fatal(err) } if _, err := auth.Authenticate(context.Background(), "1234567890123456789012345678901234567890123", "admin"); !errors.Is(err, apperror.ErrUnauthenticated) { t.Fatal("user role passed admin authorization") } }