package service import ( "context" "errors" "testing" "time" "git.sundynix.cn/Blizzard/Veyra/internal/apperror" "git.sundynix.cn/Blizzard/Veyra/internal/model" ) type testRepository struct { user model.User credential model.Credential session model.Session createdSessions int } func (r *testRepository) CreateUser(_ context.Context, u model.User, c model.Credential) (model.User, error) { r.user = u r.credential = c return u, nil } func (r *testRepository) FindCredential(_ context.Context, login string) (model.User, model.Credential, error) { if login != r.credential.LoginName { return model.User{}, model.Credential{}, apperror.ErrNotFound } return r.user, r.credential, nil } func (r *testRepository) CreateSession(_ context.Context, s model.Session, _ string) error { r.session = s r.createdSessions++ return nil } func (r *testRepository) FindSessionUser(_ context.Context, _, _ string, _ time.Time) (model.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) ([]model.User, error) { return []model.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: model.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") } }