113 lines
5.4 KiB
Go
113 lines
5.4 KiB
Go
package externaladmin
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"hyapp-admin-server/internal/appctx"
|
|
"hyapp-admin-server/internal/config"
|
|
"hyapp-admin-server/internal/integration/walletclient"
|
|
adminmiddleware "hyapp-admin-server/internal/middleware"
|
|
"hyapp-admin-server/internal/modules/appuser"
|
|
"hyapp-admin-server/internal/modules/hostorg"
|
|
"hyapp-admin-server/internal/modules/vipconfig"
|
|
walletv1 "hyapp.local/api/proto/wallet/v1"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func TestExternalBusinessRoutesKeepIndependentPermissionBoundaries(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
engine := gin.New()
|
|
engine.Use(func(c *gin.Context) {
|
|
// Each request gets exactly one product capability. Allowed reads reach an
|
|
// intentionally unconfigured handler and return 500; denied routes must stop
|
|
// at the permission middleware with 403 before any dependency is touched.
|
|
c.Set(adminmiddleware.ContextPermissions, []string{c.GetHeader("X-Test-Permission")})
|
|
c.Next()
|
|
})
|
|
registerBusinessWhitelist(engine.Group(""), &Handler{}, BusinessHandlers{
|
|
AppUser: appuser.New(nil, nil, nil, nil, nil, config.Config{}, nil),
|
|
HostOrg: hostorg.New(nil, nil, nil, nil, nil, nil),
|
|
})
|
|
|
|
tests := []struct {
|
|
name string
|
|
method string
|
|
path string
|
|
permission string
|
|
allowed bool
|
|
}{
|
|
{name: "BD Manager list accepts its capability", method: http.MethodGet, path: "/admin/managers", permission: "bd-manager:list", allowed: true},
|
|
{name: "BD Manager list rejects Super Admin capability", method: http.MethodGet, path: "/admin/managers", permission: "super-admin:list"},
|
|
{name: "Super Admin list accepts its capability", method: http.MethodGet, path: "/admin/bd-leaders", permission: "super-admin:list", allowed: true},
|
|
{name: "Super Admin list rejects BD Manager capability", method: http.MethodGet, path: "/admin/bd-leaders", permission: "bd-manager:list"},
|
|
{name: "user update can read support list", method: http.MethodGet, path: "/app/users", permission: "user:update", allowed: true},
|
|
{name: "user update cannot ban", method: http.MethodPost, path: "/app/users/9001/ban", permission: "user:update"},
|
|
{name: "user unban can read ban support list", method: http.MethodGet, path: "/app/users/bans", permission: "user:unban", allowed: true},
|
|
}
|
|
for _, testCase := range tests {
|
|
t.Run(testCase.name, func(t *testing.T) {
|
|
request := httptest.NewRequest(testCase.method, testCase.path, nil)
|
|
request.Header.Set("X-Test-Permission", testCase.permission)
|
|
recorder := httptest.NewRecorder()
|
|
engine.ServeHTTP(recorder, request)
|
|
if testCase.allowed && recorder.Code == http.StatusForbidden {
|
|
t.Fatalf("permission %s was rejected for %s %s: %s", testCase.permission, testCase.method, testCase.path, recorder.Body.String())
|
|
}
|
|
if !testCase.allowed && recorder.Code != http.StatusForbidden {
|
|
t.Fatalf("permission %s unexpectedly reached %s %s: status=%d body=%s", testCase.permission, testCase.method, testCase.path, recorder.Code, recorder.Body.String())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExternalVIPRoutesUseManagerCenterOwnerPolicySource(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
wallet := &externalVIPRouteWallet{}
|
|
engine := gin.New()
|
|
engine.Use(func(c *gin.Context) {
|
|
c.Request = c.Request.WithContext(appctx.WithContext(c.Request.Context(), "fami"))
|
|
c.Set(adminmiddleware.ContextPermissions, []string{"user-level:grant"})
|
|
c.Set(adminmiddleware.ContextRequestID, "external-vip-route-test")
|
|
c.Set(adminmiddleware.ContextUserID, uint(77))
|
|
c.Next()
|
|
})
|
|
registerBusinessWhitelist(engine.Group(""), &Handler{}, BusinessHandlers{VIPConfig: vipconfig.New(wallet, nil)})
|
|
|
|
vipRequest := httptest.NewRequest(http.MethodPost, "/admin/activity/vip-grants", strings.NewReader(`{"targetUserId":"9001","level":5,"reason":"manual"}`))
|
|
vipRequest.Header.Set("Content-Type", "application/json")
|
|
vipRecorder := httptest.NewRecorder()
|
|
engine.ServeHTTP(vipRecorder, vipRequest)
|
|
if vipRecorder.Code != http.StatusCreated || wallet.vipRequest == nil || wallet.vipRequest.GetGrantSource() != "manager_center" {
|
|
t.Fatalf("external VIP route source: status=%d request=%+v body=%s", vipRecorder.Code, wallet.vipRequest, vipRecorder.Body.String())
|
|
}
|
|
|
|
trialRequest := httptest.NewRequest(http.MethodPost, "/admin/activity/vip-trial-card-grants", strings.NewReader(`{"targetUserId":"9001","level":5,"durationMs":86400000,"resourceId":99,"reason":"manual"}`))
|
|
trialRequest.Header.Set("Content-Type", "application/json")
|
|
trialRecorder := httptest.NewRecorder()
|
|
engine.ServeHTTP(trialRecorder, trialRequest)
|
|
if trialRecorder.Code != http.StatusCreated || wallet.trialRequest == nil || wallet.trialRequest.GetGrantSource() != "manager_center" {
|
|
t.Fatalf("external trial route source: status=%d request=%+v body=%s", trialRecorder.Code, wallet.trialRequest, trialRecorder.Body.String())
|
|
}
|
|
}
|
|
|
|
type externalVIPRouteWallet struct {
|
|
walletclient.Client
|
|
vipRequest *walletv1.GrantVipRequest
|
|
trialRequest *walletv1.GrantVipTrialCardRequest
|
|
}
|
|
|
|
func (wallet *externalVIPRouteWallet) GrantVip(_ context.Context, request *walletv1.GrantVipRequest) (*walletv1.GrantVipResponse, error) {
|
|
wallet.vipRequest = request
|
|
return &walletv1.GrantVipResponse{TransactionId: "vip-route"}, nil
|
|
}
|
|
|
|
func (wallet *externalVIPRouteWallet) GrantVipTrialCard(_ context.Context, request *walletv1.GrantVipTrialCardRequest) (*walletv1.GrantVipTrialCardResponse, error) {
|
|
wallet.trialRequest = request
|
|
return &walletv1.GrantVipTrialCardResponse{TrialCard: &walletv1.VipTrialCard{TrialCardId: "trial-route"}}, nil
|
|
}
|