Adding New Functions to the UA Bash extension - Part 1

1 Likes
over 5 years ago

Fernando Freitas, of Novell Technical Support fame, wrote a very very useful shell script that adds command line like functions to make some of the SOAP calls found in the User Application.

Previous to this, it was a bit hard to do bulk operations on things that needed to be done via the SOAP calls. You could use a tool if you had one, but what if you did not? I have a customer who wrote a PowerShell script to do the functions they needed. What Fernando did that was very clever was to make it a Bash script, that when executed properly, acts as though there are now command line functions for each of the various API calls in the Role WSDL.

First things first, go get his script at Cool Solutions here, and play with it for a bit.
BASH functions to perform SOAP calls to RBPM  (Sep 2021, updated the link to work)

Next you will realize as you read through the shell script that he did not implement every function. To be fair, he actually says that in the script comments that some functions are missing, but those were mostly because he could not quite figure out the proper use case to make it usable in this format.

Thus you can call requestRoleAssignment() with the proper parameters and add a user to a role. But now you can do it from a command line. You can use that to revoke a role. So if you wanted to integrate, say Sentinel or Access Review with IDM Roles, you potentially could fix things by removing users from Roles, by using a shell script now, instead of having to write a SOAP client of some kind that those apps could use. (Many monitoring, security, etc apps will support local scripts). But there is more. With a little bit of Googling around you can fairly easily write a wrapper script that will read a CSV file of values, and batch process them now. (In fact I did that. I am trying to decide if I wish to share that or not still. Lets see how this series goes). But honestly it is not that hard, just steal examples from other scripts elsewhere, that is all I did.)

