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 standard_responses(handler, 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 response = organiser_domain == user_domain and "ACCEPTED" or "DECLINED" 127 return standard_responses(handler, response) 128 129 def standard_responses(handler, response): 130 131 """ 132 Using 'handler' to translate descriptions, return a tuple containing the 133 'response' and a suitable description. 134 """ 135 136 _ = handler.get_translator() 137 138 if response == "ACCEPTED": 139 return response, _("The recipient has scheduled the requested period.") 140 elif response == "DECLINED": 141 return response, _("The recipient has refused to schedule the requested period.") 142 else: 143 return response, None 144 145 # Registry of scheduling functions. 146 147 scheduling_functions = { 148 "access_control_list" : [access_control_list], 149 "same_domain_only" : [same_domain_only], 150 } 151 152 # Registries of locking and unlocking functions. 153 154 locking_functions = {} 155 unlocking_functions = {} 156 157 # Registries of listener functions. 158 159 confirmation_functions = {} 160 retraction_functions = {} 161 162 # vim: tabstop=4 expandtab shiftwidth=4