This repository has been archived by the owner on Oct 19, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathauthserver.py
executable file
·260 lines (186 loc) · 7.64 KB
/
authserver.py
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
#!/usr/bin/env python
# ##### BEGIN AGPL LICENSE BLOCK #####
# This file is part of SimpleMMO.
#
# Copyright (C) 2011, 2012 Charles Nelson
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# ##### END AGPL LICENSE BLOCK #####
'''AuthServer
A server providing registration, authentication, and allows a user to get a list of their characters.
'''
# TODO: Write a function to pull in the docstrings from defined classes here and
# append them to the module docstring
import json
import logging
from passlib.context import CryptContext
from sqlalchemy.exc import IntegrityError
import tornado
import settings
from baseserver import BaseServer, SimpleHandler, BaseHandler
from elixir_models import Character, User, db
# TODO: Make an SQLCharacterController
class UserController(object):
context = CryptContext(
schemes=["pbkdf2_sha512",],
default="pbkdf2_sha512",
# set the number of rounds that should be used...
# (appropriate values may vary for different schemes,
# and the amount of time you wish it to take)
pbkdf2_sha256__default_rounds = settings.HASH_ROUNDS,
)
@classmethod
def hash_password(cls, password):
return cls.context.encrypt(password)
@classmethod
def check_password(cls, plaintext, hashed):
return cls.context.verify(plaintext, hashed)
class RegistrationHandler(BaseHandler):
'''RegistrationHandler creates Users.
.. http:post:: /register
Creates a User in the AuthenticationServer's database.
**Example request**:
.. sourcecode:: http
POST /register HTTP/1.1
username=asdf&password=asdf
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Registration succeeded.
:param username: The name of the user to create.
:param password: The password to use for authenticating the user.
:param email: Optional. An email to associate with the user.
:status 200: Registration succeeded.
:status 400: A required parameter was missing.
:status 401: The User already exists.
'''
def post(self):
username = self.get_argument("username", "")
password = self.get_argument("password", "")
email = self.get_argument("email", "")
if not username:
return self.HTTPError(400, "A user name is required.")
if not password:
return self.HTTPError(400, "A password is required.")
user = self.register_user(username, password, email=email)
logging.info("User was %s" % user)
if user:
return self.write('Registration successful.')
else:
return self.HTTPError(401, "User already exists.")
def register_user(self, username, password, email=None):
if User.select().where(User.username==username).exists():
return False
user = User(username=username,
password=UserController.hash_password(password),
email=email)
user.save()
return user
class AuthHandler(BaseHandler):
'''AuthHandler authenticates a user and sets a session in the database.
.. http:post:: /login
Creates a User in the AuthenticationServer's database.
**Example request**:
.. sourcecode:: http
POST /login HTTP/1.1
username=asdf&password=asdf
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Set-Cookie: user="VXNlcm5hbWU=|1341639882|ec1af42349272b09f4f7ebb1be4da826500d1f6c"; expires=Mon, 06 Aug 2012 05:44:42 GMT; Path=/
Login successful.
:param username: The name of the user to log in as.
:param password: The password to use for authenticating the user.
:status 200: Login successful.
:status 401: Login failed due to bad username and/or password
'''
def post(self):
username = self.get_argument("username", "")
password = self.get_argument("password", "")
auth = self.authenticate(username, password)
if auth:
self.set_current_user(username)
self.set_admin(username)
self.write('Login successful.')
else:
raise tornado.web.HTTPError(401, 'Login Failed, username and/or password incorrect.')
def authenticate(self, username, password):
'''Compares a username/password pair against that in the database.
If they match, return True.
Else, return False.'''
# Do some database stuff here to verify the user.
user = User.get(username=username)
if not user:
return False
return UserController.check_password(plaintext=password, hashed=user.password)
def set_admin(self, user):
# Look up username in admins list in database
# if present, set secure cookie for admin
if user in settings.ADMINISTRATORS:
self.set_secure_cookie("admin", 'true')
else:
self.clear_cookie("admin")
def set_current_user(self, user):
if user:
self.set_secure_cookie("user", user, domain=None)
else:
self.clear_cookie("user")
class LogoutHandler(BaseHandler):
'''Unsets the user's cookie.
.. http:post:: /logout
Creates a User in the AuthenticationServer's database.
**Example request**:
.. sourcecode:: http
GET /logout HTTP/1.1
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Set-Cookie: user=; expires=Mon, 06 Aug 2012 05:44:42 GMT; Path=/
:status 200: Successfully logged out.
'''
def get(self):
self.clear_cookie("user")
class CharacterHandler(BaseHandler):
'''CharacterHandler gets a list of characters for the given user account.
GET /characters'''
@tornado.web.authenticated
def get(self):
self.write(json.dumps(self.get_characters(self.get_current_user())))
self.set_header('Content-Type', 'application/json')
def get_characters(self, username):
'''Queries the database for all characters owned by a particular user.'''
user = User.get(username=username)
logging.info(user.characters)
return [c.name for c in user.characters]
if __name__ == "__main__":
handlers = []
handlers.append((r"/", lambda x, y: SimpleHandler(__doc__, x, y)))
handlers.append((r"/register", RegistrationHandler))
handlers.append((r"/login", AuthHandler))
handlers.append((r"/logout", LogoutHandler))
handlers.append((r"/characters", CharacterHandler))
server = BaseServer(handlers)
server.listen(settings.AUTHSERVERPORT)
try:
user = User.get(username=settings.DEFAULT_USERNAME)
except User.DoesNotExist:
password = UserController.hash_password(settings.DEFAULT_PASSWORD)
user = User(username=settings.DEFAULT_USERNAME, password=password)
user.save()
print "Starting up Authserver..."
try:
server.start()
except KeyboardInterrupt:
print "Exiting Authserver."