thirdparty/google_appengine/google/appengine/tools/dev_appserver_login.py
author Mario Ferraro <fadinlight@gmail.com>
Sun, 15 Nov 2009 22:12:20 +0100
changeset 3093 d1be59b6b627
parent 2273 e4cb9c53db3e
permissions -rwxr-xr-x
GMaps related JS changed to use new google namespace. Google is going to change permanently in the future the way to load its services, so better stay safe. Also this commit shows uses of the new melange.js module. Fixes Issue 634.

#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Helper CGI for logins/logout in the development application server.

This CGI has these parameters:

  continue: URL to redirect to after a login or logout has completed.
  email: Email address to set for the client.
  admin: If 'True', the client should be logged in as an admin.
  action: What action to take ('Login' or 'Logout').

To view the current user information and a form for logging in and out,
supply no parameters.
"""


import cgi
import Cookie
import md5
import os
import sys
import urllib


CONTINUE_PARAM = 'continue'
EMAIL_PARAM = 'email'
ADMIN_PARAM = 'admin'
ACTION_PARAM = 'action'

LOGOUT_ACTION = 'Logout'
LOGIN_ACTION = 'Login'

LOGOUT_PARAM = 'action=%s' % LOGOUT_ACTION

COOKIE_NAME = 'dev_appserver_login'


def GetUserInfo(http_cookie, cookie_name=COOKIE_NAME):
  """Get the requestor's user info from the HTTP cookie in the CGI environment.

  Args:
    http_cookie: Value of the HTTP_COOKIE environment variable.
    cookie_name: Name of the cookie that stores the user info.

  Returns:
    Tuple (email, admin) where:
      email: The user's email address, if any.
      admin: True if the user is an admin; False otherwise.
  """
  cookie = Cookie.SimpleCookie(http_cookie)

  cookie_value = ''
  if cookie_name in cookie:
    cookie_value = cookie[cookie_name].value

  email, admin, user_id = (cookie_value.split(':') + ['', '', ''])[:3]
  return email, (admin == 'True'), user_id


def CreateCookieData(email, admin):
  """Creates cookie payload data.

  Args:
    email, admin: Parameters to incorporate into the cookie.

  Returns:
    String containing the cookie payload.
  """
  admin_string = 'False'
  if admin:
    admin_string = 'True'
  if email:
    user_id_digest = md5.new(email.lower()).digest()
    user_id = '1' + ''.join(['%02d' % ord(x) for x in user_id_digest])[:20]
  else:
    user_id = ''
  return '%s:%s:%s' % (email, admin_string, user_id)


def SetUserInfoCookie(email, admin, cookie_name=COOKIE_NAME):
  """Creates a cookie to set the user information for the requestor.

  Args:
    email: Email to set for the user.
    admin: True if the user should be admin; False otherwise.
    cookie_name: Name of the cookie that stores the user info.

  Returns:
    'Set-Cookie' header for setting the user info of the requestor.
  """
  cookie_value = CreateCookieData(email, admin)
  set_cookie = Cookie.SimpleCookie()
  set_cookie[cookie_name] = cookie_value
  set_cookie[cookie_name]['path'] = '/'
  return '%s\r\n' % set_cookie


def ClearUserInfoCookie(cookie_name=COOKIE_NAME):
  """Clears the user info cookie from the requestor, logging them out.

  Args:
    cookie_name: Name of the cookie that stores the user info.

  Returns:
    'Set-Cookie' header for clearing the user info of the requestor.
  """
  set_cookie = Cookie.SimpleCookie()
  set_cookie[cookie_name] = ''
  set_cookie[cookie_name]['path'] = '/'
  set_cookie[cookie_name]['max-age'] = '0'
  return '%s\r\n' % set_cookie


LOGIN_TEMPLATE = """<html>
<head>
  <title>Login</title>
</head>
<body>

<form method='get' action='%(login_url)s'
      style='text-align:center; font: 13px sans-serif'>
  <div style='width: 20em; margin: 1em auto;
              text-align:left;
              padding: 0 2em 1.25em 2em;
              background-color: #d6e9f8;
              border: 2px solid #67a7e3'>
    <h3>%(login_message)s</h3>
    <p style='padding: 0; margin: 0'>
      <label for='email' style="width: 3em">Email:</label>
      <input name='email' type='text' value='%(email)s' id='email'/>
    </p>
    <p style='margin: .5em 0 0 3em; font-size:12px'>
      <input name='admin' type='checkbox' value='True'
       %(admin_checked)s id='admin'/>
        <label for='admin'>Sign in as Administrator</label>
    </p>
    <p style='margin-left: 3em'>
      <input name='action' value='Login' type='submit'
             id='submit-login' />
      <input name='action' value='Logout' type='submit'
             id='submit-logout' />
    </p>
  </div>
  <input name='continue' type='hidden' value='%(continue_url)s'/>
