124 lines
3.8 KiB
Go
124 lines
3.8 KiB
Go
|
|
package services
|
||
|
|
|
||
|
|
import (
|
||
|
|
"log/slog"
|
||
|
|
"path/filepath"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestPasswordHashVerification(t *testing.T) {
|
||
|
|
hash := HashPassword("correct-horse")
|
||
|
|
if !VerifyPasswordHash(hash, "correct-horse") {
|
||
|
|
t.Fatalf("VerifyPasswordHash rejected the correct password")
|
||
|
|
}
|
||
|
|
if VerifyPasswordHash(hash, "wrong-password") {
|
||
|
|
t.Fatalf("VerifyPasswordHash accepted the wrong password")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestBootstrapCreatesAdminAndClosesRegistration(t *testing.T) {
|
||
|
|
auth := newTestAuthService(t)
|
||
|
|
available, err := auth.BootstrapAvailable()
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("BootstrapAvailable returned error: %v", err)
|
||
|
|
}
|
||
|
|
if !available {
|
||
|
|
t.Fatalf("BootstrapAvailable = false, want true")
|
||
|
|
}
|
||
|
|
|
||
|
|
user, err := auth.CreateBootstrapUser("daniel", "daniel@example.test", "password123")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
||
|
|
}
|
||
|
|
if user.Role != UserRoleAdmin {
|
||
|
|
t.Fatalf("role = %q, want admin", user.Role)
|
||
|
|
}
|
||
|
|
|
||
|
|
if _, err := auth.CreateBootstrapUser("other", "other@example.test", "password123"); err == nil {
|
||
|
|
t.Fatalf("second bootstrap unexpectedly succeeded")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLoginSessionAndDisabledUser(t *testing.T) {
|
||
|
|
auth := newTestAuthService(t)
|
||
|
|
user, err := auth.CreateBootstrapUser("daniel", "daniel@example.test", "password123")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
||
|
|
}
|
||
|
|
if _, _, err := auth.Login("daniel@example.test", "wrong"); err == nil {
|
||
|
|
t.Fatalf("Login accepted wrong password")
|
||
|
|
}
|
||
|
|
|
||
|
|
_, token, err := auth.Login("daniel@example.test", "password123")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("Login returned error: %v", err)
|
||
|
|
}
|
||
|
|
sessionUser, _, err := auth.UserForSession(token)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("UserForSession returned error: %v", err)
|
||
|
|
}
|
||
|
|
if sessionUser.ID != user.ID {
|
||
|
|
t.Fatalf("session user = %q, want %q", sessionUser.ID, user.ID)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := auth.DisableUser(user.ID, true); err != nil {
|
||
|
|
t.Fatalf("DisableUser returned error: %v", err)
|
||
|
|
}
|
||
|
|
if _, _, err := auth.UserForSession(token); err == nil {
|
||
|
|
t.Fatalf("disabled user session still resolved")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestInviteAcceptsOnceAndResetChangesPassword(t *testing.T) {
|
||
|
|
auth := newTestAuthService(t)
|
||
|
|
admin, err := auth.CreateBootstrapUser("admin", "admin@example.test", "password123")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("CreateBootstrapUser returned error: %v", err)
|
||
|
|
}
|
||
|
|
invite, err := auth.CreateInvite("friend@example.test", UserRoleUser, admin.ID, time.Hour)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("CreateInvite returned error: %v", err)
|
||
|
|
}
|
||
|
|
user, err := auth.AcceptInvite(invite.Token, "friend", "password123")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("AcceptInvite returned error: %v", err)
|
||
|
|
}
|
||
|
|
if user.Email != "friend@example.test" {
|
||
|
|
t.Fatalf("email = %q, want friend@example.test", user.Email)
|
||
|
|
}
|
||
|
|
if _, err := auth.AcceptInvite(invite.Token, "friend", "password123"); err == nil {
|
||
|
|
t.Fatalf("AcceptInvite allowed token reuse")
|
||
|
|
}
|
||
|
|
|
||
|
|
reset, err := auth.CreatePasswordResetInvite(user.ID, admin.ID)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("CreatePasswordResetInvite returned error: %v", err)
|
||
|
|
}
|
||
|
|
if _, err := auth.AcceptInvite(reset.Token, "", "newpassword123"); err != nil {
|
||
|
|
t.Fatalf("AcceptInvite reset returned error: %v", err)
|
||
|
|
}
|
||
|
|
if _, _, err := auth.Login("friend@example.test", "newpassword123"); err != nil {
|
||
|
|
t.Fatalf("Login with reset password returned error: %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func newTestAuthService(t *testing.T) *AuthService {
|
||
|
|
t.Helper()
|
||
|
|
root := t.TempDir()
|
||
|
|
upload, err := NewUploadService(1024*1024, filepath.Join(root, "data"), "http://example.test", slog.Default())
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("NewUploadService returned error: %v", err)
|
||
|
|
}
|
||
|
|
t.Cleanup(func() {
|
||
|
|
if err := upload.Close(); err != nil {
|
||
|
|
t.Fatalf("Close returned error: %v", err)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
auth, err := NewAuthService(upload.DB(), "http://example.test")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("NewAuthService returned error: %v", err)
|
||
|
|
}
|
||
|
|
return auth
|
||
|
|
}
|