Thursday, April 24, 2025

Oracle Process Automation: getting users in a group (Gen2 PCS to Gen3 OPA Upgrade: Part 9)

In this article I discuss how to get users in an IAM group to support task reassignment from a custom task list. It stands by itself as a topic but is also part of the series of articles in which I discuss some challenges you may face when upgrading Oracle Integration Gen2 (OIC 2) Process (PCS) to Gen3 (OIC 3) Oracle Process Automation (OPA) applications. The previous article can be found here.

Disclaimer: The below provides a snapshot at the time of writing. Things may have changed by the time you read it. It is also important to mention that this does not necessarily represent the view of Oracle.

Specifically for PCS to OPA migration you are also referred to the following:

When building a custom task list application for Oracle Process Automation, you may need to implement functionality to reassign a task to someone else who is in the IAM Group associated with the swimlane role the task is in. The same functionality is also available in the out-of-the-box workspace.

In PCS (OIC 2) you could use the /ic/api/process/v1/identities/groups/{groupid}/users API for this. Being part of the API’s the security of that is the same as for all others, and you cannot do more than the API is designed for, which mainly is to retrieve users, groups, and roles. Oracle Process Automation (OIC 3) does not have the equivalent of the /ic/api/process/v1/identities API’s. Instead, you are supposed to use the IAM API’s. 

The Challenge

Now here comes the challenge. To be able to populate a drop-down (or similar widget) with users you will call this API from the UI. To prevent the need of authorizing users to call this API with their own credentials, this is typically done using the OAuth 2.0 Client Credentials flow. This flow is then based on a client Confidential Application with at least the IAM User Administrator application role assigned to it, which allows adding, changing, and deleting users, groups and group memberships. Even though the UI may have been designed to only select users in a particular group, using browser developer tools, a more advanced user will be able to do any of those things. For most of my customers this is not acceptable from a security perspective and would block a go live of such an implementation.

The Remedy

Fortunately, the remedy is pretty straight-forward (albeit some work) as you can solve this by creating an Integration that serves as a proxy to IAM. It involves the following:

  1. Creation of a client Confidential Application in IAM with the User Administrator IAM application role assigned to it.
  2. Creation of a Integration Connection to call IAM REST API’s using the Client Credentials of the previous step.
  3. Creation of an Integration that returns the list of users in an IAM Group based on its display name.
The following explains each step in more detail.

1. Confidential Application Creation

The client Confidential Application is created in Cloud Console > Identity Security > Domains > [IAM domain associated with OPA] > Integrated Applications > Add Application.

The configuration looks as in the following screenshots. The parts indicated in red, is what you configure, the rest can be kept as defaulted.


2. Integration Connection Creation

The Connection in Integration is created as shown below. Mind the value of the ‘urn:opc:idm:__myscopes__’ scope, which needed to access the IAM admin API’s.


3. Integration Creation

Regarding the Integration there probably is more than 1 way to skin that cat, but I consider the following as a proper one.

The design looks as follows:
 

 

The /ic/api/process/v1/identities/groups/{groupid}/users takes the id of the IAM group as template (path) parameter. In the UI you don’t have a handle on that id and when promoting the application from one environment to the next, you probably deploy to a different domain at some point, and then that id will be different. But you can keep the group’s display name the same for all domains. Therefore, the interface of the Integration can be designed to look as follows:

Resource URI: /groups/{groupDisplayName}/users
Action: GET
Response:
{
  "items" : [ {
    "value" : "123xyz",
    "display" : "John Doe",
    "name" : jonh.doe@oracle.com
  } ],
  "count" : 1
}

The getGroupNameIAM activity in the Integration gets the group from IAM using its display name:

Resource URI: /Groups
Action: GET
Query Parameters:

  • filter (String)

Response:
{
  "Resources" : [ {
    "compartmentOcid" : "ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "displayName" : "Sample Group",
    "domainOcid" : "ocid1.domain.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "id" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "urn:ietf:params:scim:schemas:oracle:idcs:extension:dynamic:Group" : {
      "membershipType" : "static"
    }
  } ],
  "totalResults" : 1
}

The ‘urn…’ element must be included for the Integration to interpret the response.

When mapping the groupDisplayName from the Integration’s request to this API you can use the following expression:

concat ('displayName eq "', /nssrcmpr:execute/nssrcmpr:TemplateParameters/ns21:groupDisplayName, '"' )

Configured like this should return exactly 1 group. Using that group’s id, the getUsersInGroupIAM activity gets all users in that group:

