Visualizing Folder Permissions
Overview
This blog post provides a comprehensive guide to exporting folder permissions to a JSON file using PowerShell and visualizing these permissions using Python. The process involves two main scripts:
Exporting Folder Permissions with PowerShell
This script retrieves and exports folder permissions from a specified directory and its subdirectories. It uses the Get-Acl cmdlet to gather access control information and stores this data in a custom PowerShell object. The collected permissions are then converted to JSON format and saved to a file. A limitation of this script to be aware of is that it won't work on paths greater than 260 characters.
1# Define the folder path and output file path as variables
2$FolderPath = "C:\Users\"
3$OutputFile = "C:\Users\output.json"
4
5function ConvertTo-Binary {
6 param (
7 [int]$decimal
8 )
9 return [Convert]::ToString($decimal, 2).PadLeft(32, '0')
10}
11
12function Get-FolderPermissions {
13 param (
14 [string]$Path
15 )
16
17 if (-Not (Test-Path -Path $Path)) {
18 Write-Warning "Path '$Path' does not exist."
19 return $null
20 }
21
22 $acl = Get-Acl -Path $Path
23 $permissions = @()
24
25 foreach ($access in $acl.Access) {
26 $rights = [int]$access.FileSystemRights
27
28 $bitWisePermissions = [PSCustomObject]@{
29 ReadData = $rights -band 1 # 2^0
30 CreateFiles = $rights -band 2 # 2^1
31 AppendData = $rights -band 4 # 2^2
32 ReadExtendedAttributes = $rights -band 8 # 2^3
33 WriteExtendedAttributes = $rights -band 16 # 2^4
34 ExecuteFile = $rights -band 32 # 2^5
35 DeleteSubfoldersAndFiles = $rights -band 64 # 2^6
36 ReadAttributes = $rights -band 128 # 2^7
37 WriteAttributes = $rights -band 256 # 2^8
38 Write = if (($rights -band 278) -eq 278) { $rights -band 278 } else { 0 } # Is a combination of other permissions
39 Delete = $rights -band 65536 # 2^16
40 ReadPermissions = $rights -band 131072 # 2^17
41 Read = if (($rights -band 131209) -eq 131209) { $rights -band 131209 } else { 0 } # Is a combination of other permissions
42 ReadAndExecute = if (($rights -band 131231) -eq 131231) { $rights -band 131231 } else { 0 } # Is a combination of other permissions
43 Modify = if (($rights -band 197055) -eq 197055) { $rights -band 197055 } else { 0 } # Is a combination of other permissions
44 ChangePermissions = $rights -band 262144 # 2^18
45 TakeOwnership = $rights -band 524288 # 2^19
46 Synchronize = $rights -band 1048576 # 2^20
47 FullControl = if (($rights -band 2032127) -eq 2032127) { $rights -band 2032127 } else { 0 } # Is a combination of other permissions
48 }
49
50 $permissions += [PSCustomObject]@{
51 AccessControlType = $access.AccessControlType
52 BitWisePermissionsBinary = ConvertTo-Binary($rights)
53 BitWisePermissions = $bitWisePermissions
54 BitwisePermissionsDecimal = $rights
55 IdentityReference = $access.IdentityReference
56 InheritanceFlags = $access.InheritanceFlags
57 IsInherited = $access.IsInherited
58 PropagationFlags = $access.PropagationFlags
59 }
60 }
61
62 return [PSCustomObject]@{
63 Path = $Path
64 Permissions = $permissions
65 }
66}
67
68function Get-AllFolderPermissions {
69 param (
70 [string]$RootPath
71 )
72
73 $result = @()
74 $folders = Get-ChildItem -Path $RootPath -Recurse -Directory
75
76 foreach ($folder in $folders) {
77 $folderPermissions = Get-FolderPermissions -Path $folder.FullName
78 if ($folderPermissions -ne $null) {
79 $result += $folderPermissions
80 }
81 }
82
83 $rootPermissions = Get-FolderPermissions -Path $RootPath
84 if ($rootPermissions -ne $null) {
85 $result += $rootPermissions
86 }
87
88 return $result
89}
90
91$allPermissions = Get-AllFolderPermissions -RootPath $FolderPath
92$json = $allPermissions | ConvertTo-Json -Depth 100
93
94# Save JSON without BOM
95$json | Out-File -FilePath $OutputFile -Encoding ascii
96
97Write-Host "Permissions saved to $OutputFile"
This is where I got the decimal values for ACL permissions from:
1[System.Enum]::GetValues([System.Security.AccessControl.FileSystemRights]) | ForEach-Object {
2 [PSCustomObject]@{
3 Name = $_
4 Value = [int]$_
5 }
6} | Format-Table -AutoSize
The result is:
1 ReadData 1
2 ReadData 1
3 CreateFiles 2
4 CreateFiles 2
5 AppendData 4
6 AppendData 4
7 ReadExtendedAttributes 8
8 WriteExtendedAttributes 16
9 ExecuteFile 32
10 ExecuteFile 32
11DeleteSubdirectoriesAndFiles 64
12 ReadAttributes 128
13 WriteAttributes 256
14 Write 278
15 Delete 65536
16 ReadPermissions 131072
17 Read 131209
18 ReadAndExecute 131241
19 Modify 197055
20 ChangePermissions 262144
21 TakeOwnership 524288
22 Synchronize 1048576
23 FullControl 2032127
where each decimal represents a 32-bit binary integer.
ACL Permissions Table
Permission | Decimal Value | Binary Value |
---|---|---|
ReadData | 1 | 0000 0000 0000 0000 0000 0000 0000 0001 |
CreateFiles | 2 | 0000 0000 0000 0000 0000 0000 0000 0010 |
AppendData | 4 | 0000 0000 0000 0000 0000 0000 0000 0100 |
ReadExtendedAttributes | 8 | 0000 0000 0000 0000 0000 0000 0000 1000 |
WriteExtendedAttributes | 16 | 0000 0000 0000 0000 0000 0000 0001 0000 |
ExecuteFile | 32 | 0000 0000 0000 0000 0000 0000 0010 0000 |
DeleteSubdirectoriesAndFiles | 64 | 0000 0000 0000 0000 0000 0000 0100 0000 |
ReadAttributes | 128 | 0000 0000 0000 0000 0000 0000 1000 0000 |
WriteAttributes | 256 | 0000 0000 0000 0000 0000 0001 0000 0000 |
Write | 278 | 0000 0000 0000 0000 0000 0001 0001 0110 |
Delete | 65536 | 0000 0000 0000 0001 0000 0000 0000 0000 |
ReadPermissions | 131072 | 0000 0000 0000 0010 0000 0000 0000 0000 |
Read | 131209 | 0000 0000 0000 0010 0000 0000 1000 1001 |
ReadAndExecute | 131241 | 0000 0000 0000 0010 0000 0000 1010 0001 |
Modify | 197055 | 0000 0000 0000 0011 0000 1000 1011 0111 |
ChangePermissions | 262144 | 0000 0000 0000 0100 0000 0000 0000 0000 |
TakeOwnership | 524288 | 0000 0000 0000 1000 0000 0000 0000 0000 |
Synchronize | 1048576 | 0000 0000 0001 0000 0000 0000 0000 0000 |
FullControl | 2032127 | 0000 0000 0001 1111 1111 1111 1111 1111 |
Understanding Bitwise Permissions
Bitwise permissions allow for efficient storage and manipulation of permissions using binary arithmetic. Each permission is represented by a specific bit in an integer, allowing multiple permissions to be combined into a single value. Here's a breakdown of the bitwise permissions used in the PowerShell script:
- ReadData (2^0): Allows reading of file data.
- CreateFiles (2^1): Allows creating files in a directory.
- AppendData (2^2): Allows appending data to a file.
- ReadExtendedAttributes (2^3): Allows reading extended file attributes.
- WriteExtendedAttributes (2^4): Allows writing extended file attributes.
- ExecuteFile (2^5): Allows executing a file.
- DeleteSubfoldersAndFiles (2^6): Allows deleting subfolders and files.
- ReadAttributes (2^7): Allows reading file attributes.
- WriteAttributes (2^8): Allows writing file attributes.
- Delete (2^16): Allows deleting a file or folder.
- ReadPermissions (2^17): Allows reading file or folder permissions.
- ChangePermissions (2^18): Allows changing file or folder permissions.
- TakeOwnership (2^19): Allows taking ownership of a file or folder.
- Synchronize (2^20): Synchronizes access to a file or folder.
These permissions are combined using bitwise OR operations and can be checked using bitwise AND operations, allowing for efficient permission management.
Combined Permissions Breakdown
Write (278)
The Write
permission is a combination of the following:
- WriteData (2^1) = 2
- AppendData (2^2) = 4
- WriteExtendedAttributes (2^4) = 16
- WriteAttributes (2^8) = 256
Combining these with bitwise OR: [ 2 , | , 4 , | , 16 , | , 256 = 278 ]
Read (131209)
The Read
permission is a combination of the following:
- ReadData (2^0) = 1
- ReadExtendedAttributes (2^3) = 8
- ReadAttributes (2^7) = 128
- ReadPermissions (2^17) = 131072
Combining these with bitwise OR: [ 1 , | , 8 , | , 128 , | , 131072 = 131209 ]
ReadAndExecute (131241)
The ReadAndExecute
permission is a combination of Read
and ExecuteFile
:
- Read (131209)
- ExecuteFile (2^5) = 32
Combining these with bitwise OR: [ 131209 , | , 32 = 131241 ]
Modify (197055)
The Modify
permission is a combination of ReadAndExecute
, Write
, and Delete
:
- ReadAndExecute (131241)
- Write (278)
- Delete (2^16) = 65536
Combining these with bitwise OR: [ 131241 , | , 278 , | , 65536 = 197055 ]
FullControl (2032127)
The FullControl
permission includes all possible permissions:
- ReadData (2^0) = 1
- CreateFiles (2^1) = 2
- AppendData (2^2) = 4
- ReadExtendedAttributes (2^3) = 8
- WriteExtendedAttributes (2^4) = 16
- ExecuteFile (2^5) = 32
- DeleteSubdirectoriesAndFiles (2^6) = 64
- ReadAttributes (2^7) = 128
- WriteAttributes (2^8) = 256
- Delete (2^16) = 65536
- ReadPermissions (2^17) = 131072
- ChangePermissions (2^18) = 262144
- TakeOwnership (2^19) = 524288
- Synchronize (2^20) = 1048576
Combining these with bitwise OR: [ 1 , | , 2 , | , 4 , | , 8 , | , 16 , | , 32 , | , 64 , | , 128 , | , 256 , | , 65536 , | , 131072 , | , 262144 , | , 524288 , | , 1048576 = 2032127 ]
Visualizing Permissions with Python and Plotly
Once the permissions have been exported to a JSON file, we can use Python and Plotly to visualize this data. Plotly is a powerful visualization library that allows for the creation of interactive plots and charts.
The following Python script reads the JSON file generated by the PowerShell script and creates a treemap visualization of the folder permissions. The script uses Plotly's go.Treemap to create the treemap and applies a default theme for better aesthetics. Other themes can be found here.
1import os
2import json
3import plotly.graph_objects as go
4
5# Function to replace backslashes in the username
6def replace_backslashes(username):
7 return username.replace('\\', '_')
8
9# Function to create the folder structure visualization for each user
10def create_user_visualization(user_permissions, user_name, permission_types, output_dir):
11 access_control_type = {0: "Allow", 1: "Deny"}
12 inheritance_flags = {0: "None", 1: "ContainerInherit", 2: "ObjectInherit", 3: "ContainerInherit + ObjectInherit"}
13 propagation_flags = {0: "None", 1: "InheritOnly", 2: "NoPropagateInherit"}
14
15 figs = {}
16 theme = "seaborn" # Choose the theme you want to apply
17
18 for permission_type in permission_types:
19 labels = []
20 parents = []
21 texts = []
22 display_labels = {} # Map original labels to display labels
23
24 for path, permissions in user_permissions.items():
25 if permissions.get(permission_type, 0) != 0: # Only include paths with the specified permission type
26 parts = path.split('\\')
27 for i in range(len(parts)):
28 original_label = '\\'.join(parts[:i+1])
29 display_label = parts[i]
30
31 if i > 0:
32 display_label = '.\\' + display_label # Prefix with .\ if it has a parent node
33 if i < len(parts) - 1:
34 display_label += '\\' # Append \ if it has a child node
35
36 display_labels[original_label] = display_label
37
38 if display_label not in labels:
39 labels.append(display_label)
40 if i == 0:
41 parents.append("")
42 else:
43 parent_label = '\\'.join(parts[:i])
44 parent_display_label = display_labels[parent_label]
45 parents.append(parent_display_label)
46
47 access_control_type_value = permissions.get('AccessControlType', 'Unknown')
48 inheritance_flags_value = permissions.get('InheritanceFlags', 'Unknown')
49 propagation_flags_value = permissions.get('PropagationFlags', 'Unknown')
50 is_inherited = permissions.get('IsInherited', 'Unknown')
51 bitwise_permissions_decimal = permissions.get('BitwisePermissionsDecimal', 'Unknown')
52 bitwise_permissions_binary = permissions.get('BitWisePermissionsBinary', 'Unknown').lstrip('0')
53
54 texts.append(
55 f"AccessControlType: {access_control_type.get(access_control_type_value, 'Unknown')}<br>"
56 f"InheritanceFlags: {inheritance_flags.get(inheritance_flags_value, 'Unknown')}<br>"
57 f"PropagationFlags: {propagation_flags.get(propagation_flags_value, 'Unknown')}<br>"
58 f"IsInherited: {is_inherited}<br>"
59 f"BitwisePermissions (Decimal): {bitwise_permissions_decimal}<br>"
60 f"BitwisePermissions (Binary): {bitwise_permissions_binary}"
61 )
62
63 if not labels:
64 continue
65
66 fig = go.Figure(go.Treemap(
67 labels=labels,
68 parents=parents,
69 text=texts,
70 hoverinfo="label+text"
71 ))
72
73 fig.update_layout(title_text=f"{permission_type} Permissions for {user_name}", template=theme)
74 figs[permission_type] = fig
75
76 # Create a dropdown menu to select graphs
77 dropdown_buttons = [
78 {'label': perm, 'method': 'update', 'args': [{'visible': [perm == p for p in permission_types]}, {'title': f"{perm} Permissions for {user_name}"}]}
79 for perm in permission_types
80 ]
81
82 if figs:
83 final_fig = go.Figure()
84 for perm in permission_types:
85 if perm in figs:
86 final_fig.add_traces(figs[perm].data)
87 final_fig.update_traces(visible=False)
88 final_fig.data[0].visible = True
89
90 final_fig.update_layout(
91 updatemenus=[
92 {
93 'buttons': dropdown_buttons,
94 'direction': 'down',
95 'showactive': True,
96 }
97 ],
98 title_text=f"Permissions for {user_name}",
99 template=theme
100 )
101
102 os.makedirs(output_dir, exist_ok=True)
103 sanitized_user_name = replace_backslashes(user_name)
104 final_fig.write_html(os.path.join(output_dir, f"{sanitized_user_name}.html"))
105
106# Main function to process the JSON data and generate visualizations
107def main():
108 input_file = 'permissions.json'
109 output_dir = 'folder_permissions_treemap'
110
111 # Load the JSON data
112 with open(input_file, 'r') as file:
113 data = json.load(file)
114
115 # Extract permission types
116 sample_permission = data[0]['Permissions'][0]['BitWisePermissions']
117 permission_types = list(sample_permission.keys())
118
119 # Process the data to group permissions by user
120 user_permissions = {}
121 for entry in data:
122 path = entry['Path']
123 for permission in entry['Permissions']:
124 user = permission['IdentityReference']['Value']
125 if user not in user_permissions:
126 user_permissions[user] = {}
127 user_permissions[user][path] = permission['BitWisePermissions']
128 user_permissions[user][path].update({
129 'AccessControlType': permission['AccessControlType'],
130 'InheritanceFlags': permission['InheritanceFlags'],
131 'PropagationFlags': permission['PropagationFlags'],
132 'IsInherited': permission['IsInherited'],
133 'BitwisePermissionsDecimal': permission.get('BitwisePermissionsDecimal', 'Unknown'),
134 'BitWisePermissionsBinary': permission.get('BitWisePermissionsBinary', 'Unknown')
135 })
136
137 # Create visualizations for each user
138 total_users = len(user_permissions)
139 for idx, (user, permissions) in enumerate(user_permissions.items(), start=1):
140 print(f"Processing user {idx} of {total_users}: {user}")
141 create_user_visualization(permissions, user, permission_types, output_dir)
142 print(f"Finished processing user {idx} of {total_users}: {user}")
143
144if __name__ == "__main__":
145 main()
This approach provides a clear and structured method for administrators to audit and visualize folder permissions in their environment.