Welcome to Office Zealot Sign in | Join | Help

Accessing Public Folder Permissions Programmatically

I recently came across a need to programmatically access a list of users and permissions for a Public Folder. Seeing as how there is no way to do this with Outlook VBA or CDO, I had to find a way to do this. Luckily, the Exchange 5.5 SDK includes an ACL Component (ACL.dll) that provides an API to access these security settings. The full reference material is here:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/exchserv/html/comcpnts_8f04.asp.

Exchange MVP Siegfried Weber has also created a Folder Permission Viewer, an excellent utility that not only provides a UI for displaying a treeview of Outlook folders, but recreates the Folder Permissions dialog from Outlook. You can download Sig's sample here:

http://www.cdolive.com/aclviewer.htm.

Both the SDK and Sig's sample were instrumental in helping me solve my problem. It is not rocket science, and I've provided some code below that illustrates how to access the permissions for a given CDO.Folder object. Aside from a Win32API call for accessing NT account information, and a wack of ACL constants, it is pretty easy to see what is going on. All that really happens is an ACLObject for a given folder is retrieved, and through it you get a collection of ACEs (Access Control Entries), one for each user. Each ACLObject in a set of ACEs has the properties that tell us what rights the user has, such as CreateItems, DeleteAll etc. The Rights property provides access to the user's role for that folder - Owner, Author, etc. Another procedure, GetUserInfo, takes the SID (Security Identifier) from an ACLObject to get a CDO AddressEntry object. This lets us get the Exchange DN for the user, as well as their full name (as it appears in the GAL, for example).

You can easily incorporate the VB code below into your solution. As long as you have a valid CDO.Session object initiated, and you pass a CDO.Folder object to the GetPermissions procedure, you are good to go! Just tweak the code as you see fit, based on what you need to do with the users and their rights that you retrieve.

Option Explicit
Private Declare Function LookupAccountSid Lib "advapi32.dll" Alias "LookupAccountSidA" ( _
 ByVal lpSystemName As String, _
 Sid As Any, _
 ByVal name As String, _
 cbName As Long, _
 ByVal ReferencedDomainName As String, _
 cbReferencedDomainName As Long, _
 peUse As Integer _
 ) As Long
