Skip to content

Commit

Permalink
[AdminTL#83] Profile: update page and able to update password
Browse files Browse the repository at this point in the history
- Rename connection button
- Remove user_password and email_password
- Manage only 1 password and no more salt from client
  • Loading branch information
mathben committed Mar 26, 2018
1 parent 2a5e079 commit 784c199
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 41 deletions.
143 changes: 132 additions & 11 deletions src/web/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ def post(self):
return

# Login
if self.get_argument("username_or_email", ""):
if self.get_argument("username_or_email", default=""):

username_or_email = self.get_argument("username_or_email", "")
username_or_email = self.get_argument("username_or_email", default="")
if not username_or_email:
print("Email or Username is empty.", file=sys.stderr)
self.redirect("/login?invalid=username_or_email")
Expand All @@ -127,28 +127,24 @@ def post(self):
return

# Sign Up
elif self.get_argument("username"):
name = self.get_argument("username")
elif self.get_argument("username", default=""):
name = self.get_argument("username", default="")
if not name:
print("Username is empty from %s" % self.request.remote_ip, file=sys.stderr)
self.redirect("/login?invalid=username")
return

email = self.get_argument("email", default=None)

password_mail = self.get_argument("pwconfirm")
if not password_mail:
print("Password is empty from %s" % self.request.remote_ip, file=sys.stderr)
self.redirect("/login?invalid=password")
return

if self._db.create_user(name, email, password, password_mail):
if self._db.create_user(name, email=email, password=password):
self.redirect("/login")
return
else:
self.redirect("/login?invalid=signup")
return

self.redirect("/login")


class GoogleOAuth2LoginHandler(base_handler.BaseHandler, tornado.auth.GoogleOAuth2Mixin):
@tornado.gen.coroutine
Expand Down Expand Up @@ -489,6 +485,131 @@ def get(self):
self.finish()


class ProfileCmdUpdatePasswordHandler(jsonhandler.JsonHandler):
@tornado.web.asynchronous
def post(self):
if self._global_arg["disable_login"]:
# Not Found
self.set_status(404)
self.send_error(404)
raise tornado.web.Finish()

# Be sure the user is connected
current_user = self.get_current_user()
if not current_user:
print("Cannot send user command if not connect. %s" % self.request.remote_ip, file=sys.stderr)
# Forbidden
self.set_status(403)
self.send_error(403)
raise tornado.web.Finish()
self.prepare_json()

# Validate password is not empty
old_password = self.get_argument("old_password")
new_password = self.get_argument("new_password")
if not old_password or not new_password:
print("Password is empty from %s" % self.request.remote_ip, file=sys.stderr)
data = {"error": "Password is empty."}
self.write(data)
self.finish()
return

# Validate old_password is good before update with the new_password
success_password = self._db.compare_password(old_password, self.current_user.get("password"))
if not success_password:
print("Wrong password from ip %s." % self.request.remote_ip)
data = {"error": "Wrong password."}
self.write(data)
self.finish()
return

# Validate the password is a new one
success_password = self._db.compare_password(new_password, self.current_user.get("password"))
if success_password:
print("Same password from ip %s." % self.request.remote_ip)
data = {"status": "Same password."}
self.write(data)
self.finish()
return

# Update password
current_user["password"] = self._db.generate_password(new_password)
self._db.update_user(current_user)

# TODO Need to validate insertion
data = {"status": "Password updated."}
self.write(data)
self.finish()


class ProfileCmdAddNewPasswordHandler(jsonhandler.JsonHandler):
@tornado.web.asynchronous
def post(self):
if self._global_arg["disable_login"]:
# Not Found
self.set_status(404)
self.send_error(404)
raise tornado.web.Finish()

# Be sure the user is connected
current_user = self.get_current_user()
if not current_user:
print("Cannot send user command if not connect. %s" % self.request.remote_ip, file=sys.stderr)
# Forbidden
self.set_status(403)
self.send_error(403)
raise tornado.web.Finish()

# Validate if can add a new password
if current_user["password"]:
# Already contain a password
print("User password is not empty from %s" % self.request.remote_ip, file=sys.stderr)
data = {"error": "User password is not empty."}
self.write(data)
self.finish()
return

self.prepare_json()

# Validate password is not empty
password = self.get_argument("password")
if not password:
print("Password is empty from %s" % self.request.remote_ip, file=sys.stderr)
data = {"error": "Password is empty."}
self.write(data)
self.finish()
return

# Update password
current_user["password"] = self._db.generate_password(password)
self._db.update_user(current_user)

# TODO Need to validate insertion
data = {"status": "Password added."}
self.write(data)
self.finish()


class ProfileCmdInfoHandler(jsonhandler.JsonHandler):
@tornado.web.asynchronous
@tornado.web.authenticated
def get(self):
# TODO not sure it's secure
user = self.current_user
return_user = {
"email": user.get("email"),
"name": user.get("name"),
"password": user.get("password"),
"user_id": user.get("user_id"),
"google_id": user.get("google_id"),
"facebook_id": user.get("facebook_id"),
"twitter_id": user.get("twitter_id"),
"permission": user.get("permission"),
}
self.write(return_user)
self.finish()


class StatSeasonPass(jsonhandler.JsonHandler):
@tornado.web.asynchronous
def get(self):
Expand Down
3 changes: 2 additions & 1 deletion src/web/partials/_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
<li><a href="/profile/">{{current_user.get("name")}}</a></li>
<li><a href="/logout">Déconnexion</a></li>
{% elif not disable_login %}
<li ng-class="{ active: isActive('/login') }"><a href="/login">Connexion ou Inscription</a></li>
<li ng-class="{ active: isActive('/login') }"><a href="/login">Connexion</a></li>
{% end %}
</ul>

Expand Down Expand Up @@ -188,6 +188,7 @@ <h1 class="detect_javascript_enable">
<script src="{{ static_url('resources/js/tl_module/page_ctrl/page_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/manual_ctrl/manual_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/lore_ctrl/lore_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/profile_ctrl/profile_ctrl.js') }}"></script>

<script src="{{ static_url('bower_components/qrcode-generator/js/qrcode.js') }}"></script>
<script src="{{ static_url('bower_components/qrcode-generator/js/qrcode_UTF8.js') }}"></script>
Expand Down
2 changes: 1 addition & 1 deletion src/web/partials/admin/_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
<li><a href="/profile/">{{current_user.get("name")}}</a></li>
<li><a href="/logout">Déconnexion</a></li>
{% elif not disable_login %}
<li ng-class="{ active: isActive('/login') }"><a href="/login">Connexion ou Inscription</a></li>
<li ng-class="{ active: isActive('/login') }"><a href="/login">Connexion</a></li>
{% end %}
</ul>

Expand Down
8 changes: 6 additions & 2 deletions src/web/partials/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ <h3 class="omb_authTitle"><b>Connexion</b> ou <a href="#" ng-click="show_login=f

<!-- TODO need to be binded in angularjs model -->
<button class="btn btn-lg btn-primary btn-block" type="submit"
onclick="if(username_or_email.value && loginForm.password.value){password.value=hashSha256(password.value, username_or_email.value);};">
onclick="if(username_or_email.value && loginForm.password.value) {
password.value=hashSha256(password.value);
};">
Se connecter
</button>
</form>
Expand Down Expand Up @@ -161,7 +163,9 @@ <h3 class="omb_authTitle"><a href="#" ng-click="show_login=true;" style="text-de
<br/>

