tag:blogger.com,1999:blog-166195632024-03-14T02:08:11.581+00:00Joined Up Thinking In Identity & Access ManagementIn a world where technology is supposed to make things simpler, why is it that the world seems to be more complicated? This blog is made up of the ramblings of an IT Security Consultant specialising in IBM Security software with a heavy focus on IGI, ITIM/ISIM, ITAM/ISAM and ITDI/ISDI.
All opinions expressed are my own and have nothing to do with any employer past or present. I hope you find them useful.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.comBlogger107125tag:blogger.com,1999:blog-16619563.post-22525625264155358322021-01-29T18:21:00.001+00:002021-01-29T18:21:08.058+00:00Introducing... Madigan Solutions<p>This blog hasn't seen much action over the last year or so. That's not because I haven't got things to say, but I was definitely lacking in time to do so.</p><p>In any case - I've been busying myself with a new venture and most of the tooling and content of my personal website will disappear in the coming days.</p><p>Instead, I would encourage avid readers (and there may be one or two of those) to pop along to <a href="https://www.madigansolutions.com/">https://www.madigansolutions.com/</a>.</p><p>This blog can now be considered "functionally complete" and probably irrelevant given the dates on many of the articles.</p><p>It's been a blast and it will continue to be a blast with the rest of the Madigan folk.</p>Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com1tag:blogger.com,1999:blog-16619563.post-3630253599930259182019-07-29T20:16:00.002+01:002019-09-11T16:56:59.048+01:00IGI & NFS MountsThe IBM knowledge base reads like it was constructed by a technical writer who has very little experience of actually using the products they are describing. At least, that is how I read most of the official documentation.<br />
<br />
Frequently, documentation might tell you how to do something, but it won't necessarily tell you why you should do it; what circumstances would require you to do it; and even the steps described are often missing some key piece of information.<br />
<br />
The documentation that supports how to create an NFS Mount Point for Enterprise Connector input files is a case in point:<br />
<br />
<ul>
<li>the documentation describes what you need to do, ish</li>
<li>the documentation is lacking why you need to do it</li>
<li>the documentation only provides one side of the NFS equation</li>
</ul>
<br />
<br />
<h4>
Why would you move your files to an NFS Mount Point?</h4>
If you have an IGI cluster (and if you are running a Production Service, you really ought to have a cluster), then you MUST use NFS for your input files! You need all of your cluster members to be accessing the same source repository for your files and the ONLY way to do that is via NFS.<br />
<br />
<h4>
What do you need to do at the NFS Server side?</h4>
Well, let's take a Red Hat based system as an example for hosting our input files. You would perform the following tasks:<br />
<blockquote class="tr_bq">
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: x-small;">yum -y install nfs-utils</span></blockquote>
<blockquote class="tr_bq">
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: x-small;">useradd -u 50001 identity</span></blockquote>
<blockquote class="tr_bq">
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: x-small;">mkdir /nfsroot</span></blockquote>
<blockquote class="tr_bq">
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: x-small;">mkdir /nfsroot/connectors</span></blockquote>
<blockquote class="tr_bq">
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: x-small;">chown -R identity:identity /nfsroot/connectors</span></blockquote>
<blockquote class="tr_bq">
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: x-small;">echo "/nfsroot 10.10.10.0/24(rw,no_subtree_check)" > /etc/exports</span></blockquote>
<blockquote class="tr_bq">
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: x-small;">systemctl start nfs</span></blockquote>
<blockquote class="tr_bq">
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: x-small;">systemctl enable nfs</span></blockquote>
<br />
NOTE: Change 10.10.10.0 to your subnet as appropriate.<br />
<br />
NOTE: Update your firewall rules to allow traffic on ports 111 (TCP/UDP) and 2049 (TCP).<br />
<br />
<h4>
What do you need to do at the IGI Appliance side?</h4>
Navigate to <b>Manage</b> > <b>Network Settings</b> > <b>Network File System</b>, then click on the New icon.<br />
<br />
Complete the following:<br />
<br />
<ul>
<li><b>Host Name</b>: Linux Server Name or IP Address which hosts your NFS service</li>
<li><b>Remote Directory</b>: /nfsroot</li>
<li><b>Local Directory</b>: linuxserver</li>
</ul>
<br />
NOTE: Change <b>linuxserver</b> to whatever label you desire.<br />
<br />
<h4>
What do you need to do at the IGI Application side?</h4>
Navigate to <b>Enterprise Connectors</b> and create your CSV based connector as normal but within the Input Folder attribute, use the following syntax:<br />
<blockquote class="tr_bq">
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: x-small;">/nfsmount/linuxserver/application</span></blockquote>
<br />
NOTE: Change <b>linuxserver</b> to whatever you set within the Appliance.<br />
NOTE: Ensure that the <b>application</b> folder exists on the Linux Server for the relevant application you are creating a connector for. (Ideally, this would mirror your application name, right?)<br />
<br />
And that, my friends, is that!Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-8545733844369241452019-01-31T08:33:00.001+00:002019-01-31T08:33:15.027+00:00IAM Respite - Champagne TimeI say Champagne Time. I don't really mean that. I mean there's a bottle of Champagne up for grabs as part of my <a href="https://www.stephen-swann.co.uk/games">Guinness Six Nations 2019 predictor game</a>.<br />
<br />
It's a simple game. 15 matches. 15 score predictions. The most accurate person walks off with Champagne (or a substitute drink of their choice).<br />
<br />
It's free to enter; takes only marginal skill; and enhances the joy of the tournament.<br />
<br />
Don't be shy - <a href="https://www.stephen-swann.co.uk/games">give it a go</a> and forget all about that Identity & Access Management stuff for a few minutes.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-42266651645472948702018-04-16T07:48:00.000+01:002018-04-16T07:48:37.843+01:00IGI Internal EventsIGI gurus should understand that adding rules to Live Events is really the only way you are going to get maximum value from your IGI deployment. Out of the box features within identity tools are rarely sufficiently detailed enough to allow for production deployment and there is always a need to <i>enhance</i> these processes with organisation-specific rules and definitions.<br />
<br />
IGI gurus will also be aware that the firing of rules can be somewhat hit and miss. Placing objects into the <b>USER_ERC</b> table will fire a Live Event on the IN queue, but writing an advanced rule to perform a SQL update of the same database table using the internal scheduler will NOT fire a Live Event!<br />
<br />
Similarly, modifying a user in the Administration Console will not fire an event either unless you specifically "Enable Internal Events" on your Settings tab.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6GUqzY9iaDcxqBOe50NFG9SYYrPk1QZFPTnZ_w4GxW7C4qWomW-_uUjyZU8v1ZMU3vMOVMTHKSI2Xqs1dyXMRAcuIK6oZjfNf2-QhOmRee3hzBwzGd_uCMWbJNuYsf46gX55ltQ/s1600/Internal+Events.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="528" data-original-width="1182" height="142" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6GUqzY9iaDcxqBOe50NFG9SYYrPk1QZFPTnZ_w4GxW7C4qWomW-_uUjyZU8v1ZMU3vMOVMTHKSI2Xqs1dyXMRAcuIK6oZjfNf2-QhOmRee3hzBwzGd_uCMWbJNuYsf46gX55ltQ/s320/Internal+Events.png" width="320" /></a></div>
<br />
<br />
Enabling Internal Events, however, will only allow a handful of events to trigger a rule, For example, you can add business logic to the Add Entitlement event, but you cannot add business logic to the Publish Entitlement event or the Add Entitlement to an OU event. Maybe one day this will be enabled (and I really hope some of the IBM Development Team read this)!<br />
<br />
Anyone who has ever enabled internal events, then clicked on the <b>Monitor/INTERNAL</b> tab to view the events may be sorely disappointed. I call it the "Tab of Disappointment" and why? Well, it will be empty. No matter what events are being fired, it will be empty. That is, until you add this little gem to your system:<br />
<br />
<span style="color: blue; font-family: Courier New, Courier, monospace;">when</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> event : EventBean( )</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;">then</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;">/*</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> * Version: 1.0</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> * Date : 2018-02-28</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> * Purpose: Saves the event so it is viewable in the logs</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> */</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> event.setState(1l);</span><br />
<br />
This piece of code should be added to the BEFORE ruleflow within <b>Rules/Live/Internal</b>. This code instructs the platform to always save the event within the database tables which makes the event visible in the <b>Monitor/INTERNAL</b> tab.<br />
<br />
Now you can see what is going on in your system and maybe even replay events.<br />
<br />
NOTE: Should you wish to look in your log files for anything you may be spewing out from your rules within <b>Rules/Live/Internal</b>, you may have to look at the <b>accessgovernancecore_event_out.log</b> file. Don't ask why!Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-86570721737333656512018-01-15T15:32:00.000+00:002018-01-15T15:32:31.731+00:00Déjà Vu - IGI Handling of AD Dormant AccountsI know I had this problem years ago with ISIM. Why do I have this problem again with IGI?<br />
<br />
Years ago, the ISIM adapter for Active Directory would reconcile the Last Logon Date that was specific to which ever Domain Controller the adapter connected to. That meant, of course, that the REAL last logon date for a user was not being reconciled and any life-cycle rule built to query that attribute and act upon the value contained therein would, in all probability, cause a major "hoo-ha".<br />
<br />
IBM resolved the issue by adding <b>erADLastLogonTimeStamp</b> which returned the domain replicated last logon date (albeit, the date could be +/- 14 days in accuracy for reasons which Bill Gates would be best to explain).<br />
<br />
IGI is a new product but uses the ISIM adapters. However, the internal IGI mapping for of Last Logon is using the old <b>erADLastLogon </b>date rather than the slightly more reliable <b>erADLastLogonTimeStamp</b>.<br />
<br />
Doh!<br />
<br />
What are these crazy kids thinking? Did the ISIM guys not talk to the IGI guys? (That said, I've checked the latest <b>resource.def</b> file for the ISIM adapter and I'm disappointed to report that the <b>erLastAccessDate </b>mapping is set to <b>erADLastLogon</b>!).<br />
<br />
In any case... if you want to run any kind of dormancy rule on Active Directory accounts in IGI, make sure you do this BEFORE you reconcile your service:<br />
<br />
<span style="color: #0b5394; font-family: Courier New, Courier, monospace;">update itimuser.entity_schema_mapping set custom_attribute_name='eradlastlogontimestamp' where custom_class_name='eradaccount' and system_attribute_name='erlastaccessdate';</span><br />
<br />
This little gem will ensure that the correct AD attribute is used as the last access date rather than the pitiful <b>erADLastLogon </b>attribute which is borderline useless.<br />
<br />
Maybe IBM will update their configuration and <b>resource.def</b> file and documentation at some point (again).Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-88432994213669129022017-12-19T08:29:00.001+00:002017-12-19T08:29:48.098+00:00IGI Default Account Attributes<b><span style="color: red;">DISCLAIMER: This article is not applicable to IGI v5.2.3 or later!</span></b><br />
<br />
Setting account defaults in IGI is rather like setting account defaults in IBM Security Identity Manager (ISIM). For those familiar with both products, you will recognise that the ISIM screens were copied/pasted into the IGI platform with very little alteration. (One difference, of course, is the ability to set enforcement on attributes, but dealing with that is one for another day!)<br />
<br />
Provisioning a new account can be tricky to troubleshoot, however. It seems that the logging levels can be less than helpful in certain circumstances. Consider the dreaded java.lang.NullPointerException! This can be thrown by the provisioning engine when the account defaults code is problematic.<br />
<br />
Consider that we have a need to set an attribute in a provisioning target to the value of ATTR2 on our identity record, but only when ATTR1 is set to Y. You might consider using the following code:<br />
<br />
<span style="color: blue; font-family: Courier New, Courier, monospace;">if (subject.getPropertyAsString("attr1") == "Y") {</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> return subject.getPropertyAsString("attr2");</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;">}</span><br />
<br />
The code looks great, but this code will throw our dreaded NPE in certain circumstances. Not only that, but our logs won't actually tell us which attribute and therefore which section of code is causing our NPE. Why does this happen?<br />
<br />
Well, the provisioning engine MUST get an object returned from our code in ALL circumstances and the above code only returns a value if ATTR1 is set to Y. To avoid our NPE, we need to complete the code as such:<br />
<br />
<span style="color: blue; font-family: Courier New, Courier, monospace;">if (subject.getPropertyAsString("attr1") == "Y") {</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> return subject.getPropertyAsString("attr2");</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;">} else {</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> return "";</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;">}</span><br />
<br />
The addition of a return statement within the ELSE clause will ensure we always return <i>something</i>. Our NullPointerException won't appear again...Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-86331624464767792552017-09-11T09:51:00.000+01:002017-09-28T18:07:14.485+01:00IGI - Careful With Those HierarchiesOne of the beauties of IBM Security Identity Governance & Intelligence (IGI) is the ability to create custom "hierarchies" which I've discussed <a href="http://blog.stephen-swann.co.uk/2016/11/igi-attribute-hierarchies.html">elsewhere in this blog</a>. So much power, yet so much frustration too. These things need a LOT of tender love and care.<br />
<br />
How so? Well, for two reasons:<br />
<br />
<ul>
<li>Batch only rebuilds rather than "on-the-fly" insertions; and</li>
<li>Non-completion of a rebuild</li>
</ul>
<br />
<b><u>
Batch Only Rebuilds</u></b><br />
Hierarchies are rebuilt by a batch process handled within the Task Planner. This rebuild iterates over every PERSON object in the data repository and re-evaluates the hierarchy location for each and every user which is fine for re-synchronisation of a hierarchy against the PERSON objects.<br />
<br />
But if a new PERSON appears in the system, they won't inherit their birthright entitlements until the hierarchy is regenerated. If there are a large number of PERSON objects in the system, a hierarchy rebuild may take some considerable time and may be scheduled to only execute once a day! Hardly ideal.<br />
<br />
Fortunately, the upcoming latest and greatest version of IGI is supposed to address this inadequacy and should support "on-the-fly" insertion of PERSON objects into any hierarchy (and not just the Organisational Unit Hierarchy).<br />
<br />
<b><u>
Non-Completion of Rebuilds</u></b><br />
<div>
The lack of "on-the-fly" insertions of PERSON objects into a hierarchy is doubly troublesome because a hierarchy rebuild may fail! Remember, a rebuild iterates over every PERSON object in the underlying repository. A failure to process any one of those PERSON objects could result in the hierarchy rebuild stopping.</div>
<div>
<br /></div>
<div>
In other words, in a system of 20,000 people, a failure to process person number 19,999 will mean that person number 20,000 will not get processed at all! To put that another way, the system does not FLAG the failure to process a person and basically abandons processing all subsequent users.</div>
<div>
<br /></div>
<div>
Oh dear! So what can cause a failure and should our custom hierarchy construction code be more robust? Unfortunately, our custom hierarchy construction code probably has nothing to do with any failure as there are a myriad of other reasons why a hierarchy build would merely stop (after a bout of hissy-fitting). Take these two examples:</div>
<div>
<ul>
<li>Insertion of a user into a hierarchy triggers the creation of an account as a result of a birthright entitlements attached to the hierarchy node; but the account creation throws an error because it attempts to re-use the Master UID of the user to create the account but an account with that ID already exists!</li>
<li>Insertion of a user into a hierarchy triggers the addition of an entitlement to a user's account as a result of a birthright entitlement attached to the hierarchy node; but the user seems to already own two accounts for that service - something which should not be possible with IGI in the current version (v5.2.2) but seemingly IS possible under certain circumstances!</li>
</ul>
</div>
<div>
<br /></div>
<div>
In other words, it doesn't matter how robust your hierarchy construction code is, the quality of the underlying data could throw quite a major spanner into the works! Of course, a better approach would be for the core IGI engine to handle such issues and at least move on to the next PERSON object to be processed. Maybe in the next release!</div>
<div>
<br /></div>
<div>
Meantime... take care of that data! Get a handle on your orphan management processes and check for duplicates. The following SQL command will help you with that particular diagnosis:</div>
<div>
<br /></div>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">select pw.pwdcfg,pw.person,p.code,count(*)</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">from pwdmanagement pw</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">left join person p on pw.person=p.id</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">where pw.person is not null</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">group by pw.pwdcfg,pw.person,p.code</span></blockquote>
<blockquote class="tr_bq">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-small;">having count(*)>1;</span></blockquote>
Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-8991590653478837332017-08-13T20:12:00.000+01:002017-08-13T20:12:36.580+01:00IGI v ISIM v ISAM v ITDII've been writing this blogs for many years now and I typically use it as a means of recording snippets of information that ideally I would like to refer back to and snippets of information that I think others would be interested in.<br />
<br />
So, what are you guys interested in?<br />
<br />
Well, the beauty of running a blog like this for so long is that I get to see which posts are popular and which are not! I get to see which products you guys are using and abusing. And I can tell you that the popularity of these IBM security products can be easily ranked. In reverse order (by popularity):<br />
<br />
<b>IBM Security Identity Governance & Intelligence (IGI) </b>is the new kid on the block so it is understandable that not many people are interested in what I have to say about IGI. Maybe, over time, this will change. Fingers crossed!<br />
<br />
<b>IBM Security Access Manager (ISAM)</b> has been around forever but it seems that nobody is interested anymore. That's probably a sign that organisations have shifted their focus elsewhere and that federated security (which ISAM handles really well) is the way forward and freebie tools that are SAML and OpenID:Connect ready are preferable?<br />
<br />
<b>IBM Security Identity Manager (ISIM)</b> has been around forever too, but it has always been a heavyweight beast of a product that requires significant investment and therefore is used by huge organisations only. I don't know how many ISIM customers there are out there, but I'm guessing it is a dwindling list given that IGI is seen as the long-term replacement.<br />
<br />
And then we come to <b>IBM Tivoli Directory Integrator (ITDI)</b>. Of all the things I've ever blogged about, it seems that TDI generates the most interest - and by a country mile! As an example, the last time I mentioned TDI, it managed to gather more than 300 times as many page requests as everything to do with IGI put together! One single post!<br />
<br />
TDI is still a tool I go to on a daily basis. It is truly wonderful and flexible and easy to get to grips with. Maybe I should focus more on TDI topics?<br />
<br />
In any case, I'd be interested to hear from whomever out there reads the stuff that I write. What topics would you like me to cover? What products do you think deserve focus? Do you still find any of this information worthwhile?<br />
<br />
I still hope there are plenty of you IBM Security specialists out there helping to deliver a smarter, more secure planet.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-67524016962508049432017-07-24T09:00:00.000+01:002017-07-24T09:00:38.275+01:00IGI Custom Reports - Best Practice GuideA short and sweet "quick tip" update only, I'm afraid. Anyone looking to settle down to a long read will be sorely disappointed as my time is precious and this topic doesn't deserve lengthy discussion.<br />
<br />
Creating custom reports in IBM Security Identity Governance and Intelligence (IGI) is a multi-step process:<br />
<br />
<ul>
<li>Create a SQL query to extract data</li>
<li>Create a "Report" to structure the data according to your needs</li>
<li>Assign the "Report" to a user community</li>
</ul>
<br />
<br />
What happens if you need to update your report to include an additional data element, however? Unfortunately, IGI (as of v5.2.2) does not afford you with the luxury of updating your SQL query to grab the additional data element without deleting the report. What a pain.<br />
<br />
My solution? Don't bother.<br />
<br />
Well... I say don't bother. What I mean is that there is no need to update the SQL query in-situ.<br />
<br />
A better approach might be to create a new SQL query with the additional data element, create a new report as required and associate the new report to the same user community as the old report. The old report can remain in the system although disassociating it from the user community would seem to be a reasonable final step to take.<br />
<br />
When I create a custom report, I like to follow these steps:<br />
<br />
<ul>
<li>Create a SQL query to extract data and add a version number to the name, i.e. "<b>Stephen's Query v1.0</b>"</li>
<li>Create a "Report" to structure the data according to my needs and add a version number to the name, i.e. "<b>Stephen's Report v1.0</b>"</li>
<li>Assign the "Report" to a user community, i.e. <b>IGI Users Called Stephen</b></li>
</ul>
<br />
<br />
When I need to update the report, I follow these steps:<br />
<br />
<ul>
<li>Create a SQL query to extract data and add a version number to the name, i.e. "<b>Stephen's Query v1.1</b>"</li>
<li>Create a "Report" to structure the data according to my needs and add a version number to the name, i.e. "<b>Stephen's Report v1.1</b>"</li>
<li>Assign the "Report" to a user community, i.e. <b>IGI Users Called Stephen</b></li>
<li>De-assign the "Old Report" from the user community</li>
</ul>
<br />
<br />
Of course, an added benefit is that it is much easier to "roll-back" to the previous version of the report if necessary and I didn't have to delete anything.<br />
<br />
<span style="color: red;">NOTE: I would frequently prefix all my custom reports with some identifier which helps me differentiate custom reports from the pre-packaged reports. For example, when providing services to ACME Inc, I may prefix all my reports with <b>ACME </b>and I would NEVER customise a pre-packaged report, but rather create a new custom-version of the report instead.</span>Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com1tag:blogger.com,1999:blog-16619563.post-12974635778388748802017-04-05T08:58:00.002+01:002017-04-11T20:23:39.660+01:00IGI Report BuildingIBM Security Identity Governance is undoubtedly a powerful tool. However, the documentation supplied with the tool could certainly do with some improvement and there is a desperate need for a lot more "technotes" to help deployment consultants get the best value out of the tool.<br />
<br />
As an example, IGI ships with almost 100 canned reports but one important report that seems to be missing is a report listing all the orphan/unmatched accounts within the system.<br />
<br />
Generating such a report should be a breeze using IGI's Report Designer module. But here comes another "however". The documentation doesn't really give you any kind of clue as to how the underlying database schema has been put together and therefore it is difficult to understand which tables, attributes and values should be used to construct such a report. It also doesn't help that in some cases, the naming convention used is somewhat confusing.<br />
<br />
That said, here is how you might construct a simple Orphan Accounts report.<br />
<br />
<b><u>Step 1 - Create Query</u></b><br />
Create a new query called "Orphan Accounts" with the following as the SQL Query:<br />
<br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;">select</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;"> t.name as APPNAME,</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;"> pwdm.code as USERID,</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;"> pwdm.name as FIRSTNAME,</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;"> pwdm.surname as LASTNAME,</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;"> pwdm.email as EMAIL,</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;"> pwdm.dn as DN,</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;"> pwdm.lastlogin as LASTLOGIN</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;">from</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;"> #pmschema#.pwdmanagement pwdm</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;"> left join #pmschema#.target t on pwdm.pwdcfg=t.pwdcfg</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;">where</span><br />
<span style="color: #38761d; font-family: "courier new" , "courier" , monospace;"> pwdm.state > 0</span><br />
<br />
This query pulls out the Application Name, User ID, First & Last Names, Email Address, DN and Last Login Date/Time for those accounts that are in an orphan/unmatched state. The STATE attribute having a value greater than 0 means that the account has not been matched to an identity.<br />
<br />
Make sure that you you select the "Query Column" link at the bottom of the screen and click on IMPORT in order to be left with the following:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZCcTFMPeH8uIOGW-IuYi4YFaVRlQIlaoXzPQkeAnWWzdVi7qHUEMABBSR2IAVKEV-C04p1I1_xXqExynDwJEMvvcTO_f0W_E6WbU4rJBTjcZDB1OTW7TvGyqqWUj7pXV7ZLj_CQ/s1600/step1-query.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZCcTFMPeH8uIOGW-IuYi4YFaVRlQIlaoXzPQkeAnWWzdVi7qHUEMABBSR2IAVKEV-C04p1I1_xXqExynDwJEMvvcTO_f0W_E6WbU4rJBTjcZDB1OTW7TvGyqqWUj7pXV7ZLj_CQ/s400/step1-query.png" width="400" /></a></div>
<br />
<b><u>Step 2 - Create Report</u></b><br />
Now, create a new report called "Orphan Accounts" using the query that you have just created.<br />
<br />
Reorder the columns as necessary within the Columns tab. Under Additional Data, ensure that report output formats are selected. In this case, CSV and XSLX are great options.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfd7nkqnJBs-3udiqJstBvgZ7joBbbWqPYmx3Sc9LHu06v-U9THRlMNDWczbt39e9svfNcnFLn54mZXpxJ0fD0rRr8QGWjsO615nPqYoyK-rp9Yhyphenhyphen6yk1GGjBMjSseI2owqDOmVA/s1600/step2-format.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfd7nkqnJBs-3udiqJstBvgZ7joBbbWqPYmx3Sc9LHu06v-U9THRlMNDWczbt39e9svfNcnFLn54mZXpxJ0fD0rRr8QGWjsO615nPqYoyK-rp9Yhyphenhyphen6yk1GGjBMjSseI2owqDOmVA/s320/step2-format.png" width="320" /></a></div>
<br />
Under the Localization tab, ensure the column names are assigned appropriate business friendly names.<br />
<br />
<b><u>Step 3 - Report Assignment</u></b><br />
The report has now been built, but it needs to be made available to a set of users. Within the Report Designer, navigate to Configure > Assignment > Report/Dashboard -> Entitlement and select the report you have just created.<br />
<br />
Now assign the user community to the report. When clicking on Add, you will be presented by a list of default administrative IT Roles rather than Business Roles. If you want to assign the report to a particular Business Role (i.e. Application Manager), then click on Filter, select Type and select Business Role to see a list of roles.<br />
<br />
Once the report has been assigned to your business role, you can now log in to the Service Centre as a user with that Business Role entitlement and you will see the "Orphan Accounts" report available for execution.<br />
<br />
<b><u>Summary</u></b><br />
Hopefully a comprehensive definition of the schema will be documented and made available at some point in the near future. Meanwhile, hacking around the SQL Query definitions that are provided out of the box is probably going to be your best option for constructing your own reports.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-35100477407223167062017-02-27T16:39:00.001+00:002017-02-27T20:27:30.375+00:00IGI Default EntitlementsThose of you who have downloaded the latest (and greatest) version of IBM Security Identity Governance (v5.2.2) are no doubt having a wonderful time with the new interface and the new features.<br />
<br />
But like every new software release, one or two things may not behave quite like you expected them to.<br />
<br />
As an example, the rule embedded within the <b>USER_MOVE</b> Rule Flow suggests that the <b>moveUser </b>method will move a user to a new Organisational Unit and automatically assign all default entitlements assigned to that OU. At least, the comments explicitly state that this is to be expected:
<br />
<div>
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;">// Move the user assigning the default entitlements of the new OU</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;">UtilAction.moveUser(sql, userBean, orgUnitBean);</span><br />
<div>
<br /></div>
<div>
Unfortunately, the result of this operation is somewhat disappointing. The user does indeed move... but new default entitlements are not assigned.</div>
<div>
<br /></div>
<div>
Checking a v5.2.1 demo image that I have, though, reveals the following additional code which does actually meet our expectations:</div>
<div>
<br /></div>
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;">// Assign default entitlements of OU</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;">EntitlementBean entBeanDefault = new EntitlementBean(); </span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;">entBeanDefault.setDefaultOption(true);</span><br />
<span class="Apple-tab-span" style="white-space: pre;"><span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;"> </span></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;">BeanList entsDefault = _OrgUnitAction.findEntitlementByOU(sql, false, entBeanDefault, null, orgUnitBean, null);</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;">if (!entsDefault.isEmpty()) {</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;"> for (int k = 0; k < entsDefault.size(); k++) {</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;"> EntitlementBean role = (EntitlementBean) entsDefault.get(k);</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;"> BeanList roles = JobRoleAction.find(sql, role);</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;"> if(roles==null || roles.isEmpty() ) {</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;"> throw new Exception("Role : " + role.getName() + " not found!");</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;"> }</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;"> UserAction.addRole(sql, userBean , orgUnitBean , roles , null, null, false, false);</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;"> }</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;">}</span><br />
<br />
NOTE: The following will need adding to the Package Imports:<br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;">import com.engiweb.profilemanager.common.ruleengine.action.reorganize._OrgUnitAction</span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace; font-size: medium;">import com.engiweb.profilemanager.common.ruleengine.action.JobRoleAction</span></div>
Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-63625025292831147682016-11-22T21:51:00.000+00:002017-02-27T16:40:17.564+00:00IGI Attribute HierarchiesIBM Security Identity Governance & Intelligence (or IGI for short) has a very neat feature whereby hierarchies can be constructed using any attribute associated with an identity. Now, instead of identities being firmly placed within a rigid organizational hierarchy, additional hierarchies can be created to help model entitlements more accurately.<br />
<br />
For example, it could be useful for everyone who reports to a specific manager to be automatically assigned a suite of entitlements. Additionally, we could find that rights should be assigned based on regional or office location.<br />
<br />
Fundamentally, the assignment of this rights is somewhat akin to how IBM Security Identity Manager could be configured with dynamic roles - except IGI's approach is so much more powerful.<br />
<br />
For example, let's consider identity records that contain the following attributes:<br />
<ul>
<li>Country</li>
<li>City</li>
<li>Address</li>
</ul>
<br />
It could be interesting to model that hierarchy and "virtually" place identities in a hierarchy that might look like this:<br />
<br />
World<br />
- United Kingdom<br />
- - Belfast<br />
- - - 1 Main Street<br />
- - London<br />
- - - 2 High Street<br />
- - - 3 Oxford Street<br />
- - - 4 Piccadilly<br />
- France<br />
- - Paris<br />
- - - 5 Rue de Provence<br />
<br />
etc.<br />
<br />
Modelling the hierarchy is simple. As an IGI administrator, one merely needs to navigate to <b>Access Governance Core</b> > <b>Configure</b> > <b>Rules</b>. In here, we can create a Rules Sequence called LOCATION_HIERARCHY of type Hierarchy.<br />
<br />
Now, within the Rules tab, we can select the rule class Hierarchy and rule flow LOCATION_HIERARCHY (ignoring the fact that the naming convention mismatch between sequence and flow is rather annoying).<br />
<br />
Within the package imports section, we would place the following:<br />
<br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;">import com.engiweb.profilemanager.common.bean.UserBean</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;">import com.crossideas.certification.common.bean.data.ResultBean</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;">import com.engiweb.profilemanager.common.ruleengine.action.UtilAction</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;">import java.util.ArrayList</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;">global com.engiweb.pm.dao.db.DAO sql</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;">global com.engiweb.logger.impl.Log4JImpl logger</span><br />
<br />
This suite of imports exposing methods which we can now use. Our next step is to CREATE a Rules Package with the following code:<br />
<br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;">when</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> userBean : UserBean( ) </span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> resultBean : ResultBean( )</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;">then</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;">/* Country>City>Office */</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> String country = userBean.getCountry();</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> String city = userBean.getLocality();</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> String office = userBean.getAddress();</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span>
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> if (country != null && city != null && office != null) {</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> resultBean.setResultString("World;" + country + ";" + city + ";" + office); </span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> } else {</span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> resultBean.setResultString("World"); </span><br />
<span style="color: #3d85c6; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span><br />
<br />
What does this code do? Well, it iterates over every identity in the platform and constructs a string of World;Country;City;Office which can be passed back to the core platform in order to construct a hierarchy. Give the package a "sensible" name (like Country>City>Office) and assign it to the LOCATION_HIERARCHY rule flow.<br />
<br />
The next step is to <b>Access Governance Core</b> > <b>Configure</b> > <b>Hierarchy</b> in order to create a hierarchy that can use our rule flow. Under the Actions link, click on Add. Populate the blank form with the following details:<br />
<br />
<b>Name</b>: Location Hierarchy<br />
<b>Configuration Type</b>: Advanced<br />
<b>Rule</b>: LOCATION_HIERARCHY<br />
<b>Value</b>: Hierarchy<br />
<b>Separator Char</b>: Semi-Colon (;)<br />
<br />
Save the hierarchy, re-select it and under Actions, click on BUILD. Now the system will build an appropriate hierarchy which can be viewed under <b>Access Governance Core</b> > <b>Manage</b> > <b>Groups</b>.<br />
<br />
So what can we do now?<br />
<br />
Well... let's assume we want everyone in the United Kingdom to be assigned a role. Create and publish role called "<b>United Kingdom Users</b>". Now configure the role by updating the Org Units it is assigned to (ignoring the fact it is called Org Units which will no doubt be resolved in a future fix pack!). Add an "Org Unit" of type Location Hierarchy, navigate down through the hierarchy and find <b>United Kingdom</b>, click on OK and complete the following:<br />
<br />
<b>Default</b>: Yes, and align users<br />
<b>Visibility Violation</b>: No<br />
<b>Enabled</b>: Yes<br />
<b>Hierarchy</b>: Checked<br />
<br />
That's it... every user under the United Kingdom hierarchy will automatically be assigned the United Kingdom Users role.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-13117031456589247302016-08-30T10:04:00.002+01:002016-08-30T10:04:45.941+01:00Javadoc UpdatesIt has been a while, but I've finally got round to uploading the latest Javadocs for IBM Security Identity Manager v7.0 and IBM Security Access Manager v9.0.<br />
<br />
These can be found by following the links from here: <a href="https://www.stephen-swann.co.uk/links-and-tools/">https://www.stephen-swann.co.uk/links-and-tools/</a><br />
<br />
Enjoy - if it's possible to enjoy Javadocs!Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-66206429272443346992016-03-23T08:07:00.001+00:002016-03-24T07:28:40.379+00:00Property Changes In TDI - On The FlyThis week, I was asked if it was possible to update a TDI property while the TDI server was still running and, if so, how to go about doing it.<br />
<br />
The reason for wanting to do so was akin to injecting a property into TDI at run-time so that it would "safely" shutdown an Assembly Line at a sensible point of processing. Now, there are many ways to address this actual requirement, but the fundamental question of how to inject a property into TDI is certainly something that can be explained easily.<br />
<br />
<b><u>tdisrvctl</u></b><br />
The tdisrvctl command is a terrific command for communicating with a running TDI server. In order to communicate with the TDI server, you merely need to supply some key information, such as the Port Number that the TDI Server API is listening on, and some means to identify yourself using the TDI keystores. Finally, you supply an "operation" for the TDI Server to perform. Manipulating properties can be performed with the "prop" operation. In summary, tdisrvctl needs the following parameters:<br />
<br />
<ul>
<li>-p {port}</li>
<li>-K {keystore}</li>
<li>-P {keystore password}</li>
<li>-T {trust store}</li>
<li>-W {trust store password}</li>
<li>-op prop</li>
</ul>
<br />
<br />
I set up a Project in TDI called <b>propertyhandling</b>, with an assemblyline called <b>propertyhandling</b> and a property in the <b>propertyhandling</b> properties file called <b>status</b> with a value of <b>run</b>.<br />
<br />
In my AL, I created a conditional WHILE loop with this code:<br />
<br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;">if (system.getExternalProperty("status") == "run") {</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> return true;</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;">} else {</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;"> return false;</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;">}</span><br />
<br />
I ran the AL and it trundles along nicely doing nothing but looping and consuming all available CPU. You got to love never-ending loops!<br />
<br />
I then ran this command:<br />
<br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;">tdisrvctl.bat -p 1091 -T C:\TDISOL\testserver.jks -W server -K C:\TDISOL\serverapi\testadmin.jks -P administrator -op prop -c propertyhandling -o propertyhandling -g all</span><br />
<br />
The -c, -o and -g options after the prop operation need a little explaining:<br />
<br />
<ul>
<li>-c this is the Solution Name for my configuration, in this case <b>propertyhandling</b></li>
<li>-o this is the name of the properties collection, in this case <b>propertyhandling</b></li>
<li>-g this tells the TDI Server to return the property value for the property named, in this case <b>all </b>means return the values for all the properties in the properties file</li>
</ul>
<br />
<br />
When I run the command, the result I get on-screen is this:<br />
<br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;">--- propertyhandling ---</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;"><br /></span>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;">status=run</span><br />
<br />
This is excellent news, I'm able to query the current properties held in this particular properties file. Swapping that -g argument for a -s argument means I can now manipulate the <b>status</b> property as such:<br />
<br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;">tdisrvctl.bat -p 1091 -T C:\TDISOL\testserver.jks -W server -K C:\TDISOL\serverapi\testadmin.jks -P administrator -op prop -c propertyhandling -o propertyhandling -s status=stop</span><br />
<br />
The -s require a property/value pair to be supplied. In this case, -s is telling the TDI Server to set the value of property <b>status</b> to <b>stop</b>. When I run this command, I get this result:<br />
<br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: xx-small;">CTGDJB070I The property status has been set and committed.</span><br />
<br />
That looks positive and when I check whether my assemblyline is still running, I find that it has indeed come to an end - as expected. Thanks goodness, says my CPU!<br />
<br />
Of course, there are a myriad of use cases for injecting properties at run time... safe shut-down of an assemblyline is just one.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-6574986576787882452015-05-26T09:29:00.003+01:002016-03-24T07:16:29.617+00:00TDI and HTTP POSTI was recently asked how to successfully perform an HTTP POST request using TDI's HTTP Client Connector. NULL values kept being processed by the web server and there seemed to be no obviously documented way to perform the task.<br />
<br />
On the face of it, this seems like a straightforward piece of functionality, but the truth is that it is not quite as straightforward as it seems.<br />
<br />
To understand how to pass parameters to a web server via HTTP POST using TDI, it would help to understand what is actually happening when using a standard web form.<br />
<br />
Consider the following:<br />
<br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><html></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><head /></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><body></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><form action="result.php" method="post"></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>User Name<br /></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><input type="text" name="uname"></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><br /></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>Password<br /></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><input type="password" name="pass"></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><br /></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span><input type="submit" value="submit"></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span></form></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span></body></span><br />
<span style="color: blue; font-family: "courier new" , "courier" , monospace;"></html></span><br />
<br />
The submit button on this form will cause the browser to send an HTTP request to the web server with a CONTENT-TYPE of: "<span style="color: blue; font-family: "courier new" , "courier" , monospace;">application/x-www-form-urlencoded</span>". This is the key to unlocking our problem!<br />
<br />
The values for the attributes requested in the form will be passed in the BODY of the request rather like the query string you might see in HTTP GET requests:<br />
<span style="color: blue;"><br /></span>
<span style="color: blue; font-family: "courier new" , "courier" , monospace;">uname=x&pass=y</span><br />
<br />
So to mimic form submission using the POST method via TDI, all you need to do is follow these steps:<br />
<ul>
<li>Set the Mode to CallReply (if you want to see what the web server has done with your request)</li>
<li>Set the Request method to POST</li>
<li>Set the http.Content-Type to "application/x-www-form-urlencoded"</li>
<li>Set the http.body to name/value pairs in query string format</li>
</ul>
<br />
Hopefully that will see your TDI Assembly Lines behaving themselves when acting as HTTP Client and attempting to use the POST method to transfer information.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-37073832216098046022015-05-15T15:28:00.004+01:002016-03-24T07:29:32.512+00:00LDAP Schema IssuesIt's annoying when a basic task consumes too much of your time!<br />
<br />
Getting an LDAP Operations Error when attempting to perform an <b>ldapmodify</b> on an object can be irksome. It is especially irksome if the change you are making is trivial!<br />
<br />
Consider the following object that already exists in my LDAP Server:<br />
<br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">dn: myattr=ABC,dc=com</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">objectclass: top</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">objectclass: mycustomobject</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">myattr: ABC</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">mytrivialattribute: Z</span><br />
<br />
Now consider changing that object to the following:<br />
<br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">dn: myattr=ABC,dc=com</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">objectclass: top</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">objectclass: mycustomobject</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">myattr: ABC</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">mytrivialattribute: Y</span><br />
<br />
One might reasonable expect the LDAP modify operation to be successful bearing in mind how trivial the change is. But when you get an Operations Error being thrown back at you by a hissy-fitting Directory Server, you might start to scratch your head.<br />
<br />
The <b>V3.modifiedschema</b> looked perfect as <b>mytrivialattribute</b> was defined with a syntax of <b>1.3.6.1.4.1.1466.115.121.1.15{1024}</b>. But looking inside DB2 revealed something a little more sinister.<br />
<br />
I followed these steps:<br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">db2 connect to idsldap</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">db2 describe table idsldap.mytrivialattribute</span><br />
<br />
And what I got back was:<br />
<br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">EID with column length 4</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">MYTRIVIALATTRIBUTE with column length 240</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">RMYTRIVIALATTRIBUTE with column length 240</span><br />
<br />
That didn't look right! Somehow, <b>mytrivialattribute</b> was created using default parameters and the <b>V3.modifiedschema</b> file was manually updated at a later date. As such, the database plain refused to act upon any requests to add/modify mytrivialattributes!<br />
<br />
Getting around the problem is simple and can be done in a number of ways. I like the brutal approach though:<br />
<br />
<ul>
<li>Drop the DB2 table idsldap.mytrivialattribute</li>
<li>Restart the LDAP server</li>
</ul>
<br />
Describing the table now returns:<br />
<span style="color: blue;"><br /></span>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">EID with column length 4</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">MYTRIVIALATTRIBUTE with column length 1024</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">MYTRIVIALATTRIBUTE_T with column length 240</span><br />
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">RMYTRIVIALATTRIBUTE_T with column length 240</span><br />
<br />
So what was going on? Well, setting a length of 1024 on the schema definition meant that the LDAP Server wanted to put the full string into the column named <b>MYTRIVIALATTRIBUTE</b> and to put a truncated version of the string into <b>MYTRIVIALATTRIBUTE_T</b>. But the _T column didn't exist so the server couldn't perform the operation.<br />
<br />
Dropping the table and allowing the directory server to recreate it properly on startup resolved the issue.<br />
<br />
Of course, the ramifications now begin as to why there was a mismatch in the first place, but at least the problem has been diagnosed and rectified.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-28452262805441557342015-02-09T08:06:00.000+00:002019-01-21T13:16:09.292+00:00Giving Away SecretsI've often been asked why I 'blog' (not that I update this blog anywhere near as often as I would like). I've had people say that I'm "giving away secrets" and that I won't be in demand in the job marketplace if I continually tell other people how to do what I do.<br />
<br />
<div>
That maybe so, but surely that's a good thing right? I want to be able to move on and learn new things on a daily basis and if I can get others to do the things that I traditionally do, then that creates the opportunity for me to move on?<br />
<br /></div>
<div>
Then there is the fact that I'm not getting any younger and retaining information in my head isn't as easy these days as it used to be. It's like creaking limbs and deteriorating eyesight... I find I'm becoming more forgetful (which my wife will more than happily corroborate). As such, writing all these things down is actually as much for my own private use as it is to help others.<br />
<br /></div>
<div>
And finally, altruism feels good. Now, I don't for one minute think that I'm the most altruistic person I know. Giving away information on IBM Tivoli security software can barely be described as altruistic really. But nonetheless, it feels like I'm doing a good thing. Last week, I received an email from a very kind individual which reminded me that it is a good thing. The email said:</div>
<div>
<br /></div>
<div>
<blockquote class="tr_bq">
<i>"You are a rock star and a gentleman. Thanks so much for all the helpful material you have put together! I owe you at least a suitcase of beer. Feel free to cash in anytime."</i></blockquote>
</div>
<div>
I don't get many emails of that type. Mostly, I get emails asking for free consultancy so it brightened my day when I saw the above. So, to the sender (Tim), I say thank you for the kind offer of beer but it's really not necessary. Acknowledgement that the material is worthwhile is quite enough for me.</div>
Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com1tag:blogger.com,1999:blog-16619563.post-33738694855538312942014-12-12T12:28:00.000+00:002014-12-12T12:28:27.578+00:00The Who And When Of Account RecertificationWe all know that there are rules and laws in place that mean that organisations must demonstrate that they have control over access to their IT systems. We've all heard about the concepts around recertification of access rights. But what is the best way to certify that the access rights in place are still appropriate and who should undertake that certification task?<br />
<br />
<h3>
Who</h3>
A starting point, at least, is to identify who are potential candidates for certifying access rights. They are, in no particular order:<br />
<br />
<ul>
<li>The account owner themselves</li>
<li>The account owner's line manager</li>
<li>The application owner</li>
<li>The service owner</li>
<li>The person who defined the role that granted the entitlements</li>
</ul>
<br />
<br />
Of course, some of these people may or may not actually exist. In some cases, the person might not be appropriate for undertaking certification tasks. Line Managers these days are often merely the person who you see once or twice a year for appraisals and to be given the good news about your promotion/bonus (or otherwise). Functional Managers or Project Managers may be more appropriate, but can they be identified? Is that relationship with the account owner held anywhere? Unlikely. And definitely unlikely in the context of an identity management platform!<br />
<br />
And what about service owners? Maybe this person is the person who has responsibility for ensuring the lights stay on for a particular system and maybe they don't care what the 500,000 accounts on that system are doing? Maybe her system is being used by multiple applications and she would prefer the application owners to take responsibility for the accounts?<br />
<br />
And what of the account owner themselves? Self-certifying is more than likely going to merely yield a "yes, I still need that account and all those entitlements" response! But is that better than nothing? Maybe! Then again, maybe the account owner is unaware of what the account gives them. What do they do when they are told they have an AD account which is a member of the PC-CB-100 group, will they understand what that means? For many users, the answer will be no!<br />
<br />
Ultimately, the decision as to who is best placed to perform the certification will come down to a number of factors:<br />
<br />
<ul>
<li>Can a person be identified</li>
<li>Can the person recognise the information they will be presented with and correlate that with a day-to-day job function</li>
<li>Is the person empowered to make the certification</li>
</ul>
<br />
<br />
There will be a high chance that only one person fits the bill given this criteria!<br />
<br />
<h3>
When</h3>
Some organisations like to perform regular recertifications... it's not unusual to see quarterly returns being required. For most people, however, their job function doesn't change too often and a lot of effort will have gone into gathering information which (for the most part) is unchanged from the previous round of recertifications.<br />
<br />
Is there a better way?<br />
<br />
Merely lengthening the amount of time between cycles isn't necessarily the answer. But maybe recertification can be more "targeted". Rather than a time-based approach, recertification should probably target those people who have undergone some form of change or other life-cycle event, such as:<br />
<br />
<ul>
<li>Change of Job Title</li>
<li>Change of Line Manager</li>
<li>Change of Organisational Unit</li>
<li>Return from long-term leave</li>
<li>Lack of use on the account</li>
</ul>
<br />
<br />
All of these events can be used as useful recertification triggers rather than waiting for a quarterly review. The benefits are easy to articulate to any CISO - immediate review and more control of access rights.<br />
<br />
Of course, these triggers can be used in conjunction with a time-based approach - but maybe that time-based approach should be based on the last time a recertification was performed for that user/account/entitlement rather than a fixed date in the calendar.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-8739297196235703662014-12-04T08:57:00.000+00:002016-03-24T07:29:57.978+00:00ISIM Workflow ExtensionsDuring recent ramblings, I mentioned that I would publish some of my favourite ISIM workflow extensions. The workflow extensions that are provided "out of the box" cover tasks such as adding, modifying and deleting accounts and identities as well as enforcing policy and other standard operations. However, the "out of the box" list could do with having a number of useful extensions added.<br />
<div>
<br /></div>
<div>
One of my favourites (and something I pretty much insist on being installed as soon as I've deployed ISIM) is my version of a sendmail. ISIM has the ability to send emails, but only to those identities who have an email address. However, what if you wanted to send an email to an address which was not attached to any identity? What if you wanted to email a user during a self-registration workflow at which point the identity has not yet been created?</div>
<div>
<br /></div>
<div>
My sendmail extension is just a handful of lines long, uses standard ISIM methods and takes just the following arguments: eMail Address, Subject and Body.</div>
<div>
<br /></div>
<div>
The code is very simple indeed:</div>
<div>
<br /></div>
<div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">public ActivityResult sendMail(String mailAddress, String mailSubject, String mailBody) {</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> try {</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> List<String> addresses = new ArrayList<String>();</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> addresses.add(mailAddress);</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> NotificationMessage nMessage = new NotificationMessage(addresses, mailSubject, mailBody, mailBody);</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> MailManager.notify(nMessage);</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> return new ActivityResult(ActivityResult.STATUS_COMPLETE,</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ActivityResult.SUCCESS,</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "eMail Sent",</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> null);</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> } catch (Exception e) {</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> return new ActivityResult(ActivityResult.STATUS_COMPLETE,</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> ActivityResult.FAILED,</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> "eMail Not Sent",</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> null);</span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> }</span></div>
</div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;">}</span></div>
<div>
<br /></div>
<div>
Registering the JAR and registering the extension in the <b>workflowextensions.xml</b> file is all that is required to make the extension available.</div>
<div>
<br /></div>
<div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"><ACTIVITY ACTIVITYID="sendMail" LIMIT="600000"></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> <IMPLEMENTATION_TYPE></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> <APPLICATION CLASS_NAME="com.sswann.sendMail" METHOD_NAME="sendMail" /></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </IMPLEMENTATION_TYPE></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> <TRANSITION_RESTRICTION SPLIT="XOR" /></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> <PARAMETERS></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> <IN_PARAMETERS PARAM_ID="recipient" TYPE="String" /></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> <IN_PARAMETERS PARAM_ID="subject" TYPE="String" /></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> <IN_PARAMETERS PARAM_ID="body" TYPE="String" /></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"> </PARAMETERS></span></div>
<div>
<span style="color: #0b5394; font-family: "courier new" , "courier" , monospace; font-size: x-small;"></ACTIVITY></span></div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
From here, it's merely a matter of constructing your relevant data and handling the <b>activity.resultSummary</b> correctly in your subsequent nodes.</div>
<div>
<br /></div>
<div>
Previously, I stated that workflow extensions should be created when you find you are repeating Javascript over and over again. If you need to check the status of an account owner before invoking an account operation, that would sound like a great candidate for creating a workflow extension and having a solid reusable component. A list of useful extensions might include:</div>
<div>
<ul>
<li>Check Account Already Exists</li>
<li>Check Owner Status</li>
<li>Check Manager Status</li>
<li>Read/Update LDAP Object (not an identity or account)</li>
<li>Read/Update Database Object</li>
<li>Read/Update Message on ESB (MQ)</li>
<li>Call Web Service</li>
<li>Create Helpdesk Ticket/Send Alert</li>
</ul>
</div>
<div>
<br /></div>
<div>
Have you any good candidates for an extension?</div>
Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-8206037717157668362014-06-05T21:31:00.002+01:002014-06-05T21:31:58.573+01:00LDAP Load Balancing And TimeoutsIt's been ages since I posted anything despite the fact I have lots to say. The last few months have been extraordinarily busy and challenging (and not necessarily in a technical way).<br />
<br />
Some colleagues have pointed out that I have aged significantly in recent times. It is true that the hair on my chin has gone from grey to white and that the hair around my temples is severely lacking in any kind of hue. It seems that my work-life balance has gone slightly askew.<br />
<br />
Balancing brings me rather neatly on to the topic of LDAP Load Balancing. I say Load Balancing though what I really mean to say is providing a mechanism for assuring availability of Directory Servers for IBM Security Identity Manager. My preference is for load to go to one directory server which replicates to a second directory server which can be used should something go awry on the primary.<br />
<br />
So, what's the best way to ensure traffic routes to the correct directory server and stays stuck to that directory server until something happens? Well, that's the domain for the F5 BIG-IP beast. Or is it.<br />
<br />
There is plenty of documentation around the web that states that one should tread carefully when attempting to use these kind of tools to front a directory server (and IBM Tivoli Directory Server in particular). In recent dealings, I've observed the following which is worth sharing:<br />
<br />
<u>Point 1</u><br />
Be careful of the BIG-IP idle timeout default of 300 seconds. Any connection that BIG-IP thinks is stale after 300 seconds will be torn down. (You should see how a TDI Assembly Line in iterator mode behaves with that without auto-reconnect and skip-forward disabled!)<br />
<br />
<u>Point 2</u><br />
Be careful of the TCP connection setup and seriously consider using Performance Layer 4 as the connection profile on the BIG-IP. A 50% increase in throughput was not atypical in some of my recent tests.<br />
<br />
<u>Point 3</u><br />
Ensure that the default settings for LDAP pool handling are updated in ISIM's <b>enRole.properties</b> file. In particular, pay attention to the following:<br />
<br />
<b>enrole.connectionpool.timeout</b><br />
<b>enrole.connectionpool.retryCountForSUException</b><br />
<br />
The <b>timeout</b> should be set just a little below the BIG-IP's idle timeout. The <b>retryCountForSUException</b> should be set to ensure that ISIM reconnects should the BIG-IP device tear-down the connection regardless of the timeout.<br />
<br />
And with those tips, you should have a highly available infrastructure with a level of tolerance.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com1tag:blogger.com,1999:blog-16619563.post-20114837457880971522014-03-06T11:42:00.000+00:002014-03-06T11:42:02.397+00:00Understanding ISIM ReconciliationsMost ITIM/ISIM gurus will understand what goes on during Service reconciliation. Let's be honest, most gurus have had to write countless adapters and perform countless bouts of debugging problems when they arise.<br />
<br />
What happens, though, if you have one ISIM environment that can reconcile a service successfully but a second ISIM environment which cannot reconcile the exact same service. And let us, for a moment, assume that both ISIM environments are configured identically.<br />
<br />
Of course, if something works in one environment and doesn't work in another, there must be a difference somewhere. But the ISIM version is the same, the service profile is the same, the service definition is the same, it's talking to the same TDI instance which performs the grunt work of retrieving all the data from the target service. On the face of it - there is no reason for one environment to behave any differently than the other.<br />
<br />
Yet it can happen! In fact, I recently saw an ISIM environment get itself into a hung state trying to reconcile a service yet all the reconcilable objects had made their way into ISIM.<br />
<br />
When ISIM reconciles a service, the result is that supporting data objects are stored in the LDAP under the service container (erglobalid=123,ou=services,erglobalid=00000000000000000000,ou=org,dc=com) and <i>ultimately</i> accounts are stored under the ou=accounts and ou=orphans containers. I say ultimately for a reason. The accounts are actually temporarily stored under the service container too before being moved to a more appropriate container.<br />
<br />
And therein lay the difference in my two environments. The working environment had no accounts stored under the service container prior to executing the reconciliation. Somehow, the non-working environment did have some accounts left hanging about under the service container.<br />
<br />
A ruthless LDAPDELETE on all objects sub-ordinate to the service was all that was required for the reconciliation to complete successfully.<br />
<br />
Next time you have a misbehaving reconciliation process, why not check to see how previous reconciliations have been left by having a look under your service container. You might be surprised at what you will find.Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com1tag:blogger.com,1999:blog-16619563.post-86976659800836589092013-09-26T09:23:00.002+01:002016-03-24T07:30:24.243+00:00ITIM Best Practices For Workflow DesignIt sounds rather arrogant to call this blog entry "Best Practices" when it is merely my meandering thoughts that I'm about to spew forth. But a number of experiences of other people's work recently has almost moved me to tears.<br />
<br />
So here is my list of "best practices".<br />
<br />
<h3>
Workflow Start Nodes</h3>
The Start Node (and the End Node for that matter) is not somewhere that code should be placed. Code can be placed in these nodes, of course. But it shouldn't ever be placed in here. For example, how could I know that the following Workflow has been customised:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEin3CcH3tu-ASP1DLV9pmWUOW5Ea9l99Reg7Nqz5gOddtu3e36OKHYwqbFVOfpZYIm6rQrg5OisRKo21_OVUXNvNrS5YNkE2WkX8LjEBGCePf3i8LgZGCtX00rR8z_7t6y5Lm7wRw/s1600/start1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEin3CcH3tu-ASP1DLV9pmWUOW5Ea9l99Reg7Nqz5gOddtu3e36OKHYwqbFVOfpZYIm6rQrg5OisRKo21_OVUXNvNrS5YNkE2WkX8LjEBGCePf3i8LgZGCtX00rR8z_7t6y5Lm7wRw/s1600/start1.png" /></a></div>
<br />
<br />
If you have code that needs to be run as soon as the workflow starts, place a Script Node immediately after the Start Node and name it appropriately. This is MUCH better:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggiq9T1LNKfInmd9T-XVHhhsYjDt2sw6C2LvhNTwVHZQXtaogudPg1YonREuSSGFufcAHSS1yYjX0yY3KZ5GJF3G1Wm8kGi8aEFJ-qJlrDjwFUwU3_DIkW2tj3SZuvbFREr_eVnQ/s1600/start2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggiq9T1LNKfInmd9T-XVHhhsYjDt2sw6C2LvhNTwVHZQXtaogudPg1YonREuSSGFufcAHSS1yYjX0yY3KZ5GJF3G1Wm8kGi8aEFJ-qJlrDjwFUwU3_DIkW2tj3SZuvbFREr_eVnQ/s1600/start2.png" /></a></div>
<br />
<br />
<h3>
Script Node Size</h3>
If I open a Script Node and find that there are 1,000 lines of code in there, I will cry. If you have 1,000 lines of code you are probably doing something wrong. I would much rather see multiple Script Nodes with a sensible name describing their function laid out in an organised manner that gives me a good visual representation of the process rather than having to wade through hundreds or thousands of lines of code.<br />
<br />
<h3>
Workflow Extensions</h3>
If you have written some Javascript that looks cool and funky and re-usable, then make it cool and funky and re-usable by creating a Workflow Extension. Also, feel free to give extensions back to the community! (I shall publish some of my favourites soon!)<br />
<br />
<h3>
Hardcoding!</h3>
If I see a hardcoded erglobalid reference in a Script Node, I will, in all possibility, hunt the developer down and do very un-Tivolian things to him or her. Their assumption that their code will work when promoted through various environments is very flawed and they are being lazy. Do Not Hardcode!<br />
<br />
Commenting & Logging<br />
You might think your code is readable, but the chances are that it could still do with some comments here and there. Even if the code is understandable, the reasoning behind performing the function may not be so clear and requirements documents and design documents have a habit of disappearing!<br />
<br />
When it comes to logging, log appropriately and update the status of the workflow throughout the workflow process. The following statements are great to see in Script Nodes because we can successfully handle any problems and ensure that we always navigate our way to the End Node.<br />
<br />
<span style="color: blue;"><span style="font-size: x-small;"><span style="color: #0b5394; font-family: "courier new" , "courier" , monospace;">activity.setResult(activity.SUCCESS, "Attributes Successfully Verified");</span></span></span><br />
<span style="color: #0b5394;"><span style="color: blue;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">activity.setResult(activity.FAILED, "Attribute Verification Failed due to " + errMsg</span></span></span><span style="color: blue;"><span style="font-size: x-small;"><span style="font-family: "courier new" , "courier" , monospace;">);</span></span></span></span><br />
<br />
<h3>
And Finally</h3>
The chances are that someone will come along after you have done all your hard work and they will look at your workflow. Do you want them to cry? Or do you want them to be delighted in what they have seen? Make sure you delight people!Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-32403809233843067472013-06-07T09:28:00.001+01:002013-06-07T09:28:07.965+01:00Careful Where You Point That FingerOver the last 9 months, I have managed to shed almost 50lbs in weight - that in excess of 20Kg for those living in the metric world. As part of the process of losing weight, I took to tracking my walking and cycling habits using Endomondo on my phone - and what an excellent piece of software that is.<br />
<br />
I did have a problem, though!<br />
<br />
When I started the application and enabled GPS, I would wait quite a considerably amount of time before my location could be determined. Indeed, I had completed the bulk of some of my journeys by the time I got the "GPS Excellent" notification.<br />
<br />
Initially, my thoughts went along these lines:<br />
<br />
<i>"Endomondo have coded their freebie application to not pick up my location in the hope that I will purchase the Pro version of their app"</i><br />
<br />
I did purchase the Pro version - mostly because I thought the app was terrific with only a slight nod towards the hope that my GPS issues would be resolved. My GPS issues weren't resolved.<br />
<br />
So I then thought:<br />
<br />
<i>"I must have dropped my phone at some point and 'disturbed' its ability to perform it's GPS magic"</i><br />
<br />
This seemed like a reasonable thought as all my friends and colleagues and perfectly acceptable Endomondo experiences on their phones. Indeed, my Samsung Galaxy II seemed to be really struggling when compared to a colleague's Nexus - and they were both Android phones.<br />
<br />
I figured it must be time for a new phone. And proceeded to spend the next couple of weeks evaluating my options. And then... I woke up one morning to discover that my phone wanted to apply an update to my Android operating system.<br />
<br />
An hour or so later, I had a shiny new OS. Admittedly, performance was awful initially as all my apps needed updating too. And, all my icons had been resorted alphabetically (for which I could quite happily exact some kind of tortuous revenge on the developer of the upgrade process). Apart from the initial dodgy performance and the tears that followed the pain of re-organising my icons, I noticed two things:<br />
<ul>
<li>Playing 7x7 was super-fast - 7x7 is one of those simple yet addictive games that can keep me going for hours</li>
<li>GPS performance was perfect!</li>
</ul>
<br />
Wonderful! When starting Endomondo Pro now, I have to wait no longer than 2 or 3 seconds before I have my "GPS Excellent" notification. And to think my problems were with the app or the phone.<br />
<br />
So. What's the point of this story I hear you ask! Well, it is far too easy to make a judgement about an app, or a product or almost anything you might encounter during the course of your life. My problems had nothing to do with Endomondo's developers' coding ability; nor had it anything to do with how Samsung hardware technicians had put my phone together. Yet, even for a techie like me, my thought processes led me to think bad thoughts about both!<br />
<br />
I have seen senior managers in enterprises make some very strange decisions in the past. They may decide that they no longer need "Huge Software Developer's Amazing IT Solution" because it costs too much and doesn't perform the way they expected and instead by "Enormous Software Developer's Stupendous IT Solution" in the anticipation that it will cost less and perform perfectly.<br />
<br />
And frequently, I don't see the cost benefit and the performance problems still exist.<br />
<br />
Sometimes the problems we are faced with in the IT world are not being caused by the applications we are using. Sometimes we need to dig a little deeper to find out what the real cause of a problem is - whether it be the hardware platform we are using; the operating system in use; the networking setup; the interfaces; and of course, the users and their expectations.<br />
<br />
And finally... maybe some of the big boys can learn from the developers of the apps we now use on a daily basis on our smartphones. Endomondo does exactly what I need in a manner which makes sense for me. Can we say the same thing for enterprise software?Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0tag:blogger.com,1999:blog-16619563.post-82552162057537507222013-04-16T00:22:00.000+01:002013-04-16T00:22:01.675+01:00Tempus FugitI remember being a follower of a blogger who wrote about IBM Tivoli security solutions and becoming quite concerned for his well being when he "stopped" blogging for a while. When he had gone fully six months without blogging, I had myself convinced that something terrible had happened to him personally.<br />
<br />
And now I find that I have done the same thing. My periodical briefings have stopped. But fear not - it's not because of any ill health. It's not because of a change in career. It's not even to do with boredom. It has everything to do with being far too busy and that's the best reason of all for the temporary blip in my output.<br />
<br />
But, time flies... and too much time has passed since I last committed my thoughts to writing.<br />
<br />
So what has happened since in the last 6 months? Well, the IBM Tivoli re-branding exercise is in full swing with IBM Security Identity Manager and IBM Security Access Manager products having been released.<br />
<br />
And what has changed in those products?<br />
<br />
ISAM has a brand new deployment process which is greatly simplified. However, for TAMeb users who have deployed their software on Windows, beware? The upgrade process might not behave as you expect? Why? The move to a 64-bit architecture, that's why? Think seriously about any attempt to perform an in-situ upgrade!<br />
<br />
You might like to also check compression settings on your WebSEALs - a respected colleague of mine has already encountered some fun and games with those!<br />
<br />
And ISIM? Is it just a pure re-branding exercise? Not at all. Some functional additions are definitely welcome like the controls added to the services offering retry operations on failed resources. Account ownership and role attributes look interesting (despite how they have been implemented). Privileged Identity Management is a great addition as is the inclusion of supported web services API.<br />
<br />
But core processing that ITIM administrators will know and love is still there!<br />
<br />
And what of my work recently? Well, it's a matter of spending a lot of time concentrating on federated security, and environmental upgrades. Working on pre-sales; sprucing up designs; sizing projects; and helping those around me get best use out of their IBM Tivoli/Security solutions.<br />
<br />
So much has happened in recent months, though, that I hardly know where to start in documenting it all. There has been fun with SAML v2. Flirtations with FESI extension re-writes. Dalliances with web services and APIs. Encounters with business process re-engineering.<br />
<br />
My next article, however, will likely be an IBM Tivoli Directory Integrator article centred on best practice for collaborative development. That sounds like a tricky one!Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com2tag:blogger.com,1999:blog-16619563.post-52421311224775368272012-10-10T07:56:00.000+01:002012-10-10T07:56:46.786+01:00Internet Explorer Is A Very Naughty BoyIt has been three months since I last felt the urge to post anything on my blog. There isn't any real particular reason why there should've been a hiatus with the possible exception that the sun was shining (sometimes) and I was out and about rather than being tied to my desk.<br />
<br />
But the days are shorter than the nights now. There is definitely a nip in the air. Being "out and about" isn't quite as enjoyable as it was just a few weeks ago.<br />
<br />
And so here I am... ready and willing to commit some more of my findings to this blog.<br />
<br />
So what shall I tell you? Well, what about the fact that Internet Explorer is a very naughty boy! Hardly a startling revelation. Those of us working in the web world already understand how often we come across situations whereby Firefox, Chrome, Safari and Opera can all render a page correctly, but Internet Explorer seems to fail to do so! It gets rather tedious after while, right?<br />
<br />
This week, I had the joys of diagnosing why a page protected by WebSEAL wouldn't render in Internet Explorer. Capturing the HTTP Headers whizzing back and forth in Internet Explorer and Firefox provided the answer quite quickly: Internet Explorer would sometimes not bother to send the session cookie back to WebSEAL.<br />
<br />
Why would it "sometimes" just not bother to do this? Well, there is some <a href="http://blogs.msdn.com/b/ieinternals/archive/2009/08/20/wininet-ie-cookie-internals-faq.aspx">well documented evidence</a> that Internet Explorer (up to version 8) treats cookies in a rather unexpected fashion. Internet Explorer can start dropping in-memory cookies as it has a finite limit on the number of in-memory cookies it can handle!<br />
<br />
Those clever people in the development labs of IBM, however, have come across this before and the problem can be alleviated by setting the <span style="font-family: "Courier New",Courier,monospace;"><b>resend-webseal-cookies</b></span> parameter to <span style="font-family: "Courier New",Courier,monospace;"><b>yes</b></span> in the WebSEAL configuration file. This ensures that the cookie gets set with <i>every</i> request!<br />
<br />
For many of you, you will have come across this quirk before. Many times, potentially. For those just getting started out with your WebSEAL deployment, though, make sure you have the ability to take a grab of the HTTP Headers from within your browser. It's amazing what you can see inside them!<br />
<br />
<u><b>Useful Header Inspection Tools</b></u><br />
<ul>
<li><a href="http://www.blunck.se/iehttpheaders.html">ieHTTPHeaders</a> for Internet Explorer, by Jonas Blunck</li>
<li><a href="https://addons.mozilla.org/en-US/firefox/addon/live-http-headers/">Live HTTP Headers</a> for Firefox, by Daniel Savard & Nikolas Coukouma</li>
<li><a href="http://www.fiddler2.com/fiddler2/">Fiddler2</a>, by Telerik</li>
</ul>
<br />
I promise to blog more... now that that winter is almost upon us!Stephen Swannhttp://www.blogger.com/profile/02171157277282964684noreply@blogger.com0