How to synchronize users

<< Click to Display Table of Contents >>

Navigation:  How to's >

How to synchronize users

Overview

For Automation Service, you need to register your end users in the Bizagi Work portal.

When using an integrated authentication (connecting with your corporate Identity Manager), you should set registering your end users set as a recurring task so that you constantly synchronize their information (replicate any change regarding users in your Identity Manager, import a new user, or disable one).

 

This means that the current set of end users should be available when you access the admin options for users in the Work portal:

 

ImportUsers_11

 

To import and synchronize end users, you have several options:

1. Have a SOAP web services client on your side (at your premises) which invokes the Bizagi SOAP API in order to push user information.

2. Have secure web services on your side (for your Identity provider) that a Bizagi process can invoke to pulls user information.

You will need to define the Bizagi process.

3. Have Bizagi connect to your Active Directory through an out-of-the-box feature which is set to run on a daily basis.

For Automation Service to access an on-premises system which does not offer a public endpoint, such as an Active Directory, you need to use a VPN.

 

In scenarios where the above options are not feasible or optimal, you may choose to use a solution which isn't as technical: use a Bizagi process which expects an Excel file to be uploaded.

You need to build the Bizagi process and generate the Excel file from the user information managed by your Identity Provider.

This article illustrates how to build the Bizagi process and configure it for this purpose, through a series of guided steps.

 

note_pin

This process does not support re-enabling users. If you want to enable a disabled user, you have to do that manually

 

 

What you need to do

The general outline to build the Bizagi process mentioned above, has three main steps:

 

1. Download, customize or create an Excel connector.

To read the incoming information of users from an Excel file, you will need an specialized Bizagi Connector.

You can create a connector using our Connector Editor, or download the one used in this example.

At this point, you should already know which format (i.e, columns definitions) you will employ for user information.

 

2. Implement the basics of the Bizagi process.

Create a process using Bizagi Studio which expects as input an Excel file with the users to be added or synchronized to your project.

When modeling the process, you define basic aspects such as the data model employed and the user interfaces.

At this point you can also add other configuration aspects to the process, such as making sure it has adequate access rights (i.e, restricted to non-admins).

 

3. Define processing rules.

Define business rules to be executed in order to interpret incoming information.

 

 

Example

In this example, all users are uploaded to a single organization (the default one), using the following attributes:

Username: should be unique per user, when combined with the Domain information.

Email: should be unique per user.

Full Name

Domain: should be unique per user, when combined with the Username information.

Role: this information has a multiple relationship with users. One user may have multiple roles, and a same role can be applicable to many users.

Enabled

Stakleholder: this information has a multiple relationship with users. One user may be multiple Stakeholder, and a same Stakeholder can be applicable to many users.

 

Because a single user can have more than one role, it is best to have multiple rows in the Excel file where each row defines a role for a user (so a users basic information could be repeated within several rows).

The structure of the Excel file employed in this example is:

 

ImportUsers_01

 

If you want to reuse this Excel file so that it works with the example in this article, download it from: http://resources.bizagi.com/docs/UsersUploadSample.xlsx.

 

Procedure

Follow these steps:

 

1. Download, customize or create an Excel connector.

Download a ready-to-use connector to use with this example from http://resources.bizagi.com/docs/Read Users From Excel.bizc.

Alternatively, create a connector from scratch or edit the downloaded one using our Connector Editor.

 

In the end, you should have a connector which complies to the Excel file structure, in a .bizc file:

 

ImportUsers_12

 

When you move to Bizagi Studio, you should make sure you install and create an instance of this connector.

Its configuration demands no specific settings:

 

ImportUsers_13

 

 

2. Implement the basics of the Bizagi process.

This step is about using Bizagi Studio to implement the process to import, update or disable users, through assisted steps.

 

2.1 Model the process

Model a process consisting of just two activities: a manual activity in which you upload the Excel file, and a system activity which processes the file.

 

The following image illustrates this model:

 

ImportUsers_02

 

2.2 Define the data model

Define a data model to support this process's use case. It needs a collection to temporarily store users, a file attachment attribute to capture the incoming Excel file, and other data for configuration so you can target specific information within the Excel file.

 

The following image illustrates the suggested data model:

 

ImportUsers_03

 

2.3 Design the user interfaces

The only activity in this process which needs user intervention is the Load File activity.

Create a form like the following to support uploading the Excel file and previewing the records it holds.

 

ImportUsers_04

 

For this form, create an action to execute the connector (to fetch all information coming from the uploaded Excel file).

The actions should be set as suggested in the following image:

 

ImportUsers_05

 

Inputs for executing the connector are the following:

 

ImportUsers_06

 

Outputs for the execution of the connector are the following:

 

ImportUsers_07

 

No error handling configuration is needed.

 

2.4 Grant access rights for case creation to admin users

At this point we recommend you carry out configuration tweaks such as granting access to this process to admin users only.

 

ImportUsers_14

 

 

3. Define processing rules.

