imip-agent

imiptools/handlers/scheduling/access.py

1359:8cb064fcd9f1
2017-10-22 Paul Boddie Reworked various aspects of the recurrence computation implementation, removing explicit sort operations and changing day selection to produce results in order.
     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