Resource URI: /Groups/{groupid}
Action: GET
Response:
{
  "members" : [ {
    "value" : "123xyz",
    "display" : "John Doe",
    "name" : "jonh.doe@oracle.com"
  } ],
  "urn:ietf:params:scim:schemas:oracle:idcs:extension:dynamic:Group" : {
    "membershipType" : "static"
  }
}

Again, the ‘urn…’ element in the response is just there for Integration to be able to interpret it. The mapping of it to the Integration’s response is pretty straight-forward:

 

The Default Fault Handlers is there to prevent the Integration from returning any detailed information from the backend, should it fail. I let it return a 401 in all cases. This being another best practice you should apply where it concerns security. The “not found” route returns a 404 when no IAM group can be found with the provided groupDisplayName.

Previous articles on the PCS to OPA migration subject:

Friday, January 03, 2025

Oracle Integration Gen2 PCS to Gen3 OPA Upgrade: Part 8

In series of articles, I discuss some challenges you may face when upgrading Oracle Integration Gen2 (OIC 2) Process (PCS) to Gen3 (OIC 3) Oracle Process Automation (OPA) applications. In this episode I discuss a challenge with the Boundary Error event handling. The previous article can be found here.

Disclaimer: The below provides a snapshot at the time of writing. Things may have changed by the time you read it. It is also important to mention that this does not necessarily represent the view of Oracle.

Specifically for PCS to OPA migration you are also referred to the following:

In OIC Process there are two ways to handle faults with integrations:

·       Use the out-of-the-box fault policies (the default when activating),

·       Model fault handling and deactivate fault policies.

