{"id":882,"date":"2010-08-27T11:16:59","date_gmt":"2010-08-27T11:16:59","guid":{"rendered":"https:\/\/www.wapshere.com\/missmiis\/?p=882"},"modified":"2010-08-27T11:16:59","modified_gmt":"2010-08-27T11:16:59","slug":"openldap-provisioning","status":"publish","type":"post","link":"https:\/\/www.wapshere.com\/missmiis\/openldap-provisioning","title":{"rendered":"OpenLDAP Provisioning"},"content":{"rendered":"<p>After getting the <a href=\"https:\/\/www.wapshere.com\/missmiis\/compiling-the-openldap-xma-to-use-with-fim-2010\">OpenLDAP XMA working on FIM<\/a>\u00c2\u00a0I hoped it would be possible to provision to it using FIM codeless sync. Unfortunately the conclusion I have come to is No, it isn&#8217;t.<!--more-->\u00c2\u00a0<\/p>\n<p>The sticking problem is the\u00c2\u00a0objectClass. Typically objects in LDAP will have more than one objectClass, and the only way to set them in MIIS\/ILM is in the metaverse extension code. It is no different with FIM. There is no way to set objectClass from a codeless sync rule, and you can&#8217;t set it in an export attribute flow.\u00c2\u00a0<\/p>\n<p>So a Metaverse extension is still the way to go. Here is an example of a provisioning rule for OpenLDAP. I am creating a posixAccount, mostly as a way of showing how to add the dependant objectClasses. Your objectClasses will no doubt vary.<\/p>\n<p>I have included a couple of functions that I am using to generate a password hash and to find the next uidNumber. Note for the latter function the Terminate sub is also implicated.<\/p>\n<div><\/div>\n<p><code><\/p>\n<pre>Imports Microsoft.MetadirectoryServices\r\nImports System.DirectoryServices\r\nImports System.Text\r\nImports System.IO\r\nImports System.Security.Cryptography\r\n\r\nPublic Class MVExtensionObject\r\n    Implements IMVSynchronization\r\n\r\n    '' It is a better practise to put these constants in a lookup file\r\n    Const OL_SERVER As String = \"myldapserver.mydomain.com\"\r\n    Const OL_ROOT As String = \"dc=myldap,dc=mydomain,dc=com\"\r\n\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0\u00c2\u00a0Const OL_OU_USERS As String = \"ou=users,dc=myldap,dc=mydomain,dc=com\"\r\n    Const OL_UIDFILE As String = \"C:\\Program Files\\Microsoft Forefront Identity Manager\\2010\\Synchronization Service\\MaData\\openLDAP\\uidNumber.txt\"\r\n    Const OL_READ_USER As String = \"cn=fimread,ou=users,dc=myldap,dc=mydomain,dc=com\"\r\n    Const OL_READ_PW As String = \"password\"\r\n    Const OL_DEFAULT_GROUP As String = \"1000\"\r\n\r\n    Public Sub Initialize() Implements IMVSynchronization.Initialize\r\n        ' TODO: Add initialization code here\r\n    End Sub\r\n\r\n    Public Sub Terminate() Implements IMVSynchronization.Terminate\r\n    '' Deleting this file forces a new LDAP search the next time, allowing for accounts which may have been created manually.\r\n        If File.Exists(OL_UIDFILE) Then\r\n            File.Delete(OL_UIDFILE)\r\n        End If\r\n    End Sub\r\n\r\n    Public Sub Provision(ByVal mventry As MVEntry) Implements IMVSynchronization.Provision\r\n        Select Case mventry.ObjectType\r\n            Case \"person\"\r\n                Provision_OpenLDAPPerson(mventry)\r\n        End Select\r\n    End Sub\r\n\r\n    Sub Provision_OpenLDAPPerson(ByVal mventry As MVEntry)\r\n        Dim ShouldExist As Boolean = False\r\n        Dim DoesExist As Boolean\r\n        Dim CSEntry As CSEntry\r\n        Dim OLMA As ConnectedMA = mventry.ConnectedMAs(\"openLDAP\")\r\n        Dim expectedDN As ReferenceValue\r\n        Dim Container As String\r\n\r\n        '' Should the account exist? (Add more logic here to decide)\r\n        ShouldExist = True\r\n\r\n        '' Does the account already exist?\r\n        If OLMA.Connectors.Count = 0 Then\r\n            DoesExist = False\r\n        Else\r\n            DoesExist = True\r\n        End If\r\n\r\n        '' Generate the expected DN for the user - to use in creating or moving\r\n        If mventry(\"accountName\").IsPresent And ShouldExist Then\r\n            Dim RDN As String = \"cn=\" &amp; mventry(\"accountName\").StringValue.ToLower\r\n            expectedDN = OLMA.EscapeDNComponent(RDN).Concat(OL_OU_USERS)\r\n        End If\r\n\r\n        '' Take action based on values of ShouldExist and DoesExist\r\n        If ShouldExist And DoesExist Then\r\n            '' Check if rename needed\r\n            If csentry.DN.ToString.ToLower &lt;&gt; expectedDN.ToString.ToLower Then\r\n                csentry.DN = expectedDN\r\n            End If\r\n\r\n        ElseIf ShouldExist And Not DoesExist Then\r\n            '' Create Account\r\n\r\n            ' Set objectClasses\r\n            Dim oc As ValueCollection\r\n            oc = Utils.ValueCollection(\"inetOrgPerson\")\r\n            oc.Add(\"person\")\r\n            oc.Add(\"organizationalPerson\")\r\n            oc.Add(\"inetOrgPerson\")\r\n            oc.Add(\"posixAccount\")\r\n            oc.Add(\"shadowAccount\")\r\n            oc.Add(\"simpleSecurityObject\")\r\n            CSEntry = OLMA.Connectors.StartNewConnector(\"posixAccount\", oc)\r\n            CSEntry.DN = expectedDN\r\n\r\n            ' Find next available uidNumber\r\n            CSEntry(\"uidNumber\").Value = = NextUidNumber(OL_SERVER, OL_ROOT).ToString\r\n\r\n            ' Set initial password\r\n            CSEntry(\"userPassword\").Value = \"{MD5}\" &amp; GenerateHash(\"INitial001\")\r\n\r\n            ' The following could also be done as export flow rules\r\n            CSEntry(\"gidNumber\").Value = OL_DEFAULT_GROUP\r\n            CSEntry(\"sn\").Value = mventry(\"lastName\").Value\r\n            CSEntry(\"givenName\").Value = mventry(\"firstName\").Value\r\n            CSEntry(\"cn\").Value = mventry(\"accountName\").Value.ToLower\r\n            CSEntry(\"uid\").Value = mventry(\"accountName\").Value.ToLower\r\n            CSEntry(\"displayName\").Value = mventry(\"firstName\").StringValue &amp; \" \" &amp; mventry(\"lastName\").StringValue.ToUpper\r\n\r\n            CSEntry.CommitNewConnector()\r\n\r\n        ElseIf Not ShouldExist And DoesExist Then\r\n            '' Delete\r\n            CSEntry = OLMA.Connectors.ByIndex(0)\r\n            CSEntry.Deprovision()\r\n\r\n        End If\r\n    End Sub\r\n\r\n    Public Function NextUidNumber(ByVal ldapServerName As String, ByVal searchRoot As String) As Integer\r\n    '' The first time the function runs it finds the highest uidNumber in LDAP and stores it in a text file.\r\n    '' On subsequent runs it retrieves the last uidNumber straight from the text file. This allows for sync runs\r\n    '' where multiple accounts are created.\r\n    '' The text file is deleted as part of the Terminate routine.\r\n\r\n        Dim oSearcher As New DirectorySearcher\r\n        Dim oResults As SearchResultCollection\r\n        Dim oResult As SearchResult\r\n        Dim lastuid As Integer = 0\r\n\r\n        If File.Exists(UIDFILE) Then\r\n            Dim fileReader As New StreamReader(UIDFILE)\r\n            lastuid = CInt(fileReader.ReadLine)\r\n            fileReader.Close()\r\n        Else\r\n            Try\r\n                With oSearcher\r\n                    .SearchRoot = New DirectoryEntry(\"LDAP:\/\/\" &amp; ldapServerName &amp; \"\/\" &amp; searchRoot, _\r\n                                                     \"cn=root,dc=ldap,dc=eesp,dc=ch\", \"hesso_2010\", AuthenticationTypes.FastBind)\r\n                    .PropertiesToLoad.Add(\"uidNumber\")\r\n                    .Filter = \"uidNumber=*\"\r\n                    oResults = .FindAll()\r\n                End With\r\n\r\n                For Each oResult In oResults\r\n                    If oResult.GetDirectoryEntry().Properties(\"uidNumber\").Value &gt; lastuid Then\r\n                        lastuid = oResult.GetDirectoryEntry().Properties(\"uidNumber\").Value\r\n                    End If\r\n                Next\r\n\r\n            Catch e As Exception\r\n                Throw New UnexpectedDataException(e.Message)\r\n                Return -1\r\n            End Try\r\n        End If\r\n\r\n        Dim fileWriter As New StreamWriter(UIDFILE, False)\r\n        fileWriter.WriteLine(lastuid + 1)\r\n        fileWriter.Close()\r\n\r\n        Return lastuid + 1\r\n    End Function\r\n\r\n    Private Function GenerateHash(ByVal SourceText As String) As String\r\n        ' Returns the MD5 hash of the SourceText string.\r\n        Dim Ue As New UTF7Encoding()\r\n        Dim ByteSourceText() As Byte = Ue.GetBytes(SourceText)\r\n        Dim Md5 As New MD5CryptoServiceProvider()\r\n        Dim ByteHash() As Byte = Md5.ComputeHash(ByteSourceText)\r\n        Return Convert.ToBase64String(ByteHash)\r\n    End Function\r\n\r\nEnd Class<\/pre>\n<p>\u00c2\u00a0<\/p>\n<p><\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>After getting the OpenLDAP XMA working on FIM\u00c2\u00a0I hoped it would be possible to provision to it using FIM codeless sync. Unfortunately the conclusion I have come to is No, it isn&#8217;t.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","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,34,28,46],"tags":[],"class_list":["post-882","post","type-post","status-publish","format-standard","hentry","category-fim-2010","category-ilm2007","category-miis2003","category-openldap"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/pkp1o-ee","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/882","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=882"}],"version-history":[{"count":7,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/882\/revisions"}],"predecessor-version":[{"id":958,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/posts\/882\/revisions\/958"}],"wp:attachment":[{"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/media?parent=882"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/categories?post=882"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wapshere.com\/missmiis\/wp-json\/wp\/v2\/tags?post=882"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}