mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
- auth/session: 256-bit crypto-random tokens, SHA-256 hashing for storage - auth/password: bcrypt cost 12, strength validation (8-72 chars) - auth/ratelimit: sliding-window RateLimiter with lockout, thread-safe - db/models: User, Session, Invite, Role types - db/auth_queries: full user/session/invite CRUD with in-memory test coverage - api/middleware: AuthMiddleware (Bearer token), RequirePermission (bitfield), RateLimitMiddleware (X-Real-IP, Retry-After header) - api/auth_handler: POST register/login, POST logout, GET me - Generic errors — username existence never revealed - Rate limits: 3/min register, 5/min login, lockout after 10 failures - api/invite_handler: create/list/revoke behind MANAGE_INVITES permission - bluemonday sanitization on all user-supplied string fields Test coverage: auth 90.9%, db 84.4%, api 80.9%
107 lines
2.8 KiB
Go
107 lines
2.8 KiB
Go
package auth_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/owncord/server/auth"
|
|
)
|
|
|
|
func TestHashPassword_DiffersFromPlaintext(t *testing.T) {
|
|
hash, err := auth.HashPassword("mypassword")
|
|
if err != nil {
|
|
t.Fatalf("HashPassword() error = %v", err)
|
|
}
|
|
if hash == "mypassword" {
|
|
t.Error("HashPassword() hash equals plaintext")
|
|
}
|
|
}
|
|
|
|
func TestHashPassword_BcryptPrefix(t *testing.T) {
|
|
hash, err := auth.HashPassword("mypassword")
|
|
if err != nil {
|
|
t.Fatalf("HashPassword() error = %v", err)
|
|
}
|
|
if !strings.HasPrefix(hash, "$2") {
|
|
t.Errorf("HashPassword() = %q, want bcrypt prefix $2*", hash)
|
|
}
|
|
}
|
|
|
|
func TestCheckPassword_CorrectPassword(t *testing.T) {
|
|
hash, err := auth.HashPassword("correctpassword")
|
|
if err != nil {
|
|
t.Fatalf("HashPassword() error = %v", err)
|
|
}
|
|
if !auth.CheckPassword(hash, "correctpassword") {
|
|
t.Error("CheckPassword() returned false for correct password")
|
|
}
|
|
}
|
|
|
|
func TestCheckPassword_WrongPassword(t *testing.T) {
|
|
hash, err := auth.HashPassword("correctpassword")
|
|
if err != nil {
|
|
t.Fatalf("HashPassword() error = %v", err)
|
|
}
|
|
if auth.CheckPassword(hash, "wrongpassword") {
|
|
t.Error("CheckPassword() returned true for wrong password")
|
|
}
|
|
}
|
|
|
|
func TestCheckPassword_EmptyPassword(t *testing.T) {
|
|
hash, err := auth.HashPassword("somepassword")
|
|
if err != nil {
|
|
t.Fatalf("HashPassword() error = %v", err)
|
|
}
|
|
if auth.CheckPassword(hash, "") {
|
|
t.Error("CheckPassword() returned true for empty password")
|
|
}
|
|
}
|
|
|
|
func TestCheckPassword_EmptyHash(t *testing.T) {
|
|
if auth.CheckPassword("", "somepassword") {
|
|
t.Error("CheckPassword() returned true with empty hash")
|
|
}
|
|
}
|
|
|
|
func TestValidatePasswordStrength_Valid(t *testing.T) {
|
|
cases := []string{
|
|
"12345678", // exactly 8 chars
|
|
"abcdefghij", // 10 chars
|
|
strings.Repeat("a", 72), // exactly 72 chars (bcrypt max)
|
|
}
|
|
for _, pw := range cases {
|
|
if err := auth.ValidatePasswordStrength(pw); err != nil {
|
|
t.Errorf("ValidatePasswordStrength(%q) error = %v, want nil", pw, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidatePasswordStrength_TooShort(t *testing.T) {
|
|
cases := []string{
|
|
"", // empty
|
|
"1234567", // 7 chars
|
|
"abc", // 3 chars
|
|
}
|
|
for _, pw := range cases {
|
|
if err := auth.ValidatePasswordStrength(pw); err == nil {
|
|
t.Errorf("ValidatePasswordStrength(%q) error = nil, want error", pw)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidatePasswordStrength_TooLong(t *testing.T) {
|
|
pw := strings.Repeat("a", 73) // 73 chars — over bcrypt 72 byte limit
|
|
if err := auth.ValidatePasswordStrength(pw); err == nil {
|
|
t.Errorf("ValidatePasswordStrength(%q) error = nil, want error for >72 chars", pw)
|
|
}
|
|
}
|
|
|
|
func TestHashPassword_TwoCallsDifferentHashes(t *testing.T) {
|
|
// bcrypt includes a random salt
|
|
h1, _ := auth.HashPassword("password")
|
|
h2, _ := auth.HashPassword("password")
|
|
if h1 == h2 {
|
|
t.Error("HashPassword() produced identical hashes for the same password (salt missing?)")
|
|
}
|
|
}
|