Adding New Functions to the UA Bash extension - Part 2

Adding New Functions to the UA Bash extension - Part 2

Fernando Freitas of NTS fame wrote a great shell script extension to support the RBPM SOAP functions. Get it here: BASH functions to perform SOAP calls to RBPM

The problem is he only implemented the Role service WSDL functions and there are many more in User App that might be needed.

Specifically I need the createResource function from the Resource service WSDL. So how to extend it? Read this series, and continue on as I work through that process.

When last we left off, you should have gotten the RBPM script from the URL above, gotten the WSDL you are interested in, opened it in SOAP UI and copied out the text of the function you care about.

Our example is createResource which looks like this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://www.novell.com/resource/service">
<soapenv:Header/>
<soapenv:Body>
<ser:createResourceRequest>
<!--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:createResourceRequest>
</soapenv:Body>
</soapenv:Envelope>


Now, like all good scripters we proceed to plagiarize someone else's work, since why should we reinvent the wheel when someone else did most of the work already.

Open up Fernando's script, and copy out an example of a function that looks vaguely similar. In my case, I will start by modifying the createRole function since that seems like a reasonable one to start with.

This function looks like this:

### Function: createRole
# Usage:
# createRole $username $password $rbpm_url $output_file $rolename $description $rolelevel $category $correlation_id
# if the $correlation_id is omitted, will call createRoleRequest, otherwise will call createRoleAidRequest
# if the category is omitted with use the value "default"
# Order of parameters is important, it is not possible to use the correlation_id and skip category
#
createRole()
{


USAGE="Function Usage:

createRole username password rbpm_url output_file rolename description rolelevel category correlation_id
if the correlation_id is omitted, will call createRoleRequest, otherwise will call createRoleAidRequest
if the category is omitted with use the value "default"
Order of parameters is important, it is not possible to provide a correlation_id and skip category at the same time
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" ]]
then
echo "$USAGE"
return 1
fi

if [[ -z "$8" ]]
then
CAT=default
else
CAT="$8"
fi

if [[ -z "$9" ]]
then
NOCID=true
else
NOCID=false
CID="$9"
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}/role/service"

if [[ "$NOCID" = "true" ]]
then
ACTION="SOAPAction: 'http://www.novell.com/role/service/createRole'"
SOAPCALL=createRoleRequest
else
ACTION="SOAPAction: 'http://www.novell.com/role/service/createRoleAid'"
SOAPCALL=createRoleAidRequest
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/role/service'>\
<soapenv:Header/>\
<soapenv:Body>\
<ser:${SOAPCALL}>\
<ser:role>\
<ser:approvers/>\
<ser:container/>\
<ser:description>${6}</ser:description>\
<ser:entityKey/>\
<ser:name>${5}</ser:name>\
<ser:owners/>\
<ser:quorum/>\
<ser:requestDef/>\
<ser:revokeRequestDef/>\
<ser:roleCategoryKeys>\
<ser:categorykey>\
<ser:categoryKey>${CAT}</ser:categoryKey>\
</ser:categorykey>\
</ser:roleCategoryKeys>\
<ser:roleLevel>${7}</ser:roleLevel>\
<ser:systemRole>false</ser:systemRole>\
</ser:role>"

if [[ "$NOCID" = "false" ]]
then
POST="${POST}<ser:correlationId>${CID}</ser:correlationId>"
fi

POST="${POST}</ser:${SOAPCALL}>\
</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 -k -u "$1:$SENHA" -H "$CTYPE" -H "$ACTION" -d "$POST" "$URL" -o "$4"
}


Now lets break that down, so it is easy to understand. There are two easy parts to address first, the beginning and the end. The beginning is really just commentary, explaining what the function does. Please remember to update this, since it is really handy to have notes to help people later understand what is going.

The end is the final actual request.

curl $_CURL_OPTIONS -k -u "$1:$SENHA" -H "$CTYPE" -H "$ACTION" -d "$POST" "$URL" -o "$4"


