snapshot of https://github.com/casbin/casbin.git (master, 2026-09-15; blobs > 131072B omitted)
This commit is contained in:
137
transaction_conflict.go
Normal file
137
transaction_conflict.go
Normal file
@@ -0,0 +1,137 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package casbin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/casbin/v3/model"
|
||||
"github.com/casbin/casbin/v3/persist"
|
||||
)
|
||||
|
||||
// ConflictError represents a transaction conflict error.
|
||||
type ConflictError struct {
|
||||
Operation persist.PolicyOperation
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e *ConflictError) Error() string {
|
||||
return fmt.Sprintf("transaction conflict: %s for operation %v", e.Reason, e.Operation)
|
||||
}
|
||||
|
||||
// ConflictDetector detects conflicts between transaction operations and current model state.
|
||||
type ConflictDetector struct {
|
||||
baseModel model.Model // Model snapshot when transaction started
|
||||
currentModel model.Model // Current model state
|
||||
operations []persist.PolicyOperation // Operations to be applied
|
||||
}
|
||||
|
||||
// NewConflictDetector creates a new conflict detector instance.
|
||||
func NewConflictDetector(baseModel, currentModel model.Model, operations []persist.PolicyOperation) *ConflictDetector {
|
||||
return &ConflictDetector{
|
||||
baseModel: baseModel,
|
||||
currentModel: currentModel,
|
||||
operations: operations,
|
||||
}
|
||||
}
|
||||
|
||||
// DetectConflicts checks for conflicts between the transaction operations and current model state.
|
||||
// Returns nil if no conflicts are found, otherwise returns a ConflictError describing the conflict.
|
||||
func (cd *ConflictDetector) DetectConflicts() error {
|
||||
for _, op := range cd.operations {
|
||||
var err error
|
||||
switch op.Type {
|
||||
case persist.OperationAdd:
|
||||
// Add operations never conflict
|
||||
continue
|
||||
|
||||
case persist.OperationRemove:
|
||||
err = cd.detectRemoveConflict(op)
|
||||
|
||||
case persist.OperationUpdate:
|
||||
err = cd.detectUpdateConflict(op)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// detectRemoveConflict checks for conflicts in remove operations.
|
||||
func (cd *ConflictDetector) detectRemoveConflict(op persist.PolicyOperation) error {
|
||||
for _, rule := range op.Rules {
|
||||
// Check if policy existed in base model
|
||||
baseHasPolicy, err := cd.baseModel.HasPolicy(op.Section, op.PolicyType, rule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !baseHasPolicy {
|
||||
continue // Policy didn't exist when transaction started
|
||||
}
|
||||
|
||||
// Check if policy still exists in current model
|
||||
currentHasPolicy, err := cd.currentModel.HasPolicy(op.Section, op.PolicyType, rule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !currentHasPolicy {
|
||||
return &ConflictError{
|
||||
Operation: op,
|
||||
Reason: "policy has been removed by another transaction",
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// detectUpdateConflict checks for conflicts in update operations.
|
||||
func (cd *ConflictDetector) detectUpdateConflict(op persist.PolicyOperation) error {
|
||||
for i, oldRule := range op.OldRules {
|
||||
if i >= len(op.Rules) {
|
||||
break
|
||||
}
|
||||
newRule := op.Rules[i]
|
||||
|
||||
// Check if old policy still exists
|
||||
oldExists, err := cd.currentModel.HasPolicy(op.Section, op.PolicyType, oldRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !oldExists {
|
||||
return &ConflictError{
|
||||
Operation: op,
|
||||
Reason: "policy to be updated no longer exists",
|
||||
}
|
||||
}
|
||||
|
||||
// Check if new policy already exists
|
||||
newExists, err := cd.currentModel.HasPolicy(op.Section, op.PolicyType, newRule)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if newExists {
|
||||
return &ConflictError{
|
||||
Operation: op,
|
||||
Reason: "target policy already exists",
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user