1 #!/usr/bin/env python 2 3 """ 4 Access-control-related scheduling functionality. 5 6 Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from imiptools.data import get_address, get_addresses 23 from imiptools.text import parse_line 24 25 def access_control_list(handler, args): 26 27 """ 28 Attempt to schedule the current object of the given 'handler' using an 29 access control list provided in the given 'args', applying it to the 30 organiser. 31 """ 32 33 # Obtain either a file from the user's preferences directory... 34 35 if not args: 36 acl = handler.get_preferences().get("acl") 37 lines = acl.strip().split("\n") 38 39 # Or obtain the contents of a specific file. 40 41 else: 42 try: 43 f = open(args[0]) 44 except IOError: 45 return None 46 try: 47 lines = f.readlines() 48 finally: 49 f.close() 50 51 # Use the current object's identities with the ACL rules. 52 53 organiser = get_address(handler.obj.get_value("ORGANIZER")) 54 attendees = get_addresses(handler.obj.get_values("ATTENDEE")) 55 56 response = None 57 58 for line in lines: 59 parts = parse_line(line.strip()) 60 61 # Skip empty lines. 62 63 if not parts: 64 continue 65 66 # Accept either a single word with an action or a rule. 67 # NOTE: Should signal an error with the format. 68 69 if len(parts) == 1: 70 action = parts[0] 71 elif len(parts) >= 3: 72 action, role, identities = parts[0], parts[1], map(get_address, parts[2:]) 73 else: 74 return None 75 76 if action.lower() == "accept": 77 result = "ACCEPTED" 78 elif action.lower() in ["decline", "reject"]: 79 result = "DECLINED" 80 else: 81 return None 82 83 # With only an action, prepare a default response in case none of 84 # the rules match. 85 86 if len(parts) == 1: 87 response = result 88 continue 89 90 # Where no default has been set, use an implicit default based on 91 # the action appearing in a rule. 92 93 elif not response: 94 response = result == "ACCEPTED" and "DECLINED" or "ACCEPTED" 95 96 # Interpret a rule, attempting to match identities to properties. 97 98 if role.lower() in ["organiser", "organizer"]: 99 match = organiser in identities 100 elif role.lower() in ["attendee", "attendees"]: 101 match = set(attendees).intersection(identities) 102 else: 103 return None 104 105 # Use the result of any match. 106 107 if match: 108 response = result 109 110 return response 111 112 def same_domain_only(handler, args): 113 114 """ 115 Attempt to schedule the current object of the given 'handler' if the 116 organiser employs an address in the same domain as the resource. 117 """ 118 119 organiser = get_address(handler.obj.get_value("ORGANIZER")) 120 user = get_address(handler.user) 121 122 organiser_domain = organiser.rsplit("@", 1)[-1] 123 user_domain = user.rsplit("@", 1)[-1] 124 125 return organiser_domain == user_domain and "ACCEPTED" or "DECLINED" 126 127 # Registry of scheduling functions. 128 129 scheduling_functions = { 130 "access_control_list" : access_control_list, 131 "same_domain_only" : same_domain_only, 132 } 133 134 # vim: tabstop=4 expandtab shiftwidth=4