WebStack

Annotated examples/Common/Login/__init__.py

138:ef59d738a382
2004-05-29 paulb [project @ 2004-05-29 23:06:28 by paulb] Added a success screen for mod_python, which won't allow redirects and cookies in the same transaction.
paulb@126 1
#!/usr/bin/env python
paulb@126 2
paulb@126 3
"An example login screen."
paulb@126 4
paulb@126 5
import WebStack.Generic
paulb@126 6
import md5
paulb@126 7
paulb@126 8
class LoginResource:
paulb@126 9
paulb@126 10
    "A resource providing a login screen."
paulb@126 11
paulb@138 12
    def __init__(self, authenticator, use_redirect=1):
paulb@126 13
paulb@138 14
        """
paulb@138 15
        Initialise the resource with an 'authenticator'. If the optional 'use_redirect'
paulb@138 16
        flag is set to 0, a confirmation screen is given instead of redirecting the user
paulb@138 17
        back to the original application.
paulb@138 18
        """
paulb@126 19
paulb@126 20
        self.authenticator = authenticator
paulb@138 21
        self.use_redirect = use_redirect
paulb@126 22
paulb@126 23
    def respond(self, trans):
paulb@126 24
paulb@126 25
        fields = trans.get_fields_from_body()
paulb@126 26
        redirect = ""
paulb@126 27
paulb@126 28
        if fields.has_key("redirect"):
paulb@126 29
            redirects = fields["redirect"]
paulb@126 30
            redirect = redirects[0]
paulb@126 31
            if self.authenticator.authenticate(trans):
paulb@138 32
                if self.use_redirect:
paulb@138 33
                    trans.set_header_value("Location", redirect)
paulb@138 34
                    trans.set_response_code(307)
paulb@138 35
                    return
paulb@138 36
                else:
paulb@138 37
                    self._show_success(trans, redirect)
paulb@138 38
                    return
paulb@126 39
        else:
paulb@126 40
            fields = trans.get_fields_from_path()
paulb@126 41
            if fields.has_key("redirect"):
paulb@126 42
                redirects = fields["redirect"]
paulb@126 43
                redirect = redirects[0]
paulb@126 44
paulb@138 45
        self._show_login(trans, redirect)
paulb@138 46
paulb@138 47
    def _show_login(self, trans, redirect):
paulb@138 48
paulb@126 49
        # When authentication fails or is yet to take place, show the login
paulb@126 50
        # screen.
paulb@126 51
paulb@135 52
        trans.set_content_type(WebStack.Generic.ContentType("text/html"))
paulb@126 53
        out = trans.get_response_stream()
paulb@126 54
        out.write("""
paulb@126 55
<html>
paulb@126 56
  <head>
paulb@126 57
    <title>Login Example</title>
paulb@126 58
  </head>
paulb@126 59
  <body>
paulb@126 60
    <h1>Login</h1>
paulb@126 61
    <form method="POST">
paulb@126 62
      <p>Username: <input name="username" type="text" size="12"/></p>
paulb@126 63
      <p>Password: <input name="password" type="text" size="12"/></p>
paulb@126 64
      <p><input name="login" type="submit" value="Login"/></p>
paulb@126 65
      <input name="redirect" type="hidden" value="%s"/>
paulb@126 66
    </form>
paulb@126 67
  </body>
paulb@126 68
</html>
paulb@126 69
""" % redirect)
paulb@126 70
paulb@138 71
    def _show_success(self, trans, redirect):
paulb@138 72
paulb@138 73
        # When authentication fails or is yet to take place, show the login
paulb@138 74
        # screen.
paulb@138 75
paulb@138 76
        trans.set_content_type(WebStack.Generic.ContentType("text/html"))
paulb@138 77
        out = trans.get_response_stream()
paulb@138 78
        out.write("""
paulb@138 79
<html>
paulb@138 80
  <head>
paulb@138 81
    <title>Login Example</title>
paulb@138 82
  </head>
paulb@138 83
  <body>
paulb@138 84
    <h1>Login Successful</h1>
paulb@138 85
    <p>Please proceed <a href="%s">to the application</a>.</p>
paulb@138 86
  </body>
paulb@138 87
</html>
paulb@138 88
""" % redirect)
paulb@138 89
paulb@135 90
    def _decode(self, url):
paulb@135 91
paulb@135 92
        "Decode the given 'url' for redirection purposes."
paulb@135 93
paulb@135 94
        return url.replace("%3f", "?").replace("%26", "&")
paulb@135 95
paulb@126 96
class LoginAuthenticator:
paulb@126 97
paulb@130 98
    def __init__(self, secret_key, credentials, cookie_name=None):
paulb@126 99
paulb@130 100
        """
paulb@130 101
        Initialise the authenticator with a 'secret_key', the authenticator's registry of
paulb@130 102
        'credentials' and an optional 'cookie_name'.
paulb@126 103
paulb@130 104
        The 'credentials' must be an object which supports tests of the form
paulb@130 105
        '(username, password) in credentials'.
paulb@130 106
        """
paulb@126 107
paulb@126 108
        self.secret_key = secret_key
paulb@130 109
        self.credentials = credentials
paulb@130 110
        self.cookie_name = cookie_name or "LoginAuthenticator"
paulb@126 111
paulb@126 112
    def authenticate(self, trans):
paulb@126 113
paulb@126 114
        # Process any supplied parameters.
paulb@126 115
paulb@126 116
        fields = trans.get_fields_from_body()
paulb@126 117
paulb@126 118
        if fields.has_key("username") and fields.has_key("password"):
paulb@126 119
            usernames, passwords = fields["username"], fields["password"]
paulb@126 120
paulb@126 121
            # Insist on only one username and password.
paulb@126 122
paulb@126 123
            if len(usernames) == 1 and len(passwords) == 1:
paulb@126 124
                username, password = usernames[0], passwords[0]
paulb@126 125
paulb@126 126
                # Check against the class's credentials.
paulb@126 127
paulb@126 128
                if (username, password) in self.credentials:
paulb@126 129
paulb@126 130
                    # Make a special cookie token.
paulb@126 131
                    # NOTE: This should be moved into a common library.
paulb@126 132
paulb@126 133
                    trans.set_cookie_value(
paulb@130 134
                        self.cookie_name,
paulb@126 135
                        username + ":" + md5.md5(username + self.secret_key).hexdigest()
paulb@126 136
                    )
paulb@126 137
paulb@126 138
                    return 1
paulb@126 139
paulb@126 140
        return 0
paulb@126 141
paulb@126 142
# vim: tabstop=4 expandtab shiftwidth=4