make a privileges column in the users table and mark the first user as an admin
This commit is contained in:
parent
880ec44a0c
commit
b56f82cb92
6 changed files with 103 additions and 15 deletions
|
@ -1,12 +1,14 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import os, os.path, re
|
||||
import os, os.path, re, json
|
||||
|
||||
from flask import Flask, request, render_template, abort
|
||||
from flask import Flask, request, render_template, abort, Response
|
||||
app = Flask(__name__)
|
||||
|
||||
import auth, utils
|
||||
from mailconfig import get_mail_users, add_mail_user, set_mail_password, remove_mail_user, get_mail_aliases, get_mail_domains, add_mail_alias, remove_mail_alias
|
||||
from mailconfig import get_mail_users, add_mail_user, set_mail_password, remove_mail_user
|
||||
from mailconfig import get_mail_user_privileges, add_remove_mail_user_privilege
|
||||
from mailconfig import get_mail_aliases, get_mail_domains, add_mail_alias, remove_mail_alias
|
||||
|
||||
env = utils.load_environment()
|
||||
|
||||
|
@ -29,6 +31,10 @@ def index():
|
|||
|
||||
@app.route('/mail/users')
|
||||
def mail_users():
|
||||
if request.args.get("format", "") == "json":
|
||||
users = get_mail_users(env, as_json=True)
|
||||
return Response(json.dumps(users), status=200, mimetype='application/json')
|
||||
else:
|
||||
return "".join(x+"\n" for x in get_mail_users(env))
|
||||
|
||||
@app.route('/mail/users/add', methods=['POST'])
|
||||
|
@ -43,6 +49,22 @@ def mail_users_password():
|
|||
def mail_users_remove():
|
||||
return remove_mail_user(request.form.get('email', ''), env)
|
||||
|
||||
|
||||
@app.route('/mail/users/privileges')
|
||||
def mail_user_privs():
|
||||
privs = get_mail_user_privileges(request.args.get('email', ''), env)
|
||||
if isinstance(privs, tuple): return privs # error
|
||||
return "\n".join(privs)
|
||||
|
||||
@app.route('/mail/users/privileges/add', methods=['POST'])
|
||||
def mail_user_privs_add():
|
||||
return add_remove_mail_user_privilege(request.form.get('email', ''), request.form.get('privilege', ''), "add", env)
|
||||
|
||||
@app.route('/mail/users/privileges/remove', methods=['POST'])
|
||||
def mail_user_privs_remove():
|
||||
return add_remove_mail_user_privilege(request.form.get('email', ''), request.form.get('privilege', ''), "remove", env)
|
||||
|
||||
|
||||
@app.route('/mail/aliases')
|
||||
def mail_aliases():
|
||||
return "".join(x+"\t"+y+"\n" for x, y in get_mail_aliases(env))
|
||||
|
|
|
@ -46,10 +46,16 @@ def open_database(env, with_connection=False):
|
|||
else:
|
||||
return conn, conn.cursor()
|
||||
|
||||
def get_mail_users(env):
|
||||
def get_mail_users(env, as_json=False):
|
||||
c = open_database(env)
|
||||
c.execute('SELECT email FROM users')
|
||||
c.execute('SELECT email, privileges FROM users')
|
||||
if not as_json:
|
||||
return [row[0] for row in c.fetchall()]
|
||||
else:
|
||||
return [
|
||||
{ "email": row[0], "privileges": parse_privs(row[1]) }
|
||||
for row in c.fetchall()
|
||||
]
|
||||
|
||||
def get_mail_aliases(env):
|
||||
c = open_database(env)
|
||||
|
@ -122,6 +128,40 @@ def remove_mail_user(email, env):
|
|||
# Update things in case any domains are removed.
|
||||
return kick(env, "mail user removed")
|
||||
|
||||
def parse_privs(value):
|
||||
return [p for p in value.split("\n") if p.strip() != ""]
|
||||
|
||||
def get_mail_user_privileges(email, env):
|
||||
c = open_database(env)
|
||||
c.execute('SELECT privileges FROM users WHERE email=?', (email,))
|
||||
rows = c.fetchall()
|
||||
if len(rows) != 1:
|
||||
return ("That's not a user (%s)." % email, 400)
|
||||
return parse_privs(rows[0][0])
|
||||
|
||||
def add_remove_mail_user_privilege(email, priv, action, env):
|
||||
if "\n" in priv or priv.strip() == "":
|
||||
return ("That's not a valid privilege (%s)." % priv, 400)
|
||||
|
||||
privs = get_mail_user_privileges(email, env)
|
||||
if isinstance(privs, tuple): return privs # error
|
||||
|
||||
if action == "add":
|
||||
if priv not in privs:
|
||||
privs.append(priv)
|
||||
elif action == "remove":
|
||||
privs = [p for p in privs if p != priv]
|
||||
else:
|
||||
return ("Invalid action.", 400)
|
||||
|
||||
conn, c = open_database(env, with_connection=True)
|
||||
c.execute("UPDATE users SET privileges=? WHERE email=?", ("\n".join(privs), email))
|
||||
if c.rowcount != 1:
|
||||
return ("Something went wrong.", 400)
|
||||
conn.commit()
|
||||
|
||||
return "OK"
|
||||
|
||||
def add_mail_alias(source, destination, env, do_kick=True):
|
||||
if not validate_email(source, mode='alias'):
|
||||
return ("Invalid email address.", 400)
|
||||
|
|
|
@ -17,7 +17,7 @@ db_path=$STORAGE_ROOT/mail/users.sqlite
|
|||
# Create an empty database if it doesn't yet exist.
|
||||
if [ ! -f $db_path ]; then
|
||||
echo Creating new user database: $db_path;
|
||||
echo "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL UNIQUE, password TEXT NOT NULL, extra);" | sqlite3 $db_path;
|
||||
echo "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL UNIQUE, password TEXT NOT NULL, extra, privileges TEXT NOT NULL DEFAULT '');" | sqlite3 $db_path;
|
||||
echo "CREATE TABLE aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL);" | sqlite3 $db_path;
|
||||
fi
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import sys, os, os.path, glob, re, shutil
|
||||
|
||||
sys.path.insert(0, 'management')
|
||||
from utils import load_environment, save_environment, safe_domain_name
|
||||
from utils import load_environment, save_environment, shell
|
||||
|
||||
def migration_1(env):
|
||||
# Re-arrange where we store SSL certificates. There was a typo also.
|
||||
|
@ -51,6 +51,11 @@ def migration_3(env):
|
|||
# of the file will be handled by the main function.
|
||||
pass
|
||||
|
||||
def migration_4(env):
|
||||
# Add a new column to the mail users table where we can store administrative privileges.
|
||||
db = os.path.join(env["STORAGE_ROOT"], 'mail/users.sqlite')
|
||||
shell("check_call", ["sqlite3", db, "ALTER TABLE users ADD privileges TEXT NOT NULL DEFAULT ''"])
|
||||
|
||||
def get_current_migration():
|
||||
ver = 0
|
||||
while True:
|
||||
|
|
|
@ -302,17 +302,21 @@ if [ -z "`tools/mail.py user`" ]; then
|
|||
EMAIL_ADDR=me@$PRIMARY_HOSTNAME
|
||||
EMAIL_PW=1234
|
||||
echo
|
||||
echo "Creating a new mail account for $EMAIL_ADDR with password $EMAIL_PW."
|
||||
echo "Creating a new administrative mail account for $EMAIL_ADDR with password $EMAIL_PW."
|
||||
echo
|
||||
fi
|
||||
else
|
||||
echo
|
||||
echo "Okay. I'm about to set up $EMAIL_ADDR for you."
|
||||
echo "Okay. I'm about to set up $EMAIL_ADDR for you. This account will also"
|
||||
echo "have access to the box's control panel."
|
||||
fi
|
||||
|
||||
# Create the user's mail account. This will ask for a password if none was given above.
|
||||
tools/mail.py user add $EMAIL_ADDR $EMAIL_PW
|
||||
|
||||
# Make it an admin.
|
||||
hide_output tools/mail.py user make-admin $EMAIL_ADDR
|
||||
|
||||
# Create an alias to which we'll direct all automatically-created administrative aliases.
|
||||
tools/mail.py alias add administrator@$PRIMARY_HOSTNAME $EMAIL_ADDR
|
||||
fi
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import sys, getpass, urllib.request, urllib.error
|
||||
import sys, getpass, urllib.request, urllib.error, json
|
||||
|
||||
def mgmt(cmd, data=None):
|
||||
def mgmt(cmd, data=None, is_json=False):
|
||||
mgmt_uri = 'http://localhost:10222'
|
||||
|
||||
setup_key_auth(mgmt_uri)
|
||||
|
@ -18,7 +18,9 @@ def mgmt(cmd, data=None):
|
|||
else:
|
||||
print(e, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return response.read().decode('utf8')
|
||||
resp = response.read().decode('utf8')
|
||||
if is_json: resp = json.loads(resp)
|
||||
return resp
|
||||
|
||||
def read_password():
|
||||
first = getpass.getpass('password: ')
|
||||
|
@ -47,6 +49,8 @@ if len(sys.argv) < 2:
|
|||
print(" tools/mail.py user add user@domain.com [password]")
|
||||
print(" tools/mail.py user password user@domain.com [password]")
|
||||
print(" tools/mail.py user remove user@domain.com")
|
||||
print(" tools/mail.py user make-admin user@domain.com")
|
||||
print(" tools/mail.py user remove-admin user@domain.com")
|
||||
print(" tools/mail.py alias (lists aliases)")
|
||||
print(" tools/mail.py alias add incoming.name@domain.com sent.to@other.domain.com")
|
||||
print(" tools/mail.py alias remove incoming.name@domain.com")
|
||||
|
@ -55,7 +59,13 @@ if len(sys.argv) < 2:
|
|||
print()
|
||||
|
||||
elif sys.argv[1] == "user" and len(sys.argv) == 2:
|
||||
print(mgmt("/mail/users"))
|
||||
# Dump a list of users, one per line. Mark admins with an asterisk.
|
||||
users = mgmt("/mail/users?format=json", is_json=True)
|
||||
for user in users:
|
||||
print(user['email'], end='')
|
||||
if "admin" in user['privileges']:
|
||||
print("*", end='')
|
||||
print()
|
||||
|
||||
elif sys.argv[1] == "user" and sys.argv[2] in ("add", "password"):
|
||||
if len(sys.argv) < 5:
|
||||
|
@ -75,6 +85,13 @@ elif sys.argv[1] == "user" and sys.argv[2] in ("add", "password"):
|
|||
elif sys.argv[1] == "user" and sys.argv[2] == "remove" and len(sys.argv) == 4:
|
||||
print(mgmt("/mail/users/remove", { "email": sys.argv[3] }))
|
||||
|
||||
elif sys.argv[1] == "user" and sys.argv[2] in ("make-admin", "remove-admin") and len(sys.argv) == 4:
|
||||
if sys.argv[2] == "make-admin":
|
||||
action = "add"
|
||||
else:
|
||||
action = "remove"
|
||||
print(mgmt("/mail/users/privileges/" + action, { "email": sys.argv[3], "privilege": "admin" }))
|
||||
|
||||
elif sys.argv[1] == "alias" and len(sys.argv) == 2:
|
||||
print(mgmt("/mail/aliases"))
|
||||
|
||||
|
|
Loading…
Reference in a new issue