summaryrefslogtreecommitdiff
path: root/src/saml2/s2repoze/plugins/challenge_decider.py
blob: ae56a03fad17ad7c39c38fc633b0f9f47b4a8dbc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import re

from paste.httpheaders import CONTENT_TYPE
from paste.httpheaders import REQUEST_METHOD
from paste.httpheaders import USER_AGENT
from paste.request import construct_url
from repoze.who.interfaces import IRequestClassifier
import zope.interface


_DAV_METHODS = (
    "OPTIONS",
    "PROPFIND",
    "PROPPATCH",
    "MKCOL",
    "LOCK",
    "UNLOCK",
    "TRACE",
    "DELETE",
    "COPY",
    "MOVE",
)

_DAV_USERAGENTS = (
    "Microsoft Data Access Internet Publishing Provider",
    "WebDrive",
    "Zope External Editor",
    "WebDAVFS",
    "Goliath",
    "neon",
    "davlib",
    "wsAPI",
    "Microsoft-WebDAV",
)


def my_request_classifier(environ):
    """Returns one of the classifiers 'dav', 'xmlpost', or 'browser',
    depending on the imperative logic below"""
    request_method = REQUEST_METHOD(environ)
    if request_method in _DAV_METHODS:
        return "dav"
    useragent = USER_AGENT(environ)
    if useragent:
        for agent in _DAV_USERAGENTS:
            if useragent.find(agent) != -1:
                return "dav"
    if request_method == "POST":
        if CONTENT_TYPE(environ) == "text/xml":
            return "xmlpost"
        elif CONTENT_TYPE(environ) == "application/soap+xml":
            return "soap"
    return "browser"


zope.interface.directlyProvides(my_request_classifier, IRequestClassifier)


class MyChallengeDecider:
    def __init__(self, path_login="", path_logout=""):
        self.path_login = path_login
        self.path_logout = path_logout

    def __call__(self, environ, status, _headers):
        if status.startswith("401 "):
            return True
        else:
            if environ.has_key("samlsp.pending"):
                return True

            uri = environ.get("REQUEST_URI", None)
            if uri is None:
                uri = construct_url(environ)

            # require and challenge for logout and inform the challenge plugin that it is a logout we want
            for regex in self.path_logout:
                if regex.match(uri) is not None:
                    environ["samlsp.logout"] = True
                    return True

            # If the user is already authent, whatever happens(except logout),
            #   don't make a challenge
            if environ.has_key("repoze.who.identity"):
                return False

            # require a challenge for login
            for regex in self.path_login:
                if regex.match(uri) is not None:
                    return True

        return False


def make_plugin(path_login=None, path_logout=None):
    if path_login is None:
        raise ValueError("must include path_login in configuration")

    # make regexp out of string passed via the config file
    list_login = []
    for arg in path_login.splitlines():
        carg = arg.lstrip()
        if carg != "":
            list_login.append(re.compile(carg))

    list_logout = []
    if path_logout is not None:
        for arg in path_logout.splitlines():
            carg = arg.lstrip()
            if carg != "":
                list_logout.append(re.compile(carg))

    plugin = MyChallengeDecider(list_login, list_logout)

    return plugin