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 import codecs 25 26 def access_control_list(handler, args): 27 28 """ 29 Attempt to schedule the current object of the given 'handler' using an 30 access control list provided in the given 'args', applying it to the 31 organiser. 32 """ 33 34 # Obtain either a file from the user's preferences directory... 35 36 if not args: 37 acl = handler.get_preferences().get("acl") 38 lines = acl.strip().split("\n") 39 40 # Or obtain the contents of a specific file. 41 42 else: 43 try: 44 f = codecs.open(args[0], encoding="utf-8") 45 except IOError: 46 return None 47 try: 48 lines = f.readlines() 49 finally: 50 f.close() 51 52 # Use the current object's identities with the ACL rules. 53 54 organiser = get_address(handler.obj.get_value("ORGANIZER")) 55 attendees = get_addresses(handler.obj.get_values("ATTENDEE")) 56 57 response = None 58 59 for line in lines: 60 parts = parse_line(line.strip()) 61 62 # Skip empty lines. 63 64 if not parts: 65 continue 66 67 # Accept either a single word with an action or a rule. 68 # NOTE: Should signal an error with the format. 69 70 if len(parts) == 1: 71 action = parts[0] 72 elif len(parts) >= 3: 73 action, role, identities = parts[0], parts[1], map(get_address, parts[2:]) 74 else: 75 return None 76 77 if action.lower() == "accept": 78 result = "ACCEPTED" 79 elif action.lower() in ["decline", "reject"]: 80 result = "DECLINED" 81 else: 82 return None 83 84 # With only an action, prepare a default response in case none of 85 # the rules match. 86 87 if len(parts) == 1: 88 response = result 89 continue 90 91 # Where no default has been set, use an implicit default based on 92 # the action appearing in a rule. 93 94 elif not response: 95 response = result == "ACCEPTED" and "DECLINED" or "ACCEPTED" 96 97 # Interpret a rule, attempting to match identities to properties. 98 99 if role.lower() in ["organiser", "organizer"]: 100 match = organiser in identities 101 elif role.lower() in ["attendee", "attendees"]: 102 match = set(attendees).intersection(identities) 103 else: 104 return None 105 106 # Use the result of any match. 107 108 if match: 109 response = result 110 111 return response 112 113 def same_domain_only(handler, args): 114 115 """ 116 Attempt to schedule the current object of the given 'handler' if the 117 organiser employs an address in the same domain as the resource. 118 """ 119 120 organiser = get_address(handler.obj.get_value("ORGANIZER")) 121 user = get_address(handler.user) 122 123 organiser_domain = organiser.rsplit("@", 1)[-1] 124 user_domain = user.rsplit("@", 1)[-1] 125 126 return organiser_domain == user_domain and "ACCEPTED" or "DECLINED" 127 128 # Registry of scheduling functions. 129 130 scheduling_functions = { 131 "access_control_list" : access_control_list, 132 "same_domain_only" : same_domain_only, 133 } 134 135 # Registries of locking and unlocking functions. 136 137 locking_functions = {} 138 unlocking_functions = {} 139 140 # Registries of listener functions. 141 142 confirmation_functions = {} 143 retraction_functions = {} 144 145 # vim: tabstop=4 expandtab shiftwidth=4