]> git.ralfj.de Git - ansible.git/blobdiff - roles/email/templates/changepw
move newmail script to dovecot tasks; add changepw script; fix quota-warning script
[ansible.git] / roles / email / templates / changepw
diff --git a/roles/email/templates/changepw b/roles/email/templates/changepw
new file mode 100644 (file)
index 0000000..96eef60
--- /dev/null
@@ -0,0 +1,102 @@
+#!/usr/bin/env python2
+from __future__ import print_function
+import os, cgi, MySQLdb, subprocess, time
+
+# settings
+DB_NAME = 'vmail'
+DB_USER = 'vmail'
+DB_PW   = '{{postfix.dovecot.mysql_password}}'
+
+# DB stuff
+db = MySQLdb.connect(user=DB_USER, passwd=DB_PW, db=DB_NAME, charset='utf8')
+
+def db_execute(query, args):
+   cursor = db.cursor(MySQLdb.cursors.DictCursor)
+   if len(args) == 1 and isinstance(args[0], dict): args = args[0] # einzelnes dict weiterreichen
+   cursor.execute(query, args)
+   return cursor
+
+def db_fetch_one_row(query, *args):
+   cursor = db_execute(query, args)
+   result = cursor.fetchone()
+   if cursor.fetchone() is not None:
+      raise Exception("Found more than one result, only one was expected")
+   cursor.close()
+   return result
+
+def db_run(query, *args):
+   cursor = db_execute(query, args)
+   cursor.close()
+
+def get_user(name):
+   return db_fetch_one_row('SELECT * FROM users WHERE username=%s', name)
+
+def change_user_pw(name, hash):
+   db_run('UPDATE users SET password=%s WHERE username=%s', hash, name)
+
+def change_user_active(name, active):
+   db_run('UPDATE users SET active=%s WHERE username=%s', int(active), name)
+
+# interaction with dbadm pw
+def compare_pw(hash, plain):
+   try:
+      subprocess.check_output(["doveadm", "pw", "-t", hash, "-p", plain])
+      return True
+   except subprocess.CalledProcessError:
+      return False
+
+def gen_hash(plain):
+   return subprocess.check_output(["doveadm", "pw", "-s", "SHA512-CRYPT", "-r", str(64*1024), "-p", plain]).decode('utf-8').strip()
+
+# the core action
+def act(user, oldpw, newpw1, newpw2):
+   if user is None and oldpw is None and newpw1 is None and newpw2 is None:
+      return
+   if user is None or oldpw is None or newpw1 is None or newpw2 is None:
+      return "<b>Error</b>: You have to fill all the fields."
+   curdata = get_user(user)
+   if curdata is None:
+      return "<b>Error</b>: User not found."
+   if len(newpw1) < 8:
+      return "<b>Error</b>: Password must be at least 8 characters long."
+   if newpw1 != newpw2:
+      return "<b>Error</b>: New passwords do not match."
+   # slow down brute-force attacks
+   time.sleep(2.5)
+   # now go on
+   if not compare_pw(curdata['password'], oldpw):
+      return "<b>Error</b>: Old PW is wrong."
+   new_hash = gen_hash(newpw1)
+   change_user_pw(user, new_hash)
+   # potentially enable this user
+   if curdata['active'] < 0:
+      change_user_active(user, -curdata['active'])
+   return "Password successfully changed."
+
+# print headers
+print("Content-Type: text/html")
+print()
+
+# print document header
+print("<!DOCTYPE html>")
+print("<html><head>")
+print("  <meta charset='utf-8'>")
+print("  <title>User PW change</title>")
+print("  <style type='text/css'> th { text-align: right; } </style>")
+print("</head><body>")
+
+# do stuff
+form = cgi.FieldStorage()
+msg = act(form.getfirst('user'), form.getfirst('oldpw'), form.getfirst('newpw1'), form.getfirst('newpw2'))
+if msg is not None:
+   print("<p>{}</p>".format(msg))
+
+# print form
+print("  <form action='changepw' method='post'><table>")
+print("    <tr><th>Username: </th><td><input type='text' name='user' placeholder='user@domain'></td>")
+print("    <tr><th>Old Password: </th><td><input type='password' name='oldpw'></td>")
+print("    <tr><th>New Password: </th><td><input type='password' name='newpw1'></td>")
+print("    <tr><th>New Password (confirmation): </th><td><input type='password' name='newpw2'></td>")
+print("    <tr><th></th><td><input type='submit' value='Change Password'></td></tr>")
+print("  </table></form>")
+print("</body></html>")