98 lines
3.4 KiB
Go
98 lines
3.4 KiB
Go
package handler
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/go-admin-team/go-admin-core/sdk/config"
|
|
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func TestAuthenticatorAllowsPasswordOnlyInProduction(t *testing.T) {
|
|
db := loginTestDB(t)
|
|
seedLoginUser(t, db, "operator", "correct-password")
|
|
|
|
originalMode := config.ApplicationConfig.Mode
|
|
config.ApplicationConfig.Mode = "prod"
|
|
t.Cleanup(func() { config.ApplicationConfig.Mode = originalMode })
|
|
|
|
identity, err := authenticateLoginRequest(t, db, `{"username":"operator","password":"correct-password"}`)
|
|
if err != nil {
|
|
t.Fatalf("authenticate production password-only login: %v", err)
|
|
}
|
|
claims, ok := identity.(map[string]interface{})
|
|
if !ok || claims["user"] == nil || claims["role"] == nil {
|
|
t.Fatalf("unexpected authenticated identity: %#v", identity)
|
|
}
|
|
}
|
|
|
|
func TestAuthenticatorRejectsMissingAndIncorrectPassword(t *testing.T) {
|
|
db := loginTestDB(t)
|
|
seedLoginUser(t, db, "operator", "correct-password")
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
payload string
|
|
want error
|
|
}{
|
|
{name: "missing password", payload: `{"username":"operator"}`, want: jwt.ErrMissingLoginValues},
|
|
{name: "wrong password", payload: `{"username":"operator","password":"wrong-password"}`, want: jwt.ErrFailedAuthentication},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
_, err := authenticateLoginRequest(t, db, test.payload)
|
|
if !errors.Is(err, test.want) {
|
|
t.Fatalf("authentication error = %v, want %v", err, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func authenticateLoginRequest(t *testing.T, db *gorm.DB, payload string) (interface{}, error) {
|
|
t.Helper()
|
|
gin.SetMode(gin.TestMode)
|
|
recorder := httptest.NewRecorder()
|
|
context, _ := gin.CreateTestContext(recorder)
|
|
request := httptest.NewRequest("POST", "/api/v1/login", bytes.NewBufferString(payload))
|
|
request.Header.Set("Content-Type", "application/json")
|
|
context.Request = request
|
|
context.Set("db", db)
|
|
return Authenticator(context)
|
|
}
|
|
|
|
func loginTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatalf("open login test database: %v", err)
|
|
}
|
|
for _, statement := range []string{
|
|
`CREATE TABLE sys_role (role_id INTEGER PRIMARY KEY, role_name TEXT NOT NULL, status TEXT NOT NULL, role_key TEXT NOT NULL, data_scope TEXT NOT NULL, deleted_at DATETIME NULL)`,
|
|
`CREATE TABLE sys_user (user_id INTEGER PRIMARY KEY, username TEXT NOT NULL, password TEXT NOT NULL, role_id INTEGER NOT NULL, status TEXT NOT NULL, deleted_at DATETIME NULL)`,
|
|
} {
|
|
if err := db.Exec(statement).Error; err != nil {
|
|
t.Fatalf("create login test schema: %v", err)
|
|
}
|
|
}
|
|
return db
|
|
}
|
|
|
|
func seedLoginUser(t *testing.T, db *gorm.DB, username, password string) {
|
|
t.Helper()
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
t.Fatalf("hash test password: %v", err)
|
|
}
|
|
if err := db.Exec(`INSERT INTO sys_role (role_id, role_name, status, role_key, data_scope) VALUES (1, 'operator', '2', 'chorus_operator', 'all')`).Error; err != nil {
|
|
t.Fatalf("seed login role: %v", err)
|
|
}
|
|
if err := db.Exec(`INSERT INTO sys_user (user_id, username, password, role_id, status) VALUES (1, ?, ?, 1, '2')`, username, string(hash)).Error; err != nil {
|
|
t.Fatalf("seed login user: %v", err)
|
|
}
|
|
}
|