-1

I found a great function that takes a valid Active Directory LDAP distinguishedName (DN) string and converts it to properly formatted canonicalName (not CN) string written in PowerShell.

Because I am using ldapjs in Node.js and need to retrieve an AD object's canonicalName attribute (which is not available natively using any of the Node/AD/LDAP libraries because the attribute in question is "constructed"), it would be best for me to convert this function to pure JavaScript.

How can I take a stab at it? The original post where this code (inserted below) was referenced from can be found here: https://gallery.technet.microsoft.com/scriptcenter/Get-CanonicalName-Convert-a2aa82e5

An example input value:

'CN=Eric Paw,OU=Sales,OU=People,DC=example,DC=com'

Expected Result:

'example.com/People/Sales/Eric Paw'

(Hints: Resultant JS function should be able to handle deeply OU-nested objects! Also I'm guessing that RegEx expressions could probably help handle some parts of this pretty nicely but don't have the faintest clue how to implement that.)

function Get-CanonicalName ([string[]]$DistinguishedName) { 
    foreach ($dn in $DistinguishedName) {      
        ## Split the dn string up into it's constituent parts 
        $d = $dn.Split(',') 
        
        ## get parts excluding the parts relevant to the FQDN and trim off the dn syntax 
        $arr = (@(($d | Where-Object { $_ -notmatch 'DC=' }) | ForEach-Object { $_.Substring(3) }))  
        
        ## Flip the order of the array. 
        [array]::Reverse($arr)  
 
        ## Create and return the string representation in canonical name format of the supplied DN 
        "{0}/{1}" -f  (($d | Where-Object { $_ -match 'dc=' } | ForEach-Object { $_.Replace('DC=','') }) -join '.'), ($arr -join '/') 
    } 
}
halfer
  • 19,824
  • 17
  • 99
  • 186
tfrancois
  • 175
  • 10

2 Answers2

1

This doesn't answer your exact question, but it answers your problem.

Constructed attributes are available to any LDAP client - you just have to specifically ask for them.

For example, if you're doing a search, you can specify which attributes you want returned. If you don't specify anything, it will return all non-constructed attributes that have a value. If you need a constructed attribute, you need to specify it.

I don't know which library you're using, but if you're using LDAPjs, there is an attributes property in the options object that gets passed to the search method., where you can specify which attributes you want returned. You can specify canonicalName there (along with the other attributes you want).

Likewise, if you bind directly to a specific object, there is usually a way to retrieve a constructed attribute.

halfer
  • 19,824
  • 17
  • 99
  • 186
Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • Thank you for your response. I have actually specifically specified the canonicalName in the attributes property to no avail. In fact, I've examined the entire raw data the call to LDAP retrieves and every field I've requested is returned with the exception of cannonicalName. What's funny is supposedly the dn/distinguishedName attribute is a "constructed" attribute as well and that is returned without issue. But since the dn attribute contains all the elements required to construct the canonicalName (again not same as 'cn'), I think the simplest solution is to construct it programmatically. – tfrancois Jan 29 '19 at 14:47
  • And if it helps, I'm using the activedirectory2 library from npm https://www.npmjs.com/package/activedirectory2. – tfrancois Jan 29 '19 at 14:49
  • It looks like there are two places you could specify it. First, when [creating the `ActiveDirectory` object](https://www.npmjs.com/package/activedirectory2#activedirectoryoptions), there is an `attributes` collection you can pass for which attributes to retrieve for users and groups (this is for when you bind directly to a user or group - not through a search). Also, any of the "find" methods take an [`opts`](https://www.npmjs.com/package/activedirectory2#opts) object where you can specify a list of attributes too. You would be better off to show your code. It might be something simple. – Gabriel Luci Jan 29 '19 at 14:57
  • 1
    Just to clarify, `distinguishedName` is not a constructed attribute. You can see that in [Microsoft's documentation](https://msdn.microsoft.com/en-us/library/cc219839.aspx) - `systemFlags` does not have `FLAG_ATTR_IS_CONSTRUCTED` like [`canonicalName`](https://msdn.microsoft.com/en-us/library/cc220147.aspx) does. – Gabriel Luci Jan 29 '19 at 15:00
  • Thanks for your feedback, I assure you I've correctly passed the canonicalName as a desired attribute to return but its just not happening. Respectfully, it's not something I've just overlooked or don't understand. It def isn't being passed back even though I've requested it along with about 28+ other attributes. They all are returned without issue (included user photo in binary format!) but canonicalName is not. It seems as though with other libraries special handling is required for this attribute but no evident options are provided for it with activedirectory2 or ldapjs. – tfrancois Jan 30 '19 at 01:44
  • I would love it if someone could simply help with what I'm requesting as ultimately that would achieve the result I'm looking for as I've exhaustively tried everything else thus far to no avail. – tfrancois Jan 30 '19 at 01:45
0

Here a stab at answering my own question, hope it helps someone. Seems to work for me just fine even in the case where the distinguishedName attribute of the user has multiple CNs in the value/path.

function formatCanonicalName( DN ) {

  var CN = "", DC = "", OU = "";
  DN = DN.replace("\\,", "~");
  DN.split(",").forEach(function(item) {
    switch (item.substring(0,2)) {
      case 'CN': 
        if (item.indexOf('~') > -1) { item = item.split("~")[1] + " " + item.split("~")[0]; }
        CN = item.replace("CN=", "") + "/" + CN; 
        break;
      case 'OU': 
        OU = item.replace("OU=", "") + '/' + OU; 
        break;
      case 
        'DC': DC = DC + item.replace("DC=", "") + '.'; 
        break;
    };
  }); 
  return DC.substring(0, DC.length - 1) + '/' + OU + CN.substring(0, CN.length - 1); 
}
tfrancois
  • 175
  • 10