feat: publish sanitized custom.97 source
This commit is contained in:
1022 files changed
+69903
-19207
No files matched your search
@@ -0,0 +1,202 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/QuantumNous/new-api/common"
|
||||
"github.com/QuantumNous/new-api/middleware"
|
||||
"github.com/QuantumNous/new-api/service"
|
||||
"github.com/QuantumNous/new-api/service/authz"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// accessTokenExemptRoutes are /api routes that never reach dashboard
|
||||
// authentication, so a personal access token has no rule to satisfy there.
|
||||
var accessTokenExemptRoutes = []string{
|
||||
// Anonymous pages and setup.
|
||||
"GET /api/setup",
|
||||
"POST /api/setup",
|
||||
"GET /api/status",
|
||||
"GET /api/uptime/status",
|
||||
"GET /api/notice",
|
||||
"GET /api/user-agreement",
|
||||
"GET /api/privacy-policy",
|
||||
"GET /api/about",
|
||||
"GET /api/home_page_content",
|
||||
"GET /api/ratio_config",
|
||||
"GET /api/user/groups",
|
||||
|
||||
// Anonymous account entry points.
|
||||
"GET /api/verification",
|
||||
"GET /api/reset_password",
|
||||
"POST /api/user/reset",
|
||||
"POST /api/user/register",
|
||||
"GET /api/user/login/encryption-key",
|
||||
"POST /api/user/login",
|
||||
"POST /api/user/login/2fa",
|
||||
"POST /api/user/login/verify",
|
||||
"POST /api/user/login/passkey/begin",
|
||||
"POST /api/user/login/passkey/finish",
|
||||
"POST /api/user/passkey/login/begin",
|
||||
"POST /api/user/passkey/login/finish",
|
||||
"GET /api/oauth/wechat",
|
||||
"GET /api/oauth/telegram/login",
|
||||
"GET /api/oauth/telegram/bind/:flow_token",
|
||||
|
||||
// Browser session cookie endpoints.
|
||||
"POST /api/user/auth/refresh",
|
||||
"POST /api/user/auth/logout",
|
||||
|
||||
// Payment callbacks.
|
||||
"POST /api/stripe/webhook",
|
||||
"POST /api/creem/webhook",
|
||||
"POST /api/waffo/webhook",
|
||||
"POST /api/waffo-pancake/webhook/:env",
|
||||
"GET /api/user/epay/notify",
|
||||
"POST /api/user/epay/notify",
|
||||
"GET /api/subscription/epay/notify",
|
||||
"POST /api/subscription/epay/notify",
|
||||
"GET /api/subscription/epay/return",
|
||||
"POST /api/subscription/epay/return",
|
||||
|
||||
// API key authentication (TokenAuthReadOnly).
|
||||
"GET /api/usage/token/",
|
||||
"GET /api/log/token",
|
||||
}
|
||||
|
||||
// accessTokenHelperRoutes are declared through handlePermissionRoute outside
|
||||
// channelPermissionRoutes.
|
||||
var accessTokenHelperRoutes = map[string]authz.Permission{
|
||||
"GET /api/task_plugin_options": authz.TaskPluginBind,
|
||||
"GET /api/audit": authz.AuditRead,
|
||||
}
|
||||
|
||||
func newAccessTokenScopeTestEngine(t *testing.T) *gin.Engine {
|
||||
t.Helper()
|
||||
gin.SetMode(gin.TestMode)
|
||||
engine := gin.New()
|
||||
// A permission route that is also in the static table panics here.
|
||||
require.NotPanics(t, func() {
|
||||
SetApiRouter(engine)
|
||||
SetDashboardRouter(engine)
|
||||
SetRelayRouter(engine)
|
||||
SetTaskPluginProtocolRouter(engine)
|
||||
SetVideoRouter(engine)
|
||||
SetTaskRouter(engine)
|
||||
})
|
||||
return engine
|
||||
}
|
||||
|
||||
func TestAccessTokenRouteRulesCoverEveryDashboardRoute(t *testing.T) {
|
||||
engine := newAccessTokenScopeTestEngine(t)
|
||||
exempt := map[string]bool{}
|
||||
for _, key := range accessTokenExemptRoutes {
|
||||
exempt[key] = true
|
||||
}
|
||||
registered := map[string]bool{}
|
||||
panel := map[string]bool{}
|
||||
for _, route := range engine.Routes() {
|
||||
if !strings.HasPrefix(route.Path, "/api/") && !strings.HasPrefix(route.Path, "/pg/") {
|
||||
continue
|
||||
}
|
||||
key := route.Method + " " + route.Path
|
||||
registered[key] = true
|
||||
if !exempt[key] {
|
||||
panel[key] = true
|
||||
}
|
||||
}
|
||||
for _, key := range accessTokenExemptRoutes {
|
||||
assert.True(t, registered[key], "exempt route %s is no longer registered", key)
|
||||
}
|
||||
|
||||
declared := map[string]int{}
|
||||
for _, key := range middleware.AccessTokenRouteRuleKeys() {
|
||||
declared[key]++
|
||||
}
|
||||
for key := range panel {
|
||||
assert.Equal(t, 1, declared[key], "panel route %s must be declared exactly once", key)
|
||||
}
|
||||
for key := range declared {
|
||||
assert.True(t, panel[key], "declared route %s is not a registered panel route", key)
|
||||
}
|
||||
|
||||
permissionRoutes := map[string]authz.Permission{}
|
||||
for _, route := range channelPermissionRoutes {
|
||||
permissionRoutes[route.method+" "+joinPaths("/api/channel", route.path)] = route.permission
|
||||
}
|
||||
for key, permission := range accessTokenHelperRoutes {
|
||||
permissionRoutes[key] = permission
|
||||
}
|
||||
for key, permission := range permissionRoutes {
|
||||
rule, ok := middleware.AccessTokenRouteRule(key)
|
||||
require.True(t, ok, key)
|
||||
assert.Equal(t, "scope", rule.Kind(), key)
|
||||
assert.Equal(t, service.AccessTokenScopeOf(permission), rule.Scope(), key)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessTokenCatalogCoversEveryPermission(t *testing.T) {
|
||||
catalog := service.AccessTokenCatalog(1, common.RoleRootUser)
|
||||
scopes := map[string]bool{}
|
||||
resources := map[string]int{}
|
||||
for _, group := range catalog {
|
||||
for _, resource := range group.Resources {
|
||||
resources[resource.Resource]++
|
||||
for _, action := range resource.Actions {
|
||||
scopes[resource.Resource+":"+action.Action] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, permission := range authz.AllPermissions() {
|
||||
assert.True(t, scopes[service.AccessTokenScopeOf(permission)], "root catalog lacks %s", service.AccessTokenScopeOf(permission))
|
||||
}
|
||||
for resource, count := range resources {
|
||||
assert.Equal(t, 1, count, "resource %s appears in more than one catalog entry", resource)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessTokenVerificationScopesAreAllListed(t *testing.T) {
|
||||
files, err := filepath.Glob("../service/*.go")
|
||||
require.NoError(t, err)
|
||||
fileSet := token.NewFileSet()
|
||||
var scopes []string
|
||||
for _, file := range files {
|
||||
if strings.HasSuffix(file, "_test.go") {
|
||||
continue
|
||||
}
|
||||
parsed, err := parser.ParseFile(fileSet, file, nil, 0)
|
||||
require.NoError(t, err)
|
||||
for _, declaration := range parsed.Decls {
|
||||
general, ok := declaration.(*ast.GenDecl)
|
||||
if !ok || general.Tok != token.CONST {
|
||||
continue
|
||||
}
|
||||
for _, spec := range general.Specs {
|
||||
value := spec.(*ast.ValueSpec)
|
||||
for i, name := range value.Names {
|
||||
if !strings.HasPrefix(name.Name, "VerificationScope") || i >= len(value.Values) {
|
||||
continue
|
||||
}
|
||||
literal, ok := value.Values[i].(*ast.BasicLit)
|
||||
require.True(t, ok, name.Name)
|
||||
scope, err := strconv.Unquote(literal.Value)
|
||||
require.NoError(t, err)
|
||||
scopes = append(scopes, scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
require.NotEmpty(t, scopes)
|
||||
for _, scope := range scopes {
|
||||
_, listed := service.AccessTokenVerificationScope(scope)
|
||||
assert.True(t, listed, "verification scope %s has no access token rule", scope)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user