I personally had a client who needed to add roles (ok, requestRoleAssignment, that was easy), rename some roles (ok, setRoleLocalizedStrings, next) and link some resources to roles (createResourceAssociations, what's next?). Once I had my wrapper script that would read a CSV, parse it, and then call the appropriate function with the write parameters for each function, bulk operations were a piece of cake. We renamed 800 Roles in minutes. Just got the LDAP DN's of the Roles, the new name, the new Description into our CSV, and ran it through the script and done.

We needed to bulk load 18,000 users into Roles, no problem, make a CSV and send them through. Now in this case, we actually did it in 1000 user increments because it was a live User App and it seems like a bad idea to make our own Denial of Service attack on it. (Though of course, easy enough to add a pause in between if I wanted I suppose. That is not a bad idea for an enhancement).

As I got further into this, I realized that my Resource association had a twist, I actually needed to make the Resources first. With entitlement values, and tens of thousands of them. Oh dear, that would be painful, but then I thought, no big deal, I will call createResource first. I went looking in the script for the function and that is when I realized it looks like he only implemented the Role service WSDL, which is amazingly useful. But I needed stuff in the Resource service WSDL. What can I do? I hear Fernando works at a consulting firm now, so I guess I could try to pay him to do it. Or nag him to do it as a favour. I do have that embarrassing photo of him, and the B-29 Bomber named Fifi (Don't ask, but if you do, glad to share it). Blackmail, that could work.

However, that is just not my kind of thing (Blackmail, that is, other stuff, maybe) so I figured, if he could do it, how hard can it be? Well it turns out Fernando is truly a clever fellow and the approach he took is really easy to extend, it is just takes a few minutes to figure out what he did, so that you can reuse his approach for any function in any WSDL.

As I worked through the process, I figured it would be really helpful to write up HOW you would extend his approach for any arbitrary SOAP function. Then I will ask, that if you go through the process of doing it, add the code in a comment to this or his article so everyone can share it. It is a tiny bit fiddly, and I do not have a fast way of doing it, and I am lazy so I will probably only do the functions I personally need. (Unlike Fernando who was quite selfless and did the entire Role service WSDL). But if you follow these directions and implement one, please post it so Fernando can incorporate it into his, making it better for everyone.

So what do you need to do this? First get a copy of Fernando's script. One strange bug that I do not understand is that the variable _CURL_OPTIONS is defined with the value of "-k -sS". The dash k means ignore certificate trust chain issues. This is critical, since often User App does not have a public facing SSL interface, with a signed certificate from a well known Certificate Authority (CA). Also I have never quite figured out the exact format that curl is expecting for the chain of trusted CA's so I just use the -k option which makes it not care. I know there is no one in my network impersonating my User App, so this is not a security risk. The -sS option means run curl in silent mode, only showing output when there is an error. However, on SLES recently, the -k option is causing an error. I found if I remove it from the variable, and leave just -sS, then on each line where curl is called, which usually looks something like this:

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



I would just stick a -k between the $_CURL_OPTIONS and -u options.

Try some of the commands one by one and see how they how work. Look at the Usage text it shows, and get comfortable with it.

Then figure what API call you need to implement that he missed. In my case, it is createResource, which I know is in the Resource service WSDL, not the Role service WSDL. There are actually 6 service WSDL's in User App 4.02:

  1. Role

  • Resource

  • Notification

  • Provisioning

  • DAL

  • Metrics



The trick is that the User App interface did not show the Resource WSDL, you had to infer the URL by going to say Role, which is:
https://myUAServer.acme.com:8443/IDMProv/role/service?wsdl

and changing the role to resource, and then you could get the resource WSDL. Log in to User App as your admin account, go to the Role/Resources Tab (If you started at the Landing/Home page click on Manager Roles or Manage Resources to get there) and then click on the Configure RBPM link in the bottom left of the page.

On the configuration page you will find some useful things, like the Entitlements Query refresh (CODE MAP Refresh functions) that is very handy, but in 4.02 in the bottom left will be links to the 5 WSDL's. I think in 4.5 they got rid of the links there, and instead just list the URL's in the documentation. Not a big deal, they do not really change from installation to installation except for getting the proper endpoint URL embedded.

Thus first step for me was getting resource WSDL. Then I opened it in a tool like SOAPui which can parse the WSDL and show you the available functions. I browsed down to createResource, and grabbed the sample SOAP document, 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>



If this is your first SOAP interaction, welcome to the wonderful, verbose, and scary looking world of SOAP.

Quickly, SOAP uses an XML document. It has to start with a Envelope node, which has at least one or two child nodes. Header if you are using it. Not all SOAP implementations do, but I ran into one, IMS Global's LIS 2.0 standard, I think that uses the Header node for reporting errors. A sibling to Header (and also a child of Envelope) is Body. This is where the vast majority goes.

Now as you look at my example, you see that it says:

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



Not

	<Envelope>
<Header/>
<Body>



as my discussion would indicate. What is that soapenv: stuff all about? That is called a namespace, which more accurately defines the node. That is, in the first node, you see the namespace declaration for soapenv and ser, as:
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://www.novell.com/resource/service"

That is, xmlns (XML Name Space), named soapenv, is a reference to a URL that should properly define it. However these are often fake URL's. I do not pretend to understand the reasoning behind namespaces, but I know how to use them. You should be familiar with these from DirXML Script policies, where if you want to call a custom Java function you need to define a namespace for it in Policy Builder (stored in the <policy> node, much as the examples above).

Then under the Body node, is usually the actual Request. When you submit it, you should get back a similar XML document, but the node will be functionNameResponse to your functionNameRequest.

So although the function we want is createResource, we send a createResourceRequest document, which makes sense. Now inside that document is a <resource> node (using the ser namespace, so it looks like <ser:resource> but I am lazy and will skip namespaces for simplicity of reading when I have to type them out).

Now we have the actual SOAP document we need to send, we just need to customize it with the data it requires to do its job. If you look at some of the examples in Fernando's script you will see that some functions require one or two pieces of data to be inserted, others require 11. It all depends on what the function is doing.

But even before that, you need to understand how the SOAP call works. Good luck with that. People who write SOAP interfaces seem to think the answer to the question "What should it look like?" is entirely defined in the WSDL. My experience is that yes, we have nodes like:

	<!--Optional:-->



That tell us the stuff below this is optional, so can be there, or not. Similarly we get nodes like:

	<!--Zero or more repetitions:-->
<!--One or more repetitions:-->



These tell us we can one or zero instances of this block of XML or more. This allows for multivalued like things.

But this does not tell us what valid data inside a field might be. For example, in our createResource example we see a line:

	<ser:active>?</ser:active>



The question mark means, fill in data here. But what values? True? False? 0? 1? Y? N? It turns out that it took me a few years of doing this to find out that SOAPui does not show you some of the info that is in the WSDL, including allowed values for some of the fields. If you open the WSDL in a text editor, near the top, you will see something like (Using Role WSDL since it has some interesting ones):

	 <xsd:simpleType name="IdentityType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="USER"/>
<xsd:enumeration value="GROUP"/>
<xsd:enumeration value="CONTAINER"/>
<xsd:enumeration value="ROLE"/>
</xsd:restriction>
</xsd:simpleType>



So that tells you some of the available values for a particular field in the Role WSDL. In the case of the active node, the resource WSDL says:

	      <complexType name="Resource">
<sequence>
<element name="active" type="xsd:boolean"/>
<element name="allowOverride" type="xsd:boolean"/>
<element name="allowedMulty" type="xsd:boolean"/>
<element name="description" nillable="true" type="xsd:string"/>
<element name="entitlementRef" nillable="true" type="tns:NrfEntitlementRefArray"/>
<element name="entityKey" nillable="true" type="xsd:string"/>
<element name="grantApprovers" nillable="true" type="tns:ApproverArray"/>
<element name="grantQuorum" nillable="true" type="xsd:string"/>
<element name="grantRequestDef" nillable="true" type="xsd:string"/>
<element name="name" nillable="true" type="xsd:string"/>
<element name="owners" nillable="true" type="tns:DNStringArray"/>
<element name="parameters" nillable="true" type="tns:ResourceParameterArray"/>
<element name="provisioningRequestDef" nillable="true" type="xsd:string"/>
<element name="resourceCategoryKeys" nillable="true" type="tns:CategoryKeyArray"/>
<element name="revokeApprovers" nillable="true" type="tns:ApproverArray"/>
<element name="revokeQuorum" nillable="true" type="xsd:string"/>
<element name="revokeRequestDef" nillable="true" type="xsd:string"/>
</sequence>
</complexType>



Thus we see that active is boolean, which almost certainly means a simple true or false is the proper value. The other types, are defined also in the top of the WSDL so you can look up what a tns:NrfEntitlementRefArray should look like.

To recap, at this point you have the RBPM Bash shell script, you have played with it. You fixed the strange curl -k issue with using a variable, and you got the WSDL of the function you wish to work on. A bit of explanation of what SOAP documents look like, and we should be ready to move on to actually doing something, after all that introductory stuff.

Tags:

Labels:

How To-Best Practice
Comment List
Anonymous
Related Discussions
Recommended