fix platform workflow integrity and permissions
This commit is contained in:
@@ -37,7 +37,7 @@ func ListResource(ctx *gin.Context, model any) {
|
||||
page, size := PageSize(ctx)
|
||||
list := reflect.New(reflect.SliceOf(reflect.TypeOf(model).Elem()))
|
||||
var total int64
|
||||
query := ApplyKeywordFilter(ctx, impl.DBService.Model(model), model)
|
||||
query := ApplyKeywordFilter(ctx, ActiveRecords(impl.DBService.Model(model)), model)
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
@@ -74,6 +74,10 @@ func createResource(ctx *gin.Context, model any, allowedFields []string, relatio
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return
|
||||
}
|
||||
if err := ValidateResourceValues(model, values, true); err != nil {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return
|
||||
}
|
||||
encoded, err := json.Marshal(values)
|
||||
if err != nil {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
@@ -123,7 +127,7 @@ func maskCreatedSensitiveFields(value any) any {
|
||||
|
||||
func isCreatedResponseField(key string) bool {
|
||||
switch key {
|
||||
case "identity", "status", "version", "created_at", "updated_at":
|
||||
case "identity", "status", "created_at", "updated_at":
|
||||
return true
|
||||
default:
|
||||
return strings.HasSuffix(key, "_identity")
|
||||
@@ -226,7 +230,7 @@ func updateResource(ctx *gin.Context, model any, allowedFields []string, relatio
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return
|
||||
}
|
||||
if len(values) == 0 {
|
||||
if len(values) == 0 || ValidateResourceValues(model, values, false) != nil {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return
|
||||
}
|
||||
@@ -245,6 +249,74 @@ func PrepareResourceValues(ctx *gin.Context, model any, allowedFields []string,
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// ValidateResourceValues enforces invariants that database nullability and
|
||||
// frontend form metadata cannot express.
|
||||
func ValidateResourceValues(model any, values map[string]any, creating bool) error {
|
||||
positive := func(key string) bool {
|
||||
value, exists := numericValue(values[key])
|
||||
return !exists || value > 0
|
||||
}
|
||||
nonNegative := func(key string) bool {
|
||||
value, exists := numericValue(values[key])
|
||||
return !exists || value >= 0
|
||||
}
|
||||
nonEmpty := func(key string) bool {
|
||||
value, exists := values[key]
|
||||
if !exists {
|
||||
return !creating
|
||||
}
|
||||
text, ok := value.(string)
|
||||
return ok && strings.TrimSpace(text) != ""
|
||||
}
|
||||
switch model.(type) {
|
||||
case *models.EcProduct:
|
||||
if !nonEmpty("product_code") || !nonEmpty("name") || !nonNegative("price_amount") || !nonNegative("stock_quantity") {
|
||||
return errors.New("invalid commerce product")
|
||||
}
|
||||
case *models.EcCart:
|
||||
if !positive("quantity") {
|
||||
return errors.New("invalid cart quantity")
|
||||
}
|
||||
case *models.EcOrder:
|
||||
if !nonEmpty("order_no") || !nonNegative("total_amount") {
|
||||
return errors.New("invalid order")
|
||||
}
|
||||
case *models.EcOrderItem:
|
||||
if !nonEmpty("product_snapshot") || !positive("quantity") || !nonNegative("sale_amount") {
|
||||
return errors.New("invalid order item")
|
||||
}
|
||||
case *models.EcReview:
|
||||
score, exists := numericValue(values["score"])
|
||||
if (exists && (score < 1 || score > 5)) || !nonEmpty("content") {
|
||||
return errors.New("invalid review")
|
||||
}
|
||||
case *models.FinPayment:
|
||||
if !positive("amount") || !nonEmpty("channel") {
|
||||
return errors.New("invalid payment")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func numericValue(value any) (float64, bool) {
|
||||
switch number := value.(type) {
|
||||
case float64:
|
||||
return number, true
|
||||
case float32:
|
||||
return float64(number), true
|
||||
case int:
|
||||
return float64(number), true
|
||||
case int64:
|
||||
return float64(number), true
|
||||
case uint:
|
||||
return float64(number), true
|
||||
case uint64:
|
||||
return float64(number), true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func ResolveResourceRelations(input map[string]any, allowedFields []string, relations []ResourceRelation, requireRelations bool) (map[string]any, error) {
|
||||
values := FilterFields(input, allowedFields)
|
||||
for _, relation := range relations {
|
||||
@@ -286,7 +358,7 @@ func ResolveIdentityID(model any, identity string, required bool) (uint64, error
|
||||
return 0, nil
|
||||
}
|
||||
var related struct{ ID uint64 }
|
||||
if err := impl.DBService.Model(model).Select("id").Where("identity = ?", identity).First(&related).Error; err != nil {
|
||||
if err := impl.DBService.Model(model).Select("id").Where("identity = ? AND status <> ?", identity, StatusArchived).First(&related).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return related.ID, nil
|
||||
|
||||
Reference in New Issue
Block a user