Enhancing the UA Bash extension for modifyResource

Enhancing the UA Bash extension for modifyResource

Fernando Freitas wrote an awesome script that makes the User Application web service endpoint for Roles available as shell commands in bash. This is much more useful than you would think at first. After all, we can do them in a GUI, in SOAP UI, or other SOAP tools, what does bash give us?

Well if you are a shell command kind of guy or gal, it gives you a lot. Now you can use any other script framework, and just call out to these functions. As a simple example, I grabbed some sample scripts that take commands, processes them, and then executes a series of tasks, and then added in support to read input from a CSV file. Then adding in this bash shell extension, I was able to do User App role operations, in bulk, from a command line script in Linux. That turns out to be mighty useful.

For example, we had a customer who had thousands of roles, and needed to rename 900 of them in a hurry. Sure we could have done it in the GUI with enough time. We THINK we might have been able to do it by generating LDIF files to change the two attributes that seem to hold the information, but we were not sure if that would break anything. Instead we needed to call the setRoleLocalizedString() API call twice, once to set the name, a second time to set the Description. Using the CSV reading script, we were able to call this 900 times (twice each time) with one command. (As a side note, the setRoleLocalizedString API has a setting that is either 0 or 1, that determines if the value you are setting is the name or the description. That one took me a bit to figure out, figured I would save anyone else the headache.)