Now that the basics have been taken care of, you can create rules that process what has been temporarily stored in a collection to the final users entity.

 

note_pin

The Process File activity is where the uploaded file is processed.

As mentioned before, the Excel file has a multiple relation property (Role), so the processing need to make sure that multiple user entries or duplicates of existing users are not imported.

 

3.1 Define rules as activity actions

Create the following Expression in the On Exit event of the activity as shown below:

 

ImportUsers_08

 

To do this, first declare variables as we suggest below:

 

ImportUsers_09

 

Type

Name

Initial Value

int

index

Index of the current element in the list of imported users

int

counter

Number of users imported

string

inputXML

String to be executed using the Bizagi SOA Layer

string

inputXML

String with the XML of a user

List

CurrentUser

Collection to control that a user is not processed more than once

List

CurrentFullname

Collection to control that a user with multiple roles does not have different full names

List

CurrentEmail

Collection to control that a user with multiple roles does not have different emails

List

CurrentDomain

Collection to control that a user with multiple roles does not have different domains

List

CurrentRole

Collection to control that a role is not assigned more than once to the same user

List

CurrentEnabled

Collection to control that a status is not assigned more than once to the same user

List

CurrentStakeholder

Collection to control that an Stakeholder is not assigned more than once to the same user

 

3.2 Add coding into the first expression box

For the Process file graphical expression box in the image above, add the following code:

 

// Get a collection with the users uploaded. Use this method to return a collection even if only one record is sent

var colUsers = CHelper.GetValueAsCollection(<UploadUsers.ImportedUsers>);

// number of records in the file

counter = colUsers.size();

// This collections lets you control that a user with multiple roles does not have multiple domains, full names, etc.

CurrentUser = new ArrayList();

CurrentDomain = new ArrayList();

CurrentEmail = new ArrayList();

CurrentFullname = new ArrayList();

CurrentRole = new ArrayList();

CurrentEnabled = new ArrayList();

CurrentStakeholder = new ArrayList();

// Pploading of the users is done through the Bizagi SOA Layer.

inputXML = "<BizAgiWSParam>";

inputXML += "<Entities>";

 

3.3 Configure the For graphical element

For the Iterate over users graphical element, configure the following:

 

ImportUsers_10

 

3.4  Add coding into the second expression box

For the Initialize value fields for user graphical expression box shown in the image in the step 3.1, add the following code:

 

// gets the i-th user

var oUser = colUsers.get(index);

var sUsername = oUser.getXPath("Username");

// This variable is to search if a user already exists

var sSearchDomain = "";

// If the user is already in the collection, this user will not be processed

