import argparse
import shlex
import sys
import P4
from typing import Dict, List, Optional, Set
# Build up dict of specified permission level to granted permissions.
perms = {} # type: Dict[str, Set[str]]
# Start with all the atomic =levels.
for perm in ['list', 'read', 'branch', 'open', 'write',
'review', 'owner', 'admin', 'super']:
perms['=' + perm] = set([perm])
# All of the rest are built of other levels.
# Disclaimer: not actually sure these are right because the doc is
# a bit vague and I don't have the server source in front of me.
perms['list'] = perms['=list']
perms['read'] = perms['list'] | perms['=branch'] | perms['=read']
perms['review'] = perms['read'] | perms['=review']
perms['open'] = perms['read'] | perms['=open']
perms['write'] = perms['open'] | perms['=write']
# does 'admin' include 'review'?
perms['admin'] = perms['write'] | perms['=admin']
# and does 'owner' include 'admin'?
perms['owner'] = perms['=owner'] # not sure about this
perms['super'] = perms['admin'] | perms['review'] | perms['=super']
def _main():
# type: () -> None
if '--test' in sys.argv:
test_perms()
print('A-OK!')
return
# 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
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 type == 'user' and name != user and name != '*':
continue
if type == 'group' and (not groups or name not in groups and name != '*'):
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/...
read group * * //groups/all/...
read group peeps * //groups/peeps/...
read group folks * //groups/folks/...
"""
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')
assert not map.includes('//groups/foo')
map = build_protection_map(form, 'write', 'bob')
assert map.includes('//admin/foo')
assert not map.includes('//write/protected/foo')
map = build_protection_map(form, 'read', 'biff', ['peeps'])
assert map.includes('//groups/all/foo')
assert map.includes('//groups/peeps/foo')
assert not map.includes('//groups/folks/foo')
if __name__ == "__main__":
_main()