Authorization Manager - Auth Query

I am using the REST API with Authorization enabled.

When I call in to the REST API with “/task” I get tasks that task that I the current authenticated user is not assigned to. Specifically, the problem is with the authentication query:

SELECT DISTINCT
*** lots removed***
FROM ACT_RU_TASK RES
LEFT JOIN ACT_RE_PROCDEF PROCDEF
ON RES.PROC_DEF_ID_ = PROCDEF.ID_
LEFT JOIN (SELECT A.*
FROM ACT_RU_AUTHORIZATION A
WHERE A.TYPE_ < 2
AND ( A.USER_ID_ IN ( ‘tester’, ‘’ )
OR A.GROUP_ID_ IN ( ‘non-admin’ ) )
AND (( A.RESOURCE_TYPE_ = 7
AND A.PERMS_ & 2 = 2
OR A.RESOURCE_TYPE_ = 6
AND A.PERMS_ & 64 = 64 ))) AUTH
ON ( AUTH.RESOURCE_ID_ IN ( RES.ID_, PROCDEF.KEY_, '
’ ) )
WHERE ( ( RES.CASE_EXECUTION_ID_ IS NOT NULL )
OR ( AUTH.RESOURCE_ID_ IS NOT NULL ) )
AND ( RES.TENANT_ID_ IS NULL )
ORDER BY RES.ID_ ASC
LIMIT 2147483647 offset 0 ;

based upon this…

ON ( AUTH.RESOURCE_ID_ IN ( RES.ID_, PROCDEF.KEY_, ‘*’ ) )

will filter out tasks where the the Resource Id in the Auth is either equal to RES.ID_ (which is the task Id), the process definition id or *, which I am assuming is some sort of admin or “all” resource.

This would mean that we would need to know the task id ahead of time and assign an authorization for it. This doesnt really make sense and this doesn’t support a really common use case where you want to return only the tasks that use is assigned to.

To support this, this query would have a join on clause that looked something like this:

ON ( AUTH.USER_ID_ = RES.ASSIGNEE_ OR RES.ASSIGNEE_ in(‘non-admin’,‘testers’ ) )

This would only include tasks that where equal to the task is assigned to the user or the user has one of the groups in which the task is assigned to.

This relates to this problems-with-authorization where the question was never really answered.

My question is the same:

“Do I really have to set a Authorization for each known task in the system (set taskActivityId as Resource_Id) or is there an other way to achieve my goal?”

We’re just about to try and answer this same question. One guess would be that the Tenant feature could be used to provide a crude authorization mechanism. However, granular authorization for large number of users or roles will be a challenge. Reading your post, I’m word that even the basic authorization framework doesn’t work properly.

That said, off-the-shelf tools to support granular authorization are often application specific and thus not amenable to integration. Most systems use static(ish) configuration files which provide an easy mean to casually manage authorizations, but don’t really provide an enterprise solution. One thing you can consider potentially is the LDAP/AD Windows Active Directory service. I wouldn’t use it because it doesn’t fit into my environment, but it does support groups (roles) and certain authorization levels and generally is easier to integrate. It also provides authentication.

Other mechanisms for authorization might include the following:

  • TACACS+ We’ve built interfaces that permit its use for a broad range of applications, though managing it is not easy.

  • Tomcat/WildFly authorization frameworks. These Java application servers have their own authorization frameworks. I know that WildFly has it, but have never used it. It may be that these servers support the general use of their authorization frameworks for applications running within them.

  • Custom solution. It may just be easier to build a simple back end database to do this. We’ve considered that, but it depends upon your requirements as to whether this would be appropriate. You would also need to understand how to “intercept” the authorization request within Camunda so that it can be abstracted to a query.

In house, we have a authorization framework tool that we’re hoping to graft into Camunda. It is, unfortunately, proprietary and for internal use only.

mppfor_manu:

Do you know how the Camunda LDAP integration supports groups? To me I would think that this would be impossible? For their authorization mechanism to work, wouldnt permissions/ authorization information need to be in the Camunda database?

I’m not sure what, if any, integration there might be within Camunda for LDAP/AD authorization. In the WildFly distribution, there is a section in the standalone.xml file for using LDAP, but it is commented out. I can tell you that LDAP/AD (and it may be that you have to have Active Directory for this to be true) absolutely supports groups. However, I have never used the authorization functions within LDAP/AD, only the authentication function.

The following reference (which you may have already read) describes Camunda’s authorization service:

https://docs.camunda.org/manual/7.6/user-guide/process-engine/authorization-service/

Near the bottom they discuss LDAP, though the applicability here may be limited. LDAP/AD can provide both authentication and authorization, with authentication being far easier to implement (you either are or you aren’t who you say you are). The following seems to have more depth on this subject:

https://docs.camunda.org/manual/7.6/user-guide/process-engine/identity-service/

My application of an authorization framework needs to extend beyond Camunda to many different elements, which is why I need one that has broad support and that can pass internal audit and security requirements. Also, it must be easy to manage, which LDAP/AD may not be if you’re primarily a UNIX shop like we are.

Hi @joechromo,

So what exactly do you want to achieve:
(1) Do you want to get all tasks for the current authenticated user that he/she is allowed to see?
(2) Do you want to get all tasks for the current authenticated user that he/she is assigned to?