This says, use the program 'curl', which is a tool for doing operations over HTTP (and by implication HTTPS), in which we send in the _CURL_OPTIONS variable values. Per above, this is -sS, since -k is not working, so we hard code in -k after it. The -u is for username, which was built earlier as username:password, in the "$1:$SENHA" bit. We will see later how that is put together. The -H is for a header, which is set in the variable CTYPE and ACTION earlier, so we could potentially add additional headers if needed. The -d is the actual data being sent, which is built in the variable POST, and then finally the actual URL of the endpoint. This is the base URL that is passed in, followed by the relative path to the web service endpoint. In the role case it is a base of https://myServer.acme.com:8443/IDMProv followed by /role/service but in the resource case, we need to remember to change that to /resource/service for it to work. The -o switch is for storing output to a file, which we named in the variable 4. The $4 or $number variables are a reference to command line parameters, so this is the fourth thing on the command input line.

Lets look at the beginning and see the data that needs to be sent in to understand what is coming on the command line, and how we will need to change that for the Resource case. If we look at the commentary header for this function, we will see:

### Function: createRole
# Usage:
# createRole $username $password $rbpm_url $output_file $rolename $description $rolelevel $category $correlation_id
# if the $correlation_id is omitted, will call createRoleRequest, otherwise will call createRoleAidRequest
# if the category is omitted with use the value "default"
# Order of parameters is important, it is not possible to use the correlation_id and skip category


So the command line something like:
createRole $username $password $rbpm_url $output_file $rolename $description $rolelevel $category $correlation_id


The first few are obvious: username, password for the user to perform these actions. The rbpm_url is the full URL of the endpoint. The output_file is the $4 we saw above, where the logging info is written. Now we get into the specifics of this tool. Rolename ($5), description ($6), rolelevel ($7), category ($8), and correlation_id ($9) are the inputs.

We need to think about how these will change for a Resource creation vs a Role creation. To do that we look into the SOAP function. This is usually a bit iterative, as you do not always know the right answer at first without some experimentation.