'FOLDER RIGHTS
Const ACL_ROLE_OWNER = &H7FB
Const ACL_ROLE_PUBLISH_EDITOR = &H4FB
Const ACL_ROLE_EDITOR = &H47B
Const ACL_ROLE_PUB_AUTHOR = &H49B
Const ACL_ROLE_AUTHOR = &H41B
Const ACL_ROLE_NONEDITING_AUTHOR = &H413
Const ACL_ROLE_REVIEWER = &H401
Const ACL_ROLE_CONTRIBUTOR = &H402
Const ACL_ROLE_ROLE_NONE = &H400
'ACL RIGHTS
Const ACL_ACE_ID_DEFAULT = "ID_ACL_DEFAULT"
Const ACL_ACE_ID_ANONYMOUS = "ID_ACL_ANONYMOUS"
Const ACL_RIGHTS_CREATE_ITEMS = &H2
Const ACL_RIGHTS_READ_ITEMS = &H1
Const ACL_RIGHTS_CREATE_SUBFOLDERS = &H80
Const ACL_RIGHTS_FOLDER_OWNER = &H100
Const ACL_RIGHTS_FOLDER_CONTACT = &H200
Const ACL_RIGHTS_FOLDER_VISIBLE = &H400
Const ACL_RIGHTS_EDIT_OWN = &H8
Const ACL_RIGHTS_EDIT_ALL = &H28
Const ACL_RIGHTS_DEL_OWN = &H10
Const ACL_RIGHTS_DEL_ALL = &H50
Const ACL_RIGHTS_NONE = 0
Const CdoPR_EMS_AB_ASSOC_NT_ACCOUNT = &H80270102
'FOR DETERMINING ACL ROLES
Dim ACL_RIGHTS_OWNER As Long
Dim ACL_RIGHTS_PUB_EDITOR As Long
Dim ACL_RIGHTS_EDITOR As Long
Dim ACL_RIGHTS_PUB_AUTHOR As Long
Dim ACL_RIGHTS_AUTHOR As Long
Dim ACL_RIGHTS_NONEDIT_AUTHOR As Long
Dim ACL_RIGHTS_REVIEWER As Long
Dim ACL_RIGHTS_CONTRIBUTOR As Long
Dim ACL_RIGHTS_ROLE_NONE As Long
Dim ACL_RIGHTS_OWNER_2 As Long
Dim ACL_RIGHTS_ROLE_NONE_2 As Long
Dim ACL_RIGHTS_ROLE_NONE_3 As Long
Dim objSession As MAPI.Session
Dim objMAPIFolder As MAPI.Folder
Sub GetPermissions(objMAPIFolder As MAPI.Folder)    
    If objMAPIFolder Is Nothing Then Exit Sub
    Dim objFolderACE As MSExchangeACLLib.ACE
    Dim objFolderACL As MSExchangeACLLib.ACLObject
    Dim objFolderACEs As MSExchangeACLLib.IACEs
    Dim lngRights As Long
    ' ACL roles, computed of the specific rights
    ACL_RIGHTS_OWNER_2 = ACL_RIGHTS_CREATE_ITEMS Or ACL_RIGHTS_READ_ITEMS _
 Or ACL_RIGHTS_CREATE_SUBFOLDERS Or ACL_RIGHTS_FOLDER_OWNER _
 Or ACL_RIGHTS_FOLDER_VISIBLE _
 Or ACL_RIGHTS_EDIT_ALL Or ACL_RIGHTS_DEL_ALL
    ACL_RIGHTS_OWNER = ACL_RIGHTS_OWNER_2 Or ACL_RIGHTS_FOLDER_CONTACT
    ACL_RIGHTS_PUB_EDITOR = ACL_RIGHTS_CREATE_ITEMS Or ACL_RIGHTS_READ_ITEMS _
 Or ACL_RIGHTS_CREATE_SUBFOLDERS _
 Or ACL_RIGHTS_FOLDER_VISIBLE _
 Or ACL_RIGHTS_EDIT_ALL Or ACL_RIGHTS_DEL_ALL
    ACL_RIGHTS_EDITOR = ACL_RIGHTS_CREATE_ITEMS Or ACL_RIGHTS_READ_ITEMS _
 Or ACL_RIGHTS_FOLDER_VISIBLE _
 Or ACL_RIGHTS_EDIT_ALL Or ACL_RIGHTS_DEL_ALL
    ACL_RIGHTS_PUB_AUTHOR = ACL_RIGHTS_CREATE_ITEMS Or ACL_RIGHTS_READ_ITEMS _
 Or ACL_RIGHTS_CREATE_SUBFOLDERS _
 Or ACL_RIGHTS_FOLDER_VISIBLE _
 Or ACL_RIGHTS_EDIT_OWN Or ACL_RIGHTS_DEL_OWN
    ACL_RIGHTS_AUTHOR = ACL_RIGHTS_CREATE_ITEMS Or ACL_RIGHTS_READ_ITEMS _
 Or ACL_RIGHTS_FOLDER_VISIBLE _
 Or ACL_RIGHTS_EDIT_OWN Or ACL_RIGHTS_DEL_OWN
    ACL_RIGHTS_NONEDIT_AUTHOR = ACL_RIGHTS_CREATE_ITEMS Or ACL_RIGHTS_READ_ITEMS _
 Or ACL_RIGHTS_FOLDER_VISIBLE _
 Or ACL_RIGHTS_DEL_OWN
    ACL_RIGHTS_REVIEWER = ACL_RIGHTS_READ_ITEMS Or ACL_RIGHTS_FOLDER_VISIBLE
    ACL_RIGHTS_CONTRIBUTOR = ACL_RIGHTS_CREATE_ITEMS Or ACL_RIGHTS_FOLDER_VISIBLE
    ACL_RIGHTS_ROLE_NONE = ACL_RIGHTS_FOLDER_VISIBLE
    ACL_RIGHTS_ROLE_NONE_2 = ACL_RIGHTS_NONE
    ACL_RIGHTS_ROLE_NONE_3 = ACL_RIGHTS_FOLDER_CONTACT
   ' Create new folder ACL object
    Set objFolderACL = New MSExchangeACLLib.ACLObject
    If Not objFolderACL Is Nothing Then
 ' Check if valid folder is given
     If Not objMAPIFolder Is Nothing Then
     ' Bind folder and retrieve ACEs
     Set objFolderACL.CDOItem = objMAPIFolder
     Set objFolderACEs = objFolderACL.ACEs
        ' Check if ACEs list not empty
     If objFolderACEs.Count <> 0 Then
                ' Loop through the ACEs list
         For Each objFolderACE In objFolderACEs
      'Check if this user is one of the default roles
             If objFolderACE.ID <> ACL_ACE_ID_DEFAULT And objFolderACE.ID <> ACL_ACE_ID_ANONYMOUS Then
          'IF the user is not one of the above, retrieve more information
                        GetUserInfo objFolderACE.ID
          'Check the appropriate MSExchangeACLLib.ACE properties to see the rights returned
          Debug.Print objFolderACE.CreateItems
          Debug.Print objFolderACE.CreateSubFolders
          Debug.Print objFolderACE.DeleteAll
          Debug.Print objFolderACE.DeleteOwn
          Debug.Print objFolderACE.EditAll
          Debug.Print objFolderACE.EditOwn
          Debug.Print objFolderACE.FolderContact
          Debug.Print objFolderACE.FolderOwner
          Debug.Print objFolderACE.FolderVisible
          Debug.Print objFolderACE.ReadItems
          lngRights = objFolderACE.Rights
          ' Possible values for MSExchangeACLLib.ACE.Rights are the ACL Constants declared above
          'ACL_RIGHTS_OWNER, ACL_RIGHTS_OWNER_2, ACL_RIGHTS_PUB_EDITOR, etc.
      End If
         Next
     End If
        End If
    End If
    ' Destroy objects
    Set objFolderACE = Nothing
    Set objFolderACL = Nothing
    Set objFolderACEs = Nothing
