Skip to content

Authorization after an Action

Something that has come up from time to time on the FIM forum is the need to trigger an AuthZ 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’t see any evidence that it will be possible in the future either – I guess it must mess up the workflow processing on some fundamental level.

The gereral consensus on the forum has been that you need to start a new request from outside the Portal – perhaps by using a powershell script. This new request can then follow the full AuthN -> AuthZ -> Action progression. But how to trigger it?

I have now worked through this idea and it’s working, though did need quite a few policy objects in the Portal as well as the script.

The problem I needed to solve

Users need to be able to request access to a system and the access must be approved.

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’t want an approval holding up creation of the person object. The person should be created and then the approval should kick off.

In fact it’s not just for convenience - 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 request. Later on when the request is approved they will of course get to see that as well.

Here’s what it looks like

So here’s what the end result is looking like in my lab. Notice I’ve pasted over some of the text as this is a customer lab with some real names in it (not Elvis obv).

I should also add I was aiming to solve this using out of the box functionality only. I could perhaps do something more elegant with custom workflows – but I’m sure my client is not the only one who prefers to avoid that.

I added an extra tab to 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.  
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.  
I then have a bit of powershell magic going on in the background. This script:

  • detects the request,
  • sets another attribute (“AccessApproved”) that has an Approval AuthZ workflow associated with it, and
  • changes the request status to “Manager approval requested”.
 
The user’s manager receives the Approval request, which they can approve in the FIM Portal or using the Outlook client, if installed.  
Now when the person’s details are checked we see their Approved status. The Sync Service does whatever needs to be done with this information.  

FIM Policy Objects

I had to create quite a few policy objects to support all of this.

Powershell User

I created a dedicated user account to run the powershell script. I explicitly blocked this user from the “All People” set so it wouldn’t accidentally trigger other workflows.

Schema

I now have three attributes to handle the request:

  • AccessRequested (boolean)
  • AccessRequestStatus (indexed string)
  • AccessApproved (boolean)

Sets

I created the following Sets:

  • “Access Requested”: users where “AccessRequested” is true.
  • “Access Approved”: users where “AccessApproved” is true.
  • “Access Requested and Not Approved”: user in “Access Requested” and not in “Access Approved”. I use this to display the request status at this point.
  • “Access Request Ready”: the state the user should be in for an access request to make sense. In my case the user is not a member of the Approved or Requested sets, but they are a member of the “All People with a Manager” set.

Workflows

I have one AuthZ workflow:

  • “Request Approval”: triggers an Approval process when the “AccessApproved” flag is set by the powershell script.

