import argparse
import shlex
import sys
import P4
from typing import Dict, List, Optional, Set
def _main():
# type: () -> None
if '--test' in sys.argv:
return test_perms()
# Parse args.
argp = argparse.ArgumentParser(description='Run protection table checks.')
argp.add_argument('-i',
dest='input', help='Input filename for protect table', required=True)
argp.add_argument('-u',
dest='user', help='User to check', required=True)
argp.add_argument('-p',
dest='perm', help='Permission level to check', required=True)
argp.add_argument('-g',
# This is so you can pass a list of groups the user is a member of.
dest='groups', help='Group(s) to include', action='append')
argp.add_argument('path',
help='Depot path to check')
args = argp.parse_args()
# Build map from file.
f = open(args.input, 'r')
map = build_protection_map(f.read(), args.perm, args.user, args.groups)
# Check against path and print stuff.
print("%s's %s protections mapping:" % (args.user, args.perm))
print(map)
print("includes %s? %s" % (args.path, map.includes(args.path)))
def build_protection_map(form, perm, user, groups=None):
# type: (str, str, str, Optional[List[str]]) -> P4.Map
# Build up dict of specified permission level to granted permissions.
perms = {} # type: Dict[str, Set[str]]
# Start with all the atomic =levels.
for lvl in ['list', 'read', 'branch', 'open', 'write',
'review', 'owner', 'admin', 'super']:
perms['=' + lvl] = set([lvl])
# All of the rest are built of other levels.
perms['list'] = perms['=list']
perms['read'] = perms['list'] | perms['=branch'] | perms['=read']
perms['open'] = perms['read'] | perms['=open']
perms['write'] = perms['open'] | perms['=write']
perms['admin'] = perms['write'] | perms['=admin']
perms['super'] = perms['admin'] | perms['=super']
perms['review'] = perms['read'] | perms['=review']
perms['owner'] = perms['=owner'] # not sure about this
spec = P4.P4().parse_protect(form)
map = P4.Map()
for line in spec._Protections:
lvl, type, name, _, path = shlex.split(line)
# Check to see if this line is applicable to our protection map.
# Some subtlety here: exclusions remove ALL levels
# UNLESS they're specified as an =level
if perm not in perms[lvl] and \
(not path.startswith('-') or lvl.startswith('=')):
continue
if name != '*':
if type == 'user' and name != user:
continue
if type == 'group' and (not groups or name not in groups):
continue
# shlex.split removes quotes -- add them back in now for Map.
map.insert('"' + path + '"')
return map
def test_perms():
# type: () -> None
form = """
Protections:
list user bob * //list/...
read user bob * //read/...
read user bob * -//read/secret/...
write user bob * //write/...
=write user * * -//write/protected/...
admin user bob * //admin/...
super user bob * //super/...
"""
map = build_protection_map(form, 'list', 'bob')
assert map.includes('//read/foo')
assert map.includes('//write/protected/foo')
assert not map.includes('//read/secret/foo')
map = build_protection_map(form, 'write', 'bob')
assert map.includes('//admin/foo')
assert not map.includes('//write/protected/foo')
print('A-OK!')
if __name__ == "__main__":
_main()