Re (1). This can be achieved by enabling the authorization and just call /task.

Re (2). This can be achieved by setting some query parameters, for example /task?assignee=<name-of-user> or /task?assigneeExpression=${currentUser()}.

Yes, that is the expected behavior. When you call /task with enabled authorization, then you will get all tasks that the current authenticated user is allowed to see (i.e. to read), independent of the fact whether the current authenticated user is assigned to it or not. I recommend you to read 1.

It basically depends on how fine granular you want to set the authorization. You are able to define some authorization on process definition level or task level. On process definition level you can just say, that user x is allowed to read tasks from process definition invoice, which means whenever a user task is reached in a process instance of the process invoice, the user x is allowed to read it. If this is not desired, then you have to define the authorizations on task level. There you can define for each known task in the system whether the user is allowed to see this task or not.
However, the process engine has a DefaultAuthorizationProvider 2 which creates out of the box for an assignee the corresponding authorization to enable that the assignee is allowed to see the task. So, you do not have to create such authorization on your own for each user task in the system. You are also able to change this behavior by adding your own AuthorizationProvider to the process engine configuration.

So, what exactly do you want to achieve?

Cheers,
Roman

2 Likes

Roman:

Thanks for your response. I appreciate the time your are taking to address this.

Here is what I want to achieve-

First and foremost I do not want to maintain any groups/ authorities or user in Camunda database. We have a user management microservice that handles that. This information is populated in the JWT and is provided in the incoming request. Please note that I map this information into the “currentAuthentication” using the Identity Service.

Lets say I have a task called “Request to Approve Account” and in the configuration of this task I have the following-

<bpmn:userTask id="approvalTask" name="Request to Approve Account" camunda:asyncAfter="true" camunda:formKey="embedded:/app/forms/embedded/approval-form.html" camunda:candidateGroups="accounting, humanresources">

Note that I will not have this task assigned to anyone.

If user: john in group: humanresources calls GET “/task”, then I want this task to be returned in the collection.
If user: john in group: humanresources calls POST “/task/{id_of_returned_task}/complete”, then I want this task to be completed.

If user: George in group: sales calls GET “/task”, then I do not want this task to be returned in the collection.
If user: George in group: sales somehow gets the task id and calls POST “/task/{task_id}/complete”, then I want the REST API to respond with Unauthorized.

Again… please note that we cannot maintain authorizations, groups an permissions in the Camunda database since it is already maintained elsewhere.

Thanks!

May I suggest that someone provide an example of integrating Camunda authorization (and even authentication) with an external database and/or web service? This is the best way of showing those of us who must go beyond Camunda’s native authorization mechanism how it is done.

If such examples exist and I missed them, please reference them. I apologize in advance for not being smarter.=-)

Hi @joechromo,

Sorry for my delayed answer.

You do not have to maintain user, groups and group membership in the Camunda database. Therefore you can use whatever you like for example LDAP etc.

In case of authorizations it is slightly different :wink:
You could implement you own CommandChecker 1, where you can check the authorization by calling your user management microservice. Those CommandChecker are always called when executing a command like POST /task/<task-id>/complete (i.e. task completion). But in case of GET /task a database query is executed whereby the sql statement contains the corresponding authorization check (it is tightly coupled). As a result, the CommandChecker is not used for queries, hence you cannot check the authorization by calling your user management for such kind of requests.

Cheers,
Roman

Thanks @roman.smirnov. This confirms what I suspected. What I ended up with was simply overriding the Camunda Rest API to get the exact behavior that I wanted for tasks… I tried to implement a command checker but ran in to problems configuring it correctly with the spring boot starter. Here is a modified code snippet with the most of our code removed.

public class CustomTaskRestServiceImpl extends TaskRestServiceImpl {   
//Camunda incorrectly calls this method getTask, however this method actually handles any request/ method with the task ID path segment.
@Override
public TaskResource getTask(String id) {

   [stuff removed calls the identity service to find out if the current authorization has groups that are present in the    candidate groups] 
  
   if (!authorized) {
        throw new RestException(Response.Status.UNAUTHORIZED, "User is not authorized to task '{}' " + id);
    }
    return super.getTask(id);
}



@Override
public List<TaskDto> queryTasks(
    TaskQueryDto queryDto, Integer firstResult, Integer maxResults) {

  [stuff removed calls the identity service to find out if the current authorization was injected by our framework and has our groups] 
    if (applicable){
             queryDto.setCandidateGroups(groupIds);
    }
     
     return superQueryTasks(queryDto, firstResult, maxResults);
    }
}

And then in a filter we set the current authentication by mapping in our user:

SmartCosmosUser smartCosmosUser = getSmartCosmosUser(httpServletRequest);
      
        IdentityService identityService = applicationContext.getBean(IdentityService.class);

        List<String> tenants = getTenant(smartCosmosUser);
        List<String> groups = getRoles(smartCosmosUser);
        String userName = smartCosmosUser.getUsername();

        Authentication currentAuthentication = new SmartCosmosRestAuthentication(userName, groups, tenants);

How did you manage to register this CustomTaskRestServiceImpl ?