<button class="btn btn-lg btn-primary btn-block" type="submit" ng-disabled="signUpForm.$invalid || signUpForm.$pending"
onclick="var tempPW=password.value; password.value=hashSha256(tempPW, username.value);pwconfirm.value=hashSha256(tempPW, email.value);">Créer un compte
onclick="password.value=hashSha256(password.value);
// TODO empty pwconfirm
pwconfirm.value=password.value;">Créer un compte
</button>
</form>
</div>
Expand Down
27 changes: 23 additions & 4 deletions src/web/partials/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,30 @@
{% block content %}

{% if user %}
<div>Profil de {{user.get("name")}}. (ID: {{ user.get("user_id") }})</div>
</br>
<a href="/character?id_player={{ user.get("user_id") }}" class="btn btn-lg btn-default" role="button">Personnage</a>
<div ng-controller="profile_ctrl" ng-cloak>
<h1>Profil de {{! model_profile.info.name }}</h1>
<h2>Fiche de personnage</h2>
<a href="/character?id_player={{! model_profile.info.user_id }}" class="btn btn-lg btn-default" role="button">Accéder à sa fiche</a>

<h2>Information personnel</h2>
<p>Nom: {{! model_profile.info.name }}</p>
<p>Email: {{! model_profile.info.email }}</p>
<p>ID: {{! model_profile.info.user_id }}</p>
<div ng-show="model_profile.info && model_profile.info.password">
Modifier son mot de passe.<br/>
<input type="password" ng-model="dct_profile_password.old_password" placeholder="Vieux password." required>
<input type="password" ng-model="dct_profile_password.new_password" placeholder="Nouveau password." required>
<a ng-click="save_password()" class="btn btn-lg btn-default" role="button">Sauvegarder</a>
</div>
<div ng-show="model_profile.info && !model_profile.info.password">
Ajouter un mot de passe.<br/>
<input type="password" ng-model="dct_profile_new_password.password" placeholder="Mot de passe." required>
<input type="password" ng-model="dct_profile_new_password.check_password" placeholder="Confirmation du mot de passe." required>
<a ng-click="add_new_password()" class="btn btn-lg btn-default" role="button">Ajouter le mot de passe.</a>
</div>
</div>
{% else %}
<div class="alert alert-warning">Veuillez sélectionner un profil.</div>
<div class="alert alert-warning">Profil inexistant, veuillez-vous créer un <a href="/login" class="btn btn-lg btn-default" role="button">compte</a>.</div>
{% end %}

