Authorization for operations on single tasks

We’ve successfully managed to limit the tasks a user can see in a list using authorizationDefinitions.

The resulting authorizationDefinitions - in our a12-camunda-service - look like this:

{
  "name": "Teilnehmer Camunda Permission Definition",
  "description": "Permission Definition File für den teilnehmer-camunda Service",
  "repositoryPolicies": [
    {
      "name": "TeilnehmerverwalterDomainFragebogenTask_Policy",
      "description": "Repository Policy für DomainFragebogenTask und die Rolle teilnehmerverwalter",
      "target": "hasRole('teilnehmerverwalter') && #resource == 'DomainFragebogenTask'",
      "dataPreload": [
      ],
      "templates": [
        "'Fragebogen.FragebogenBasis.TeilnehmerlistenKennung:' + principal.teilnehmerlistenKennung"
      ]
    },
    {
      "name": "TeilnehmerDomainFragebogenTask_Policy",
      "description": "Repository Policy für DomainFragebogenTask und die Rolle teilnehmer",
      "target": "hasRole('teilnehmer') && #resource == 'DomainFragebogenTask'",
      "dataPreload": [
      ],
      "templates": [
        "'a12wf.task.assignee:' + principal.username"
      ]
    }
  ],
  "permissions": [
    {
      "name": "Read Document",
      "description": "Prüfungen für den Scope 'Document Read'",
      "repository-refs" : [
        "TeilnehmerverwalterDomainFragebogenTask_Policy",
        "TeilnehmerDomainFragebogenTask_Policy"
      ],
      "call-parent-scope": true,
      "scopes": [
        "Document Read"
      ]
    }
  ]
}

We are basically creating filters correlating user properties with task document properties.

Now we would like to restrict access to single tasks, namely to read-, update- and complete-task-operations, in the same manner: “allow operation if user.property == task.property”.

The relevant scopes are documented in Workflows > Custom Authorization Definition.

For operations that change the state of a task, such as complete-task, we have access to the #taskDocument. As this is user input it is no reliable source for permission checks.

We would rather have to use the task in the server-side-/before-state. I was thinking we could perform a “read task” before the “complete task”-operation and apply the authorization rules there. However, I do not know how to achieve this with authorizationDefinitions and would be happy if someone could shed some light on how to do this.

(This might be more of a UAA question, but I might not be the first one encountering this challenge in Workflows)

Side note: what struck us as somewhat confusing is the fact, that we have to define authorization rules at two different places - for list-operations in a12-camunda, for operations on single tasks in a12-workflows. This might be due to relatively recent architectural changes. We also heard about more upcoming changes (call by reference). Could you provide some outlook, in what architectural direction a12-camunda and a12-workflows are going?

@mario-grand-vale fyi

Hi Alexander,
you can get before state of the object in the 'dataPreload` section. For example:

"dataPreload": [
    "#beforeState == @myBean.readDocument(#taskDocument.id)"
],
"rules": [
  "#beforeState == 'something'"
]

Hi,
thank you very much, we use this json-File and it works fine:

{
  "name": "Teilnehmer Workflows Permission Definition",
  "description": "Permission Definition File für den teilnehmer-workflows Service",
  "policies": [
    {
      "name": "TeilnehmerverwalterTask_Policy",
      "description": "Der Nutzer muss die Rolle 'teilnehmerverwalter' haben und seine Teilnehmerlistenkennung muss der im Fragebogen entsprechen",
      "target": "hasRole('teilnehmerverwalter') && (#resource == 'SecuredWorkflowService.getTask' || #resource == 'SecuredWorkflowService.updateTask' || #resource == 'SecuredWorkflowService.complete')",
      "dataPreload": [
        "#body = @workflowService.getTask(#taskId)"
      ],
      "rules": [
        "#body.untypedData['Fragebogen']['FragebogenBasis']['TeilnehmerlistenKennung'] == principal.teilnehmerlistenKennung"
      ]
    },
    {
      "name": "TeilnehmerTask_Policy",
      "description": "Der Nutzer muss die Rolle 'teilnehmer' haben und seinen Username/Teilnehmerkennung muss dem Assignee des Tasks entsprechen",
      "target": "hasRole('teilnehmer') && (#resource == 'SecuredWorkflowService.getTask' || #resource == 'SecuredWorkflowService.updateTask' || #resource == 'SecuredWorkflowService.complete')",
      "dataPreload": [
        "#body = @workflowService.getTask(#taskId)"
      ],
      "rules": [
        "#body.a12wf.task.assignee == principal.username"
      ]
    }
  ],
  "permissions": [
    {
      "name": "Task Read Task Update Task Complete",
      "description": "Permissions für einen einzelnen Task, z.B. Task lesen oder Task schreiben.",
      "policy-refs": [
        "TeilnehmerverwalterTask_Policy",
        "TeilnehmerTask_Policy"
      ],
      "call-parent-scope": true,
      "scopes": [
        "TaskRead",
        "TaskUpdate",
        "TaskComplete"
      ]
    },
    {
      "name": "Task Assign",
      "description": "Der Scope TaskAssign wird aktuell verboten für alle Requests, die Prüfungen im Parent-Scope werden nicht aufgerufen",
      "policy-refs": [],
      "policies": [
        false
      ],
      "call-parent-scope": false,
      "scopes": [
        "TaskAssign"
      ]
    }
  ]
}