</form>

</body>
</html>
"""


def RenderLoginTemplate(login_url, continue_url, email, admin):
  """Renders the login page.

  Args:
    login_url, continue_url, email, admin: Parameters passed to
      LoginCGI.

  Returns:
    String containing the contents of the login page.
  """
  login_message = 'Not logged in'
  if email:
    login_message = 'Logged in'
  admin_checked = ''
  if admin:
    admin_checked = 'checked'

  template_dict = {


    'email': email or 'test\x40example.com',
    'admin_checked': admin_checked,
    'login_message': login_message,
    'login_url': login_url,
    'continue_url': continue_url
  }

  return LOGIN_TEMPLATE % template_dict


def LoginRedirect(login_url,
                  hostname,
                  port,
                  relative_url,
                  outfile):
  """Writes a login redirection URL to a user.

  Args:
    login_url: Relative URL which should be used for handling user logins.
    hostname: Name of the host on which the webserver is running.
    port: Port on which the webserver is running.
    relative_url: String containing the URL accessed.
    outfile: File-like object to which the response should be written.
  """
  dest_url = "http://%s:%s%s" % (hostname, port, relative_url)
  redirect_url = 'http://%s:%s%s?%s=%s' % (hostname,
                                           port,
                                           login_url,
                                           CONTINUE_PARAM,
                                           urllib.quote(dest_url))
  outfile.write('Status: 302 Requires login\r\n')
  outfile.write('Location: %s\r\n\r\n' % redirect_url)


def LoginCGI(login_url,
             email,
             admin,
             action,
             set_email,
             set_admin,
             continue_url,
             outfile):
  """Runs the login CGI.

  This CGI does not care about the method at all. For both POST and GET the
  client will be redirected to the continue URL.

  Args:
    login_url: URL used to run the CGI.
    email: Current email address of the requesting user.
    admin: True if the requesting user is an admin; False otherwise.
    action: The action used to run the CGI; 'Login' for a login action, 'Logout'
      for when a logout should occur.
    set_email: Email to set for the user; Empty if no email should be set.
    set_admin: True if the user should be an admin; False otherwise.
    continue_url: URL to which the user should be redirected when the CGI
      finishes loading; defaults to the login_url with no parameters (showing
      current status) if not supplied.
    outfile: File-like object to which all output data should be written.
  """
  redirect_url = ''
  output_headers = []

  if action:
    if action.lower() == LOGOUT_ACTION.lower():
      output_headers.append(ClearUserInfoCookie())
    elif set_email:
      output_headers.append(SetUserInfoCookie(set_email, set_admin))

    redirect_url = continue_url or login_url

  if redirect_url:
    outfile.write('Status: 302 Redirecting to continue URL\r\n')
    for header in output_headers:
      outfile.write(header)
    outfile.write('Location: %s\r\n' % redirect_url)
    outfile.write('\r\n')
  else:
    outfile.write('Status: 200\r\n')
    outfile.write('Content-Type: text/html\r\n')
    outfile.write('\r\n')
    outfile.write(RenderLoginTemplate(login_url,
                                      continue_url,
                                      email,
                                      admin))


def main():
  """Runs the login and logout CGI script."""
  form = cgi.FieldStorage()
  login_url = os.environ['PATH_INFO']
  email = os.environ.get('USER_EMAIL', '')
  admin = os.environ.get('USER_IS_ADMIN', '0') == '1'

  action = form.getfirst(ACTION_PARAM)
  set_email = form.getfirst(EMAIL_PARAM, '')
  set_admin = form.getfirst(ADMIN_PARAM, '') == 'True'
  continue_url = form.getfirst(CONTINUE_PARAM, '')

  LoginCGI(login_url,
           email,
           admin,
           action,
           set_email,
           set_admin,
           continue_url,
           sys.stdout)
  return 0


if __name__ == '__main__':
  main()