# imports import requests from configparser import ConfigParser from bottle import route, run, template, error, get, \ post, request, response, redirect, \ static_file # config configfile = "app.conf" config = ConfigParser() config.read(configfile) if config['DEFAULT']['miab_admin']: miab_admin = config['DEFAULT']['miab_admin'] else: miab_admin = "norealadmin@domain.ltd" if config['DEFAULT']['miab_passwd']: miab_passwd = config['DEFAULT']['miab_passwd'] else: miab_passwd = "norealpassword" if config['DEFAULT']['miab_url']: miab_url = config['DEFAULT']['miab_url'] else: miab_url = "https://norealmiab.domain.tld" if config['DEFAULT']['app_name']: app_name = config['DEFAULT']['app_name'] else: app_name = "Accounts" if config['DEFAULT']['static_files']: static_files = config['DEFAULT']['static_files'] else: static_files = "static" if config['DEFAULT']['cookie_secret']: cookie_secret = config['DEFAULT']['cookie_secret'] else: cookie_secret = "norealsecretDTR46SNI2390LGFsnDTRLASED2309h" if config['DEFAULT']['cookie_max_age']: cookie_max_age = int(config['DEFAULT']['cookie_max_age']) else: cookie_max_age=1800 if config['DEFAULT']['cookie_name']: cookie_name = config['DEFAULT']['cookie_name'] else: cookie_name = "accounts" if config['DEFAULT']['max_aliases']: max_aliases = int(config['DEFAULT']['max_aliases']) else: max_aliases=5 # functions # verify session # if valid we return the username def logged_in(): #read remote cookie username = request.get_cookie(cookie_name, secret=cookie_secret) if username: return username else: return False # do actual authentication against miab def miab_auth(username, password): #authenticate against miab api a = requests.get(miab_url + "/mail/users", auth=(username, password)) # if valid set cookie and return True if a.text == 'You are not an administrator.\n' or a.status_code == 200: response.set_cookie(cookie_name, username, secret=cookie_secret, max_age=cookie_max_age) return True else: #or False return False # get all aliases for a username def get_aliases(username): a = requests.get(miab_url + "/mail/aliases?format=json", auth=(miab_admin, miab_passwd)) data = a.json() return [alias['address'] for user in data[1:] for alias in user['aliases'] if alias['forwards_to'] and alias['forwards_to'][0] == username] # get all addresses available on miab def get_addresses(): # get alias data a = requests.get(miab_url + "/mail/aliases?format=json", auth=(miab_admin, miab_passwd)) aliases = a.json() # get user data u = requests.get(miab_url + "/mail/users?format=json", auth=(miab_admin, miab_passwd)) users = u.json() # generate list of all addresses on the box addresses = [alias['address'] for user in aliases for alias in user['aliases']] addresses.extend([ user['email'] for domains in users for user in domains['users']]) return addresses # get all valid domains from miab def get_domains(): u = requests.get(miab_url + "/mail/users?format=json", auth=(miab_admin, miab_passwd)) users = u.json() return [ user['domain'] for user in users ] # get forwarding alias for user def get_forwards(username): a = requests.get(miab_url + "/mail/aliases?format=json", auth=(miab_admin, miab_passwd)) aliases = a.json() return [ alias['forwards_to'][0] for domains in aliases for alias in domains['aliases'] if alias['address'] == username ] # routing # render main page @get('/') def home(): username = logged_in() message = request.get_cookie(cookie_name + "_message", secret=cookie_secret) if username: aliases = get_aliases(username) forward = get_forwards(username) # render homepage return template('default', username=username, app_name=app_name, message=message, aliases=aliases, max_aliases=max_aliases, forward=forward) else: # render login message = request.get_cookie(cookie_name + "_message", secret=cookie_secret) return template('login', app_name=app_name, cookie_max_age=cookie_max_age, message=message) # get login credentials @post('/login') def post_login(): username = request.forms.get('username') password = request.forms.get('password') if miab_auth(username, password): message = { "message": "You have logged in successfully", "alert": "success" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5) redirect('/') else: message = { "message": "Email address or password wrong", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5) redirect('/') # delete cookie @get('/logout') @get('/logout/') def logout(): if logged_in(): response.delete_cookie(cookie_name) message = { "message": "You have logged out successfully", "alert": "success" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5) redirect('/') else: redirect('/') # serve static files @get('/static/') def send_static(filename): return static_file(filename, root=static_files) # change account password @post('/password') def post_password(): username = logged_in() if username: oldpassword = request.forms.get('oldpassword') newpassword = request.forms.get('newpassword') if miab_auth(username, oldpassword): data = { "email": username, } r = requests.post(miab_url + "/mail/users/remove", data=data, auth=(miab_admin, miab_passwd)) data = { "email": username, "password": newpassword } a = requests.post(miab_url + "/mail/users/add", data=data, auth=(miab_admin, miab_passwd)) if r.status_code == 200 and a.status_code == 200: message = { "message": "Your password has been changed successfully", "alert": "success" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5) redirect('/') else: message = { "message": "Something went wrong while changing your password", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5) redirect('/') else: message = { "message": "Your supplied password is wrong", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5) redirect('/') else: redirect('/') # create an email alias @post('/alias/add') def add_alias(): username = logged_in() if username: add = request.forms.get('add') aliases = get_aliases(username) #it must be below max_aliases if len(aliases) < max_aliases: #it must be in domains domains = get_domains() addresses = get_addresses() for domain in domains: if domain == add[(add.find("@")+1):]: #it must not be in addresses for address in addresses: if address == add: #match found. break message = { "message": str(add) + " does already exist", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') #create the alias data = { "address": add, "forwards_to": username } r = requests.post(miab_url + "/mail/aliases/add", data=data, auth=(miab_admin, miab_passwd)) if r.status_code == 200: message = { "message": str(add) + " has been created successfully", "alert": "success" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: message = { "message": "Something went wrong while creating " + str(add), "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') message = { "message": str(add[(add.find("@")+1):]) + " is not a valid domain", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: message = { "message": "You have reached the alias limit", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: redirect("/") # delete an email alias @post('/alias/delete') def delete_alias(): username = logged_in() if username: delete = request.forms.get('delete') aliases = get_aliases(username) for alias in aliases: if alias == delete: #remove the alias data = { "address": delete, } r = requests.post(miab_url + "/mail/aliases/remove", data=data, auth=(miab_admin, miab_passwd)) if r.status_code == 200: message = { "message": "The alias " + str(delete) + " has been removed successfully", "alert": "success" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: message = { "message": "Something went wrong while removing an alias", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') message = { "message": "You're trying to do something filthy", "alert": "danger"} response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: redirect('/') # configure email forwarding @post('/forward/add') def add_forward(): username = logged_in() forward = request.forms.get('forward') if username: if get_forwards(username): message = { "message": "Email forwarding is already configured", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: data = { "address": username, "forwards_to": forward } c = requests.post(miab_url + "/mail/aliases/add", data=data, auth=(miab_admin, miab_passwd)) if c.status_code == 200: message = { "message": "Email forwarding to " + str(forward) + " has been configured successfully", "alert": "success" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: message = { "message": "Something went wrong while configuring email forwarding", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: redirect('/') # deconfigure email forwarding @post('/forward/delete') def delete_forward(): username = logged_in() forward = request.forms.get('forward') if username: if forward == get_forwards(username)[0]: data = { "address": username, } d = requests.post(miab_url + "/mail/aliases/remove", data=data, auth=(miab_admin, miab_passwd)) if d.status_code == 200: message = { "message": "Email forwarding has been deconfigured successfully", "alert": "success" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: message = { "message": "Something went wrong while deconfiguring email forwarding", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: message = { "message": "Your delete request does not match the configured forwarding", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: redirect('/') @post('/delete') def delete_account(): username = logged_in() password = request.forms.get('password') if username: if miab_auth(username, password): data = { "email": username, } d = requests.post(miab_url + "/mail/users/remove", data=data, auth=(miab_admin, miab_passwd)) if d.status_code == 200: message = { "message": "Your account has been deleted successfully", "alert": "success" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") response.delete_cookie(cookie_name) redirect('/') else: message = { "message": "Something went wrong deleting your account", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: message = { "message": "Your supplied password is wrong", "alert": "danger" } response.set_cookie(cookie_name + "_message", message, secret=cookie_secret, max_age=5, path="/") redirect('/') else: redirect('/') # run development webserver #run(host='localhost', port=8000, debug=True, reloader=True) # run prod server run(host='0.0.0.0', port=8000, debug=True)