summaryrefslogtreecommitdiff
path: root/src/saml2/s2repoze/plugins/formswithhidden.py
blob: 7b81321160f2ef9ec235534a3bc3ceb81688b323 (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
115
116
117
118
119
120
121
122
123
124
125
from six.moves.urllib.parse import urlencode

from paste.httpheaders import CONTENT_LENGTH
from paste.httpheaders import CONTENT_TYPE
from paste.httpheaders import LOCATION
from paste.httpexceptions import HTTPFound

from paste.request import parse_dict_querystring
from paste.request import parse_formvars
from paste.request import construct_url

from zope.interface import implements

from repoze.who.interfaces import IChallenger
from repoze.who.interfaces import IIdentifier
from repoze.who.plugins.form import FormPlugin

_DEFAULT_FORM = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
<html>
<head>
    <title>Demo Organization Log In</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <div>
        <strong>Demo Organization Log In</strong>
    </div>
    <form name="foo" method=POST action="?__do_login=true">
        <input type="text" name="login">
        <input type="password" name="password">
        <input name="submit" type="submit" value="Logga in">
        %s
    </form>
</body>
</html>
"""

HIDDEN_PRE_LINE = """<input type=hidden name="%s" value="%s">"""


class FormHiddenPlugin(FormPlugin):

    implements(IChallenger, IIdentifier)

    # IIdentifier
    def identify(self, environ):
        logger = environ.get("repoze.who.logger", "")
        logger.info("formplugin identify")
        # logger and logger.info("environ keys: %s", environ.keys())
        query = parse_dict_querystring(environ)
        # If the extractor finds a special query string on any request,
        # it will attempt to find the values in the input body.
        if query.get(self.login_form_qs):
            form = parse_formvars(environ)
            from StringIO import StringIO

            # we need to replace wsgi.input because we've read it
            # this smells funny
            environ["wsgi.input"] = StringIO()
            form.update(query)
            qinfo = {}
            for key, val in form.items():
                if key.startswith("_") and key.endswith("_"):
                    qinfo[key[1:-1]] = val
            if qinfo:
                environ["s2repoze.qinfo"] = qinfo
            try:
                login = form["login"]
                password = form["password"]
            except KeyError:
                return None
            del query[self.login_form_qs]
            query.update(qinfo)
            environ["QUERY_STRING"] = urlencode(query)
            environ["repoze.who.application"] = HTTPFound(construct_url(environ))
            credentials = {"login": login, "password": password}
            max_age = form.get("max_age", None)
            if max_age is not None:
                credentials["max_age"] = max_age
            return credentials

        return None

    # IChallenger
    def challenge(self, environ, status, app_headers, forget_headers):
        logger = environ.get("repoze.who.logger", "")
        logger.info("formplugin challenge")
        if app_headers:
            location = LOCATION(app_headers)
            if location:
                headers = list(app_headers) + list(forget_headers)
                return HTTPFound(headers=headers)

        query = parse_dict_querystring(environ)
        hidden = []
        for key, val in query.items():
            hidden.append(HIDDEN_PRE_LINE % ("_%s_" % key, val))

        logger.info("hidden: %s", hidden)
        form = self.formbody or _DEFAULT_FORM
        form = form % "\n".join(hidden)

        if self.formcallable is not None:
            form = self.formcallable(environ)

        def auth_form(environ, start_response):
            content_length = CONTENT_LENGTH.tuples(str(len(form)))
            content_type = CONTENT_TYPE.tuples("text/html")
            headers = content_length + content_type + forget_headers
            start_response("200 OK", headers)
            return [form]

        return auth_form


def make_plugin(login_form_qs="__do_login", rememberer_name=None, form=None):
    if rememberer_name is None:
        raise ValueError(
            "must include rememberer key (name of another IIdentifier plugin)"
        )
    if form is not None:
        with open(form, "r") as f:
            form = f.read()
    plugin = FormHiddenPlugin(login_form_qs, rememberer_name, form)
    return plugin