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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
|
#!/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()
|