make a privileges column in the users table and mark the first user as an admin

This commit is contained in:
Joshua Tauberer 2014-08-08 12:31:22 +00:00
parent 880ec44a0c
commit b56f82cb92
6 changed files with 103 additions and 15 deletions

View File

@ -1,12 +1,14 @@
#!/usr/bin/python3 #!/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__) app = Flask(__name__)
import auth, utils 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() env = utils.load_environment()
@ -29,7 +31,11 @@ def index():
@app.route('/mail/users') @app.route('/mail/users')
def mail_users(): def mail_users():
return "".join(x+"\n" for x in get_mail_users(env)) 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']) @app.route('/mail/users/add', methods=['POST'])
def mail_users_add(): def mail_users_add():
@ -43,6 +49,22 @@ def mail_users_password():
def mail_users_remove(): def mail_users_remove():
return remove_mail_user(request.form.get('email', ''), env) 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') @app.route('/mail/aliases')
def mail_aliases(): def mail_aliases():
return "".join(x+"\t"+y+"\n" for x, y in get_mail_aliases(env)) return "".join(x+"\t"+y+"\n" for x, y in get_mail_aliases(env))

View File

@ -46,10 +46,16 @@ def open_database(env, with_connection=False):
else: else:
return conn, conn.cursor() return conn, conn.cursor()
def get_mail_users(env): def get_mail_users(env, as_json=False):
c = open_database(env) c = open_database(env)
c.execute('SELECT email FROM users') c.execute('SELECT email, privileges FROM users')
return [row[0] for row in c.fetchall()] 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): def get_mail_aliases(env):
c = open_database(env) c = open_database(env)
@ -122,6 +128,40 @@ def remove_mail_user(email, env):
# Update things in case any domains are removed. # Update things in case any domains are removed.
return kick(env, "mail user 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): def add_mail_alias(source, destination, env, do_kick=True):
if not validate_email(source, mode='alias'): if not validate_email(source, mode='alias'):
return ("Invalid email address.", 400) return ("Invalid email address.", 400)

View File

@ -17,7 +17,7 @@ db_path=$STORAGE_ROOT/mail/users.sqlite
# Create an empty database if it doesn't yet exist. # Create an empty database if it doesn't yet exist.
if [ ! -f $db_path ]; then if [ ! -f $db_path ]; then
echo Creating new user database: $db_path; 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; echo "CREATE TABLE aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL);" | sqlite3 $db_path;
fi fi

View File

@ -8,7 +8,7 @@
import sys, os, os.path, glob, re, shutil import sys, os, os.path, glob, re, shutil
sys.path.insert(0, 'management') 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): def migration_1(env):
# Re-arrange where we store SSL certificates. There was a typo also. # 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. # of the file will be handled by the main function.
pass 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(): def get_current_migration():
ver = 0 ver = 0
while True: while True:

View File

@ -302,17 +302,21 @@ if [ -z "`tools/mail.py user`" ]; then
EMAIL_ADDR=me@$PRIMARY_HOSTNAME EMAIL_ADDR=me@$PRIMARY_HOSTNAME
EMAIL_PW=1234 EMAIL_PW=1234
echo 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 echo
fi fi
else else
echo 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 fi
# Create the user's mail account. This will ask for a password if none was given above. # 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 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. # Create an alias to which we'll direct all automatically-created administrative aliases.
tools/mail.py alias add administrator@$PRIMARY_HOSTNAME $EMAIL_ADDR tools/mail.py alias add administrator@$PRIMARY_HOSTNAME $EMAIL_ADDR
fi fi

View File

@ -1,8 +1,8 @@
#!/usr/bin/python3 #!/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' mgmt_uri = 'http://localhost:10222'
setup_key_auth(mgmt_uri) setup_key_auth(mgmt_uri)
@ -18,7 +18,9 @@ def mgmt(cmd, data=None):
else: else:
print(e, file=sys.stderr) print(e, file=sys.stderr)
sys.exit(1) 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(): def read_password():
first = getpass.getpass('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 add user@domain.com [password]")
print(" tools/mail.py user password 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 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 (lists aliases)")
print(" tools/mail.py alias add incoming.name@domain.com sent.to@other.domain.com") 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") print(" tools/mail.py alias remove incoming.name@domain.com")
@ -55,7 +59,13 @@ if len(sys.argv) < 2:
print() print()
elif sys.argv[1] == "user" and len(sys.argv) == 2: 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"): elif sys.argv[1] == "user" and sys.argv[2] in ("add", "password"):
if len(sys.argv) < 5: 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: elif sys.argv[1] == "user" and sys.argv[2] == "remove" and len(sys.argv) == 4:
print(mgmt("/mail/users/remove", { "email": sys.argv[3] })) 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: elif sys.argv[1] == "alias" and len(sys.argv) == 2:
print(mgmt("/mail/aliases")) print(mgmt("/mail/aliases"))