#!/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 = """ Login

%(login_message)s

""" 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()