if(!CurrentUser.Contains(sUsername.ToLower())) {

 CurrentUser.Add(sUsername.ToLower());

 inputXMLUser = "<WFUSER>";

 inputXMLUser += "<userName>" + sUsername + "</userName>";

 inputXMLUser += "<Organizations><idOrg key=\"1\"/></Organizations>";

 // The collections are cleared in order to start over again

 CurrentFullname.Clear();

 CurrentEmail.Clear();

 CurrentRole.Clear();

 CurrentDomain.Clear();

 CurrentEnabled.Clear();

 CurrentStakeholder.Clear();

 var colUsernames = CHelper.GetValueAsCollection(<UploadUsers.ImportedUsers>);

 // New iteration to get multiple values for the current user

 for(var i = 0; i < colUsernames.size(); i++) {

         var oUsername = colUsernames.get(i);

         if(oUsername.getXPath("Username").ToLower().Trim() == sUsername.ToLower()) {

                 var sFullName = oUsername.getXPath("Fullname");

                 var sEmail = oUsername.getXPath("EmailAddress");

                 var sDomain = oUsername.getXPath("UserDomain");

                 var sRole = oUsername.getXPath("UserRole");

                 var sEnabled = oUsername.getXPath("UserEnabled");

                 //To add single attributes, the attribute must not be empty and:

                 //if the collection is empty, the attribute has to be added

                 //if the collection contains the given attribute, this will not be processed

                 //if the collection does not contain the given attribute and is not empty, an error must be thrown

                 //set domain

                 if(sDomain && !CurrentDomain.Contains(sDomain.ToLower().Trim())) {

                         CurrentDomain.Add(sDomain.ToLower().Trim());

                         // set the domain to search if the user exists

                         sSearchDomain = sDomain;

                         if(CurrentDomain.Count > 1)

                                 CHelper.ThrowValidationError("Username " + sUsername + " has different Domains");

                         sDomain = sDomain.Replace(" ", "");

                         inputXMLUser += "<domain>" + sDomain + "</domain>";

                 }

                 //set fullname

                 if(sFullName && !CurrentFullname.Contains(sFullName.ToLower().Trim())){

                         CurrentFullname.Add(sFullName.ToLower().Trim());

                         if(CurrentFullname.Count > 1)

                                 CHelper.ThrowValidationError("Username " + sUsername + " has different Fullnames");

                         inputXMLUser += "<fullName>" + sFullName.Trim() + "</fullName>";

                 }

                 //set email

                 if(sEmail && !CurrentEmail.Contains(sEmail.ToLower().Trim())){

                         CurrentEmail.Add(sEmail.ToLower().Trim());

                         if(CurrentEmail.Count > 1)

                                 CHelper.ThrowValidationError("Username " + sUsername + " has different Emails");

                         inputXMLUser += "<contactEmail>" + sEmail.Trim() + "</contactEmail>";

                 }

                 //set enabled

                 if(sEnabled && !CurrentEnabled.Contains(sEnabled.Trim())){

                         sEnabled = sEnabled.Trim();

                         if(sEnabled != "1" && sEnabled != "0")

                                 CHelper.ThrowValidationError("Username " + sUsername + " has an invalid status");

                         CurrentEnabled.Add(sEnabled);

                         if(CurrentEmail.Count > 1)

                                 CHelper.ThrowValidationError("Username " + sUsername + " has different status");

                         inputXMLUser += "<enabled>" + sEnabled + "</enabled>";

                 }

                 //To add multiple attributes, the attribute must not be empty and:

                 //if the collection is empty, the attribute has to be added

                 //if the collection contains the given attribute, it will not be processed

                 //set multiple roles

                 var sStakeholder = oUsername.getXPath("UserStakeholder");

                 if(!sStakeholder)

                         sStakeholder = "EmptyStk2018";

                 if(sRole && !CurrentRole.Contains(sRole.ToLower().Trim())){

                         CurrentRole.Add(sRole.ToLower().Trim());

                         sRole = sRole.Replace(" ", "");

                         var iRoleId = CHelper.getEntityAttrib("Role","idRole","LOWER(roleName) = '" + sRole.ToLower() +  "'");

                         if(!iRoleId)

                                 CHelper.ThrowValidationError("The Role " + sRole + " does not exist.");

                         inputXMLUser += "<Roles><idRole key='" + iRoleId + "'/></Roles>";

                 }

                 //Stakeholders may be empty

                 if(!CurrentStakeholder.Contains(sStakeholder.ToLower().Trim())) {

                         CurrentStakeholder.Add(sStakeholder.ToLower().Trim());

                         if(sStakeholder != "EmptyStk2018") {

                                 sStakeholder = sStakeholder.Replace(" ", "");

                                 inputXMLUser += "<StakeHolders><"+sStakeholder+"><a></a></"+sStakeholder+"></StakeHolders>";

                         }

                 }

         }

 }

 //Validations

 //If any required user attributes is empty

 if(CurrentDomain.Count == 0)

         CHelper.ThrowValidationError("No domain was set for user " + sUsername);

 if(CurrentFullname.Count == 0)

         CHelper.ThrowValidationError("No full name was set for user " + sUsername);

 if(CurrentEmail.Count == 0)

         CHelper.ThrowValidationError("No email was set for user " + sUsername);

 if(CurrentRole.Count == 0)

         CHelper.ThrowValidationError("No roles was set for user " + sUsername);

 if(CurrentEnabled.Count == 0)

         CHelper.ThrowValidationError("No status was set for user " + sUsername);

 //Serch for the user with the domain to see if the user already exists

 var oUserCount = Me.getXPath("entity-list('WFUSER','userName = \"" + sUsername + "\" AND domain = \"" + sSearchDomain + "\"')");

 if(oUserCount.size() == 1) {

         inputXMLUser = inputXMLUser.Replace("<WFUSER>", "<WFUSER businessKey=\"userName = '" + sUsername + "' AND domain = '" + sSearchDomain + "'\">");

 }

 //Close the XML of the user

 inputXMLUser += "</WFUSER>";

 //Add the user XML to the XML to be processed

 inputXML += inputXMLUser;

 <UploadUsers.ProcessEnddate> = DateTime.Now;

}

 

3.5 Add coding into the third expression box

For the Insert values graphical expression box shown in the image in step 3.1, add the following code:

 

//Close the XML string

inputXML += "</Entities>";

inputXML += "</BizAgiWSParam>";

//save records

var sResult = CEntityXmlHelper.fromXmlToEntity(inputXML);

 

Execution

When you are done, run the process to test it thoroughly in your development environment.

Create a new case, upload the sample Excel file, and click the Load users button to preview the information. Finally, click Next.

 

ImportUsers_15

 

Once you have verified everything works adequately in your development and testing environments, you can deploy this process to production environments.

 

What's next?

The procedure described here is recommended for every project.

Nevertheless, you may customize the process and how it is executed. For instance:

 

If you need to consider additional user attributes, such as user properties, you need to customize and update the connector used for this purpose so that it fetches other information. You would also need to include these additional attributes in your data model and within the business rules that process the uploaded information.

On top of this process, you can rely on Bizagi SOAP web services. This means that you could choose to avoid creating the cases manually and further automate user management by having a SOAP web services client periodically invoke the Bizagi SOAP createCasesAsString web method.