{"id":1875,"date":"2012-01-16T12:16:41","date_gmt":"2012-01-16T12:16:41","guid":{"rendered":"https:\/\/www.wapshere.com\/missmiis\/?p=1875"},"modified":"2012-01-16T12:16:41","modified_gmt":"2012-01-16T12:16:41","slug":"authorization-after-an-action","status":"publish","type":"post","link":"https:\/\/www.wapshere.com\/missmiis\/authorization-after-an-action","title":{"rendered":"Authorization after an Action"},"content":{"rendered":"<p>Something that has come up from time to time on the FIM forum is the need to\u00c2\u00a0trigger an\u00c2\u00a0AuthZ workflow based on some change made by an Action workflow (or by the Sync Service). This is not possible in the FIM Service today and I don&#8217;t see any evidence that it will be possible in the future either &#8211; I guess it must mess up the workflow processing on some fundamental level.<\/p>\n<p>The gereral consensus on the forum has been that you need to start a new request from outside the Portal &#8211; perhaps by using a powershell script. This new request can then follow the full AuthN -&gt; AuthZ -&gt; Action progression. But how to trigger it?<\/p>\n<p>I have now worked through this idea and it&#8217;s working, though did need quite a few policy objects in the Portal as well as the script.<\/p>\n<p><!--more--><\/p>\n<h2>The problem I needed to solve<\/h2>\n<p>Users need to be able to request access to a system and the access must be approved.<\/p>\n<p>To simplify the user creation process in the Portal I want to include the option to request access on the user create form, but I don&#8217;t want an approval holding up creation of the person object. The person should be created and <em>then <\/em>the approval should kick off.<\/p>\n<p>In fact it&#8217;s not just for convenience\u00c2\u00a0&#8211; the administrators of the target system need forewarning that an approved access request is coming. So they should receive info about the new person via the Sync Service, and they should also receive info about the access <em>request<\/em>. Later on when the request is approved they will of course get to see that as well.<\/p>\n<h2>Here&#8217;s what it looks like<\/h2>\n<p>So here&#8217;s what the end result is looking like in my lab. Notice I&#8217;ve pasted over some of the text as this is a customer lab with some real names in it (not Elvis obv).<\/p>\n<p>I should also add I was aiming to solve this using <em>out of the box functionality only<\/em>. I could perhaps do something more elegant with custom workflows &#8211; but I&#8217;m sure my client is not the only one who prefers to avoid that.<\/p>\n<table border=\"1\">\n<tbody>\n<tr>\n<td>I added an\u00c2\u00a0extra tab\u00c2\u00a0to the User Create form where the access request is made. The tab also appears on the User Edit form so you can equally request access for an already existing user.<\/td>\n<td>\u00c2\u00a0<a href=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/01-request1.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1892\" title=\"01 request\" src=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/01-request1.jpg\" alt=\"\" width=\"246\" height=\"133\" \/><\/a><\/td>\n<\/tr>\n<tr>\n<td>The user is created straight away and without any approval. If I go check the Access Requests tab now I can see the access was requested, and who did it.<\/td>\n<td>\u00c2\u00a0<a href=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/02-request-status1.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1893\" title=\"02 request status\" src=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/02-request-status1.jpg\" alt=\"\" width=\"346\" height=\"140\" srcset=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/02-request-status1.jpg 346w, https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/02-request-status1-300x121.jpg 300w\" sizes=\"auto, (max-width: 346px) 100vw, 346px\" \/><\/a><\/td>\n<\/tr>\n<tr>\n<td>I then have a bit of powershell magic going on in the background. This script:<\/p>\n<ul>\n<li>detects the request,<\/li>\n<li>sets another attribute (&#8220;AccessApproved&#8221;) that has an Approval AuthZ workflow associated with it, and<\/li>\n<li>changes the request status to &#8220;Manager approval requested&#8221;.<\/li>\n<\/ul>\n<\/td>\n<td>\u00c2\u00a0<a href=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/03-approval-pendin.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1903\" title=\"03 approval pendin\" src=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/03-approval-pendin.jpg\" alt=\"\" width=\"342\" height=\"148\" srcset=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/03-approval-pendin.jpg 342w, https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/03-approval-pendin-300x129.jpg 300w\" sizes=\"auto, (max-width: 342px) 100vw, 342px\" \/><\/a><\/td>\n<\/tr>\n<tr>\n<td>The user&#8217;s manager receives the Approval request, which they can approve in the FIM Portal or using the Outlook client, if installed.<\/td>\n<td>\u00c2\u00a0<a href=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/04-approve.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1879\" title=\"04 approve\" src=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/04-approve.jpg\" alt=\"\" width=\"365\" height=\"103\" srcset=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/04-approve.jpg 365w, https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/04-approve-300x84.jpg 300w\" sizes=\"auto, (max-width: 365px) 100vw, 365px\" \/><\/a><\/td>\n<\/tr>\n<tr>\n<td>Now when the person&#8217;s details are checked we see their Approved status. The Sync Service does whatever needs to be done with this information.<\/td>\n<td>\u00c2\u00a0<a href=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/05-approved-statu.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1898\" title=\"05 approved statu\" src=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/05-approved-statu.jpg\" alt=\"\" width=\"333\" height=\"107\" srcset=\"https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/05-approved-statu.jpg 333w, https:\/\/www.wapshere.com\/missmiis\/wp-content\/uploads\/2012\/01\/05-approved-statu-300x96.jpg 300w\" sizes=\"auto, (max-width: 333px) 100vw, 333px\" \/><\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>FIM Policy Objects<\/h2>\n<p>I had to create quite a few policy objects to support all of this.<\/p>\n<h3>Powershell User<\/h3>\n<p>I created a dedicated user account to run the powershell script. I explicitly blocked this user from the &#8220;All People&#8221; set so it wouldn&#8217;t accidentally trigger other workflows.<\/p>\n<h3>Schema<\/h3>\n<p>I now have three attributes to handle the request:<\/p>\n<ul>\n<li>AccessRequested (boolean)<\/li>\n<li>AccessRequestStatus (indexed string)<\/li>\n<li>AccessApproved (boolean)<\/li>\n<\/ul>\n<h3>Sets<\/h3>\n<p>I created the following Sets:<\/p>\n<ul>\n<li>&#8220;Access Requested&#8221;: users where &#8220;AccessRequested&#8221; is true.<\/li>\n<li>&#8220;Access Approved&#8221;: users where &#8220;AccessApproved&#8221; is true.<\/li>\n<li>&#8220;Access Requested and Not Approved&#8221;: user in &#8220;Access Requested&#8221; and not in &#8220;Access Approved&#8221;. I use this to display the request status at this point.<\/li>\n<li>&#8220;Access Request Ready&#8221;: the state the user should be in for an access request to make sense. In my case the user is\u00c2\u00a0not a member of the\u00c2\u00a0Approved or Requested sets, but they are a member\u00c2\u00a0of the &#8220;All People with a Manager&#8221; set.<\/li>\n<\/ul>\n<h3>Workflows<\/h3>\n<p>I have one AuthZ workflow:<\/p>\n<ul>\n<li>&#8220;Request Approval&#8221;: triggers an Approval process when the &#8220;AccessApproved&#8221; flag is set by the powershell script.<\/li>\n<\/ul>\n<p>And two Action workflows:<\/p>\n<ul>\n<li>&#8220;Set Access Status&#8221;: uses the Function Evaluator\u00c2\u00a0to write &#8220;Requested by [\/\/Requestor\/DisplayName]&#8221; into the &#8220;AccessRequestStatus&#8221; attribute.<\/li>\n<li>&#8220;Clear Access Request&#8221;: uses the Function Evaluator to set &#8220;AccessRequested&#8221; to &#8220;false&#8221;, and clear the &#8220;AccessReqeustStatus&#8221;. This is run after the approval and clears the request, whether it was approved or rejected.<\/li>\n<\/ul>\n<p>Additionally I changed the workflow I run whenever a new user transitions in to the All People set, that sets a few default values. When working with booleans in the Portal it is always better if they have a value &#8211; null booleans have a habit of setting themselves to &#8216;false&#8217; when an object is edited &#8211; sometimes resulting in &#8220;Access Denied&#8221; messages.\u00c2\u00a0So to this workflow I add:<\/p>\n<ul>\n<li>AccessRequested = IIF(IsPresent(AccessRequested),AccessRequested,&#8217;false&#8217;)<\/li>\n<li>AccessApproved = &#8216;false&#8217;<\/li>\n<\/ul>\n<h3>MPRs<\/h3>\n<p>&#8220;All People may request access&#8221;:<\/p>\n<ul>\n<li>Modify, Create and Read to attributes &#8220;AccessRequested&#8221; and &#8220;AccessRequestStatus&#8221;,<\/li>\n<li>Set before &#8220;Access Request Ready&#8221;; set after &#8220;Access Requested&#8221;,<\/li>\n<li>Action WF &#8220;Set Access Status&#8221;.<\/li>\n<\/ul>\n<p>&#8220;All People may read pending access request status&#8221;:<\/p>\n<ul>\n<li>Read attributes &#8220;AccessRequested&#8221; and &#8220;AccessRequestStatus&#8221;,<\/li>\n<li>Target set &#8220;Access Requested and Not Approved&#8221;.<\/li>\n<\/ul>\n<p>&#8220;All People may read approved request status&#8221;:<\/p>\n<ul>\n<li>Read attribute &#8220;AccessApproved&#8221;,<\/li>\n<li>Target set &#8220;Access Approved&#8221;.<\/li>\n<\/ul>\n<p>&#8220;Powershell user can trigger Access Approval&#8221;:<\/p>\n<ul>\n<li>Read and Modify attribute &#8220;AccessApproved&#8221;,<\/li>\n<li>Set before &#8220;Access Requested&#8221;, set after &#8220;All People&#8221;,<\/li>\n<li>AuthZ WF &#8220;Request Approval&#8221;,<\/li>\n<li>Action WF &#8220;Clear Access Request&#8221;.<\/li>\n<\/ul>\n<p>&#8220;Powershell user may set Access Request details&#8221;:<\/p>\n<ul>\n<li>Read and Modify attributes &#8220;AccessRequested&#8221;, &#8220;AccessApproved&#8221; and &#8220;AccessRequestStatus&#8221;,<\/li>\n<li>Target set &#8220;All People&#8221;.<\/li>\n<\/ul>\n<h3>RCDC<\/h3>\n<p>I modified the User Create and User Edit RCDCs to include the new fields. It is important to include the &#8220;my:Rights&#8221; parameter to allow the MPRs to display and hide the fields appropriately.<\/p>\n<h3>Email Templates<\/h3>\n<p>I also created some specific email templates for the Approval workflow. It is quite handy to include the\u00c2\u00a0AccessRequestStatus attribute in the template as it includes the name of the person who made the original request. This is something you would have with a direct approval, but is lost by having the powershell user trigger the approval.<\/p>\n<h2>Powershell Script<\/h2>\n<p>The powershell script is run under the special user account I created for it and sync&#8217;d to the Portal. It should run on a regular schedule in the background.<\/p>\n<pre>if(@(get-pssnapin | where-object {$_.Name -eq \"FIMAutomation\"} ).count -eq 0) {add-pssnapin FIMAutomation}\r\n$DefaultUri = \"http:\/\/localhost:5725\"\r\n\r\nfunction ModifyImportObject\r\n{\r\n    PARAM([string]$TargetIdentifier, $ObjectType = \"Resource\")\r\n    END\r\n    {\r\n        $importObject = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject\r\n        $importObject.ObjectType = $ObjectType\r\n        $importObject.TargetObjectIdentifier = $TargetIdentifier\r\n        $importObject.SourceObjectIdentifier = $TargetIdentifier\r\n        $importObject.State = 1 # Put\r\n        $importObject\r\n    }\r\n}\r\n\r\nfunction AddImportChangeToImportObject\r\n{\r\n    PARAM($ImportChange, $ImportObject)\r\n    END\r\n    {\r\n        if ($ImportObject.Changes -eq $null)\r\n        {\r\n            $ImportObject.Changes = (,$ImportChange)\r\n        }\r\n        else\r\n        {\r\n            $ImportObject.Changes += $ImportChange\r\n        }\r\n    }\r\n}\r\n\r\nfunction CreateImportChange\r\n{\r\n    PARAM($AttributeName, $AttributeValue, $Operation)\r\n    END\r\n    {\r\n        $importChange = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportChange\r\n        $importChange.Operation = $Operation\r\n        $importChange.AttributeName = $AttributeName\r\n        $importChange.AttributeValue = $AttributeValue\r\n        $importChange.FullyResolved = 1\r\n        $importChange.Locale = \"Invariant\"\r\n        $importChange\r\n    }\r\n}\r\n\r\nfunction SetSingleValue\r\n{\r\n    PARAM($ImportObject, $AttributeName, $NewAttributeValue, $FullyResolved=1)\r\n    END\r\n    {\r\n        $ImportChange = CreateImportChange -AttributeName $AttributeName -AttributeValue $NewAttributeValue -Operation 1\r\n        $ImportChange.FullyResolved = $FullyResolved\r\n        AddImportChangeToImportObject $ImportChange $ImportObject\r\n    }\r\n}\r\n\r\nfunction ConvertResourceToHashtable\r\n{\r\n    PARAM([Microsoft.ResourceManagement.Automation.ObjectModel.ExportObject]$ExportObject)\r\n    END\r\n    {\r\n        $hashtable = @{\"ObjectID\" = \"Not found\"}\r\n        foreach($attribute in $exportObject.ResourceManagementObject.ResourceManagementAttributes)\r\n        {\r\n            if ($attribute.IsMultiValue -eq 1)\r\n            {\r\n                $hashtable[$attribute.AttributeName] = $attribute.Values\r\n            }\r\n            else\r\n            {\r\n                $hashtable[$attribute.AttributeName] = $attribute.Value\r\n            }\r\n        }\r\n        $hashtable\r\n    }\r\n}\r\n\r\n# Find users with the trigger attribute set\r\n$objects = export-fimconfig -customconfig (\"\/Person[AccessRequestStatus != 'Manager approval requested']\")\r\n\r\n# Referenced objects also returned so make sure we get one with the attribute set\r\nforeach ($object in $objects)\r\n{\r\n  $hash = ConvertResourceToHashtable -exportobject $object\r\n  if ($hash.Contains('AccessRequestStatus'))\r\n  {\r\n    $hash\r\n\r\n    $importObject = ModifyImportObject -TargetIdentifier $hash.Item('ObjectID') -objecttype $hash.Item('ObjectType')\r\n    $importChanges = SetSingleValue -importobject $importObject -attributename 'AccessApproved' -newattributevalue $true\r\n    import-fimconfig -importObject $importObject\r\n\r\n    $importObject = ModifyImportObject -TargetIdentifier $hash.Item('ObjectID') -objecttype $hash.Item('ObjectType')\r\n    $importChanges = SetSingleValue -importobject $importObject -attributename 'AccessRequestStatus' -newattributevalue 'Manager approval requested'\r\n    import-fimconfig -importObject $importObject\r\n\r\n  }\r\n}<\/pre>\n<h2>And if you want to do this more than once?<\/h2>\n<p>I&#8217;ve shown you how to triger an authorization following a completed action, using a powershell script and a whole bunch of policy objects. In my case I actually have two types of access to request and there may be more in the future. However I couldn&#8217;t find a suitable way to roll them all into the one set of policy objects. Using a multi-value attribute to send messages to the powershell script was briefly appealing, but I had to forget it due to limitations\u00c2\u00a0with FIM\u00c2\u00a0xpath and the Function Evaluator. So for this approach, the answer for multiple request requirements is to replicate the whole system of policy objects for each case.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Something that has come up from time to time on the FIM forum is the need to\u00c2\u00a0trigger an\u00c2\u00a0AuthZ workflow based on some change made by an Action workflow (or by the Sync Service). This is not possible in the FIM Service today and I don&#8217;t see any evidence that it will be possible in the&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[42,60,62,23,54,45],"tags":[],"class_list":["post-1875","post","type-post","status-publish","format-standard","hentry","category-fim-2010","category-fim-2010-r2","category-mpr","category-powershell","category-sets","category-workflow"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pkp1o-uf","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/1875","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/comments?post=1875"}],"version-history":[{"count":13,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/1875\/revisions"}],"predecessor-version":[{"id":1908,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/1875\/revisions\/1908"}],"wp:attachment":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/media?parent=1875"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/categories?post=1875"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/tags?post=1875"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}