End Sub
Private Sub GetUserInfo(strEntryID)
On Error Resume Next
    ' Declare variables
    Dim objAddressEntry As MAPI.AddressEntry, objFields As MAPI.Fields
    Dim strX As String, intX As Integer
    Dim bByte() As Byte
    Dim tmp As Integer
    Dim i As Integer
    Dim ret As Boolean
    Dim strSID As String
    Dim strName As String
    Dim strDomain As String
    Dim iType As Integer
    ' Get address entry
    Set objAddressEntry = objSession.GetAddressEntry(strEntryID)
    Debug.Print objAddressEntry.Address
    Debug.Print objAddressEntry.name
    Set objFields = objAddressEntry.Fields
    If Err.Number <> 0 Then GoTo Exitt:
    'CODE FOR GETTING THE NT DOMAIN AND USERNAME
    '---------------------------------------------------------------------------
    'Get the PR_EMS_AB_ASSOC_NT_ACCOUNT (&H80270102) field
    strSID = objFields(CdoPR_EMS_AB_ASSOC_NT_ACCOUNT).Value
    'The SID is stored in a hexadecimal representation of the binary SID
    'so we convert it and store it in a byte array
    tmp = Len(strSID) / 2 - 1
    ReDim bByte(tmp) As Byte
    For i = 0 To tmp - 1
     bByte(i) = CInt("&h" & Mid(strSID, (i * 2) + 1, 2))
    Next
    'Allocate space for the strings so the API won't GPF
    strName = Space(64)
    strDomain = Space(64)
    'Get the NT Domain and UserName
    ret = LookupAccountSid(vbNullString, bByte(0), strName, Len(strName), strDomain, Len(strDomain), iType)
    If ret Then 'Strip the Null characters from the returned strings strDomain = Left(strDomain, InStr(strDomain, Chr(0)) - 1)
 strName = Left(strName, InStr(strName, Chr(0)) - 1)
 Debug.Print strDomain & "\" & strName
    Else
 'error!
 Set objAddressEntry = Nothing
        Exit Sub
    End If
    '---------------------------------------------------------------------------
