From 4edd58f4d3a965803fb49906d538e36e7cd698d3 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Sun, 25 Mar 2018 00:12:04 -0400 Subject: [PATCH] [#83] Profile: update page and able to update password - Rename connection button --- src/web/handlers.py | 84 +++++++++++++++++++ src/web/partials/_base.html | 3 +- src/web/partials/admin/_base.html | 2 +- src/web/partials/profile.html | 19 ++++- src/web/py_class/db.py | 24 ++++-- .../js/tl_module/profile_ctrl/profile_ctrl.js | 47 +++++++++++ src/web/web.py | 4 + 7 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 src/web/resources/js/tl_module/profile_ctrl/profile_ctrl.js diff --git a/src/web/handlers.py b/src/web/handlers.py index b3259fdb..80c4884a 100644 --- a/src/web/handlers.py +++ b/src/web/handlers.py @@ -489,6 +489,90 @@ def get(self): self.finish() +class ProfileCmdPasswordHandler(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_name = self.get_argument("old_password_name") + old_password_email = self.get_argument("old_password_email") + new_password_name = self.get_argument("new_password_name") + new_password_email = self.get_argument("new_password_email") + if not (old_password_email and old_password_name) or not (new_password_email and new_password_name): + 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_name = self._db.compare_password(old_password_name, self.current_user.get("password_name")) + success_password_email = self._db.compare_password(old_password_email, self.current_user.get("password_email")) + if not (success_password_name or success_password_email): + 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_name = self._db.compare_password(new_password_name, self.current_user.get("password_name")) + success_password_email = self._db.compare_password(new_password_email, self.current_user.get("password_email")) + if success_password_name or success_password_email: + print("Same password from ip %s." % self.request.remote_ip) + data = {"status": "Same password."} + self.write(data) + self.finish() + return + + # Update password + print("Good password") + current_user["password_name"] = self._db.generate_password(new_password_name) + current_user["password_email"] = self._db.generate_password(new_password_email) + self._db.update_user(current_user) + + # TODO Need to validate insertion + data = {"status": "Password updated."} + 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_name": user.get("password_name"), + "password_email": user.get("password_email"), + "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): diff --git a/src/web/partials/_base.html b/src/web/partials/_base.html index 254a751d..b7eea911 100644 --- a/src/web/partials/_base.html +++ b/src/web/partials/_base.html @@ -95,7 +95,7 @@
  • {{current_user.get("name")}}
  • Déconnexion
  • {% elif not disable_login %} -
  • Connexion ou Inscription
  • +
  • Connexion
  • {% end %} @@ -188,6 +188,7 @@

    + diff --git a/src/web/partials/admin/_base.html b/src/web/partials/admin/_base.html index 0dfa0d7c..a0ee2d01 100644 --- a/src/web/partials/admin/_base.html +++ b/src/web/partials/admin/_base.html @@ -91,7 +91,7 @@
  • {{current_user.get("name")}}
  • Déconnexion
  • {% elif not disable_login %} -
  • Connexion ou Inscription
  • +
  • Connexion
  • {% end %} diff --git a/src/web/partials/profile.html b/src/web/partials/profile.html index bff45953..d964acba 100644 --- a/src/web/partials/profile.html +++ b/src/web/partials/profile.html @@ -3,11 +3,22 @@ {% block content %} {% if user %} -
    Profil de {{user.get("name")}}. (ID: {{ user.get("user_id") }})
    -
    -Personnage +
    +

    Profil de {{! model_profile.info.name }}

    +

    Fiche de personnage

    + Accéder à sa fiche + +

    Information personnel

    +

    Nom: {{! model_profile.info.name }}

    +

    Email: {{! model_profile.info.email }}

    +

    ID: {{! model_profile.info.user_id }}

    + Modifier son mot de passe.
    + + + Sauvegarder +
    {% else %} -
    Veuillez sélectionner un profil.
    +
    Profil inexistant, veuillez-vous créer un compte.
    {% end %} {% end %} \ No newline at end of file diff --git a/src/web/py_class/db.py b/src/web/py_class/db.py index 84613bed..a2d3d19a 100644 --- a/src/web/py_class/db.py +++ b/src/web/py_class/db.py @@ -24,6 +24,16 @@ def __init__(self, parser): self._query_user = tinydb.Query() + @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_name=None, password_mail=None, google_id=None, facebook_id=None, twitter_id=None, permission="Joueur"): if self._db_user.contains(self._query_user.name == name): @@ -39,11 +49,12 @@ def create_user(self, name, email=None, password_name=None, password_mail=None, user_id = uuid.uuid4().hex if password_name: - secure_pass_name = bcrypt.hashpw(password_name.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') + secure_pass_name = self.generate_password(password_name) else: secure_pass_name = None + if password_mail: - secure_pass_mail = bcrypt.hashpw(password_mail.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') + secure_pass_mail = self.generate_password(password_mail) else: secure_pass_mail = None @@ -68,7 +79,7 @@ def get_user(self, name=None, email=None, password=None, id_type="user", user_id 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')): + if password and ddb_password and self.compare_password(password, ddb_password): return _user # If no name provided, lookup user by email @@ -78,8 +89,7 @@ def get_user(self, name=None, email=None, password=None, id_type="user", user_id 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')): + if password and ddb_password and self.compare_password(password, ddb_password): return _user else: return _user @@ -107,10 +117,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 ( diff --git a/src/web/resources/js/tl_module/profile_ctrl/profile_ctrl.js b/src/web/resources/js/tl_module/profile_ctrl/profile_ctrl.js new file mode 100644 index 00000000..88027221 --- /dev/null +++ b/src/web/resources/js/tl_module/profile_ctrl/profile_ctrl.js @@ -0,0 +1,47 @@ +// Formulaire de Traitre-Lame +"use strict"; + +characterApp.controller("profile_ctrl", ["$scope", "$q", "$http", "$window", /*"$timeout",*/ function ($scope, $q, $http, $window) { + $scope.model_profile = { + info: {} + }; + + $scope.dct_profile_password = { + "old_password": "", + "new_password": "" + }; + + // Get profile info + $http({ + method: "get", + url: "/cmd/profile/get_info", + headers: {"Content-Type": "application/json; charset=UTF-8"}, + timeout: 5000 + }).then(function (response/*, status, headers, config*/) { + $scope.model_profile.info = response.data; + }); + + + $scope.save_password = function (e) { + if ($scope.dct_profile_password.old_password != "" && $scope.dct_profile_password.new_password) { + var data = { + "old_password_name": hashSha256($scope.dct_profile_password.old_password, $scope.model_profile.info.name), + "old_password_email": hashSha256($scope.dct_profile_password.old_password, $scope.model_profile.info.email), + "new_password_name": hashSha256($scope.dct_profile_password.new_password, $scope.model_profile.info.name), + "new_password_email": hashSha256($scope.dct_profile_password.new_password, $scope.model_profile.info.email) + }; + // send command to server + $http({ + method: "post", + url: "/cmd/profile/update_password", + headers: {"Content-Type": "application/json; charset=UTF-8"}, + data: data, + timeout: 5000 + }).then(function (response/*, status, headers, config*/) { + console.info(response.data); + $scope.dct_profile_password.old_password = ""; + $scope.dct_profile_password.new_password = ""; + }); + } + }; +}]); diff --git a/src/web/web.py b/src/web/web.py index 47c9d68c..6017d214 100644 --- a/src/web/web.py +++ b/src/web/web.py @@ -119,6 +119,10 @@ def main(parse_arg): tornado.web.url(r"/cmd/lore/?", handlers.LoreHandler, name='cmd_lore', kwargs=settings), tornado.web.url(r"/cmd/stat/total_season_pass/?", handlers.StatSeasonPass, name='cmd_stat_total_season_pass', kwargs=settings), + tornado.web.url(r"/cmd/profile/update_password/?", handlers.ProfileCmdPasswordHandler, + name='cmd_profile_update_password', kwargs=settings), + tornado.web.url(r"/cmd/profile/get_info/?", handlers.ProfileCmdInfoHandler, + name='cmd_profile_get_info', kwargs=settings), # auto ssl tornado.web.url(r"/.well-known/acme-challenge.*", handlers.AutoSSLHandler, name="auto_ssl")