Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
P
ptbcli
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Daniele Nicolodi
ptbcli
Commits
15bc0b1d
Commit
15bc0b1d
authored
1 year ago
by
Daniele Nicolodi
Browse files
Options
Downloads
Patches
Plain Diff
vorhabenACL: New tool to verify ACL set on folders in 4-3-Vorhaben
parent
04cfe00a
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
vorhabenACL.py
+280
-0
280 additions, 0 deletions
vorhabenACL.py
with
280 additions
and
0 deletions
vorhabenACL.py
0 → 100644
+
280
−
0
View file @
15bc0b1d
import
os
import
pathlib
import
re
import
subprocess
import
sys
from
ctypes
import
windll
,
byref
from
ctypes.wintypes
import
DWORD
import
click
import
openpyxl
PATH
=
r
'
o:\4-3\4-3-Vorhaben
'
DATA
=
r
'
o:\4-3\4-3-Vorhaben\Zugriffsliste.xlsx
'
IDS
=
{
'
luecke
'
:
2
,
'
klose
'
:
5
,
'
knigge
'
:
2
,
}
PHONETICS
=
str
.
maketrans
({
'
ö
'
:
'
oe
'
,
'
ü
'
:
'
ue
'
,
'
ß
'
:
'
ss
'
})
def
setup_console
():
# Enable ANSI escape sequences.
kernel
=
windll
.
kernel32
mode
=
DWORD
()
STD_OUTPUT_HANDLE
=
-
11
ENABLE_VIRTUAL_TERMINAL_PROCESSING
=
0x0004
stdout
=
kernel
.
GetStdHandle
(
STD_OUTPUT_HANDLE
)
kernel
.
GetConsoleMode
(
stdout
,
byref
(
mode
))
kernel
.
SetConsoleMode
(
stdout
,
mode
.
value
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING
)
class
style
:
black
=
'
\033
[30m
'
red
=
'
\033
[31m
'
green
=
'
\033
[32m
'
yellow
=
'
\033
[33m
'
blue
=
'
\033
[34m
'
magenta
=
'
\033
[35m
'
cyan
=
'
\033
[36m
'
white
=
'
\033
[37m
'
reset
=
'
\033
[39m
'
bright_black
=
'
\033
[90m
'
bright_red
=
'
\033
[91m
'
bright_green
=
'
\033
[92m
'
bright_yellow
=
'
\033
[93m
'
bright_blue
=
'
\033
[94m
'
bright_magenta
=
'
\033
[95m
'
bright_cyan
=
'
\033
[96m
'
bright_white
=
'
\033
[97m
'
bold
=
'
\033
[1m
'
dim
=
'
\033
[2m
'
normal
=
'
\033
[22m
'
reset
=
'
\033
[0m
'
# ICACLS preserves the canonical ordering of ACE entries:
# Explicit denials
# Explicit grants
# Inherited denials
# Inherited grants
#
# perm is a permission mask and can be specified in one of two forms:
# a sequence of simple rights:
# N - no access
# F - full access
# M - modify access
# RX - read and execute access
# R - read-only access
# W - write-only access
# D - delete access
# a comma-separated list in parentheses of specific rights:
# DE - delete
# RC - read control
# WDAC - write DAC
# WO - write owner
# S - synchronize
# AS - access system security
# MA - maximum allowed
# GR - generic read
# GW - generic write
# GE - generic execute
# GA - generic all
# RD - read data/list directory
# WD - write data/add file
# AD - append data/add subdirectory
# REA - read extended attributes
# WEA - write extended attributes
# X - execute/traverse
# DC - delete child
# RA - read attributes
# WA - write attributes
# inheritance rights may precede either form and are applied
# only to directories:
# (OI) - object inherit
# (CI) - container inherit
# (IO) - inherit only
# (NP) - don't propagate inherit
# (I) - permission inherited from parent container
def
get_acl
(
path
):
r
=
subprocess
.
run
([
'
icacls
'
,
os
.
fspath
(
path
)],
check
=
True
,
capture_output
=
True
,
text
=
True
)
acl
=
{}
# Parse command output.
lines
=
[
line
for
line
in
r
.
stdout
.
splitlines
()
if
line
.
strip
()]
m
=
re
.
match
(
r
'
^Successfully processed ([0-9]+) files; Failed processing ([0-9]+) files$
'
,
lines
[
-
1
])
if
not
m
or
int
(
m
.
group
(
1
))
!=
1
or
int
(
m
.
group
(
2
))
!=
0
:
raise
RuntimeError
(
line
)
# icacls uses the most stupid output format ever. This is the only
# way I found to reliebly enough split the path prefix from the
# ACL listing.
assert
len
(
lines
)
>
2
l
=
re
.
match
(
r
'
+
'
,
lines
[
-
2
]).
end
()
for
line
in
lines
[:
-
1
]:
user
,
permissions
=
line
[
l
:].
split
(
'
:
'
)
acl
[
user
]
=
permissions
return
acl
def
parse_permissions
(
s
):
groups
=
re
.
findall
(
r
'
\([A-Z,]+\)
'
,
s
)
*
inheritance
,
permissions
=
groups
inherited
=
inheritance
and
inheritance
[
0
]
==
'
(I)
'
return
inherited
,
tuple
(
permissions
[
1
:
-
1
].
split
(
'
,
'
))
def
get_group_members
(
group
):
r
=
subprocess
.
run
([
'
net
'
,
'
localgroup
'
,
'
/domain
'
,
group
],
check
=
False
,
capture_output
=
True
,
text
=
True
)
if
r
.
returncode
==
2
:
return
lines
=
iter
(
r
.
stdout
.
splitlines
())
# Skip header.
for
line
in
lines
:
if
re
.
match
(
r
'
^-+$
'
,
line
):
break
# Collect group members.
for
line
in
lines
:
if
line
==
'
The command completed successfully.
'
:
break
yield
line
.
strip
()
def
collect_users
(
name
,
users
=
None
):
if
users
is
None
:
users
=
set
()
if
not
name
.
startswith
(
'
8GP
'
):
users
.
add
(
name
)
return
users
for
member
in
get_group_members
(
name
):
collect_users
(
member
,
users
)
return
users
def
print_group_members
(
name
,
level
=
1
):
prefix
=
'
'
*
level
if
name
.
startswith
(
'
8GP
'
):
for
member
in
get_group_members
(
name
):
print
(
f
'
{
prefix
}{
member
}
'
)
print_group_members
(
member
,
level
+
1
)
def
_print_acl
(
path
,
print_inherited
=
True
,
print_traversal
=
False
):
print
(
f
'
{
style
.
bright_green
}{
path
}{
style
.
reset
}
'
)
acl
=
get_acl
(
path
)
for
user
,
permissions
in
acl
.
items
():
inherited
,
permissions
=
parse_permissions
(
permissions
)
if
inherited
and
not
print_inherited
:
continue
if
not
print_traversal
and
permissions
==
(
'
RX
'
,):
continue
permissions
=
'
,
'
.
join
(
permissions
)
print
(
f
'
{
user
:
50
s
}
:
{
permissions
}
'
)
domain
,
name
=
user
.
split
(
'
\\
'
)
print_group_members
(
name
)
print
()
def
guess_username
(
name
):
surname
,
_
=
name
.
split
(
'
,
'
,
1
)
username
=
surname
.
lower
().
translate
(
PHONETICS
)[:
6
]
assert
username
.
isascii
(),
username
x
=
IDS
.
get
(
username
,
1
)
return
f
'
{
username
}{
x
:
02
d
}
'
def
parse_excel_sheet
():
doc
=
openpyxl
.
open
(
DATA
,
read_only
=
True
,
data_only
=
True
)
names
=
[]
for
x
in
doc
[
'
Zugriffsliste
'
][
1
][
4
:]:
if
x
.
value
==
'
ENDE
'
:
break
names
.
append
(
guess_username
(
x
.
value
)
if
x
.
value
and
not
x
.
value
.
startswith
(
'
AG
'
)
else
None
)
acl
=
{}
for
row
in
doc
[
'
Zugriffsliste
'
].
iter_rows
(
min_row
=
4
):
if
not
row
[
1
].
value
:
break
path
=
row
[
1
].
value
acl
[
os
.
path
.
join
(
PATH
,
path
)]
=
set
(
names
[
i
]
for
i
in
range
(
len
(
names
))
if
row
[
i
+
4
].
value
)
return
acl
@click.group
()
def
main
():
setup_console
()
@main.command
()
@click.option
(
'
-v
'
,
'
--verbose
'
,
count
=
True
)
def
verify
(
verbose
):
"""
Verify ACL on folders.
"""
errors
=
0
master
=
parse_excel_sheet
()
for
entry
in
sorted
(
os
.
listdir
(
PATH
)):
if
entry
.
startswith
(
'
.
'
):
continue
path
=
os
.
path
.
join
(
PATH
,
entry
)
acl
=
get_acl
(
path
)
users
=
set
()
for
user
,
permissions
in
acl
.
items
():
inherited
,
permissions
=
parse_permissions
(
permissions
)
if
inherited
:
continue
assert
permissions
==
(
'
RX
'
,
'
W
'
,
'
DC
'
),
permissions
domain
,
name
=
user
.
split
(
'
\\
'
)
users
=
collect_users
(
name
,
users
)
authorized
=
master
.
get
(
path
,
set
())
if
not
os
.
path
.
isdir
(
path
):
assert
not
authorized
assert
not
users
continue
if
verbose
:
print
(
f
'
.
{
path
}
'
)
def
_style
(
name
):
if
name
not
in
authorized
:
return
f
'
{
style
.
red
}{
name
}{
style
.
reset
}
'
if
name
not
in
users
:
return
f
'
{
style
.
yellow
}{
name
}{
style
.
reset
}
'
return
name
s
=
'
,
'
.
join
(
_style
(
user
)
for
user
in
sorted
(
authorized
.
union
(
users
)))
print
(
f
'
{
s
}
'
)
else
:
not_authorized
=
users
-
authorized
if
not_authorized
:
print
(
f
'
.
{
style
.
reset
}
'
)
s
=
'
,
'
.
join
(
f
'
{
style
.
red
}{
name
}{
style
.
reset
}
'
for
name
in
sorted
(
not_authorized
))
print
(
f
'
{
s
}
'
)
errors
=
1
sys
.
exit
(
errors
)
@main.command
()
@click.option
(
'
--json
'
,
is_flag
=
True
)
def
dump
(
json
):
"""
Dump ACL defined in Excel sheet.
"""
acl
=
parse_excel_sheet
()
if
json
:
import
json
class
Encoder
(
json
.
JSONEncoder
):
def
default
(
self
,
obj
):
if
isinstance
(
obj
,
set
):
return
list
(
obj
)
return
super
().
default
(
self
,
obj
)
print
(
json
.
dumps
(
acl
,
cls
=
Encoder
))
else
:
for
path
,
users
in
acl
.
items
():
print
(
f
'
.
{
path
}
'
)
users
=
'
,
'
.
join
(
users
)
print
(
f
'
{
users
}
'
)
print
()
if
__name__
==
'
__main__
'
:
main
()
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment