From bdf6ac7ac77c24b3f980bea41f6ac36ed17ce6cd Mon Sep 17 00:00:00 2001
From: nd <git@notandy.de>
Date: Thu, 16 Jul 2020 19:00:07 +0200
Subject: [PATCH] fully working self service

---
 .../templates/mailverification.mail.txt       | 10 +++
 uffd/selfservice/templates/self.html          |  2 +-
 uffd/selfservice/views.py                     | 63 +++++++++++++++++--
 uffd/templates/base.html                      |  2 +-
 uffd/user/models.py                           |  9 +++
 uffd/user/templates/user.html                 |  2 +-
 6 files changed, 81 insertions(+), 7 deletions(-)
 create mode 100644 uffd/selfservice/templates/mailverification.mail.txt

diff --git a/uffd/selfservice/templates/mailverification.mail.txt b/uffd/selfservice/templates/mailverification.mail.txt
new file mode 100644
index 00000000..e9ff7e31
--- /dev/null
+++ b/uffd/selfservice/templates/mailverification.mail.txt
@@ -0,0 +1,10 @@
+Hi {{ user.displayname }},
+
+you have requested to change your mail address.
+To do so, visit this url: {{ url_for('.self_token_mail', token=token, _external=True) }} 
+**Please note this link is only valid for 48h**
+
+If you did not request a mail address change, you should change your password asap because somebody else logged in and requested this.
+
+Kind regards,
+uffd
diff --git a/uffd/selfservice/templates/self.html b/uffd/selfservice/templates/self.html
index 6e89f8de..4f9756d0 100644
--- a/uffd/selfservice/templates/self.html
+++ b/uffd/selfservice/templates/self.html
@@ -25,7 +25,7 @@
 		<label for="user-password1">password</label>
 		<input type="password" class="form-control" id="user-password1" name="password1" placeholder="do not change">
 		<small class="form-text text-muted">
-			No special requirements but please don't be stupid and use a password manager.
+			At least 8 characters, no other special requirements. But please don't be stupid and use a password manager.
 		</small>
 	</div>
 	<div class="form-group col-md-6">
diff --git a/uffd/selfservice/views.py b/uffd/selfservice/views.py
index ff823e40..04908696 100644
--- a/uffd/selfservice/views.py
+++ b/uffd/selfservice/views.py
@@ -10,7 +10,7 @@ from uffd.csrf import csrf_protect
 from uffd.user.models import User, Group
 from uffd.session import get_current_user, login_required, is_valid_session
 from uffd.ldap import get_conn, escape_filter_chars, loginname_to_dn
-from uffd.selfservice.models import PasswordToken
+from uffd.selfservice.models import PasswordToken, MailToken
 from uffd.database import db
 
 bp = Blueprint("selfservice", __name__, template_folder='templates', url_prefix='/self/')
@@ -25,8 +25,25 @@ def self_index():
 @csrf_protect(blueprint=bp)
 @login_required()
 def self_update():
-	# TODO: actualy update the user...
-	return 'OK', 200
+	user = get_current_user()
+	if request.values['displayname'] != user.displayname:
+		if user.set_displayname(request.values['displayname']):
+			flash('Display name changed.')
+		else:
+			flash('Display name is not valid.')
+	if request.values['password1']:
+		if not request.values['password1'] == request.values['password2']:
+			flash('Passwords do not match')
+		else:
+			if user.set_password(request.values['password1']):
+				flash('Password changed.')
+			else:
+				flash('Password could not be set.')
+	if request.values['mail'] != user.mail:
+		send_mail_verification(user.loginname, request.values['mail'])
+		flash('We sent you an email, please verify your mail address.')
+	user.to_ldap()
+	return redirect(url_for('.self_index'))
 
 @bp.route("/passwordreset", methods=(['GET', 'POST']))
 @csrf_protect(blueprint=bp)
@@ -38,7 +55,7 @@ def self_forgot_password():
 	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:
+	if user and user.mail == mail:
 		send_passwordreset(loginname)
 	return redirect(url_for('session.login'))
 
@@ -72,6 +89,44 @@ def self_token_password(token):
 		session.commit()
 		return redirect(url_for('session.login'))
 
+@bp.route("/token/mail_verification/<token>")
+@login_required()
+def self_token_mail(token):
+	session = db.session
+	dbtoken = MailToken.query.get(token)
+	if not dbtoken or dbtoken.created < (datetime.datetime.now() - datetime.timedelta(days=2)):
+		flash('Token expired, please try again.')
+		if dbtoken:
+			session.delete(dbtoken)
+			session.commit()
+		return redirect(url_for('.self_index'))
+
+	user = User.from_ldap_dn(loginname_to_dn(dbtoken.loginname))
+	user.set_mail(dbtoken.newmail)
+	user.to_ldap()
+	flash('New mail set')
+	session.delete(dbtoken)
+	session.commit()
+	return redirect(url_for('.self_index'))
+
+def send_mail_verification(loginname, newmail):
+	session = db.session
+	expired_tokens = MailToken.query.filter(MailToken.created < (datetime.datetime.now() - datetime.timedelta(days=2))).all()
+	for i in expired_tokens:
+		session.delete(i)
+	token = MailToken()
+	token.loginname = loginname
+	token.newmail = newmail
+	session.add(token)
+	session.commit()
+
+	user = User.from_ldap_dn(loginname_to_dn(loginname))
+
+	msg = EmailMessage()
+	msg.set_content(render_template('mailverification.mail.txt', user=user, token=token.token))
+	msg['Subject'] = 'Mail verification'
+	send_mail(newmail, msg)
+
 def send_passwordreset(loginname):
 	session = db.session
 	expired_tokens = PasswordToken.query.filter(PasswordToken.created < (datetime.datetime.now() - datetime.timedelta(days=2))).all()
diff --git a/uffd/templates/base.html b/uffd/templates/base.html
index bc28ec2f..7b553a8c 100644
--- a/uffd/templates/base.html
+++ b/uffd/templates/base.html
@@ -85,7 +85,7 @@
 		<div class="container mt-2">
 			<div class="row">
 				{% for message in get_flashed_messages() %}
-				<div class="alert alert-primary col" role="alert">{{ message }}</div>
+				<div class="alert alert-primary col-12" role="alert">{{ message }}</div>
 				{% endfor %}
 			</div>
 		</div>
diff --git a/uffd/user/models.py b/uffd/user/models.py
index 89918335..f00dffaa 100644
--- a/uffd/user/models.py
+++ b/uffd/user/models.py
@@ -106,7 +106,16 @@ class User():
 		return True
 
 	def set_password(self, value):
+		if len(value) < 8:
+			return False
 		self.newpassword = value
+		return True
+
+	def set_mail(self, value):
+		if len(value) < 3 or '@' not in value:
+			return False
+		self.mail = value
+		return True
 
 class Group():
 	gid = None
diff --git a/uffd/user/templates/user.html b/uffd/user/templates/user.html
index e25d37ba..0fa6163c 100644
--- a/uffd/user/templates/user.html
+++ b/uffd/user/templates/user.html
@@ -39,7 +39,7 @@
 		<input type="password" class="form-control" id="user-password" name="password" placeholder="mail to set it will be sent" readonly>
 		{% endif %}
 		<small class="form-text text-muted">
-			No special requirements but please don't be stupid and use a password manager.
+			At least 8 characters, no other special requirements. But please don't be stupid and use a password manager.
 		</small>
 	</div>
 	<div class="form-group col "id="accordion">
-- 
GitLab