Scott Baker | 5380c52 | 2014-06-06 14:49:43 -0700 | [diff] [blame] | 1 | from fnmatch import fnmatch |
| 2 | |
| 3 | class AccessControlList: |
| 4 | def __init__(self, aclText=None): |
| 5 | self.rules = [] |
| 6 | if aclText: |
| 7 | self.import_text(aclText) |
| 8 | |
| 9 | def import_text(self, aclText): |
| 10 | # allow either newline or ';' to separate rules |
| 11 | aclText = aclText.replace("\n", ";") |
| 12 | for line in aclText.split(";"): |
| 13 | line = line.strip() |
| 14 | if line.startswith("#"): |
| 15 | continue |
| 16 | |
| 17 | if line=="": |
| 18 | continue |
| 19 | |
| 20 | parts = line.split() |
| 21 | |
| 22 | if len(parts)==2 and (parts[1]=="all"): |
| 23 | # "allow all" has no pattern |
| 24 | parts = (parts[0], parts[1], "") |
| 25 | |
| 26 | if len(parts)!=3: |
| 27 | raise ACLValidationError(line) |
| 28 | |
| 29 | (action, object, pattern) = parts |
| 30 | |
| 31 | if action not in ["allow", "deny"]: |
| 32 | raise ACLValidationError(line) |
| 33 | |
| 34 | if object not in ["site", "user", "all"]: |
| 35 | raise ACLValidationError(line) |
| 36 | |
| 37 | self.rules.append( (action, object, pattern) ) |
| 38 | |
| 39 | def __str__(self): |
| 40 | lines = [] |
| 41 | for rule in self.rules: |
| 42 | lines.append( " ".join(rule) ) |
| 43 | return ";\n".join(lines) |
| 44 | |
| 45 | def test(self, user): |
| 46 | for rule in self.rules: |
| 47 | if self.match_rule(rule, user): |
| 48 | return rule[0] |
| 49 | return "deny" |
| 50 | |
| 51 | def match_rule(self, rule, user): |
| 52 | (action, object, pattern) = rule |
| 53 | |
| 54 | if (object == "site"): |
| 55 | if fnmatch(user.site.name, pattern): |
| 56 | return True |
| 57 | elif (object == "user"): |
| 58 | if fnmatch(user.email, pattern): |
| 59 | return True |
| 60 | elif (object == "all"): |
| 61 | return True |
| 62 | |
| 63 | return False |
| 64 | |
| 65 | |
| 66 | if __name__ == '__main__': |
| 67 | class fakesite: |
| 68 | def __init__(self, siteName): |
| 69 | self.name = siteName |
| 70 | |
| 71 | class fakeuser: |
| 72 | def __init__(self, email, siteName): |
| 73 | self.email = email |
| 74 | self.site = fakesite(siteName) |
| 75 | |
| 76 | u_scott = fakeuser("scott@onlab.us", "ON.Lab") |
| 77 | u_bill = fakeuser("bill@onlab.us", "ON.Lab") |
| 78 | u_andy = fakeuser("acb@cs.princeton.edu", "Princeton") |
| 79 | u_john = fakeuser("jhh@cs.arizona.edu", "Arizona") |
| 80 | u_hacker = fakeuser("somehacker@foo.com", "Not A Real Site") |
| 81 | |
| 82 | # check the "deny all" rule |
| 83 | acl = AccessControlList("deny all") |
| 84 | assert(acl.test(u_scott) == "deny") |
| 85 | |
| 86 | # a blank ACL results in "deny all" |
| 87 | acl = AccessControlList("") |
| 88 | assert(acl.test(u_scott) == "deny") |
| 89 | |
| 90 | # check the "allow all" rule |
| 91 | acl = AccessControlList("allow all") |
| 92 | assert(acl.test(u_scott) == "allow") |
| 93 | |
| 94 | # allow only one site |
| 95 | acl = AccessControlList("allow site ON.Lab") |
| 96 | assert(acl.test(u_scott) == "allow") |
| 97 | assert(acl.test(u_andy) == "deny") |
| 98 | |
| 99 | # some complicated ACL |
| 100 | acl = AccessControlList("""allow site Princeton |
| 101 | allow user *@cs.arizona.edu |
| 102 | deny site Arizona |
| 103 | deny user scott@onlab.us |
| 104 | allow site ON.Lab""") |
| 105 | |
| 106 | assert(acl.test(u_scott) == "deny") |
| 107 | assert(acl.test(u_bill) == "allow") |
| 108 | assert(acl.test(u_andy) == "allow") |
| 109 | assert(acl.test(u_john) == "allow") |
| 110 | assert(acl.test(u_hacker) == "deny") |
| 111 | |
| 112 | print acl |
| 113 | |