{% end %}
43 changes: 21 additions & 22 deletions src/web/py_class/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,20 @@ def __init__(self, parser):

self._query_user = tinydb.Query()

def create_user(self, name, email=None, password_name=None, password_mail=None, google_id=None, facebook_id=None,
twitter_id=None, permission="Joueur"):
@staticmethod
def generate_password(password):
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')

@staticmethod
def compare_password(user_password, hash_password):
if not user_password or not hash_password:
return False
return bcrypt.checkpw(user_password.encode('utf-8'), hash_password.encode('utf-8'))

def create_user(self, name, email=None, password=None, google_id=None, facebook_id=None, twitter_id=None,
permission="Joueur"):

# Validate no duplicate user
if self._db_user.contains(self._query_user.name == name):
print("Cannot create user %s, already exist." % name, file=sys.stderr)
return
Expand All @@ -38,18 +50,10 @@ def create_user(self, name, email=None, password_name=None, password_mail=None,
while self._db_user.contains(self._query_user.user_id == user_id):
user_id = uuid.uuid4().hex

if password_name:
secure_pass_name = bcrypt.hashpw(password_name.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
else:
secure_pass_name = None
if password_mail:
secure_pass_mail = bcrypt.hashpw(password_mail.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
else:
secure_pass_mail = None
secure_pass = self.generate_password(password) if password else None

data = {"email": email, "name": name, "password_name": secure_pass_name, "password_mail": secure_pass_mail,
"user_id": user_id, "google_id": google_id, "facebook_id": facebook_id, "twitter_id": twitter_id,
"permission": permission}
data = {"email": email, "name": name, "password": secure_pass, "user_id": user_id, "google_id": google_id,
"facebook_id": facebook_id, "twitter_id": twitter_id, "permission": permission}

eid = self._db_user.insert(data)
return self._db_user.get(eid=eid)
Expand All @@ -67,8 +71,8 @@ def get_user(self, name=None, email=None, password=None, id_type="user", user_id
_user = self._db_user.get(self._query_user.name == name)
if _user:
# Validate password
ddb_password = _user.get("password_name")
if password and ddb_password and bcrypt.checkpw(password.encode('utf-8'), ddb_password.encode('utf-8')):
ddb_password = _user.get("password")
if password and ddb_password and self.compare_password(password, ddb_password):
return _user

# If no name provided, lookup user by email
Expand All @@ -77,9 +81,8 @@ def get_user(self, name=None, email=None, password=None, id_type="user", user_id
if _user:
if not force_email_no_password:
# Validate password
ddb_password = _user.get("password_mail")
if password and ddb_password and bcrypt.checkpw(password.encode('utf-8'),
ddb_password.encode('utf-8')):
ddb_password = _user.get("password")
if password and ddb_password and self.compare_password(password, ddb_password):
return _user
else:
return _user
Expand Down Expand Up @@ -107,10 +110,6 @@ def get_user(self, name=None, email=None, password=None, id_type="user", user_id
# print("Missing user name, email or id to get user.", file=sys.stderr)
return

if not _user:
# print("User not found", file=sys.stderr)
return

def user_exist(self, email=None, user_id=None, name=None):
"""Returns True if all the arguments given are found"""
return not (email and not self._db_user.get(self._query_user.email == email)) and not (
Expand Down
Loading

0 comments on commit 784c199

Please sign in to comment.