Exitt:
    ' Destroy objects
    Set objAddressEntry = Nothing
    Set objFields = Nothing
    Exit Sub
End Sub
Published Tuesday, March 16, 2004 12:35 PM by legault
Filed under: ,

Comments

#

I found your posting while looking for something to delete users from the permissions list. I need to do this to resolve lots of 9551 events generated by zombie accounts. I need to go through the ACEs and delete the zombies accounts from the Public Folder ACEs. It looks loke your solution addresses Exchange 55. Have seen any ACL components made for Exchange 2000/2003? Thanks, Konan
Thursday, June 17, 2004 11:40 AM by Anonymous

#

Actually, the ACL component can be used with Exchange 2000/2003 as well.
Thursday, June 17, 2004 11:48 AM by legault

# Where is the sample code

".........You can easily incorporate the VB code below into your solution......."

Why cant i see any sample code on this page at all??
Thursday, October 28, 2004 7:26 AM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

MH - whoa! It WAS there. I don't know what the heck happened! I hope I have it archived somewhere...
Thursday, October 28, 2004 2:55 PM by legault

# C#??

Before i start understanding and translating the code, do you by any chance have the same example in c#?

Or just any example showing how to modify the permissions of a public folder, using c#?
Friday, October 29, 2004 6:09 AM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

MH: No, sorry, I don't have any C# samples for that. It shouldn't be too hard to translate though.
Friday, October 29, 2004 8:52 AM by legault

# re: Accessing Public Folder Permissions Programmatically

Have you ever tried to work with acl.dll from asp.net?

I have made an asp.net application to display and edit folder permissions. It seems like acl.dll dosn't properly close the connection to the server.

I know the closing is in cdo.dll but when i post acl code between the session.logon and the session.logoff the connection is not closed on the exchange server.

Any ideas?
Thursday, February 17, 2005 5:07 AM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

Martin: I'm not much of an ASP.NET guy, so I'm not sure what the implications are here. Are you destroying all relevant CDO objects in your code after Logoff? I don't think there are permission issues here, but I wonder what effect using the ASP.NET or Internet Guest Account would have. Then again, if you can login, it is not likely to be permission related.

How are you monitoring the open connection?
Monday, February 21, 2005 9:15 AM by legault

# re: Accessing Public Folder Permissions Programmatically

I'm getting an RPC error when I try to set the MSExchangeACLLib.ACLObject = to an existing public folder. Any suggestions? Error description "RPC could not call the server or could not return the results of calling the server."
Thursday, April 21, 2005 1:34 PM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

Thursday, April 21, 2005 2:03 PM by legault

# re: Accessing Public Folder Permissions Programmatically

Thanks. I saw that too. If it's supposed to work with Exch 5.5 and 2000/2003 it doesn't make sense for it to be 16-bit client issue. Thanks for looking. I was hoping to get lucky and get a quick fix.
Thursday, April 21, 2005 2:37 PM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

Check out Public Folder Analyzer from Priasoft - www.priasoft.com/exchangemigration/public_folder_analyzer.asp
Wednesday, June 08, 2005 10:29 PM by Anonymous

# LookupAccountSid

Hi,

