1.9.30 sync.
[gae.git] / python / google / appengine / tools / dev_appserver_login.py
bloba4d6df59249fa9ee0252f5230ed04f4f84ae3246
1 #!/usr/bin/env python
3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Helper CGI for logins/logout in the development application server.
19 This CGI has these parameters:
21 continue: URL to redirect to after a login or logout has completed.
22 email: Email address to set for the client.
23 admin: If 'True', the client should be logged in as an admin.
24 action: What action to take ('Login' or 'Logout').
26 To view the current user information and a form for logging in and out,
27 supply no parameters.
28 """
32 import cgi
33 import Cookie
34 import os
35 import sys
36 import urllib
37 import hashlib
42 CONTINUE_PARAM = 'continue'
43 EMAIL_PARAM = 'email'
44 ADMIN_PARAM = 'admin'
45 ACTION_PARAM = 'action'
48 LOGOUT_ACTION = 'Logout'
49 LOGIN_ACTION = 'Login'
52 LOGOUT_PARAM = 'action=%s' % LOGOUT_ACTION
55 COOKIE_NAME = 'dev_appserver_login'
59 def GetUserInfo(http_cookie, cookie_name=COOKIE_NAME):
60 """Get the requestor's user info from the HTTP cookie in the CGI environment.
62 Args:
63 http_cookie: Value of the HTTP_COOKIE environment variable.
64 cookie_name: Name of the cookie that stores the user info.
66 Returns:
67 Tuple (email, admin, user_id) where:
68 email: The user's email address, if any.
69 admin: True if the user is an admin; False otherwise.
70 user_id: The user ID, if any.
71 """
72 try:
73 cookie = Cookie.SimpleCookie(http_cookie)
74 except Cookie.CookieError:
75 return '', False, ''
77 cookie_value = ''
78 if cookie_name in cookie:
79 cookie_value = cookie[cookie_name].value
81 email, admin, user_id = (cookie_value.split(':') + ['', '', ''])[:3]
82 return email, (admin == 'True'), user_id
85 def CreateCookieData(email, admin):
86 """Creates cookie payload data.
88 Args:
89 email, admin: Parameters to incorporate into the cookie.
91 Returns:
92 String containing the cookie payload.
93 """
94 admin_string = 'False'
95 if admin:
96 admin_string = 'True'
97 if email:
98 user_id_digest = hashlib.md5(email.lower()).digest()
99 user_id = '1' + ''.join(['%02d' % ord(x) for x in user_id_digest])[:20]
100 else:
101 user_id = ''
102 return '%s:%s:%s' % (email, admin_string, user_id)
105 def SetUserInfoCookie(email, admin, cookie_name=COOKIE_NAME):
106 """Creates a cookie to set the user information for the requestor.
108 Args:
109 email: Email to set for the user.
110 admin: True if the user should be admin; False otherwise.
111 cookie_name: Name of the cookie that stores the user info.
113 Returns:
114 'Set-Cookie' header for setting the user info of the requestor.
116 cookie_value = CreateCookieData(email, admin)
117 set_cookie = Cookie.SimpleCookie()
118 set_cookie[cookie_name] = cookie_value
119 set_cookie[cookie_name]['path'] = '/'
120 return '%s\r\n' % set_cookie
123 def ClearUserInfoCookie(cookie_name=COOKIE_NAME):
124 """Clears the user info cookie from the requestor, logging them out.
126 Args:
127 cookie_name: Name of the cookie that stores the user info.
129 Returns:
130 'Set-Cookie' header for clearing the user info of the requestor.
132 set_cookie = Cookie.SimpleCookie()
133 set_cookie[cookie_name] = ''
134 set_cookie[cookie_name]['path'] = '/'
135 set_cookie[cookie_name]['max-age'] = '0'
136 return '%s\r\n' % set_cookie
140 LOGIN_TEMPLATE = """<html>
141 <head>
142 <title>Login</title>
143 </head>
144 <body>
146 <form method="get" action="%(login_url)s"
147 style="text-align:center; font: 13px sans-serif">
148 <div style="width: 20em; margin: 1em auto;
149 text-align:left;
150 padding: 0 2em 1.25em 2em;
151 background-color: #d6e9f8;
152 border: 2px solid #67a7e3">
153 <h3>%(login_message)s</h3>
154 <p style="padding: 0; margin: 0">
155 <label for="email" style="width: 3em">Email:</label>
156 <input name="email" type="text" value="%(email)s" id="email"/>
157 </p>
158 <p style="margin: .5em 0 0 3em; font-size:12px">
159 <input name="admin" type="checkbox" value="True"
160 %(admin_checked)s id="admin"/>
161 <label for="admin">Sign in as Administrator</label>
162 </p>
163 <p style="margin-left: 3em">
164 <input name="action" value="Login" type="submit"
165 id="submit-login" />
166 <input name="action" value="Logout" type="submit"
167 id="submit-logout" />
168 </p>
169 </div>
170 <input name="continue" type="hidden" value="%(continue_url)s"/>
171 </form>
173 </body>
174 </html>
178 def RenderLoginTemplate(login_url, continue_url, email, admin):
179 """Renders the login page.
181 Args:
182 login_url, continue_url, email, admin: Parameters passed to
183 LoginCGI.
185 Returns:
186 String containing the contents of the login page.
188 login_message = 'Not logged in'
189 if email:
190 login_message = 'Logged in'
191 admin_checked = ''
192 if admin:
193 admin_checked = 'checked'
195 template_dict = {
198 'email': (cgi.escape(email, quote=1) or 'test\x40example.com'),
199 'admin_checked': admin_checked,
200 'login_message': login_message,
201 'login_url': cgi.escape(login_url, quote=1),
202 'continue_url': cgi.escape(continue_url, quote=1)
205 return LOGIN_TEMPLATE % template_dict
209 def LoginRedirect(login_url,
210 hostname,
211 port,
212 relative_url,
213 outfile):
214 """Writes a login redirection URL to a user.
216 Args:
217 login_url: Relative URL which should be used for handling user logins.
218 hostname: Name of the host on which the webserver is running.
219 port: Port on which the webserver is running.
220 relative_url: String containing the URL accessed.
221 outfile: File-like object to which the response should be written.
223 dest_url = "http://%s:%s%s" % (hostname, port, relative_url)
224 redirect_url = 'http://%s:%s%s?%s=%s' % (hostname,
225 port,
226 login_url,
227 CONTINUE_PARAM,
228 urllib.quote(dest_url))
229 outfile.write('Status: 302 Requires login\r\n')
230 outfile.write('Location: %s\r\n\r\n' % redirect_url)
233 def LoginCGI(login_url,
234 email,
235 admin,
236 action,
237 set_email,
238 set_admin,
239 continue_url,
240 outfile):
241 """Runs the login CGI.
243 This CGI does not care about the method at all. For both POST and GET the
244 client will be redirected to the continue URL.
246 Args:
247 login_url: URL used to run the CGI.
248 email: Current email address of the requesting user.
249 admin: True if the requesting user is an admin; False otherwise.
250 action: The action used to run the CGI; 'Login' for a login action, 'Logout'
251 for when a logout should occur.
252 set_email: Email to set for the user; Empty if no email should be set.
253 set_admin: True if the user should be an admin; False otherwise.
254 continue_url: URL to which the user should be redirected when the CGI
255 finishes loading; defaults to the login_url with no parameters (showing
256 current status) if not supplied.
257 outfile: File-like object to which all output data should be written.
259 redirect_url = ''
260 output_headers = []
262 if action:
263 if action.lower() == LOGOUT_ACTION.lower():
264 output_headers.append(ClearUserInfoCookie())
265 elif set_email:
266 output_headers.append(SetUserInfoCookie(set_email, set_admin))
268 redirect_url = continue_url or login_url
270 if redirect_url:
271 outfile.write('Status: 302 Redirecting to continue URL\r\n')
272 for header in output_headers:
273 outfile.write(header)
274 outfile.write('Location: %s\r\n' % redirect_url)
275 outfile.write('\r\n')
276 else:
277 outfile.write('Status: 200\r\n')
278 outfile.write('Content-Type: text/html\r\n')
279 outfile.write('\r\n')
280 outfile.write(RenderLoginTemplate(login_url,
281 continue_url,
282 email,
283 admin))
287 def main():
288 """Runs the login and logout CGI script."""
289 form = cgi.FieldStorage(environ=os.environ)
290 login_url = os.environ['PATH_INFO']
291 email = os.environ.get('USER_EMAIL', '')
292 admin = os.environ.get('USER_IS_ADMIN', '0') == '1'
294 action = form.getfirst(ACTION_PARAM)
295 set_email = form.getfirst(EMAIL_PARAM, '')
296 set_admin = form.getfirst(ADMIN_PARAM, '') == 'True'
297 continue_url = form.getfirst(CONTINUE_PARAM, '')
299 LoginCGI(login_url,
300 email,
301 admin,
302 action,
303 set_email,
304 set_admin,
305 continue_url,
306 sys.stdout)
307 return 0
311 if __name__ == '__main__':
312 main()