imip-agent

Annotated imiptools/handlers/scheduling/access.py

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