And two Action workflows:

  • “Set Access Status”: uses the Function Evaluator to write “Requested by [//Requestor/DisplayName]” into the “AccessRequestStatus” attribute.
  • “Clear Access Request”: uses the Function Evaluator to set “AccessRequested” to “false”, and clear the “AccessReqeustStatus”. This is run after the approval and clears the request, whether it was approved or rejected.

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 – null booleans have a habit of setting themselves to ‘false’ when an object is edited – sometimes resulting in “Access Denied” messages. So to this workflow I add:

  • AccessRequested = IIF(IsPresent(AccessRequested),AccessRequested,’false’)
  • AccessApproved = ‘false’

MPRs

“All People may request access”:

  • Modify, Create and Read to attributes “AccessRequested” and “AccessRequestStatus”,
  • Set before “Access Request Ready”; set after “Access Requested”,
  • Action WF “Set Access Status”.

“All People may read pending access request status”:

  • Read attributes “AccessRequested” and “AccessRequestStatus”,
  • Target set “Access Requested and Not Approved”.

“All People may read approved request status”:

  • Read attribute “AccessApproved”,
  • Target set “Access Approved”.

“Powershell user can trigger Access Approval”:

  • Read and Modify attribute “AccessApproved”,
  • Set before “Access Requested”, set after “All People”,
  • AuthZ WF “Request Approval”,
  • Action WF “Clear Access Request”.

“Powershell user may set Access Request details”:

  • Read and Modify attributes “AccessRequested”, “AccessApproved” and “AccessRequestStatus”,
  • Target set “All People”.

RCDC

I modified the User Create and User Edit RCDCs to include the new fields. It is important to include the “my:Rights” parameter to allow the MPRs to display and hide the fields appropriately.

Email Templates

I also created some specific email templates for the Approval workflow. It is quite handy to include the AccessRequestStatus 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.

Powershell Script

The powershell script is run under the special user account I created for it and sync’d to the Portal. It should run on a regular schedule in the background.

if(@(get-pssnapin | where-object {$_.Name -eq "FIMAutomation"} ).count -eq 0) {add-pssnapin FIMAutomation}
$DefaultUri = "http://localhost:5725"

function ModifyImportObject
{
    PARAM([string]$TargetIdentifier, $ObjectType = "Resource")
    END
    {
        $importObject = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject
        $importObject.ObjectType = $ObjectType
        $importObject.TargetObjectIdentifier = $TargetIdentifier
        $importObject.SourceObjectIdentifier = $TargetIdentifier
        $importObject.State = 1 # Put
        $importObject
    }
}

function AddImportChangeToImportObject
{
    PARAM($ImportChange, $ImportObject)
    END
    {
        if ($ImportObject.Changes -eq $null)
        {
            $ImportObject.Changes = (,$ImportChange)
        }
        else
        {
            $ImportObject.Changes += $ImportChange
        }
    }
}

function CreateImportChange
{
    PARAM($AttributeName, $AttributeValue, $Operation)
    END
    {
        $importChange = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportChange
        $importChange.Operation = $Operation
        $importChange.AttributeName = $AttributeName
        $importChange.AttributeValue = $AttributeValue
        $importChange.FullyResolved = 1
        $importChange.Locale = "Invariant"
        $importChange
    }
}

function SetSingleValue
{
    PARAM($ImportObject, $AttributeName, $NewAttributeValue, $FullyResolved=1)
    END
    {
        $ImportChange = CreateImportChange -AttributeName $AttributeName -AttributeValue $NewAttributeValue -Operation 1
        $ImportChange.FullyResolved = $FullyResolved
        AddImportChangeToImportObject $ImportChange $ImportObject
    }
}

function ConvertResourceToHashtable
{
    PARAM([Microsoft.ResourceManagement.Automation.ObjectModel.ExportObject]$ExportObject)
    END
    {
        $hashtable = @{"ObjectID" = "Not found"}
        foreach($attribute in $exportObject.ResourceManagementObject.ResourceManagementAttributes)
        {
            if ($attribute.IsMultiValue -eq 1)
            {
                $hashtable[$attribute.AttributeName] = $attribute.Values
            }
            else
            {
                $hashtable[$attribute.AttributeName] = $attribute.Value
            }
        }
        $hashtable
    }
}

# Find users with the trigger attribute set
$objects = export-fimconfig -customconfig ("/Person[AccessRequestStatus != 'Manager approval requested']")

# Referenced objects also returned so make sure we get one with the attribute set
foreach ($object in $objects)
{
  $hash = ConvertResourceToHashtable -exportobject $object
  if ($hash.Contains('AccessRequestStatus'))
  {
    $hash

    $importObject = ModifyImportObject -TargetIdentifier $hash.Item('ObjectID') -objecttype $hash.Item('ObjectType')
    $importChanges = SetSingleValue -importobject $importObject -attributename 'AccessApproved' -newattributevalue $true
    import-fimconfig -importObject $importObject

    $importObject = ModifyImportObject -TargetIdentifier $hash.Item('ObjectID') -objecttype $hash.Item('ObjectType')
    $importChanges = SetSingleValue -importobject $importObject -attributename 'AccessRequestStatus' -newattributevalue 'Manager approval requested'
    import-fimconfig -importObject $importObject

  }
}

And if you want to do this more than once?

I’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’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 with FIM xpath 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.

{ 4 } Comments

  1. Eugene | January 16, 2012 at 12:52 pm | Permalink

    Carol, why do you run this PS in a background usign scheduled tasks?
    I would do it using a FIM PowerShell activity and run import-fimconfig.

  2. Carol | January 16, 2012 at 8:42 pm | Permalink

    As I said, doing it with OOB activities. I also don’t think it would have saved me much work. Perhaps the need to have the Status attribute which I search on, but actually I quite like that as it gives feedback to the user.

  3. Brian Desmond | January 17, 2012 at 12:01 am | Permalink

    The building block activities have a new flag in R2 that lets you put a request from the Action WF through AuthZ

  4. Carol | January 17, 2012 at 12:29 am | Permalink

    I did not know that. However we’d still be talk custom activity right? Still – that’s good to know.

Post a Comment

Your email is never published nor shared. Required fields are marked *
*