From f87d7b9e5d18d52df3df6ddb203f116ab7d31c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Gr=C3=B6ger?= Date: Sun, 28 Jun 2020 12:31:58 +0200 Subject: [PATCH] =?UTF-8?q?passwordreset=20hinzugef=C3=BCgt=20und=20gitign?= =?UTF-8?q?ore=20verbessert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + geruecht/config.yml.example | 19 +++++++++ geruecht/configparser.py | 15 +++++++ geruecht/controller/emailController.py | 21 ++++++++-- .../controller/mainController/__init__.py | 3 +- .../mainController/mainPasswordReset.py | 39 +++++++++++++++++++ geruecht/routes.py | 16 ++++++++ 7 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 geruecht/config.yml.example create mode 100644 geruecht/controller/mainController/mainPasswordReset.py diff --git a/.gitignore b/.gitignore index 24662c6..f48f2f7 100644 --- a/.gitignore +++ b/.gitignore @@ -124,3 +124,4 @@ dmypy.json # custom test_pricelist/ test_project/ +geruecht.config.yml diff --git a/geruecht/config.yml.example b/geruecht/config.yml.example new file mode 100644 index 0000000..06f3e71 --- /dev/null +++ b/geruecht/config.yml.example @@ -0,0 +1,19 @@ +AccessTokenLifeTime: 1800 +Database: + URL: + user: + passwd: + database: +LDAP: + URL: + dn: + USER_DN: + ADMIN_DN: + ADMIN_SECRET: +Mail: + URL: + port: + user: + passwd: + email: + crypt: SSL/STARTLS \ No newline at end of file diff --git a/geruecht/configparser.py b/geruecht/configparser.py index 2106e93..b2835c7 100644 --- a/geruecht/configparser.py +++ b/geruecht/configparser.py @@ -42,6 +42,21 @@ class ConifgParser(): DEBUG.info( 'No Config for port in LDAP found. Set it to default: {}'.format(389)) self.config['LDAP']['port'] = 389 + if 'ADMIN_DN' not in self.config['LDAP']: + DEBUG.info( + 'No Config for ADMIN_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) + ) + self.config['LDAP']['ADMIN_DN'] = None + if 'ADMIN_SECRET' not in self.config['LDAP']: + DEBUG.info( + 'No Config for ADMIN_SECRET in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) + ) + self.config['LDAP']['ADMIN_SECRET'] = None + if 'USER_DN' not in self.config['LDAP']: + DEBUG.info( + 'No Config for USER_DN in LDAP found. Set it to default {}. (Maybe Password reset not working)'.format(None) + ) + self.config['LDAP']['USER_DN'] = None self.ldap = self.config['LDAP'] DEBUG.info("Set LDAPconfig: {}".format(self.ldap)) if 'AccessTokenLifeTime' in self.config: diff --git a/geruecht/controller/emailController.py b/geruecht/controller/emailController.py index b49b0e6..067a34d 100644 --- a/geruecht/controller/emailController.py +++ b/geruecht/controller/emailController.py @@ -44,7 +44,7 @@ class EmailController(): subject = 'Dienstanfrage am {}'.format(date) text = MIMEText( "Hallo {} {},\n" - "{} fragt, ob du am {} den Dienst {} übernehmen willst.\nBeantworte die Anfrage im Userportal von Flaschengeist.".format(user.firstname, user.lastname, from_user, date, job_kind['name']), 'utf-8') + "{} fragt, ob du am {} den Dienst {} übernehmen willst.\nBeantworte die Anfrage im Userportal von Flaschengeist.".format(user.firstname, user.lastname, from_user, date, job_kind['name']), 'plain') debug.debug("subject is {{ {} }}, text is {{ {} }}".format(subject, text.as_string())) return (subject, text) @@ -55,7 +55,7 @@ class EmailController(): subject = 'Diensteinladung am {}'.format(date) text = MIMEText( "Hallo {} {},\n" - "{} fragt, ob du am {} mit Dienst haben willst.\nBeantworte die Anfrage im Userportal von Flaschengeist.".format(user.firstname, user.lastname, from_user, date), 'utf-8') + "{} fragt, ob du am {} mit Dienst haben willst.\nBeantworte die Anfrage im Userportal von Flaschengeist.".format(user.firstname, user.lastname, from_user, date), 'plain') debug.debug("subject is {{ {} }}, text is {{ {} }}".format(subject, text.as_string())) return (subject, text) @@ -71,11 +71,22 @@ class EmailController(): add = '' text = MIMEText( "Hallo {} {},\nDu hast {} im Wert von {:.2f} €. {}\n\nDiese Nachricht wurde automatisch erstellt.".format( - user.firstname, user.lastname, type, abs(sum) / 100, add), 'plain', 'utf-8') + user.firstname, user.lastname, type, abs(sum) / 100, add), 'plain') debug.debug("subject is {{ {} }}, text is {{ {} }}".format(subject, text.as_string())) return (subject, text) - def sendMail(self, user, type='credit', jobtransact=None): + def passwordReset(self, user, data): + debug.info("create email passwort reset for user {{ {} }}".format(user)) + subject = Header("Password vergessen") + text = MIMEText( + "Hallo {} {},\nDu hast dein Password vergessen!\nDies wurde nun mit Flaschengeist zurückgesetzt.\nDein neues Passwort lautet:\n{}\n\nBitte ändere es sofort in deinem Flaschengeistprolif in https://flaschengeist.wu5.de.".format( + user.firstname, user.lastname, data['password'] + ), 'plain' + ) + debug.debug("subject is {{ {} }}, text is {{ {} }}".format(subject, text.as_string())) + return (subject, text) + + def sendMail(self, user, type='credit', jobtransact=None, **kwargs): debug.info("send email to user {{ {} }}".format(user)) try: if user.mail == 'None' or not user.mail: @@ -91,6 +102,8 @@ class EmailController(): subject, text = self.jobTransact(user, jobtransact) elif type == 'jobinvite': subject, text = self.jobInvite(user, jobtransact) + elif type == 'passwordReset': + subject, text = self.passwordReset(user, kwargs) else: raise Exception("Fail to send Email. No type is set. user={}, type={} , jobtransact={}".format(user, type, jobtransact)) diff --git a/geruecht/controller/mainController/__init__.py b/geruecht/controller/mainController/__init__.py index 602f6ab..ae3f73a 100644 --- a/geruecht/controller/mainController/__init__.py +++ b/geruecht/controller/mainController/__init__.py @@ -5,7 +5,7 @@ import geruecht.controller.emailController as ec from geruecht.model.user import User from datetime import datetime, timedelta from geruecht.logger import getDebugLogger -from ..mainController import mainJobKindController, mainCreditListController, mainPricelistController, mainUserController, mainWorkerController, mainWorkgroupController, mainJobInviteController, mainJobRequestController, mainRegistrationController +from ..mainController import mainJobKindController, mainCreditListController, mainPricelistController, mainUserController, mainWorkerController, mainWorkgroupController, mainJobInviteController, mainJobRequestController, mainRegistrationController, mainPasswordReset db = dc.DatabaseController() ldap = lc.LDAPController() @@ -23,6 +23,7 @@ class MainController(mainJobKindController.Base, mainJobInviteController.Base, mainJobRequestController.Base, mainRegistrationController.Base, + mainPasswordReset.Base, metaclass=Singleton): def __init__(self): diff --git a/geruecht/controller/mainController/mainPasswordReset.py b/geruecht/controller/mainController/mainPasswordReset.py new file mode 100644 index 0000000..f82b6a4 --- /dev/null +++ b/geruecht/controller/mainController/mainPasswordReset.py @@ -0,0 +1,39 @@ +from geruecht import ldap, ldapConfig, getDebugLogger +import geruecht.controller.emailController as ec +from ldap3.utils.hashed import hashed +from ldap3 import HASHED_SALTED_MD5, MODIFY_REPLACE +import string +import random + +emailController = ec.EmailController() +debug = getDebugLogger() + +def randomString(stringLength=8): + letters = string.ascii_letters + string.digits + return ''.join(random.choice(letters) for i in range(stringLength)) + +class Base: + def resetPassword(self, data): + debug.info("forgot password {{ {} }}".format(data)) + adminConn = ldap.connect(ldapConfig['ADMIN_DN'], ldapConfig['ADMIN_SECRET']) + if 'username' in data: + search = 'uid={}'.format(data['username'].lower()) + elif 'mail' in data: + search = 'mail={}'.format(data['mail'].lower()) + else: + debug.error("username or mail not set") + raise Exception('username or mail not set') + adminConn.search(ldapConfig['USER_DN'], '(&(objectClass=person)({}))'.format(search), + attributes=['cn', 'sn', 'givenName', 'uid', 'mail']) + for user in adminConn.response: + user_dn = user['dn'] + uid = user['attributes']['uid'][0] + mail = user['attributes']['mail'][0] + mody = {} + password = randomString() + salted_password = hashed(HASHED_SALTED_MD5, password) + mody['userPassword'] = [(MODIFY_REPLACE, [salted_password])] + debug.info("reset password for {{ {} }}".format(user_dn)) + adminConn.modify(user_dn, mody) + emailController.sendMail(self.getUser(uid), type='passwordReset', password=password) + return mail \ No newline at end of file diff --git a/geruecht/routes.py b/geruecht/routes.py index c8f7810..298b36d 100644 --- a/geruecht/routes.py +++ b/geruecht/routes.py @@ -144,6 +144,22 @@ def _saveLifeTime(**kwargs): "exception in save lifetime for accesstoken.", exc_info=True) return jsonify({"error": str(err)}), 500 +@app.route("/passwordReset", methods=['POST']) +def _passwordReset(): + try: + debug.info('password reset') + data = request.get_json() + mail = mainController.resetPassword(data) + index = mail.find('@') + for i in range(index): + if i == 0: + continue + mail = mail.replace(mail[i], "*", 1) + return jsonify({"ok": "ok", "mail": mail}) + except Exception as err: + debug.warning("excetpion in password reset", exc_info=True) + return jsonify({"error": str(err)}), 409 + @app.route("/logout", methods=['GET']) @login_required(groups=[MONEY, GASTRO, VORSTAND, EXTERN, USER], bar=True) def _logout(**kwargs):