Looking at the SOAP above, I see some obvious things we need at first. Midway through there is a <name> node (remember, I am ignoring namespaces for simplicity's sake, but it is of course really <ser:name>) that we will probably pass in the variable $5 but rename the command parameter to resourcename to keep it consistent. There is a <description> node, so lets keep that as well. Now that we are into new territory, as most of the rest of the Role stuff no longer applies (Except correlationId, but that is best left as the last field, to allow it to be skipped if we want) lets start looking at the SOAP from the top.

Hmm, <active> is probably important. I suppose we could just hard code it to true every time, but lets make it a parameter just in case. Lets do the same for <allowOverride> and <allowedMulty>. Now you can control what the values are, for these booleans. I think that allowOverride is one of those settings in the UA that I do not fully understand. The allowedMulty I think is related to the DirXML-Entitlement setting that allows the same entitlement to be granted multiple times but I am not certain. I usually default them to true so that is probably ok.

That gives us a command line more like this:

createResource $username $password $rbpm_url $output_file $resourcename $description $allowoverride $allowedMulty $correlation_id


But we have not handled the really important thing in a Resource, the entitlement value that is statically linked with it. I do not have time for a long discussion of Roles, Resources, and Entitlements here, but you can read way more than you probably care about to know in this series of articles I wrote. In the first two, you will see what NetIQ was recommending as a way to retrofit Resource support to an older driver, and then my discussion of what it is, those policies were actually doing. They reported the WHAT, I reported the WHY.
- Convert Driver Entitlements to New RBPM 3.7 Resource Model
- Converting Entitlements to Resources, more details

Then lots of discussion about how entitlements works in the this series:
Series: Talking about Entitlements

Short point that matters. Entitlements can have no values, administrator defined values, or query based values. Thus one Entitlement object can provide service for thousands of different things in the end system. Alas with Resources, we have to statically define a Resource object for every single possible Entitlement value. This is frustrating, but also why scripting this is so critical. If I have 18,000 groups that I wish to deliver via Roles, I would need to make 18,000 Roles so I could deliver them, that is life. But I also now have to make 18,000 Resources as well. They are an interesting abstraction, but they sometimes seem like more of a pain than they are worth.

Having said all that, the key takeaway is that the thing you really need to set on a Resource, is the Entitlement payload. Looking into the SOAP document there seem to be two areas this could be set. The documentation for this is pretty weak as far as I can find, so I have to rely on experimentation to find out the answer.

It seems like the part near the top that is the nrfentitlementRef, is the important one. Which makes sense as the attribute on the nrfResource object that holds the Path syntax attribute value is called nrfEntitlementRef looks like this:

            <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>


Later in the WSDL there is a section near the end:

            <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>


This also looks like it would be a way to specify the Entitlement payload value (the stuff that on the nrfResource is stored i n nrfEntitlementRef, and when applied to a user is stored in the DirXML-EntitlementRef), but it seems like you do not have to use this section, so I think we can ignore this entire segment and focus on the first one.

In that first <nrfentitlementRef> we have a entitlementCorrelationId, and I am not sure what to write in there, so I think I will ignore it and see what happens. The entitmentDn is critical, and almost certainly has to be the LDAP format for the Entitlement object this is referencing. We will need to pass that, so add it to the command line. Next we need the entitlementParamaters, which should be the payload, that in an Entitlement grant, is the <param> node of the path.xml component. The value you pass in here depends on how you defined your entitlements in your entitlementConfiguration object for the driver. Is your paremeter-format="legacy" or ="idm4"? Legacy means any string is fine. If you specified idm4, then the data MUST be in JSON format. JSON is the JavaScript Object Notation format, that is basically curly braces, with key/value pairs, surround by quotation marks, colons between key and value, and commas between pairs. Thus a typical value might be (I made this example up by hand so excuse me if it is mostly nonsense):
{"ID":"someGUIDLookingValue","ID2":"cn=GroupName,ou=Groups,dc=domain,dc=com"}

Now I think we may have to experiment with how this gets passed on the command line, since the quotation and commas might cause issues with the script. But we need to pass this in, so that is another command line addition.

There are values for grantApprovers, grantRequestDefs, grantQuorum owners, provisioningRequestDef, revokeApprovers, revokeRequestDef, revokeQuorim and some other things. While we could potentially expand this to support them, that starts getting mighty complicated, and for my specific use case is not needed. Also we get into trouble since these are not mandatory things, and then we have issues with deciding what to do if they are skipped.

What I would suggest, if you need any or all of those extra functions is simply copy my finished example once this is all done, and expand it, with a new function name (Perhaps call it createRole-Approver, createRole-Revoke) and add in the mandatory things you need for those cases. This way you can have different sets of required parameters on the command line for each of your functions that need to do extra work. Supporting every possible combination in one function is probably more effort than it is work. To put that another way, it needs someone who is much better at scripting than I am. These people do exist, and it is possible that Fernando is one of those people, and you can see in the way he handled correlationId an approach to handling it, but I am not entirely sure how to expand that greatly beyond a single optional parameter. Thus this simpler approach may just be easier.

One last setting that I happen to think is important is the Category key.

            <ser:resourceCategoryKeys>
<!--Zero or more repetitions:-->
<ser:categorykey>
<ser:categoryKey>?</ser:categoryKey>
</ser:categorykey>
</ser:resourceCategoryKeys>


Just seeing that tells me that the Resources can actually have more than one category assigned to them, which I did not know before, so that is interesting. What does seem to be missing is how to specify the container I want the Resource created in. There is support for containers under the Role and Resource objects, which is nice to help sort things, when you have many thousands of Roles and Resources. Categories is a good way to sort them as well. I will see if I can figure out the container thing, but until then, at least lets have the category added to the command line. So now that looks something more like this:

createResource $username $password $rbpm_url $output_file $resourcename $description $allowoverride $allowedMulty $entitlementDN $entitlementParam $category $correlation_id


Now that we have examined the WSDL, decided what we need to fill in, built up the command line and the variable names we are ready to start working inside the body of the function to actually make it all happen the right way. I will start on that in the next article.


Tags (2)

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:
‎2016-02-05 19:39
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.