Ship SCUM user SQL management page

This commit is contained in:
npc0-hue
2026-08-24 16:43:00 +08:00
parent 3005e0510c
commit d4e3f68032
19 changed files with 351 additions and 115 deletions
+2 -2
View File
@@ -119,7 +119,7 @@ func isRemoteAdapterCapability(capability string) bool {
domain.JobCapabilityRemoteRsyncRead, domain.JobCapabilityRemoteRsyncWrite,
domain.JobCapabilityRemoteRunFilesRead, domain.JobCapabilityRemoteRunFilesWrite,
domain.JobCapabilityRemoteRunProcessStart, domain.JobCapabilityRemoteRunProcessStop,
domain.JobCapabilityRemoteRunDBMySQLQuery, domain.JobCapabilityRemoteRunDBSQLiteQuery,
domain.JobCapabilityRemoteRunDBMySQLQuery, domain.JobCapabilityRemoteRunDBMySQLExecute, domain.JobCapabilityRemoteRunDBSQLiteQuery, domain.JobCapabilityRemoteRunDBSQLiteExecute,
domain.JobCapabilityRemoteRunLogsTransfer, domain.JobCapabilityRemoteRunRCONCommand:
return true
default:
@@ -156,7 +156,7 @@ func remoteAdapterKindForCapability(capability string) domain.RemoteAdapterKind
return domain.RemoteAdapterRunFile
case domain.JobCapabilityRemoteRunProcessStart, domain.JobCapabilityRemoteRunProcessStop:
return domain.RemoteAdapterRunProcess
case domain.JobCapabilityRemoteRunDBMySQLQuery, domain.JobCapabilityRemoteRunDBSQLiteQuery:
case domain.JobCapabilityRemoteRunDBMySQLQuery, domain.JobCapabilityRemoteRunDBMySQLExecute, domain.JobCapabilityRemoteRunDBSQLiteQuery, domain.JobCapabilityRemoteRunDBSQLiteExecute:
return domain.RemoteAdapterDatabase
case domain.JobCapabilityRemoteRunRCONCommand:
return domain.RemoteAdapterRCON
+21 -20
View File
@@ -1624,36 +1624,36 @@ func TestFindBridgeQueryTemplateRequiresPagePermissionAndRemoteAction(t *testing
}
}
func TestCoreServiceRejectsArbitrarySQLBridgeInputBeforeJob(t *testing.T) {
func TestCoreServiceDispatchesSQLiteExecuteSQLText(t *testing.T) {
svc, plugin, _, session, instance := createSQLiteQueryBridgeFixture(t)
result, err := svc.ExecutePluginBridgeAction(session, domain.PluginBridgeExecuteRequest{
RequestID: "query-template-sql-rejected-1",
RequestID: "sqlite-execute-1",
PluginID: plugin.ID,
RouteKey: "remote",
ServerInstanceID: instance.ID,
Action: domain.PluginBridgeActionRemoteAccessRequest,
Payload: map[string]string{
"capability": domain.JobCapabilityRemoteRunDBSQLiteQuery,
"declarationKey": "scum-db-read",
"targetKey": "scum-db.player-lookup",
"idempotencyKey": "query-template-sql-rejected-1",
"input.templateKey": "players.by-id",
"input.sqlText": "SELECT * FROM users",
"capability": domain.JobCapabilityRemoteRunDBSQLiteExecute,
"declarationKey": "scum-db-read",
"targetKey": "scum-db.player-lookup",
"idempotencyKey": "sqlite-execute-1",
"input.mode": "execute",
"input.sqlText": "UPDATE prisoner SET stamina = 855 WHERE id = 'steam-123';",
},
})
if err != nil {
t.Fatalf("execute arbitrary SQL bridge input: %v", err)
t.Fatalf("execute sqlite SQL bridge input: %v", err)
}
if result.Status != "error" || result.Error == nil || !strings.Contains(strings.ToLower(result.Error.Message), "unsafe") {
t.Fatalf("expected arbitrary SQL input rejection, got %+v", result)
if result.Status != "queued" || result.Result["jobId"] == "" {
t.Fatalf("expected queued sqlite execute job, got %+v", result)
}
jobs, listErr := svc.ListJobs(domain.JobFilter{ServerInstanceID: instance.ID})
if listErr != nil {
t.Fatalf("list jobs after arbitrary SQL rejection: %v", listErr)
job, getErr := svc.store.Jobs().Get(result.Result["jobId"])
if getErr != nil {
t.Fatalf("get sqlite execute job: %v", getErr)
}
if len(jobs) != 0 {
t.Fatalf("arbitrary SQL rejection created jobs: %+v", jobs)
if job.Capability != domain.JobCapabilityRemoteRunDBSQLiteExecute || job.ExecutionInput.Inputs["sqlText"] == "" || job.ExecutionInput.Inputs["mode"] != "execute" {
t.Fatalf("expected sqlite execute inputs, got %#v", job)
}
}
@@ -1898,7 +1898,8 @@ func createSQLiteQueryBridgeFixture(t *testing.T) (*CoreService, domain.GamePlug
svc := newTestCoreService()
plugin, endpoint := createPluginAndRunEndpoint(t, svc)
capability := domain.JobCapabilityRemoteRunDBSQLiteQuery
plugin.RequiredRunCapabilities = append(plugin.RequiredRunCapabilities, capability)
executeCapability := domain.JobCapabilityRemoteRunDBSQLiteExecute
plugin.RequiredRunCapabilities = append(plugin.RequiredRunCapabilities, capability, executeCapability)
plugin.DeclaredPermissions = append(plugin.DeclaredPermissions, "server.remote.access")
plugin.Permissions.RemoteAccess = true
plugin.BridgeActions = append(plugin.BridgeActions, string(domain.PluginBridgeActionRemoteAccessRequest))
@@ -1911,14 +1912,14 @@ func createSQLiteQueryBridgeFixture(t *testing.T) (*CoreService, domain.GamePlug
})
plugin.RemoteAccess = domain.GamePluginRemoteAccess{
Methods: []string{"run"},
RunCapabilities: []string{capability},
RunCapabilities: []string{capability, executeCapability},
DatabaseEngines: []string{"sqlite"},
}
plugin.RuntimeProfiles.TransportProfiles = append(plugin.RuntimeProfiles.TransportProfiles, domain.RuntimeTransportProfile{
Key: "scum-db-read",
Kind: "sqlite",
TargetKey: "scum-db.player-lookup",
Capabilities: []string{capability},
Capabilities: []string{capability, executeCapability},
})
plugin.GameClientBridge = domain.GameClientBridgeManifest{
QueryTemplates: []domain.GameClientBridgeQueryTemplateDeclaration{
@@ -1944,7 +1945,7 @@ func createSQLiteQueryBridgeFixture(t *testing.T) (*CoreService, domain.GamePlug
if err := svc.store.GamePlugins().Update(plugin); err != nil {
t.Fatalf("update sqlite query plugin fixture: %v", err)
}
endpoint.Capabilities = append(endpoint.Capabilities, capability)
endpoint.Capabilities = append(endpoint.Capabilities, capability, executeCapability)
if err := svc.store.RunEndpoints().Update(endpoint); err != nil {
t.Fatalf("update sqlite query endpoint fixture: %v", err)
}