As you can see, this can be really useful. Now imagine you have 18,000 users you need to add to one or many roles. Lots of possible ways to do this, but having a script like this that can read input from a CSV sure makes it easy. You could for example use a Delimited Text driver (I recommend Stefaan Van Cauwenberge's Generic Text driver, it is free and way better than the NetIQ one) that has a policy to call the Add Role token for every line it reads in.

He has put a bunch of his plugins and tools available on his web site at:
http://vancauwenberge.info

You could do it in a Workflow that accepts text in a box, that is a list of DN's to add to a Role, and then call an Integration Activity to make the SOAP call. This has an issue that the User App will run out of memory as it loops pretty quickly, limiting the number of entries that can be processed in a single run.

However, using this script, you would be making many thousands of calls, but they queue up in order and get processed as fast as they can be processed. You can easily add a pause into the script to delay it if needed.

All this is nice to know, and if you read my series on how you might add a new function that Fernando missed, you will see how the script works and how you might extend it.

Series: Adding New Functions to the UA Bash extension

As I spent more time with this I realized there were probably more things I would like to do with this tool and API set. So in the previous article I realized that there were a couple more API calls in the Resource endpoint that would be interesting, like refreshCodeMap a simple one, and a pair of more complex ones, the requestResource* set. That is, asterisk could be Grant or Revoke. What I really want to work on is modifyRole, since that seems like it would be very useful, but I think that is going to be harder than I expect, so I figured starting with modifyResource, hopefully a bit simpler seems like a good idea and a good way to get my feet wet on this issue.

As always, first thing we need to do is look at a sample SOAP document to understand what is going on, and what we need to provide. We will need the work I did in updating createRole to support null characters in the article:
Enhancing the UA Bash extension for createRole

That is because there are so many things we could send in, not everything will be sent every time, assuming that even works, that we need support for optionals.

Get the WSDL file from the User Application. There is a GUI way to do it and a URL you can point at to save the XML. Using the GUI, login to the User Application as a Role and Resource Administrator. Click on the Role and Resource Catalog link.

Oddly inconsistent, this is NOT in the Administration tag, and you instead in the Role and Resource page, look to the bottom left for a link to RBPM Administration. Once you enter that page, on the bottom left again will be links to download the various WSDL's but one link is missing (I think it may actually be the Resource link).

The URL's for the endpoint are fairly standard for SOAP interfaces, so you got to https://MyServer.ip.com:myPort/IDMProv/resource/service?wsdl

This should return the WSDL file as an XML you can view, so chose to save source in your browser.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://www.novell.com/resource/service">
<soapenv:Header/>
<soapenv:Body>
<ser:modifyResourceRequest>
<!--Optional:-->
<ser:resource>
<ser:active>?</ser:active>
<ser:allowOverride>?</ser:allowOverride>
<ser:allowedMulty>?</ser:allowedMulty>
<ser:description>?</ser:description>
<ser:entitlementRef>
<!--Zero or more repetitions:-->
<ser:nrfentitlementref>
<ser:entitlementCorrelationId>?</ser:entitlementCorrelationId>
<ser:entitlementDn>?</ser:entitlementDn>
<ser:entitlementParameters>?</ser:entitlementParameters>
<ser:src>?</ser:src>
</ser:nrfentitlementref>
</ser:entitlementRef>
<ser:entityKey>?</ser:entityKey>
<ser:grantApprovers>
<!--Zero or more repetitions:-->
<ser:approver>
<ser:approverDN>?</ser:approverDN>
<ser:sequence>?</ser:sequence>
</ser:approver>
</ser:grantApprovers>
<ser:grantQuorum>?</ser:grantQuorum>
<ser:grantRequestDef>?</ser:grantRequestDef>
<ser:name>?</ser:name>
<ser:owners>
<!--Zero or more repetitions:-->
<ser:dnstring>
<ser:dn>?</ser:dn>
</ser:dnstring>
</ser:owners>
<ser:parameters>
<!--Zero or more repetitions:-->
<ser:resourceparameter>
<ser:codemapKey>?</ser:codemapKey>
<ser:entitlementDn>?</ser:entitlementDn>
<ser:hiddenFlag>?</ser:hiddenFlag>
<ser:key>?</ser:key>
<ser:paramLabel>?</ser:paramLabel>
<ser:staticFlag>?</ser:staticFlag>
<ser:type>?</ser:type>
<ser:value>?</ser:value>
<ser:valueLabel>?</ser:valueLabel>
</ser:resourceparameter>
</ser:parameters>
<ser:provisioningRequestDef>?</ser:provisioningRequestDef>
<ser:resourceCategoryKeys>
<!--Zero or more repetitions:-->
<ser:categorykey>
<ser:categoryKey>?</ser:categoryKey>
</ser:categorykey>
</ser:resourceCategoryKeys>
<ser:revokeApprovers>
<!--Zero or more repetitions:-->
<ser:approver>
<ser:approverDN>?</ser:approverDN>
<ser:sequence>?</ser:sequence>
</ser:approver>
</ser:revokeApprovers>
<ser:revokeQuorum>?</ser:revokeQuorum>
<ser:revokeRequestDef>?</ser:revokeRequestDef>
</ser:resource>
</ser:modifyResourceRequest>
</soapenv:Body>
</soapenv:Envelope>


Well right off the bat, we see that some of the ideas from extending createRole apply as the very first few nodes look quite familiar to that API call.
active
allowOverride
allowMulty
description
entitlementRef (and its children nodes)

It is worth taking a moment to discuss entitlementRef in the context of a modify, since I am told that it is not supported to modify the entitlement (or value) assigned to a Resource after it has been assigned to a user. One reason for this is that the Roles and Resource Service Driver does not event on this sort of thing and would not go out and refresh the values assigned to users. Thus the supported way is to delete the resource (It is probably best to remove all assignments first via SOAP. In principle eDirectory on delete of the nrfResource object will clean up all DN based references to the object, but User App and its database are unknowns as to how they will react. We know that nrfAssignedResource is a DN attribute stored on the user, which would get cleared up, but we do not really understand how that interacts with data in the User App database, or anywhere else) and then recreate it again and assign it.

In fact, if you remove the resource from its Role association, then everyone who gets the Resource because of the Role should in principle lose the Resource upon that change. Then make a new Resource, same name or different and add it back for RRSD to update the users. All this work is done by the RRSD driver based upon events related to the nrfRole, nrfResource, and nrfResourceAssociation objects. The nrfRole object is linked to the nrfResouce object by the nrfResourceAssociation object. This is a clear case of mapping a database onto a directory service. In a database, you have two tables, and link them together with a third table of links. In a directory, you use a DN style reference to point them at each other. Which is strange, since nrfRole objects have attributes like:
nrfChildRoles
nrfParentRoles

These are DN syntax attributes, which means they point at another object. Pretty much what we would need to link a Role to a Resource. Thus it leaves me wondering why the decision to use a third object to link the two together. Usually the reason for a third object like this is to carry additional information about the link. That is, the reason an nrfRole object is more useful than a Group object is that a group just has Owner and Group Membership attributes. The linkage of user to role is requested and maintained by an nrfRequest object, which can have a start and end date and possibly more data about the linkage which is better than a simple Group.

In User Application, the UI allowed you to modify this entitlement assignment after the Resource was created, but in Catalog Access (the /rra URL) you no longer can, by design. So you can probably use this SOAP function to add an entitlement to a Resource without one, as a modify but you cannot change one with it. Or maybe you actually can, but should not. So take care whilst using this function.

I also noticed that the Approvers XML nodes are different here than in createRole. There they are <approvers> and here they are <grantApprovers> and <revokeApprovers>. Interestingly in the Role example there is only an approver, no revoke approver or grant approver. In both there are PRD's that can differ for approval and request (grantRequestDef and revokeRequestDef) though the node names differ from Role (requestDef) to Resource (grantRequestDef). None of this is a big deal, but it is interesting to see, since it gives some insight into the development process under the covers and how that might differ Role to Resource.

The entire <ser:parameters> node can for the most part be ignored, it is possible that a Resource might need them, but I do not know how to use it, nor have any examples.

That means we should probably support the following as well:
grantApprovers
grantRequestDef
grantQuorum
revokeApprovers
revokeRequestDef
revokeQuorum
provisioningRequestDef
entityKey (DN of target resource)
name (name of target resource)
owners (probably only handle one)
resourceCategoryKeys

Well that is a lot of command line parameters to pass in, I can see why Fernando was hesitant to take on this particular function call. This would leave us with a command line looking like:

modifyResource username password rbpm_url output_file entityKey name active allowOverride allowMulty description grantApprovers grantRequestDef grantQuorum revokeApprovers revokeRequestDef revokeQuorum provisioningRequestDef owner category correlation_id

We will have to use the idea of sending a null value (literal string 'null' controlled by a variable that I proposed in an earlier article in this series about extending the createRole function to more parameters) for things you do not wish to change and thus can be left out. Otherwise it is too hard to handle all the possible options.

A friend suggested using getopt() instead, which solves the NULL problem and is probably, long term, a better choice. I am not sure I am ready to re-write all of Fernando's examples using that approach, but I do think that would be a good idea to try and implement in at least one function to try and make it simpler to handle multiple possible options.



### Function: modifyResource  #### Cannot get it to change anything in SOAPUI. Need further testing. 
# Usage:
# modifyResource username password rbpm_url output_file entityKey name active allowOverride allowMulty
# description grantApprovers grantRequestDef grantQuorum revokeApprovers revokeRequestDef
# revokeQuorum provisioningRequestDef owner category correlation_id
# You must enter a NULL value (see variable defined at top for literal string) for any empty fields
# only a subset of the SOAP call capabilities is exposed by this function
# if the $correlation_id is omitted, will call modifyRoleRequest, otherwise will call modifyRoleAidRequest
#
# Note from the forums:
# The correct endpoint to use for adding/removing a child role is the
#+ same one that you use to add/remove a user: requestRoleAssignment. If
#+ you were to create a Parent-Child relationship in the UI you would see
#+ that a Role Request is created in the UA for this.
#

modifyResource()
{

USAGE="Function Usage:

modifyResource username password rbpm_url output_file entityKey name active allowOverride allowMulty description
grantApprovers grantRequestDef grantQuorum revokeApprovers revokeRequestDef revokeQuorum
provisioningRequestDef owner category correlation_id

modification can be . Note that this does not cover everything the SOAP call can do, only what this function has been coded to handle
if the $correlation_id is omitted, will call modifyRoleRequest, otherwise will call modifyRoleAidRequest
rbpm_url should be in the format:
protocol://server:port/servicename
for example:
https://rbpm.lab.novell.com:8543/IDMProv";

if [[ "X$_RBPM_SOAP_ROLE_DEBUG" = "Xtrue" ]]
then
dbgparams=$#
dbgparam=1
while [ "$dbgparam" -le "$dbgparams" ]
do
echo -n "Parameter "
echo -n \$$dbgparam
echo -n " = "
eval echo \$$dbgparam
(( dbgparam++ ))
done
fi

# Initial Parameters check
if [[ -z "$1" || -z "$2" || -z "$3" || -z "$4" || -z "$5" || -z "$6" || -z "$7" || -z "$8" || -z "$9" || -z "${10}" || -z "${11}" || -z "${12}" || -z "${13}" || -z "${14}" || -z "${15}" || -z "${16}" || -z "$17}" || -z "${18}" || -z "${19}" ]]
then
echo "$USAGE"
return 1
fi

if [[ -z "${20}" ]]
then
NOCID=true
else
NOCID=false
CID="${20}"
fi

if [[ "X$2" = "X-W" ]]
then
read -sp "Please enter the password for user $1: " SENHA
echo
else
SENHA=$2
fi

# Setup for the SOAP call
URL="${3}/resource/service"

if [[ "$NOCID" = "true" ]]
then
ACTION="SOAPAction: 'http://www.novell.com/resource/service/modifyResource'"
SOAPCALL=modifyResourceRequest
else
ACTION="SOAPAction: 'http://www.novell.com/resource/service/modifyResourceAid'"
SOAPCALL=modifyResourceAidRequest
fi
CTYPE='Content-Type: text/xml;charset=UTF-8'

# Build SOAP XML envelope and call to be issued

POST="<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://www.novell.com/resource/service">
<soapenv:Header/>\
<soapenv:Body>\
<ser:${SOAPCALL}>\
<ser:resource>"

if [[ "${6}" != "${NULL}" ]]
then
POST="${POST}<ser:name>${6}</ser:name>"
else
POST="${POST}<ser:name/>"
fi

if [[ "${7}" != "${NULL}" ]]
then
POST="${POST}<ser:active>${7}</ser:active>"
else
POST="${POST}<ser:active/>"
fi

if [[ "${8}" != "${NULL}" ]]
then
POST="${POST}<ser:allowOverride>${8}</ser:allowOverride>"
else
POST="${POST}<ser:allowOverride/>"
fi

if [[ "${9}" != "${NULL}" ]]
then
POST="${POST}<ser:allowedMulty>${9}</ser:allowedMulty>"
else
POST="${POST}<ser:allowedMulty/>"
fi

if [[ "${10}" != "${NULL}" ]]
then
POST="${POST}<ser:description>${10}</ser:description>"
else
POST="${POST}<ser:description/>"
fi

POST="${POST}<ser:entitlementRef/>"

if [[ "${11}" != "${NULL}" ]]
then
POST="${POST}<ser:grantApprovers><ser:approver>
<ser:approverDN>${11}</ser:approverDN>
</ser:approver>
</ser:grantApprovers>"
else
POST="${POST}<ser:grantApprovers/>"
fi

if [[ "${12}" != "${NULL}" ]]
then
POST="${POST}<ser:grantRequestDef>${12}</ser:grantRequestDef>"
else
POST="${POST}<ser:grantRequestDef/>"
fi

if [[ "${13}" != "${NULL}" ]]
then
POST="${POST}<ser:grantQuorum>${13}</ser:grantQuorum>"
else
POST="${POST}<ser:grantQuorum/>"
fi

if [[ "${14}" != "${NULL}" ]]
then
POST="${POST}<ser:revokeApprovers><ser:approver>
<ser:approverDN>${14}</ser:approverDN>
</ser:approver>
</ser:revokeApprovers>"
else
POST="${POST}<ser:revokeApprovers/>"
fi

POST="${POST}<ser:parameters/>"

if [[ "${15}" != "${NULL}" ]]
then
POST="${POST}<ser:revokeRequestDef>${15}</ser:revokeRequestDef>"
else
POST="${POST}<ser:revokeRequestDef/>"
fi

if [[ "${16}" != "${NULL}" ]]
then
POST="${POST}<ser:revokeQuorum>${16}</ser:revokeQuorum>"
else
POST="${POST}<ser:revokeQuorum/>"
fi

if [[ "${17}" != "${NULL}" ]]
then
POST="${POST}<ser:provisioningRequestDef>${17}</ser:provisioningRequestDef>"
else
POST="${POST}<ser:provisioningRequestDef/>"
fi

if [[ "${18}" != "${NULL}" ]]
then
POST="${POST}<ser:owners><ser:dnstring>
<ser:dn>${18}</ser:dn>
</ser:dnstring></ser:owners>"
else
POST="${POST}<ser:owners/>"
fi

if [[ "${19}" != "${NULL}" ]]
then
POST="${POST}<ser:resourceCategoryKeys><ser:categorykey>
<ser:categoryKey>${19}</ser:categoryKey>
</ser:categorykey>
</ser:resourceCategoryKeys>"
else
POST="${POST}<ser:resourceCategoryKeys/>"
fi

POST="${POST}</ser:modifyResourceRequest>\
</soapenv:Body>\
</soapenv:Envelope>"

if [[ "X$_RBPM_SOAP_ROLE_DEBUG" = "Xtrue" ]]
then
echo
echo POST data:
echo $POST
echo
fi

# Issue the request
curl $_CURL_OPTIONS -u "$1:$SENHA" -H "$CTYPE" -H "$ACTION" -d "$POST" "$URL" -o "$4"
}


Take a look at this script and let me know what you think. I have found this toolkit immensely helpful when working with Roles. I have used it to bulk grant hundreds of roles, and thousand of Role assignments. I wrapped this script file in one of my own that reads a CSV file and processes all the lines, one after another which makes this so much more powerful.

Here is hoping this series dissecting how it works will help others get used to it, and figure out how to extend it further. If you have an User App SOAP API you need to call in bulk and need to modify or add to Ferando's example scripts, please make sure to either upload it as an article or else post it as a comment. This way everyone can share on it.

He only implemented some of the functions, there are many more still not covered so lots of room for improvement.

DISCLAIMER:

Some content on Community Tips & Information pages is not officially supported by Micro Focus. Please refer to our Terms of Use for more detail.
Top Contributors
Version history
Revision #:
1 of 1
Last update:
‎2017-01-30 22:31
Updated by:
 
The opinions expressed above are the personal opinions of the authors, not of Micro Focus. By using this site, you accept the Terms of Use and Rules of Participation. Certain versions of content ("Material") accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company. As of September 1, 2017, the Material is now offered by Micro Focus, a separately owned and operated company. Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.