mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-30 06:16:37 +08:00
332 lines
9.8 KiB
Go
332 lines
9.8 KiB
Go
// Copyright 2025 linux.do
|
|
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package auth_source
|
|
|
|
import ("bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/Rain-kl/Wavelet/internal/apps/oauth"
|
|
"github.com/Rain-kl/Wavelet/internal/model"
|
|
"github.com/Rain-kl/Wavelet/internal/testhelper"
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/Rain-kl/Wavelet/internal/common/response")
|
|
|
|
func setupTestRouter(authUser *model.User) *gin.Engine {
|
|
r := testhelper.NewTestGinEngine()
|
|
adminGroup := r.Group("/api/v1/admin")
|
|
|
|
// Mock authentication middleware
|
|
adminGroup.Use(func(c *gin.Context) {
|
|
if authUser != nil {
|
|
oauth.SetToContext(c, oauth.UserObjKey, authUser)
|
|
}
|
|
c.Next()
|
|
})
|
|
|
|
adminGroup.GET("/auth-sources", ListAuthSources)
|
|
adminGroup.POST("/auth-sources", CreateAuthSource)
|
|
adminGroup.PUT("/auth-sources/:id", UpdateAuthSource)
|
|
adminGroup.PUT("/auth-sources/:id/toggle", ToggleAuthSource)
|
|
adminGroup.DELETE("/auth-sources/:id", DeleteAuthSource)
|
|
return r
|
|
}
|
|
|
|
func TestListAuthSources(t *testing.T) {
|
|
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
|
defer cleanup()
|
|
|
|
// Seed source
|
|
source := model.AuthSource{
|
|
ID: 1,
|
|
Name: "google",
|
|
Type: "oidc",
|
|
DisplayName: "Google Auth",
|
|
IsActive: true,
|
|
ClientID: "client_id_123",
|
|
ClientSecret: "client_secret_456",
|
|
OpenIDDiscoveryURL: "https://accounts.google.com",
|
|
}
|
|
dbConn.Create(&source)
|
|
|
|
adminUser := &model.User{ID: 1001, Username: "admin", IsAdmin: true}
|
|
router := setupTestRouter(adminUser)
|
|
|
|
req, _ := http.NewRequest("GET", "/api/v1/admin/auth-sources", nil)
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected 200 OK, got %d", w.Code)
|
|
}
|
|
|
|
var resp response.Any
|
|
_ = json.Unmarshal(w.Body.Bytes(), &resp)
|
|
|
|
dataBytes, _ := json.Marshal(resp.Data)
|
|
var sources []model.AuthSource
|
|
_ = json.Unmarshal(dataBytes, &sources)
|
|
|
|
if len(sources) != 1 {
|
|
t.Errorf("expected 1 auth source, got %d", len(sources))
|
|
}
|
|
if sources[0].Name != "google" {
|
|
t.Errorf("expected name 'google', got '%s'", sources[0].Name)
|
|
}
|
|
// Verify sanitize removed the secret
|
|
if sources[0].ClientSecret != "" {
|
|
t.Error("client secret should be sanitized")
|
|
}
|
|
if !sources[0].ClientSecretConfigured {
|
|
t.Error("client secret configured flag should be true")
|
|
}
|
|
}
|
|
|
|
func TestCreateAuthSource(t *testing.T) {
|
|
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
|
defer cleanup()
|
|
|
|
adminUser := &model.User{ID: 1001, Username: "admin", IsAdmin: true}
|
|
router := setupTestRouter(adminUser)
|
|
|
|
t.Run("create successfully", func(t *testing.T) {
|
|
reqPayload := AuthSourceRequest{
|
|
Name: "github",
|
|
Type: "oidc",
|
|
DisplayName: "GitHub OIDC",
|
|
IsActive: true,
|
|
ClientID: "client_id_gh",
|
|
ClientSecret: "client_secret_gh",
|
|
OpenIDDiscoveryURL: "https://github.com",
|
|
}
|
|
body, _ := json.Marshal(reqPayload)
|
|
req, _ := http.NewRequest("POST", "/api/v1/admin/auth-sources", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected 200 OK, got %d. Body: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
// Verify database
|
|
var src model.AuthSource
|
|
dbConn.Where("name = ?", "github").First(&src)
|
|
if src.ClientID != "client_id_gh" {
|
|
t.Errorf("expected client_id_gh, got '%s'", src.ClientID)
|
|
}
|
|
})
|
|
|
|
t.Run("create invalid validation failure", func(t *testing.T) {
|
|
reqPayload := AuthSourceRequest{
|
|
Name: "invalid name!",
|
|
Type: "oidc",
|
|
DisplayName: "Invalid",
|
|
IsActive: true,
|
|
ClientID: "client_id_val",
|
|
ClientSecret: "client_secret_val",
|
|
OpenIDDiscoveryURL: "https://discovery.url",
|
|
}
|
|
body, _ := json.Marshal(reqPayload)
|
|
req, _ := http.NewRequest("POST", "/api/v1/admin/auth-sources", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected 400 Bad Request, got %d", w.Code)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUpdateAuthSource(t *testing.T) {
|
|
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
|
defer cleanup()
|
|
|
|
// Seed source
|
|
source := model.AuthSource{
|
|
ID: 1,
|
|
Name: "microsoft",
|
|
Type: "oidc",
|
|
DisplayName: "Microsoft",
|
|
IsActive: true,
|
|
ClientID: "old_client_id",
|
|
ClientSecret: "old_secret",
|
|
OpenIDDiscoveryURL: "https://login.microsoftonline.com",
|
|
}
|
|
dbConn.Create(&source)
|
|
|
|
adminUser := &model.User{ID: 1001, Username: "admin", IsAdmin: true}
|
|
router := setupTestRouter(adminUser)
|
|
|
|
t.Run("update keep client secret", func(t *testing.T) {
|
|
reqPayload := AuthSourceRequest{
|
|
Name: "microsoft",
|
|
Type: "oidc",
|
|
DisplayName: "Microsoft Updated",
|
|
IsActive: true,
|
|
ClientID: "new_client_id",
|
|
ClientSecret: "", // empty implies keeping existing secret
|
|
OpenIDDiscoveryURL: "https://login.microsoftonline.com",
|
|
}
|
|
body, _ := json.Marshal(reqPayload)
|
|
req, _ := http.NewRequest("PUT", "/api/v1/admin/auth-sources/1", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200 OK, got %d. Body: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var src model.AuthSource
|
|
dbConn.First(&src, 1)
|
|
if src.DisplayName != "Microsoft Updated" {
|
|
t.Errorf("expected display name update, got '%s'", src.DisplayName)
|
|
}
|
|
if src.ClientSecret != "old_secret" {
|
|
t.Errorf("expected old secret to be preserved, got '%s'", src.ClientSecret)
|
|
}
|
|
})
|
|
|
|
t.Run("update new client secret", func(t *testing.T) {
|
|
reqPayload := AuthSourceRequest{
|
|
Name: "microsoft",
|
|
Type: "oidc",
|
|
DisplayName: "Microsoft Updated Again",
|
|
IsActive: true,
|
|
ClientID: "new_client_id",
|
|
ClientSecret: "brand_new_secret",
|
|
OpenIDDiscoveryURL: "https://login.microsoftonline.com",
|
|
}
|
|
body, _ := json.Marshal(reqPayload)
|
|
req, _ := http.NewRequest("PUT", "/api/v1/admin/auth-sources/microsoft", bytes.NewBuffer(body)) // Using Name instead of ID
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("expected 200 OK, got %d. Body: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var src model.AuthSource
|
|
dbConn.First(&src, 1)
|
|
if src.ClientSecret != "brand_new_secret" {
|
|
t.Errorf("expected secret update, got '%s'", src.ClientSecret)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestToggleAuthSource(t *testing.T) {
|
|
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
|
defer cleanup()
|
|
|
|
source := model.AuthSource{
|
|
ID: 1,
|
|
Name: "test_source",
|
|
Type: "oidc",
|
|
DisplayName: "Test Source",
|
|
IsActive: false,
|
|
ClientID: "",
|
|
ClientSecret: "",
|
|
OpenIDDiscoveryURL: "https://test.discovery.url",
|
|
}
|
|
dbConn.Create(&source)
|
|
|
|
adminUser := &model.User{ID: 1001, Username: "admin", IsAdmin: true}
|
|
router := setupTestRouter(adminUser)
|
|
|
|
t.Run("cannot activate without credentials", func(t *testing.T) {
|
|
payload := ToggleAuthSourceRequest{IsActive: true}
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("PUT", "/api/v1/admin/auth-sources/1/toggle", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected 400 Bad Request when activating without client_id/secret, got %d", w.Code)
|
|
}
|
|
})
|
|
|
|
t.Run("toggle success after setting credentials", func(t *testing.T) {
|
|
// Set credentials first
|
|
dbConn.Model(&model.AuthSource{}).Where("id = ?", 1).Updates(map[string]interface{}{
|
|
"client_id": "id",
|
|
"client_secret": "secret",
|
|
})
|
|
|
|
payload := ToggleAuthSourceRequest{IsActive: true}
|
|
body, _ := json.Marshal(payload)
|
|
req, _ := http.NewRequest("PUT", "/api/v1/admin/auth-sources/1/toggle", bytes.NewBuffer(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected 200 OK, got %d. Body: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var src model.AuthSource
|
|
dbConn.First(&src, 1)
|
|
if !src.IsActive {
|
|
t.Error("auth source should be activated")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestDeleteAuthSource(t *testing.T) {
|
|
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
|
defer cleanup()
|
|
|
|
source := model.AuthSource{
|
|
ID: 1,
|
|
Name: "delete_me",
|
|
Type: "oidc",
|
|
DisplayName: "Delete Me",
|
|
IsActive: true,
|
|
ClientID: "id",
|
|
ClientSecret: "secret",
|
|
OpenIDDiscoveryURL: "https://delete.me",
|
|
}
|
|
dbConn.Create(&source)
|
|
|
|
externalAccount := model.ExternalAccount{
|
|
ID: 10,
|
|
AuthSourceID: 1,
|
|
UserID: 50,
|
|
ExternalID: "ext_50",
|
|
}
|
|
dbConn.Create(&externalAccount)
|
|
|
|
adminUser := &model.User{ID: 1001, Username: "admin", IsAdmin: true}
|
|
router := setupTestRouter(adminUser)
|
|
|
|
req, _ := http.NewRequest("DELETE", "/api/v1/admin/auth-sources/1", nil)
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected 200 OK, got %d", w.Code)
|
|
}
|
|
|
|
// Verify AuthSource is deleted
|
|
var srcCount int64
|
|
dbConn.Model(&model.AuthSource{}).Where("id = ?", 1).Count(&srcCount)
|
|
if srcCount != 0 {
|
|
t.Error("AuthSource should be deleted from the database")
|
|
}
|
|
|
|
// Verify ExternalAccount bindings are also deleted
|
|
var extCount int64
|
|
dbConn.Model(&model.ExternalAccount{}).Where("auth_source_id = ?", 1).Count(&extCount)
|
|
if extCount != 0 {
|
|
t.Error("related ExternalAccount bindings should be deleted")
|
|
}
|
|
}
|