from fnmatch import fnmatch """ A General-purpose ACL mechanism. [allow | deny] "allow all" and "deny all" are shorthand for allowing or denying all objects. Lines are executed from top to bottom until a match was found, typical iptables style. An implicit 'deny all' exists at the bottom of the list. For example, allow site Max Planck Institute deny site Arizona allow region US deny user scott@onlab.us allow user *@onlab.us """ class AccessControlList: def __init__(self, aclText=None): self.rules = [] if aclText: self.import_text(aclText) def import_text(self, aclText): # allow either newline or ';' to separate rules aclText = aclText.replace("\n", ";") for line in aclText.split(";"): line = line.strip() if line.startswith("#"): continue if line=="": continue parts = line.split() if len(parts)==2 and (parts[1]=="all"): # "allow all" has no pattern parts = (parts[0], parts[1], "") if len(parts)!=3: raise ACLValidationError(line) (action, object, pattern) = parts if action not in ["allow", "deny"]: raise ACLValidationError(line) if object not in ["site", "user", "all"]: raise ACLValidationError(line) self.rules.append( (action, object, pattern) ) def __str__(self): lines = [] for rule in self.rules: lines.append( " ".join(rule) ) return ";\n".join(lines) def test(self, user, site=None): for rule in self.rules: if self.match_rule(rule, user): return rule[0] return "deny" def match_rule(self, rule, user, site=None): (action, object, pattern) = rule if (site==None): site = user.site if (object == "site"): if fnmatch(site.name, pattern): return True elif (object == "user"): if fnmatch(user.email, pattern): return True elif (object == "all"): return True return False if __name__ == '__main__': # self-test class fakesite: def __init__(self, siteName): self.name = siteName class fakeuser: def __init__(self, email, siteName): self.email = email self.site = fakesite(siteName) u_scott = fakeuser("scott@onlab.us", "ON.Lab") u_bill = fakeuser("bill@onlab.us", "ON.Lab") u_andy = fakeuser("acb@cs.princeton.edu", "Princeton") u_john = fakeuser("jhh@cs.arizona.edu", "Arizona") u_hacker = fakeuser("somehacker@foo.com", "Not A Real Site") # check the "deny all" rule acl = AccessControlList("deny all") assert(acl.test(u_scott) == "deny") # a blank ACL results in "deny all" acl = AccessControlList("") assert(acl.test(u_scott) == "deny") # check the "allow all" rule acl = AccessControlList("allow all") assert(acl.test(u_scott) == "allow") # allow only one site acl = AccessControlList("allow site ON.Lab") assert(acl.test(u_scott) == "allow") assert(acl.test(u_andy) == "deny") # some complicated ACL acl = AccessControlList("""allow site Princeton allow user *@cs.arizona.edu deny site Arizona deny user scott@onlab.us allow site ON.Lab""") assert(acl.test(u_scott) == "deny") assert(acl.test(u_bill) == "allow") assert(acl.test(u_andy) == "allow") assert(acl.test(u_john) == "allow") assert(acl.test(u_hacker) == "deny") print acl