my access 2003 crashes after some calls to lookupaccountsid with ..,len(str...), . After replacing the paramete with a variable of type long the programm runs stable.

a.
Thursday, August 25, 2005 6:57 AM by Anonymous

# LookupAccountSid

Hi again,

After some testing I recognized, that the declaration of lookupaccountsid (from ms) was wrong. The last Parameter has to be of type long (not integer).

a.
Saturday, August 27, 2005 2:35 AM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

Hi Eric,
I took your code to read out the permissions of our public folder hierarchy. After some public folders I get the following error message in the Exchange Server eventlog:
Source: MSExchangeIS, EventID : 9646 " MAPI Session "..." exceeded the maximum of 50 objects of type objACLview"

There is an article of MS about this:
http://support.microsoft.com/?id=830829

I don't want to change any server setting.
Did I miss to close the ACL objects?

Do you have any idea?


Thanks Thomas
Tuesday, September 20, 2005 4:05 AM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

Thanks for the pointer Thomas, I wasn't aware of this code being affected by EX 2003 (I usually test on EX 2000). If you used my GetPermission procedure above as is, then the used objects *should* get destroyed. Otherwise, I guess there's no alternative than to be a victim to the strange inner workings of MAPI unless you modify the registry as per that KB article.
Tuesday, September 20, 2005 8:35 AM by legault

# re: Accessing Public Folder Permissions Programmatically

Is it possible to do a find / replace on public folders permissions using this info?
Wednesday, February 22, 2006 1:22 PM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

I'd really like an example in c# if anyone has managed to develop this. I need to be able to work out if a specific windows account has at least read permissions on a public folder.
Friday, February 24, 2006 6:31 PM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

SK: The short answer is yes. The implementation is up to you, but all you really need to do is:

- use CDO to return an AddressEntry object for a specific user
- create a new MSExchange.ACE object
- pass the AddressEntry.ID value to the ACE object
- pass one of the ROLE_ constants for a particular permission level to ACE.Rights
- add the ACE object to an IACEs collection returned from a ACLObject whose CDOItem property is set to an Exchange folder
- then call ACLObject.Update
Tuesday, February 28, 2006 1:35 PM by legault

# re: Accessing Public Folder Permissions Programmatically

Using this example and others I have cobbled together a library that gets and sets public folder permissions. The problem I am running into is that if the object is not classified as cdoUser the permissions cannot be set. In my environment, this is true with Security Groups and Distribution Groups. Do you have any examples of how to set permissions for those?
Tuesday, March 07, 2006 4:00 PM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

Bill: Sorry, I haven't tried using Groups yet when applying PF permissions; I'm surprised they'd act differently. Maybe if I can get some free time I'll have a chance to try it out and find out how it's done.
Friday, March 10, 2006 1:05 PM by legault

# re: Accessing Public Folder Permissions Programmatically

You may also want to check out Folder Permissions Manager from Symprex, which allows you to centrally manage permissions on both mailbox folders and public folders. You can view and apply permissions to one folder at a time and to folders including sub-folders. Permissions templates can be stored and reused to apply a specific set of permissions again. Also includes different ways to apply permissions, such as append, replace, overwrite, remove etc. Can also report permissions to CSV and Excel files.
Wednesday, May 17, 2006 2:08 AM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

I built a html page and embed an Outlook object in it. I can use this html page to display any sub-folders by calling the object folder name, i.e. "in-box", "Calendar" and so for.

But I can not use the same method to call the object folders that those under [Public Folders], I already set the sub-folders' access permission to [Reviewer].
Monday, August 21, 2006 3:58 PM by Anonymous

# re: Accessing Public Folder Permissions Programmatically

wong: You're experiencing either a client access or permissions issue. The Outlook View Control is certainly capable of displaying any Outlook folder, whether in a Mailbox, PST or Public Folder.
Tuesday, September 12, 2006 8:46 AM by legault
Anonymous comments are disabled