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 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') if __name__ == "__main__": _main()