Merge pull request #91 from valueonag/feat/cost-control
fixed automation bug rbac set
This commit is contained in:
commit
a0a76b1337
4 changed files with 46 additions and 13 deletions
|
|
@ -42,8 +42,10 @@ class AutomationObjects:
|
|||
# Initialize database with proper configuration
|
||||
self._initializeDatabase()
|
||||
|
||||
# Initialize RBAC (dbApp = self.db since we use poweron_app)
|
||||
self.rbac = RbacClass(self.db, dbApp=self.db)
|
||||
# Initialize RBAC - AccessRules are in poweron_app, not poweron_automation!
|
||||
from modules.security.rootAccess import getRootDbAppConnector
|
||||
dbApp = getRootDbAppConnector()
|
||||
self.rbac = RbacClass(self.db, dbApp=dbApp)
|
||||
|
||||
# Update database context
|
||||
self.db.updateContext(self.userId)
|
||||
|
|
@ -100,7 +102,9 @@ class AutomationObjects:
|
|||
record = self.db.getRecordset(model, {"id": recordId})
|
||||
if record:
|
||||
return record[0].get("_createdBy") == self.userId
|
||||
return True
|
||||
else:
|
||||
return False # Record not found = no access
|
||||
return True # No recordId needed (e.g., for CREATE)
|
||||
return False
|
||||
|
||||
# =========================================================================
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class EventManager:
|
|||
"""
|
||||
queue = self._queues.get(context_id)
|
||||
if not queue:
|
||||
logger.warning(f"No queue found for workflow {context_id}, event not emitted")
|
||||
# DEBUG level: This is normal for background workflows without active SSE listener
|
||||
return
|
||||
|
||||
event = {
|
||||
|
|
|
|||
|
|
@ -102,21 +102,30 @@ class RbacClass:
|
|||
allRulesWithPriority = self._getRulesForRoleIds(roleIds, context, mandateId, featureInstanceId)
|
||||
|
||||
# Für jede Rolle die spezifischste Regel finden
|
||||
# Priority order: 1) Role priority (instance > mandate > global), 2) Item specificity (exact > prefix > generic)
|
||||
rolePermissions = {}
|
||||
for priority, rule in allRulesWithPriority:
|
||||
# Find most specific rule for this item
|
||||
if self._ruleMatchesItem(rule, item):
|
||||
roleId = rule.roleId
|
||||
# Speichere mit Priorität (höhere Priorität überschreibt)
|
||||
if roleId not in rolePermissions or priority > rolePermissions[roleId][0]:
|
||||
rolePermissions[roleId] = (priority, rule)
|
||||
# Calculate item specificity: 0=generic (null), 1=prefix match, 2=exact match
|
||||
itemSpecificity = self._getItemSpecificity(rule, item)
|
||||
|
||||
# Compare: first by role priority, then by item specificity
|
||||
if roleId not in rolePermissions:
|
||||
rolePermissions[roleId] = (priority, itemSpecificity, rule)
|
||||
else:
|
||||
existingPriority, existingSpecificity, _ = rolePermissions[roleId]
|
||||
# Higher role priority wins, or same priority but higher specificity
|
||||
if priority > existingPriority or (priority == existingPriority and itemSpecificity > existingSpecificity):
|
||||
rolePermissions[roleId] = (priority, itemSpecificity, rule)
|
||||
|
||||
# Find highest priority among matching rules
|
||||
highestPriority = max((p for p, _ in rolePermissions.values()), default=0)
|
||||
# Find highest priority among matching rules (role priority only, specificity already handled per role)
|
||||
highestPriority = max((p for p, _, _ in rolePermissions.values()), default=0)
|
||||
|
||||
# Combine permissions ONLY from rules with highest priority
|
||||
# This ensures instance-specific rules (Priority 3) override global rules (Priority 1)
|
||||
for roleId, (priority, rule) in rolePermissions.items():
|
||||
for roleId, (priority, specificity, rule) in rolePermissions.items():
|
||||
# Only use rules with highest priority
|
||||
if priority < highestPriority:
|
||||
continue
|
||||
|
|
@ -433,6 +442,23 @@ class RbacClass:
|
|||
|
||||
return False
|
||||
|
||||
def _getItemSpecificity(self, rule: AccessRule, item: str) -> int:
|
||||
"""
|
||||
Calculate the specificity of a rule's item match.
|
||||
|
||||
Returns:
|
||||
0 = generic rule (item=None)
|
||||
1 = prefix match
|
||||
2 = exact match
|
||||
"""
|
||||
if rule.item is None:
|
||||
return 0 # Generic rule - lowest specificity
|
||||
if rule.item == item:
|
||||
return 2 # Exact match - highest specificity
|
||||
if item and item.startswith(rule.item + "."):
|
||||
return 1 # Prefix match - medium specificity
|
||||
return 0 # No match (shouldn't happen if _ruleMatchesItem was true)
|
||||
|
||||
def findMostSpecificRule(self, rules: List[AccessRule], item: str) -> Optional[AccessRule]:
|
||||
"""
|
||||
Find the most specific rule for an item (longest matching prefix wins).
|
||||
|
|
|
|||
|
|
@ -306,6 +306,9 @@ def createAutomationEventHandler(automationId: str, eventUser):
|
|||
logger.error(f"Automation {automationId} has no creator user")
|
||||
return
|
||||
|
||||
# Get mandate context from automation definition
|
||||
automationMandateId = getattr(automation, "mandateId", None)
|
||||
|
||||
# Get creator user from database using services
|
||||
eventServices = getServices(eventUser, None)
|
||||
creatorUser = eventServices.interfaceDbApp.getUser(creatorUserId)
|
||||
|
|
@ -313,10 +316,10 @@ def createAutomationEventHandler(automationId: str, eventUser):
|
|||
logger.error(f"Creator user {creatorUserId} not found for automation {automationId}")
|
||||
return
|
||||
|
||||
# Get services for creator user (provides access to interfaces)
|
||||
creatorServices = getServices(creatorUser, None)
|
||||
# Get services for creator user WITH mandate context from automation
|
||||
creatorServices = getServices(creatorUser, automationMandateId)
|
||||
|
||||
# Execute automation with creator user's context
|
||||
# Execute automation with creator user's context and mandate
|
||||
# executeAutomation is in same module, so we can call it directly
|
||||
await executeAutomation(automationId, creatorServices)
|
||||
logger.info(f"Successfully executed automation {automationId} as user {creatorUserId}")
|
||||
|
|
|
|||
Loading…
Reference in a new issue