Skip to Content

One of the things many customers complained about in IdM 7.1 was that when role assignments were provisioned to an ABAP backend all roles in the backend were first removed and then the new set of roles (so all old roles + the added ones) was assigned. This is not only more complicated than it needs to be, it also screws up the change records in the backend. Why not simply adding or removing the roles that were added/removed?

Well, I cannot solve the mystery why it wasn’t implemented that way in the first place. I can only tell you that I configured a real delta provisioning in a 7.2 system that only provisions the delta change. This means: When you add one role there will be an RFC call to the backend that adds this one role. When you remove one role in the backend this one role will be removed. The rest will remain untouched. Some of you will say: Hey! This way IdM and the backend can more easily get out of sync because in 7.1 if someone added a role assignment in SU01 in the backend then this was kind of rolled back when IdM performed the next role add/remove operation because that operation replaced the entire set of assigned roles in the backend by the set of assigned roles in IdM. Well, this is true. But now, in 7.2, you have the choice. You can configure it the way you like.

Now let’s go into details.

The basis of everything is note 1626816. It explains that you need to use another pass type. toSAPIdentity instead of toSAP. So in the end what we have to do is something like this:

1. a custom plugin that will be configured as MX_HOOK4_TASK and MX_HOOK5_TASK, respectively

2. a script function that computes the delta

Item 2 is the one that is elegantly left out in the note. No hint where the value for <i>delta_roles</i> comes from.

Fortunately, one can compute it easily with a little DB table knowledge. I have attached the script custom_getAddedRoleNames. In order to fully understand it, you need a little theory about the SAP Provisioning Framework in IdM 7.2.

plugins2.PNG

The provisioning tasks which are configured as repository variables MX_ADD_MEMBER_TASK/MX_DEL_MEMBER_TASK are identical for all repository types in 7.2. The difference between provisioning for an ABAP or Java system or an Active Directory is made through the repository variables MX_HOOK1_TASK, MX_HOOK2_TASK, etc. Only here, the logic goes into repository specific handling. These hook tasks are started by a call to uProvision (see screenshot).

plugins1.PNG

Now, let’s have a look at the view idmv_link_ext. A pending assignment like one where the role assignment plugin task (pointed to by the repository variable MX_HOOK4_TASK) is being executed is already present in the link table (even though the link is not yet established). Its “exec state” is “waiting” (technically: column mcexecstate has value 512). You’re probably wondering: Where is the link to what I said before about the hook task configuration? It is in the columns mcaddaudit and mcdelaudit. These columns contain the audits which process the provisioning/deprovisioning. And the audit that processes the hook task is a child audit of these. So the following SQL statement which uses the refaudit column in mxp_audit to link child and parent audit gives us the pending assignments we’re interested in:

select mcothermskeyvalue,mcexecstate,mcvalidfrom,mcvalidto from idmv_link_ext where (

  mcaddaudit = (

    select refaudit from mxp_audit where auditid=<current audit>

  ) or mcdelaudit = (

    select refaudit from mxp_audit where auditid=<current audit>

  )

) and mcthismskey = <user mskey> and mcothermskeyvalue like ‘PRIV:ROLE:<current repository>:%’

(please replace <current audit> by the id of the current audit and <current repository> by the name of the repository you’re going to provision to).

Here’s the entire function:

// Main function: custom_getAddedRoleNames

function custom_getAddedRoleNames(Par)

{

    var usermskey = uGetEntryID ();

    var repname = “%$rep.$NAME%”;

    var auditid = uGetAuditID ();

    var is_id = parseInt (“%$glb.SAP_MASTER_IDS_ID%”);

    // select role names of privileges which are being added/removed.

    // by looking at mcexecstate we check which action to perform.

    var sql = “select mcothermskeyvalue,mcexecstate,mcvalidfrom,mcvalidto from idmv_link_ext where (mcaddaudit = ( \

                 select refaudit from mxp_audit where auditid=” + auditid +”\

               ) or mcdelaudit = ( \

                 select refaudit from mxp_audit where auditid=” + auditid +”\

               )) and mcthismskey = ” + usermskey + ” and mcothermskeyvalue like ‘PRIV:ROLE:” + repname + “:%'”;

    uWarning (sql);

    var result = uSelect (sql);

    var deltarolestring = “”;

   

    if (result != null && result.length > 0 && result.indexOf (“!ERROR”) == -1) {

        var arr = result.split (“!!”);

        var add_array = [];

        var remove_array = [];

        for (var ii=0; ii<arr.length; ii++) {

            var res_line = arr [ii];

            uWarning (res_line);

            var res_arr = res_line.split (“|”);

            var item = {};

            uWarning (res_arr [0] + ” ” + res_arr [1] + ” ” + res_arr [2] + ” ” + res_arr [3]);

            item.rolename = res_arr [0].substr (21, res_arr [0].length – 21);

            if (res_arr [1] == “512”) {

                add_array.push (item);

                item.validfrom = res_arr [2]==”null”?””:res_arr [2];

                item.validto = res_arr [3]==”null”?””:res_arr [3];

            }

            else if (res_arr [1] == “1536”)

                remove_array.push (item);

            else

                uError (“Wrong link state ” + res_arr [1] + ” for privilege ” + res_arr [0]);

        }

        if (add_array.length > 0) {

            for (var jj=0; jj<add_array.length; jj++) {

                var obj = add_array [jj];

                deltarolestring = deltarolestring + “{A}{VALIDFROM=” + obj.validfrom +

                                  “!!VALIDTO=” + obj.validto + “}” + obj.rolename + “|”;

            }

        }

        if (remove_array.length > 0) {

            for (var jj=0; jj<remove_array.length; jj++) {

                var obj = remove_array [jj];

                deltarolestring = deltarolestring + “{D}” + obj.rolename + “|”;

            }

        }

        if (deltarolestring.length > 0)

            deltarolestring = deltarolestring.substring (0, deltarolestring.length – 1);

    }

    uWarning (“D => ” + deltarolestring);

    return deltarolestring;

}

The code after the SQL statement just tries to identify whether the result line corresponds to an add or a delete operation (note that “waiting for add” is code 512 whereas “waiting for delete” is 512+1024=1536 (1024 represents the delete operation)) and compiles the input argument string for the toSAPIdentity pass.

To report this post you need to login first.

3 Comments

You must be Logged on to comment or reply to a post.

  1. Bin Wang

    Hi Kai,

    Thanks for sharing the information here! Nice blog as always!

    About 3 months ago I have implemented the delta connector for one of our customers according to the mentioned SAP note. Just one more supplement here, the delta connector is not supported by the CUA system, as any other SAP Business Suite  connectors.

    Kind Regards,

    Bin Wang

    (0) 
  2. Sean O'Riordan

    Hi,

    I’m trying to implement the above solution but i have encountered an issue. I have been able to succcesfully provision/de-provision only the changed sap roles but I am unable to use this solution for PRIV:FUNCTION_SET privileges….(BAdIs)

    I was wondering if you know if it is possible to use your above solution with these FUNCTION_SET privileges also?

    Thanks,

    Sean

    (0) 

Leave a Reply