diff --git a/uffd/default_config.cfg b/uffd/default_config.cfg index f6fd34521cc2e7e041e1a7aacd2dfbe88bb30c24..58b850123492995039bf5aca899879b6f8020b5a 100644 --- a/uffd/default_config.cfg +++ b/uffd/default_config.cfg @@ -12,3 +12,10 @@ ACL_LDAP_GROUP_USEREDIT="admins" ACL_ADMIN_GROUP="admin" ACL_SELFSERVICE_GROUP="user" + +MAIL_SERVER='smtp.gmail.com' +MAIL_PORT=465 +MAIL_USERNAME='yourId@gmail.com' +MAIL_PASSWORD='*****' +MAIL_USE_STARTTLS=True +MAIL_FROM_ADDRESS='foo@bar.com' diff --git a/uffd/selfservice/templates/forgot_password.html b/uffd/selfservice/templates/forgot_password.html new file mode 100644 index 0000000000000000000000000000000000000000..673eec16e1f368c895b5c798af94b8290cc00bcf --- /dev/null +++ b/uffd/selfservice/templates/forgot_password.html @@ -0,0 +1,27 @@ +{% extends 'base.html' %} + +{% block body %} +<form action="{{ url_for(".self_forgot_password") }}" method="POST"> +<div class="row mt-2 justify-content-center"> + <div class="col-lg-6 col-md-10" style="background: #f7f7f7; box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); padding: 30px;"> + <div class="text-center"> + <img src="{{ url_for("static", filename="chaosknoten.png") }}" class="col-lg-8 col-md-12" > + </div> + <div class="col-12"> + <h2 class="text-center">Forgot password</h2> + </div> + <div class="form-group col-12"> + <label for="user-loginname">login name</label> + <input type="text" class="form-control" id="user-loginname" name="loginname" required="required" tabindex = "1"> + </div> + <div class="form-group col-12"> + <label for="user-mail">mail address</label> + <input type="text" class="form-control" id="user-mail" name="mail" required="required" tabindex = "2"> + </div> + <div class="form-group col-12"> + <button type="submit" class="btn btn-primary btn-block" tabindex = "3">Send password reset mail</button> + </div> + </div> +</div> +</form> +{% endblock %} diff --git a/uffd/selfservice/templates/passwordreset.mail.txt b/uffd/selfservice/templates/passwordreset.mail.txt new file mode 100644 index 0000000000000000000000000000000000000000..9560ff86ed72d01120d5f160a1480a71648b77e6 --- /dev/null +++ b/uffd/selfservice/templates/passwordreset.mail.txt @@ -0,0 +1,10 @@ +Hi {{ user.displayname }}, + +you have requested a password reset. +To reset your password, visit this url: {{ url_for('.self_token_password', token=token, _external=True) }} +**Please note this link is only valid for 48h** + +If you did not request a password reset, you do not need to do anything. + +Kind regards, +uffd diff --git a/uffd/selfservice/templates/reset_password.html b/uffd/selfservice/templates/set_password.html similarity index 100% rename from uffd/selfservice/templates/reset_password.html rename to uffd/selfservice/templates/set_password.html diff --git a/uffd/selfservice/views.py b/uffd/selfservice/views.py index 9d745347764194e3647e5bcaff65885963b30664..ff823e40f4a764529d15a27136f27b73e2a593a3 100644 --- a/uffd/selfservice/views.py +++ b/uffd/selfservice/views.py @@ -1,5 +1,8 @@ import datetime +import smtplib +from email.message import EmailMessage + from flask import Blueprint, render_template, request, url_for, redirect, flash, current_app from uffd.navbar import register_navbar @@ -23,9 +26,22 @@ def self_index(): @login_required() def self_update(): # TODO: actualy update the user... - send_passwordreset('uffdtest') return 'OK', 200 +@bp.route("/passwordreset", methods=(['GET', 'POST'])) +@csrf_protect(blueprint=bp) +def self_forgot_password(): + if request.method == 'GET': + return render_template('forgot_password.html') + + loginname = request.values['loginname'] + mail = request.values['mail'] + flash("We sent a mail to this users mail address if you entered the correct mail and login name combination") + user = User.from_ldap_dn(loginname_to_dn(loginname)) + if user.mail == mail: + send_passwordreset(loginname) + return redirect(url_for('session.login')) + @bp.route("/token/password/<token>", methods=(['POST', 'GET'])) def self_token_password(token): session = db.session @@ -38,16 +54,16 @@ def self_token_password(token): return redirect(url_for('session.login')) if not 'loginname' in request.values: flash('Please set a new password.') - return render_template('reset_password.html', token=token) + return render_template('set_password.html', token=token) else: if not request.values['loginname'] == dbtoken.loginname: - flash('That is not the correct login name. Please start the password reset process again') + flash('That is not the correct login name for this token. Your token is now invalide. Please start the password reset process again') session.delete(dbtoken) session.commit() return redirect(url_for('session.login')) if not request.values['password1']: flash('Please specify a new password.') - return render_template('reset_password.html', token=token) + return render_template('set_password.html', token=token) user = User.from_ldap_dn(loginname_to_dn(dbtoken.loginname)) user.set_password(request.values['password1']) user.to_ldap() @@ -56,8 +72,6 @@ def self_token_password(token): session.commit() return redirect(url_for('session.login')) - - def send_passwordreset(loginname): session = db.session expired_tokens = PasswordToken.query.filter(PasswordToken.created < (datetime.datetime.now() - datetime.timedelta(days=2))).all() @@ -66,5 +80,21 @@ def send_passwordreset(loginname): token = PasswordToken() token.loginname = loginname session.add(token) - # TODO: send mail session.commit() + + user = User.from_ldap_dn(loginname_to_dn(loginname)) + + msg = EmailMessage() + msg.set_content(render_template('passwordreset.mail.txt', user=user, token=token.token)) + msg['Subject'] = 'Password reset' + send_mail(user.mail, msg) + +def send_mail(to, msg): + server = smtplib.SMTP(host=current_app.config['MAIL_SERVER'], port=current_app.config['MAIL_PORT']) + if current_app.config['MAIL_USE_STARTTLS']: + server.starttls() + server.login(current_app.config['MAIL_USERNAME'], current_app.config['MAIL_PASSWORD']) + msg['From'] = current_app.config['MAIL_FROM_ADDRESS'] + msg['To'] = to + server.send_message(msg) + server.quit() diff --git a/uffd/session/templates/login.html b/uffd/session/templates/login.html index 3c36bc305586d73ba95d30aa67718776f5c401d3..4ca476e2161f191d0927a4ea0600b99e64a2ddb6 100644 --- a/uffd/session/templates/login.html +++ b/uffd/session/templates/login.html @@ -23,7 +23,7 @@ </div> <div class="clearfix col-12"> <a href="#" class="float-left">Register</a> - <a href="#" class="float-right">Forgot Password?</a> + <a href="{{ url_for("selfservice.self_forgot_password") }}" class="float-right">Forgot Password?</a> </div> </div> </div> diff --git a/uffd/session/views.py b/uffd/session/views.py index 269c0a49a721abf6af5bcf18535b1bab529462e9..d0a06fdd2c98321be2b7d4d9153e66d2e2856898 100644 --- a/uffd/session/views.py +++ b/uffd/session/views.py @@ -53,7 +53,6 @@ def is_valid_session(): if not user: return False if datetime.datetime.now().timestamp() > session['logintime'] + current_app.config['SESSION_LIFETIME_SECONDS']: - flash('Session timed out') return False return True bp.add_app_template_global(is_valid_session)