Yesterday my network admin asked me if I could write a simple tool that could provide him with with a spreadsheet of what users had access to a certain database, and through what groups and roles. A couple of hours later I had created an agent that analyze the ACL and identify the users who can access it. The result is presented as a CSV file.
I am sharing the code below. It is pretty straight forward. As you can see, I am using lists to hold the data for easy export later to CSV. Run the code with the Lotusscript debugger turned on, and put a breakpoint before the CSV export starts, and you can see how the data is stored in the lists.
The function ExpandGroups() is called recursively to drill down, if the group contains additional groups. This function also use a lookup into a custom view, (LookupPeople), that we have in our corporate NAB, I am sure you can modify this code with something that works for you.
Enjoy! As always, use the code on your own risk, no warranties, etc.
%REM Agent Export ACL Info to CSV Created Nov 14, 2011 by Karl-Henry Martinsson/Deep-South Description: Read ACl for specified database and create a CSV file with info about each user's access (roles, groups, delete access, access level). %END REM Option Public Option Declare Dim nab As NotesDatabase Type RowData role As String group As String username As String deletedoc As String level As String levelno As Integer End Type Class GroupData Public roles List As String Public Sub New() End Sub End Class Class PersonData Public accesslevel As Integer Public roles List As String Public deletedoc As boolean Public accessthrough List As String Public Sub New() me.deletedoc = False End Sub Public Sub SetAccessLevel(level As Integer) If me.Accesslevel<level Then me.AccessLevel = level End If End Sub Public Function GetAccessLevelText() Select Case me.AccessLevel Case 0 : GetAccessLevelText = "No Access" Case 1 : GetAccessLevelText = "Depositor" Case 2 : GetAccessLevelText = "Reader" Case 3 : GetAccessLevelText = "Author" Case 4 : GetAccessLevelText = "Editor" Case 5 : GetAccessLevelText = "Designer" Case 6 : GetAccessLevelText = "Manager" End Select End Function End Class Class RoleData Public groups List As String Public Sub New() End Sub End Class Sub Initialize Dim ws As New NotesUIWorkspace Dim session As New NotesSession Dim db As NotesDatabase Dim pview As NotesView Dim pdoc As NotesDocument Dim acl As NotesACL Dim entry As NotesACLEntry Dim person List As PersonData Dim group List As GroupData Dim role List As RoleData Dim users As Variant Dim row List As RowData Dim cnt As Long Dim groupname As String Dim filename As String Dim rowstr As String Dim dbname As String Dim servername As String servername = InputBox$("Enter server for database:","Select Server") If servername = "" Then Exit Sub End If dbname = InputBox$("Enter full path of database:","Select Database") If dbname = "" Then Exit Sub End If set nab = New NotesDatabase(servername,"names.nsf") Set db = New NotesDatabase(servername,dbname) Set acl = db.ACL Set entry = acl.GetFirstEntry() Do While Not entry Is Nothing If entry.Isgroup Then If IsElement(group(entry.Name))=False Then Set group(entry.Name) = New GroupData() ForAll r In entry.Roles group(entry.Name).roles(r) = r End ForAll End If users = ExpandGroup(entry.Name) If IsList(users) then ForAll u In users If IsElement(person(u))=False Then Set person(u) = New PersonData() End If Call person(u).SetAccessLevel(entry.level) If entry.Candeletedocuments Then person(u).deletedoc = True End If person(u).accessthrough(entry.Name) = entry.Name ForAll r In entry.Roles If FullTrim(r)<>"" then person(u).roles(r) = r End if End ForAll End ForAll End If ElseIf entry.IsPerson Then If IsElement(person(entry.Name))=False Then Set person(entry.Name) = New PersonData() End If Call person(entry.Name).SetAccessLevel(entry.level) If entry.Candeletedocuments Then person(entry.Name).deletedoc = True End If person(entry.Name).accessthrough("ACL") = "ACL" ForAll r In entry.Roles If FullTrim(r)<>"" Then person(entry.Name).roles(r) = r End if End ForAll End If Set entry = acl.GetNextEntry(entry) Loop ForAll g In group ForAll rr In g.roles If IsElement(role(rr)) = False Then Set role(rr) = New RoleData End If role(rr).groups(CStr(ListTag(g))) = ListTag(g) End Forall End ForAll ' *** Time to export the data cnt = 0 Set pview = nab.GetView("(LookupPeople)") ForAll p In person ForAll gg In p.accessthrough groupname = gg If IsElement(group(groupname)) And groupname<>"ACL" Then ForAll r2 In group(groupname).roles cnt = cnt + 1 row(cnt).username = ListTag(p) row(cnt).group = groupname If p.deletedoc then row(cnt).deletedoc = "Y" Else row(cnt).deletedoc = "N" End If row(cnt).levelno = p.accesslevel row(cnt).level = p.GetAccessLevelText() row(cnt).role = ListTag(r2) End ForAll End If End ForAll End ForAll filename = "c:\ACL_" filename = filename & Replace(Replace(dbname,"/","-"),"\","-") filename = filename & ".csv" Open filename For Output As #1 rowstr = |"UserRole","Group","User Name","Del","Level","Access"| Print #1, rowstr ForAll x In row rowstr = |"| & x.role & |",| rowstr = rowstr & |"| & x.group & |",| rowstr = rowstr & |"| & x.username & |",| rowstr = rowstr & |"| & x.deletedoc & |",| rowstr = rowstr & || & x.levelno & |,| rowstr = rowstr & |"| & x.level & |"| Print #1, rowstr End ForAll Close #1 MsgBox "ACL exported to " & filename,,"Finished" End Sub %REM Function ExpandGroup Created Nov 14, 2011 by Karl-Henry Martinsson/Deep-South Description: Returns a list of users in specified group in NAB %END REM Function ExpandGroup(entryName As string) As Variant Dim nabview As NotesView Dim nabdoc As NotesDocument Dim pview As NotesView Dim pdoc As NotesDocument Dim uname As NotesName Dim tmplist As variant Dim userlist List As String If FullTrim(entryName) = "" Then ExpandGroup = "" Exit Function End If Set nabview = nab.GetView("Groups") Set nabdoc = nabview.GetDocumentByKey(entryname) If nabdoc Is Nothing Then ExpandGroup = "" Exit function End If ForAll n In nabdoc.GetItemValue("Members") If Left$(n,3)= "CN=" Then Set uname = New NotesName(n) userlist(uname.Common) = uname.Common Else Set pview = nab.GetView("(LookupPeople)") Set pdoc = pview.GetDocumentByKey(CStr(n)) If Not pdoc Is Nothing Then userlist(CStr(n)) = CStr(n) else tmplist = ExpandGroup(CStr(n)) If IsList(tmplist) Then ForAll t In tmplist userlist(t) = t End ForAll End If End If End If End ForAll ExpandGroup = userlist End Function
Hi, Thank you for this. If I can get it working this will help me tremendously. Do you know what I would need to type as far as input if I am running this agent against Domino running on IBM i? I have tried a few things but I haven’t had any success with it running. Can you give an example of what type of input is required for each box?
Matt, I am not an expert on IBM i-series, so I can’t really tell you how the paths are defined there. You may want to reach out to Steve McDonagh, he is a IBM i-series expert if i remember correctly.
I figured it out. Thank you again for the great tool!!