-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
User auth #3 ready #71
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
import json | ||
import tornado | ||
import tornado.web | ||
import hashlib | ||
import tornado.auth | ||
import sys | ||
import base_handler | ||
import jsonhandler | ||
|
@@ -44,54 +44,231 @@ def get(self): | |
class LoginHandler(base_handler.BaseHandler): | ||
@tornado.web.asynchronous | ||
def get(self): | ||
if self.get_secure_cookie("user"): | ||
self.redirect("/") | ||
|
||
invalid_login = self.get_argument("invalid", default=None) | ||
if self._global_arg["disable_login"]: | ||
return | ||
self.render('login.html', **self._global_arg) | ||
invalid_login = "disable_login" | ||
|
||
self.render('login.html', invalid_login=invalid_login, **self._global_arg) | ||
|
||
@tornado.web.asynchronous | ||
def post(self): | ||
if self._global_arg["disable_login"]: | ||
return | ||
email = self.get_argument("username") | ||
name = self.get_argument("name") | ||
password = self.get_argument("password") | ||
self.redirect("/login?invalid=disable_login") | ||
|
||
if self.get_secure_cookie("user"): | ||
print("Need to logout before login or sign in.", file=sys.stderr) | ||
print("Need to logout before login or sign up.", file=sys.stderr) | ||
return | ||
if not email: | ||
print("User name is empty.", file=sys.stderr) | ||
|
||
#EXTREMELY IMPORTANT to prevent accessing accounts that do not yet have a password. | ||
password = self.get_argument("password") | ||
if not password: | ||
print("Password is empty.", file=sys.stderr) | ||
secure_pass = hashlib.sha256(password.encode('UTF-8')).hexdigest() | ||
if name: | ||
user = self._db.create_user(email, name, secure_pass) | ||
self.redirect("/login?invalid=password") | ||
|
||
|
||
#Login | ||
if self.get_arguments("username") == []: | ||
|
||
username_or_email = self.get_argument("username_or_email") | ||
if not username_or_email: | ||
print("Email or Username is empty.", file=sys.stderr) | ||
self.redirect("/login?invalid=username_or_email") | ||
|
||
#Try finding the user by mail... | ||
user = None | ||
if "@" in username_or_email: | ||
print("in mail") | ||
user = self._db.get_user(email=username_or_email, password=password) | ||
#... or by name. | ||
if not user: | ||
print("not in mail") | ||
user = self._db.get_user(name=username_or_email, password=password) | ||
|
||
#If user is found, give him a secure cookie based on his user id | ||
if user: | ||
self.give_cookie(user.get("user_id")) | ||
else: | ||
print("Invalid email/password combination", file=sys.stderr) | ||
self.redirect("/login?invalid=login") | ||
|
||
#Sign Up | ||
else: | ||
user = self._db.get_user(email, secure_pass) | ||
name = self.get_argument("username") | ||
if not name: | ||
print("Username is empty.", file=sys.stderr) | ||
self.redirect("/login?invalid=username") | ||
|
||
email = self.get_argument("email", default=None) | ||
|
||
if user: | ||
uuid = user.get("uuid") | ||
if uuid: | ||
self.set_secure_cookie("user", uuid) | ||
self.redirect("/") | ||
password_mail = self.get_argument("pwconfirm") | ||
if not password_mail: | ||
print("Password is empty.", file=sys.stderr) | ||
self.redirect("/login?invalid=password") | ||
|
||
if self._db.create_user(name, email, password, password_mail): | ||
self.redirect("/login") | ||
else: | ||
self.redirect("/login?invalid=signup") | ||
|
||
class GoogleOAuth2LoginHandler(base_handler.BaseHandler, tornado.auth.GoogleOAuth2Mixin): | ||
@tornado.gen.coroutine | ||
def get(self): | ||
if self.get_argument('code', False): | ||
try: | ||
google_user = yield self.get_authenticated_user( | ||
redirect_uri=self._global_arg["url"]+'/cmd/auth/google', | ||
code=self.get_argument('code')) | ||
access_token = google_user["access_token"] | ||
google_user = yield self.oauth2_request( | ||
"https://www.googleapis.com/oauth2/v1/userinfo", | ||
access_token=access_token) | ||
|
||
# Save the user with e.g. set_secure_cookie | ||
google_id = google_user["id"] | ||
user = self._db.get_user(id_type="google", user_id=google_id) | ||
|
||
#Login | ||
#If user is found, give him a secure cookie based on his user_id and Google access_token | ||
if user: | ||
self.give_cookie(user.get("user_id"), google_access_token=access_token) | ||
|
||
#Sign up | ||
else: | ||
name = google_user["name"] | ||
email = google_user.get("email") | ||
user = self._db.create_user(name, email, google_id=google_id) | ||
if user: | ||
self.give_cookie(user.get("user_id"), google_access_token=access_token) | ||
else: | ||
self.redirect("/login?invalid=google") | ||
|
||
except KeyError as exception: | ||
print("KeyError: "+str(exception)+" in GoogleOAuth2LoginHandler", file=sys.stderr) | ||
self.redirect("/login?invalid=google") | ||
return | ||
|
||
else: | ||
yield self.authorize_redirect( | ||
redirect_uri=self._global_arg["url"]+'/cmd/auth/google', | ||
client_id=self.settings['google_oauth']['key'], | ||
scope=['profile', 'email'], | ||
response_type='code', | ||
extra_params={'approval_prompt': 'auto'}) | ||
|
||
class FacebookGraphLoginHandler(base_handler.BaseHandler, tornado.auth.FacebookGraphMixin): | ||
@tornado.gen.coroutine | ||
def get(self): | ||
if self.get_argument("code", None): | ||
try: | ||
facebook_user = yield self.get_authenticated_user( | ||
redirect_uri=self._global_arg["url"]+'/cmd/auth/facebook', | ||
client_id=self.settings["facebook_api_key"], | ||
client_secret=self.settings["facebook_secret"], | ||
code=self.get_argument("code"), | ||
extra_fields=["email"]) | ||
access_token = facebook_user["access_token"] | ||
|
||
facebook_id = facebook_user["id"] | ||
user = self._db.get_user(id_type="facebook", user_id=facebook_id) | ||
|
||
#Login | ||
#If user is found, give him a secure cookie based on his user_id and Facebook access_token | ||
if user: | ||
self.give_cookie(user.get("user_id"), facebook_access_token=access_token) | ||
|
||
#Sign up | ||
else: | ||
name = facebook_user["name"] | ||
email = facebook_user.get("email") | ||
user = self._db.create_user(name, email, facebook_id=facebook_id) | ||
if user: | ||
self.give_cookie(user.get("user_id"), facebook_access_token=access_token) | ||
else: | ||
self.redirect("/login?invalid=facebook") | ||
|
||
except KeyError as exception: | ||
print("KeyError: "+str(exception)+" in FacebookGraphLoginHandler", file=sys.stderr) | ||
self.redirect("/login?invalid=facebook") | ||
return | ||
|
||
else: | ||
yield self.authorize_redirect( | ||
redirect_uri=self._global_arg["url"]+'/cmd/auth/facebook', | ||
client_id=self.settings["facebook_api_key"], | ||
#Permissions: https://developers.facebook.com/docs/facebook-login/permissions | ||
extra_params={"scope": "email"}) | ||
|
||
class TwitterLoginHandler(base_handler.BaseHandler, tornado.auth.TwitterMixin): | ||
@tornado.gen.coroutine | ||
def get(self): | ||
if self.get_argument("oauth_token", None): | ||
try: | ||
twitter_user = yield self.get_authenticated_user() | ||
access_token = twitter_user.get("access_token") | ||
twitter_user = yield self.twitter_request("/account/verify_credentials", | ||
access_token=access_token, include_email="true") | ||
|
||
twitter_id = twitter_user["id_str"] | ||
user = self._db.get_user(id_type="twitter", user_id=twitter_id) | ||
|
||
#Login | ||
#If user is found, give him a secure cookie based on his user_id and Twitter access_token | ||
if user: | ||
self.give_cookie(user.get("user_id"), twitter_access_token=access_token) | ||
|
||
#Sign up | ||
else: | ||
name = twitter_user["name"] | ||
email = twitter_user.get("email") | ||
user = self._db.create_user(name, email, twitter_id=twitter_id) | ||
if user: | ||
self.give_cookie(user.get("user_id"), twitter_access_token=access_token) | ||
else: | ||
self.redirect("/login?invalid=twitter") | ||
except KeyError as exception: | ||
print("KeyError: "+str(exception)+" in TwitterLoginHandler", file=sys.stderr) | ||
self.redirect("/login?invalid=twitter") | ||
return | ||
else: | ||
yield self.authenticate_redirect() | ||
|
||
class LogoutHandler(base_handler.BaseHandler): | ||
def get(self): | ||
if self._global_arg["disable_login"]: | ||
return | ||
self.clear_cookie("user") | ||
self.redirect(u"/") | ||
if self.current_user: | ||
self.clear_cookie("user") | ||
self.redirect("/") | ||
else: | ||
self.redirect("/login") | ||
|
||
|
||
class AdminHandler(base_handler.BaseHandler): | ||
@tornado.web.asynchronous | ||
# @userapp.tornado.authorized() | ||
# @userapp.tornado.has_permission('admin') | ||
@tornado.web.authenticated | ||
def get(self): | ||
if self._global_arg["disable_admin"]: | ||
return | ||
self.render('admin_character.html', **self._global_arg) | ||
if self.current_user.get("permission") == "Admin": | ||
self.render('admin_character.html', **self._global_arg) | ||
else: | ||
print("Insufficient persmissions", file=sys.stderr) | ||
self.redirect("/") #TODO : HTTP error 403: Forbidden | ||
|
||
class ProfileHandler(base_handler.BaseHandler): | ||
@tornado.web.asynchronous | ||
@tornado.web.authenticated | ||
def get(self, user_id=None): | ||
if self._global_arg["disable_character"]: | ||
return | ||
if user_id: | ||
user = self._db.get_user(user_id=user_id) | ||
else: | ||
user = self.current_user | ||
self.render('profile.html', user=user, **self._global_arg) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. serait-ce mieux renommer profile.html pour user_profile.html ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DONE On abandonne pour le moment ce commentaire. |
||
|
||
class CharacterHandler(base_handler.BaseHandler): | ||
@tornado.web.asynchronous | ||
|
@@ -107,9 +284,9 @@ def get(self): | |
if self._global_arg["disable_character"]: | ||
return | ||
|
||
player_id = self.request.query[len("player_id="):] | ||
user_id = self.request.query[len("user_id="):] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Il va falloir migrer la base de donnée avec les nouvelles clés utilisés ou reprendre les anciennes clés et les modifier plus tard pour des noms plus juste. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. player_id -> user_id There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DONE - ce n'est plus pertinant dans le contexte qu'on efface la base de donnée du passé. |
||
is_admin = self.request.query == "is_admin" | ||
if player_id == "" and not is_admin: | ||
if user_id == "" and not is_admin: | ||
# leave now, missing permission | ||
self.finish() | ||
return | ||
|
@@ -118,7 +295,7 @@ def get(self): | |
if is_admin: | ||
data = json.dumps(self._db.get_all_user()) | ||
else: | ||
data = json.dumps(self._db.get_all_user(id=player_id)) | ||
data = json.dumps(self._db.get_all_user(user_id=user_id)) | ||
|
||
self.write(data) | ||
self.finish() | ||
|
@@ -130,17 +307,17 @@ def post(self): | |
self.prepare_json() | ||
|
||
# user_id = self.get_argument("user_id") | ||
player = self.get_argument("player") | ||
user = self.get_argument("user") | ||
character = self.get_argument("character") | ||
delete_player_id = self.get_argument("delete_player_id") | ||
delete_character_id = self.get_argument("delete_character_id") | ||
delete_user_by_id = self.get_argument("delete_user_by_id") | ||
delete_character_by_id = self.get_argument("delete_character_by_id") | ||
|
||
# exception, if delete_player_id, create player if not exist | ||
if not player and delete_player_id: | ||
player = {"id": delete_player_id} | ||
# exception, if delete_user_by_id, create user if not exist | ||
if not user and delete_user_by_id: | ||
user = {"user_id": delete_user_by_id} | ||
|
||
self._db.update_player(player, character, delete_player_id=delete_player_id, | ||
delete_character_id=delete_character_id) | ||
self._db.update_user(user, character, delete_user_by_id=delete_user_by_id, | ||
delete_character_by_id=delete_character_by_id) | ||
|
||
|
||
class ManualHandler(jsonhandler.JsonHandler): | ||
|
@@ -155,3 +332,21 @@ class LoreHandler(jsonhandler.JsonHandler): | |
def get(self): | ||
self.write(self._lore.get_str_all()) | ||
self.finish() | ||
|
||
|
||
class ValidateAuthHandler(base_handler.BaseHandler): | ||
"""This class is designed purely for client-side validation""" | ||
@tornado.web.asynchronous | ||
def get(self): | ||
name = self.get_argument("username", default=None) | ||
email = self.get_argument("email", default=None) | ||
|
||
if name: | ||
self.write("0" if (self._db.user_exists(name=name) or self._db.user_exists(email=name)) else "1") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pourquoi ne pas retourner un json? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Puis mettre une boolean au lieu de "0" ou "1"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DONE - ça va être bon pour le moment. La requête est envoyé à chaque caractère. Ça doit être optimisé dans le future. |
||
elif email: | ||
self.write("0" if (self._db.user_exists(email=email) or self._db.user_exists(name=email)) else "1") | ||
|
||
# Produce a missing argument error | ||
else: | ||
self.get_argument("username or email") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Est-ce que c'est ceci qu'il faut aller chercher? «username_or_email» There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. De toute façon, on ne fait rien avec. On veut créer une erreur? Je ne comprend pas la raison de cette ligne. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DONE - Via l'interface usagé web, on ne peut pas se rendre à cette étape. Seul des tests unitaires ou des requêtes http en ne passant pas via l'interface usagé pourra exécuter cette ligne. Un todo a été ajouté. |
||
self.finish() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
key.json n'est pas un nom significatif
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Contains keys and secrets needed for third-party authentification."
Pourquoi pas le renommer auth.json?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DONE, renommé pour auth.json