As I already argued in the article that describes the Custom Fault Handling Pattern [http://kettenisblogs.blogspot.com/2022/10/oic-structured-process-custom-fault.html], the first option is probably not what is required. Instead, you may prefer using an Error Boundary event and model explicit fault handling as part of the process flow.

This is what I have done in a small PCS application to demo the working. First, I have modeled a Business Type with the name Fault that has attributes to hold fault information:

 

Next to the pretty standard code, summary and detail attributes, I also have attributes for a (unique) businessId and a timestamp for when the fault occurred. I then created a Business Exception with name IntegrationFault, based on the Fault Business Type. I then used the Business Exception to throw, catch and handle any fault in the process, like I did in the below Reusable Subprocess (applying the Service Handler pattern as I first introduced in an article about describing how to do “multi-threading” in PCS [http://kettenisblogs.blogspot.com/2022/02/oic-parallel-gateway-and-multi.html]):

 

Not shown in detail, but one of the Boundary Errors catches the RemoteFault and the other the BindingFault. Both are then forwarded to an Error End event raising the IntegrationFault Business Exception. Mind, the Service Handler does not handle the exception itself, which therefore is thrown back to the calling process:

As you can see, the calling process has an Event Subprocess that catches all IntegrationFaults. Granted, a bit overcomplicated for such a simple process, but in a real process application you probably have 2 or more integration calls and soon you will appreciate the Service Handler and Custom Fault Handling patterns 😉.

To easily showcase the result, I have put a Human Task in the Fault Handler, displaying the fault details.

For an APIInvocationError for a Bad Request this looks like this:


And for a Not Found like this:


In case of a 401 Unauthorized or 403 Forbidden, it looks like this:


Mind, this won’t return the 401 but instead a 500, which makes sense as this fault is caused by using wrong credentials as configured in OIC, so there is no point for the consumer to try again until this has been fixed on the OIC side (hence the 500). However, the actual, useful information is dug down in [CDATA] and it is not trivial to return in a easy-to-read way.

In case of the Integration not being available this looks like this:


As you can see, in this case the error is returned (by OIC) as an HTML page.

 

The challenge

When migrating an application handling Business Exceptions from PCS to OPA, you will find that the specific Exception caught by the Boundary Error events is not supported by OPA. This implies that any PCS process with a Boundary Error event that is configured to catch a specific error, cannot be (in-place) upgraded and instead must be refactored / migrated. I did not check the situation where you chose to catch all business and system exceptions, assuming you make all this effort to catch the error as specific as possible to simplify analyzing issues (compare with Java Exception versus NullPointerException, NumberFormatException, etc.).

 

The remedy

The good news is that this challenge can be addressed by introducing (what even results in) a simplification of the process, as unlike OIC 2 with its RemoteFault and BindingFault, in case of OIC 3 you only need to catch a single Service Invocation Failure to handle both.

So, in this model that got invalid after the import:

I can delete one of the Boundary Errors events and configure the other to catch the Service Invocation Failure:


The original IntegrationFault Business Exception has been migrated, although the result is not exactly the same. Any Business Exception has a code, message, and details attribute. The first two are fixed, but for details you can choose what type it should have, and when migrating from OIC 2 this became the Fault Business Type I created before. That Fault already had a code and message attribute of its own but that cannot be used in mappings (as you can see in the next picture).

You will find that the data association also became invalid as all source fields are different:


Completely new are the errorName and details.payload and details.uri elements, of which the payload contains the detailed error information. Not to lose the errorName along the way, I added it to my custom Fault Business Type. So, the mappings can be remedied as follows:


I don’t need to fix the mapping to the Error End event as for me that is just fine. Note how the fault is mapped 1:1 to the input.errorInfo.details, as that attribute is based on the same Business Type. The only data I need to add to it, are the businessId and the timestamp.


So, now my Service Handler is done, and I also don’t need to reconfigure the Event Subprocess as that catches the IntegrationFault that was migrated as-is, with its mapping based on the Fault Business Type:

Now let’s see how that works out runtime. I will skip discussing the change I had to make to the UI, as that is just to showcase the result. As you saw before, on OIC 2 I handled the 400 Bad Request. That gives practically the same result on OIC 3:


A 404 Not Found thrown by the backend, for example because it can’t find an city having a specific id, can also be handled the same way (picture concerning a Service Request but the idea is the same):


But from here one it has changed to what I consider to be an improvement. A 401 Unauthorized raised by the Integration’s Trigger connection is returned as a Service Invocation Failure which I can handle as the same way as all other faults. Notice though that there are payload details because the Integration never started:


An Invoke to a back-end using wrong credentials results in a oracle.cloud.connector.api.CloudInvocationException. When returned to the calling process using Fault Return, this also results in a Service Invocation Failure, this time with details (because the Integration was started, and I used the mapping to the Fault Return to propagate the details):


Another improvement (at least in my opinion) is the way that a deactivated or non-deployed Integration call is handled. This now results in a 404 Not Found, instead of the 503 Service Unavailable that OIC 2 used to return. You might consider not showing the detailed message, as that gives away the Integration’s URI:


 

Previous articles on the subject:

 

Sunday, September 15, 2024

Oracle Integration Gen2 PCS to Gen3 OPA Upgrade: Part 7

In series of articles, I discuss some challenges you may face when upgrading Oracle Integration Gen2 (OIC 2) Process (PCS) to Gen3 (OIC 3) Oracle Process Automation (OPA) applications. In this episode I discuss a challenge you may face with task assignment to an individual (instead of the default role assignment) when migrating from Oracle OIC 2 Process (PCS) to OIC 3 Oracle Process Automation (OPA). The previous article can be found here.

Disclaimer: The below provides a snapshot at the time of writing. Things may have changed by the time you read it. It is also important to mention that this does not necessarily represent the view of Oracle.

Specifically for PCS to OPA migration you are also referred to the following:

 

In the following example I have 2 Human Tasks, of which 1 is assigned based on the swimlane, and the other by assigning it to a specific person:



The first task has assignment set to “Any member of current swimlane role”:


 

And the other has it set to some task payload element, which is TaskAssignmentForm.processRequest.id:


 

When running this process, in Workspace you can see the first being assigned to the process-specific role, which is SMP Task Assignment.ProcessOwner. The “(R)” at the end confirms the assignment is role-based:

 

The second task is assigned to me directly:



The Challenge

I can import, validate and activate the process without issues. Looking at the properties all looks good for the first task:


 

And the second one:


 


When running the process, the first task is assigned to the swimlane role, as it was for PCS:


However, the second one runs into an error


The message points out that the element with name input_taskAssignmentForm cannot be resolved.

On closer inspection of the design-time task assignment you can see it has been migrated to use input.TaskAssignmentForm.processRequest.id, which as such looks OK as it basically is the same element as it was for PCS:


 


However, the issue is that on assignment this element is not initiated yet. So, practically speaking this is a kind-of NullPointerException.


The Remedy

The resolution is quite simple, I should use an expression pointing to an element of the process payload itself instead, which in my case is called taskAssignmentFormDataObject:


 

When I now run the process, also the second task is assigned properly:


 

Note: in PCS you don’t have access to the process payload in the task assignment expression editor. So, there is no way I could have prevented this issue in PCS already.

Next articles on the subject:

Previous articles on the subject: