fix: complete platform admin audit remediation

This commit is contained in:
2026-07-27 11:00:21 +08:00
parent a545559fa8
commit 48fc80b003
6 changed files with 268 additions and 90 deletions

View File

@@ -17,6 +17,7 @@ type route struct {
type contract struct {
Domain string `json:"domain"`
Name string `json:"name"`
Path string `json:"path"`
PageKind string `json:"pageKind"`
Mode string `json:"mode"`
}
@@ -36,7 +37,7 @@ func main() {
expected := platform.ExpectedResources()
contracts := make([]contract, 0, len(expected))
for _, item := range expected {
contracts = append(contracts, contract{Domain: item.Domain, Name: item.Name, PageKind: item.PageKind, Mode: string(item.Mode)})
contracts = append(contracts, contract{Domain: item.Domain, Name: item.Name, Path: item.Path, PageKind: item.PageKind, Mode: string(item.Mode)})
}
if err := json.NewEncoder(os.Stdout).Encode(manifest{Resources: contracts, Routes: routes}); err != nil {
panic(err)

View File

@@ -19,6 +19,7 @@ const (
type ResourceContract struct {
Domain string
Name string
Path string
PageKind string
Mode ResourceMode
}
@@ -69,51 +70,42 @@ func filterFields(values map[string]any, allowedFields []string) gin.H {
// ExpectedResources returns the complete platform-admin resource catalogue.
func ExpectedResources() []ResourceContract {
return []ResourceContract{
{Domain: "gas", Name: "gas_basic", Mode: Writable, PageKind: "list"},
{Domain: "gas", Name: "gas_account", Mode: Writable, PageKind: "list"},
{Domain: "delivery", Name: "delivery_basic", Mode: Writable, PageKind: "list"},
{Domain: "delivery", Name: "delivery_account", Mode: Writable, PageKind: "list"},
{Domain: "staff", Name: "staff_account", Mode: Writable, PageKind: "list"},
{Domain: "staff", Name: "staff_credential", Mode: Writable, PageKind: "list"},
{Domain: "user", Name: "user_account", Mode: Writable, PageKind: "list"},
{Domain: "user", Name: "user_address", Mode: Writable, PageKind: "list"},
{Domain: "user", Name: "user_service_relation", Mode: Writable, PageKind: "list"},
{Domain: "device", Name: "dev_smart_cylinder_valve", Mode: Writable, PageKind: "list"},
{Domain: "device", Name: "dev_device_binding", Mode: Writable, PageKind: "list"},
{Domain: "safety", Name: "saf_rule", Mode: Writable, PageKind: "list"},
{Domain: "safety", Name: "saf_event", Mode: Writable, PageKind: "list"},
{Domain: "safety", Name: "saf_inspection", Mode: Writable, PageKind: "list"},
{Domain: "ec", Name: "ec_category", Mode: Writable, PageKind: "list"},
{Domain: "ec", Name: "ec_product", Mode: Writable, PageKind: "list"},
{Domain: "ec", Name: "ec_product_attribute", Mode: Writable, PageKind: "list"},
{Domain: "ec", Name: "ec_product_image", Mode: Writable, PageKind: "list"},
{Domain: "ec", Name: "ec_cart", Mode: Writable, PageKind: "list"},
{Domain: "ec", Name: "ec_order", Mode: Writable, PageKind: "list"},
{Domain: "ec", Name: "ec_order_item", Mode: Writable, PageKind: "list"},
{Domain: "ec", Name: "ec_review", Mode: Writable, PageKind: "list"},
{Domain: "delivery", Name: "delivery_task", Mode: Writable, PageKind: "list"},
{Domain: "delivery", Name: "delivery_track", Mode: Writable, PageKind: "list"},
{Domain: "delivery", Name: "delivery_track_point", Mode: Writable, PageKind: "list"},
{Domain: "finance", Name: "fin_payment", Mode: Writable, PageKind: "list"},
{Domain: "finance", Name: "fin_settlement", Mode: Writable, PageKind: "list"},
{Domain: "finance", Name: "fin_reconciliation", Mode: Writable, PageKind: "list"},
{Domain: "content", Name: "cnt_content", Mode: Writable, PageKind: "list"},
{Domain: "notification", Name: "ntf_template", Mode: Writable, PageKind: "list"},
{Domain: "customer_service", Name: "cs_ticket", Mode: Writable, PageKind: "list"},
{Domain: "platform", Name: "platfrom_account", Mode: Writable, PageKind: "list"},
{Domain: "platform", Name: "platform_role", Mode: Writable, PageKind: "list"},
{Domain: "platform", Name: "platform_menu", Mode: Writable, PageKind: "tree"},
{Domain: "device", Name: "dev_telemetry", Mode: ReadOnly, PageKind: "list"},
{Domain: "wallet", Name: "wallet", Mode: ReadOnly, PageKind: "list"},
{Domain: "wallet", Name: "wallet_ledger", Mode: ReadOnly, PageKind: "list"},
{Domain: "wallet", Name: "wallet_recharge", Mode: ReadOnly, PageKind: "list"},
{Domain: "wallet", Name: "wallet_withdrawal", Mode: ReadOnly, PageKind: "list"},
{Domain: "report", Name: "report", Mode: ReadOnly, PageKind: "list"},
{Domain: "report", Name: "report_item", Mode: ReadOnly, PageKind: "list"},
{Domain: "report", Name: "report_metric_snapshot", Mode: ReadOnly, PageKind: "list"},
{Domain: "audit", Name: "aud_operation_log", Mode: ReadOnly, PageKind: "list"},
{Domain: "audit", Name: "aud_export_log", Mode: ReadOnly, PageKind: "list"},
{Domain: "audit", Name: "aud_approval", Mode: ReadOnly, PageKind: "list"},
{Domain: "safety", Name: "saf_event_disposal", Mode: AppendOnly, PageKind: "list"},
resourceContract("gas", "gas_basic", Writable, "list"), resourceContract("gas", "gas_account", Writable, "list"),
resourceContract("delivery", "delivery_basic", Writable, "list"), resourceContract("delivery", "delivery_account", Writable, "list"),
resourceContract("staff", "staff_account", Writable, "list"), resourceContract("staff", "staff_credential", Writable, "list"),
resourceContract("user", "user_account", Writable, "list"), resourceContract("user", "user_address", Writable, "list"), resourceContract("user", "user_service_relation", Writable, "list"),
resourceContract("device", "dev_smart_cylinder_valve", Writable, "list"), resourceContract("device", "dev_device_binding", Writable, "list"), resourceContract("device", "dev_telemetry", ReadOnly, "list"),
resourceContract("safety", "saf_rule", Writable, "list"), resourceContract("safety", "saf_event", Writable, "list"), resourceContract("safety", "saf_inspection", Writable, "list"), resourceContract("safety", "saf_event_disposal", AppendOnly, "list"),
resourceContract("ec", "ec_category", Writable, "list"), resourceContract("ec", "ec_product", Writable, "list"), resourceContract("ec", "ec_product_attribute", Writable, "list"), resourceContract("ec", "ec_product_image", Writable, "list"), resourceContract("ec", "ec_cart", Writable, "list"), resourceContract("ec", "ec_order", Writable, "list"), resourceContract("ec", "ec_order_item", Writable, "list"), resourceContract("ec", "ec_review", Writable, "list"),
resourceContract("delivery", "delivery_task", Writable, "list"), resourceContract("delivery", "delivery_track", Writable, "list"), resourceContract("delivery", "delivery_track_point", Writable, "list"),
resourceContract("finance", "fin_payment", Writable, "list"), resourceContract("finance", "fin_settlement", Writable, "list"), resourceContract("finance", "fin_reconciliation", Writable, "list"),
resourceContract("content", "cnt_content", Writable, "list"), resourceContract("notification", "ntf_template", Writable, "list"), resourceContract("customer_service", "cs_ticket", Writable, "list"),
resourceContract("platform", "platfrom_account", Writable, "list"), resourceContract("platform", "platform_role", Writable, "list"), resourceContract("platform", "platform_menu", Writable, "tree"),
resourceContract("wallet", "wallet", ReadOnly, "list"), resourceContract("wallet", "wallet_ledger", ReadOnly, "list"), resourceContract("wallet", "wallet_recharge", ReadOnly, "list"), resourceContract("wallet", "wallet_withdrawal", ReadOnly, "list"),
resourceContract("report", "report", ReadOnly, "list"), resourceContract("report", "report_item", ReadOnly, "list"), resourceContract("report", "report_metric_snapshot", ReadOnly, "list"),
resourceContract("audit", "aud_operation_log", ReadOnly, "list"), resourceContract("audit", "aud_export_log", ReadOnly, "list"), resourceContract("audit", "aud_approval", ReadOnly, "list"),
}
}
func resourceContract(domain, name string, mode ResourceMode, pageKind string) ResourceContract {
return ResourceContract{Domain: domain, Name: name, Path: resourcePath(domain, name), Mode: mode, PageKind: pageKind}
}
func resourcePath(domain, name string) string {
switch name {
case "staff_account":
return "/staff/account"
case "staff_credential":
return "/staff/credential"
case "user_account":
return "/user/account"
case "user_address":
return "/user/address"
case "user_service_relation":
return "/user/service_relation"
case "saf_event_disposal":
return "/safety/saf_event/:identity/disposals"
default:
return "/" + domain + "/" + name
}
}

View File

@@ -4,9 +4,36 @@ import (
"net/http"
"testing"
"git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform"
"github.com/gin-gonic/gin"
)
func TestEveryContractHasRegisteredRoute(t *testing.T) {
engine := gin.New()
RegisterPlatform("heqi", engine)
routes := make(map[string]map[string]bool)
for _, route := range engine.Routes() {
if routes[route.Path] == nil {
routes[route.Path] = make(map[string]bool)
}
routes[route.Path][route.Method] = true
}
for _, contract := range platform.ExpectedResources() {
path := "/heqi/platform/v1" + contract.Path
switch contract.Mode {
case platform.ReadOnly:
assertRouteMethods(t, routes, path, http.MethodGet)
assertRouteMethods(t, routes, path+"/:identity", http.MethodGet)
case platform.AppendOnly:
assertRouteMethods(t, routes, path, http.MethodPost)
default:
assertRouteMethods(t, routes, path, http.MethodGet, http.MethodPost)
assertRouteMethods(t, routes, path+"/:identity", http.MethodGet, http.MethodPut, http.MethodDelete)
assertRouteMethods(t, routes, path+"/:identity/status", http.MethodPatch)
}
}
}
func TestPlatformGasRouteUsesGasBasic(t *testing.T) {
engine := gin.New()
RegisterPlatform("heqi", engine)