1
0
mirror of https://github.com/mail-in-a-box/mailinabox.git synced 2025-04-04 00:17:06 +00:00

Display and allow chaninging a comment/description for aliases. Change the default comment for required aliases to "Required alias".

This commit is contained in:
downtownallday 2020-08-25 12:00:55 -04:00
parent 2b981db1d9
commit 22bfef6f59
7 changed files with 85 additions and 30 deletions

View File

@ -203,6 +203,7 @@ def mail_aliases():
def mail_aliases_add(): def mail_aliases_add():
return add_mail_alias( return add_mail_alias(
request.form.get('address', ''), request.form.get('address', ''),
request.form.get('description', ''),
request.form.get('forwards_to', ''), request.form.get('forwards_to', ''),
request.form.get('permitted_senders', ''), request.form.get('permitted_senders', ''),
env, env,

View File

@ -312,7 +312,8 @@ def get_mail_aliases(env, as_map=False):
# { dn: {string}, # { dn: {string},
# mail: {string} # mail: {string}
# forward_tos: {array of string}, # forward_tos: {array of string},
# permited_senders: {array of string} # permited_senders: {array of string},
# description: {string}
# } # }
# #
c = open_database(env) c = open_database(env)
@ -323,7 +324,7 @@ def get_mail_aliases(env, as_map=False):
permitted_senders = { rec["mail"][0].lower(): rec["member"] for rec in pager } permitted_senders = { rec["mail"][0].lower(): rec["member"] for rec in pager }
# get all alias groups # get all alias groups
pager = c.paged_search(env.LDAP_ALIASES_BASE, "(objectClass=mailGroup)", attributes=['mail','member','rfc822MailMember']) pager = c.paged_search(env.LDAP_ALIASES_BASE, "(objectClass=mailGroup)", attributes=['mail','member','rfc822MailMember', 'description'])
# make a dict of aliases # make a dict of aliases
# key=email(lowercase), value=(email, forward-tos, permitted-senders). # key=email(lowercase), value=(email, forward-tos, permitted-senders).
@ -352,7 +353,8 @@ def get_mail_aliases(env, as_map=False):
"dn": alias["dn"], "dn": alias["dn"],
"mail": alias_email, "mail": alias_email,
"forward_tos": forward_tos, "forward_tos": forward_tos,
"permitted_senders": allowed_senders "permitted_senders": allowed_senders,
"description": alias["description"][0]
} }
if not as_map: if not as_map:
@ -382,7 +384,8 @@ def get_mail_aliases_ex(env):
# address_display: "name@domain.tld", # full Unicode # address_display: "name@domain.tld", # full Unicode
# forwards_to: ["user1@domain.com", "receiver-only1@domain.com", ...], # forwards_to: ["user1@domain.com", "receiver-only1@domain.com", ...],
# permitted_senders: ["user1@domain.com", "sender-only1@domain.com", ...] OR null, # permitted_senders: ["user1@domain.com", "sender-only1@domain.com", ...] OR null,
# required: True|False # required: True|False,
# description: ""
# }, # },
# ... # ...
# ] # ]
@ -390,27 +393,38 @@ def get_mail_aliases_ex(env):
# ... # ...
# ] # ]
aliases=get_mail_aliases(env, as_map=True)
required_aliases = get_required_aliases(env) required_aliases = get_required_aliases(env)
domains = {} domains = {}
for address, forwards_to, permitted_senders in get_mail_aliases(env):
for alias_lc in aliases:
alias=aliases[alias_lc]
address=alias_lc
# get alias info # get alias info
forwards_to=alias["forward_tos"]
permitted_senders=alias["permitted_senders"]
description=alias["description"]
domain = get_domain(address) domain = get_domain(address)
required = (address in required_aliases) required = (address in required_aliases)
# add to list # add to list
if not domain in domains: if not domain in domains:
domains[domain] = { domains[domain] = {
"domain": domain, "domain": domain,
"aliases": [], "aliases": [],
} }
domains[domain]["aliases"].append({ domains[domain]["aliases"].append({
"address": address, "address": address,
"address_display": prettify_idn_email_address(address), "address_display": prettify_idn_email_address(address),
"forwards_to": [prettify_idn_email_address(r.strip()) for r in forwards_to.split(",")], "forwards_to": [prettify_idn_email_address(r.strip()) for r in forwards_to],
"permitted_senders": [prettify_idn_email_address(s.strip()) for s in permitted_senders.split(",")] if permitted_senders is not None else None, "permitted_senders": [prettify_idn_email_address(s.strip()) for s in permitted_senders] if permitted_senders is not None and len(permitted_senders)>0 else None,
"required": required, "required": required,
"description": description
}) })
# Sort domains. # Sort domains.
domains = [domains[domain] for domain in utils.sort_domains(domains.keys(), env)] domains = [domains[domain] for domain in utils.sort_domains(domains.keys(), env)]
@ -857,10 +871,11 @@ def convert_rfc822MailMember(env, conn, dn, mail):
pass pass
def add_mail_alias(address, forwards_to, permitted_senders, env, update_if_exists=False, do_kick=True): def add_mail_alias(address, description, forwards_to, permitted_senders, env, update_if_exists=False, do_kick=True):
# Add a new alias group with permitted senders. # Add a new alias group with permitted senders.
# #
# address: the email address of the alias # address: the email address of the alias
# description: a text description of the alias
# forwards_to: a string of newline and comma separated email address # forwards_to: a string of newline and comma separated email address
# where mail is delivered # where mail is delivered
# permitted_senders: a string of newline and comma separated email addresses of local users that are permitted to MAIL FROM the alias. # permitted_senders: a string of newline and comma separated email addresses of local users that are permitted to MAIL FROM the alias.
@ -958,20 +973,30 @@ def add_mail_alias(address, forwards_to, permitted_senders, env, update_if_exist
# save to db # save to db
conn = open_database(env) conn = open_database(env)
existing_alias, existing_permitted_senders = find_mail_alias(env, address, ['member','rfc822MailMember'], conn) existing_alias, existing_permitted_senders = find_mail_alias(env, address, ['member','rfc822MailMember', 'description'], conn)
if existing_alias and not update_if_exists: if existing_alias and not update_if_exists:
return ("Alias already exists (%s)." % address, 400) return ("Alias already exists (%s)." % address, 400)
cn="%s" % uuid.uuid4() cn="%s" % uuid.uuid4()
dn="cn=%s,%s" % (cn, env.LDAP_ALIASES_BASE) dn="cn=%s,%s" % (cn, env.LDAP_ALIASES_BASE)
if address.startswith('@') and \ if not description:
len(validated_forwards_to)==1 and \ # supply a default description for new entries that did not
validated_forwards_to[0].startswith('@'): # specify one
description = "Domain alias %s->%s" % (address, validated_forwards_to[0]) if not existing_alias:
elif address.startswith('@'): if address.startswith('@') and \
description = "Catch-all for %s" % address len(validated_forwards_to)==1 and \
else: validated_forwards_to[0].startswith('@'):
description ="Mail group %s" % address description = "Domain alias %s->%s" % (address, validated_forwards_to[0])
elif address.startswith('@'):
description = "Catch-all for %s" % address
else:
description ="Mail alias %s" % address
# when updating, ensure the description has a value because
# the ldap schema does not allow an empty field
else:
description=" "
attrs = { attrs = {
"mail": address, "mail": address,
"description": description, "description": description,
@ -980,7 +1005,7 @@ def add_mail_alias(address, forwards_to, permitted_senders, env, update_if_exist
} }
op = conn.add_or_modify(dn, existing_alias, op = conn.add_or_modify(dn, existing_alias,
['member', 'rfc822MailMember' ], ['member', 'rfc822MailMember', 'description' ],
['mailGroup'], ['mailGroup'],
attrs) attrs)
if op == 'modify': if op == 'modify':
@ -1104,7 +1129,7 @@ def kick(env, mail_result=None):
# Doesn't exist. # Doesn't exist.
administrator = get_system_administrator(env) administrator = get_system_administrator(env)
if address == administrator: return # don't make an alias from the administrator to itself --- this alias must be created manually if address == administrator: return # don't make an alias from the administrator to itself --- this alias must be created manually
add_mail_alias(address, administrator, "", env, do_kick=False) add_mail_alias(address, "Required alias", administrator, "", env, do_kick=False)
if administrator not in existing_aliases: return # don't report the alias in output if the administrator alias isn't in yet -- this is a hack to supress confusing output on initial setup if administrator not in existing_aliases: return # don't report the alias in output if the administrator alias isn't in yet -- this is a hack to supress confusing output on initial setup
results.append("added alias %s (=> %s)\n" % (address, administrator)) results.append("added alias %s (=> %s)\n" % (address, administrator))

View File

@ -35,6 +35,15 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-group">
<label for="addaliasDescription" class="col-sm-1 control-label">Comment</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="addaliasDescription">
<div style="margin-top: 3px; padding-left: 3px; font-size: 90%" class="text-muted">
An optional description of the alias
</div>
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="addaliasForwardsTo" class="col-sm-1 control-label">Forwards To</label> <label for="addaliasForwardsTo" class="col-sm-1 control-label">Forwards To</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -81,6 +90,7 @@
<th>Alias<br></th> <th>Alias<br></th>
<th>Forwards To</th> <th>Forwards To</th>
<th>Permitted Senders</th> <th>Permitted Senders</th>
<th>Comment</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -103,6 +113,7 @@
<td class='address'> </td> <td class='address'> </td>
<td class='forwardsTo'> </td> <td class='forwardsTo'> </td>
<td class='senders'> </td> <td class='senders'> </td>
<td class='description'> </td>
</tr> </tr>
</table> </table>
</div> </div>
@ -170,6 +181,7 @@ function show_aliases() {
n.find('td.forwardsTo').append($("<div></div>").text(alias.forwards_to[j])) n.find('td.forwardsTo').append($("<div></div>").text(alias.forwards_to[j]))
for (var j = 0; j < (alias.permitted_senders ? alias.permitted_senders.length : 0); j++) for (var j = 0; j < (alias.permitted_senders ? alias.permitted_senders.length : 0); j++)
n.find('td.senders').append($("<div></div>").text(alias.permitted_senders[j])) n.find('td.senders').append($("<div></div>").text(alias.permitted_senders[j]))
n.find('td.description').append($("<div></div>").text(alias.description));
$('#alias_table tbody').append(n); $('#alias_table tbody').append(n);
} }
} }
@ -208,6 +220,7 @@ var is_alias_add_update = false;
function do_add_alias() { function do_add_alias() {
var title = (!is_alias_add_update) ? "Add Alias" : "Update Alias"; var title = (!is_alias_add_update) ? "Add Alias" : "Update Alias";
var form_address = $("#addaliasAddress").val(); var form_address = $("#addaliasAddress").val();
var form_description = $("#addaliasDescription").val();
var form_forwardsto = $("#addaliasForwardsTo").val(); var form_forwardsto = $("#addaliasForwardsTo").val();
var form_senders = ($('#addaliasForwardsToAdvanced').prop('checked') ? $("#addaliasSenders").val() : ''); var form_senders = ($('#addaliasForwardsToAdvanced').prop('checked') ? $("#addaliasSenders").val() : '');
if ($('#addaliasForwardsToAdvanced').prop('checked') && !/\S/.exec($("#addaliasSenders").val())) { if ($('#addaliasForwardsToAdvanced').prop('checked') && !/\S/.exec($("#addaliasSenders").val())) {
@ -220,6 +233,7 @@ function do_add_alias() {
{ {
update_if_exists: is_alias_add_update ? '1' : '0', update_if_exists: is_alias_add_update ? '1' : '0',
address: form_address, address: form_address,
description: form_description,
forwards_to: form_forwardsto, forwards_to: form_forwardsto,
permitted_senders: form_senders permitted_senders: form_senders
}, },
@ -237,9 +251,10 @@ function do_add_alias() {
function aliases_reset_form() { function aliases_reset_form() {
$("#addaliasAddress").prop('disabled', false); $("#addaliasAddress").prop('disabled', false);
$("#addaliasAddress").val('') $("#addaliasAddress").val('');
$("#addaliasForwardsTo").val('') $("#addaliasDescription").val('');
$("#addaliasSenders").val('') $("#addaliasForwardsTo").val('');
$("#addaliasSenders").val('');
$('#alias-cancel').addClass('hidden'); $('#alias-cancel').addClass('hidden');
$('#add-alias-button').text('Add Alias'); $('#add-alias-button').text('Add Alias');
is_alias_add_update = false; is_alias_add_update = false;
@ -247,6 +262,7 @@ function aliases_reset_form() {
function aliases_edit(elem) { function aliases_edit(elem) {
var address = $(elem).parents('tr').attr('data-address'); var address = $(elem).parents('tr').attr('data-address');
var description = $(elem).parents('tr').find('.description div').text().trim();
var receiverdivs = $(elem).parents('tr').find('.forwardsTo div'); var receiverdivs = $(elem).parents('tr').find('.forwardsTo div');
var senderdivs = $(elem).parents('tr').find('.senders div'); var senderdivs = $(elem).parents('tr').find('.senders div');
var forwardsTo = ""; var forwardsTo = "";
@ -264,6 +280,7 @@ function aliases_edit(elem) {
$('#alias-cancel').removeClass('hidden'); $('#alias-cancel').removeClass('hidden');
$("#addaliasAddress").prop('disabled', true); $("#addaliasAddress").prop('disabled', true);
$("#addaliasAddress").val(address); $("#addaliasAddress").val(address);
$("#addaliasDescription").val(description);
$("#addaliasForwardsTo").val(forwardsTo); $("#addaliasForwardsTo").val(forwardsTo);
$('#addaliasForwardsToAdvanced').prop('checked', senders != ""); $('#addaliasForwardsToAdvanced').prop('checked', senders != "");
$('#addaliasForwardsToNotAdvanced').prop('checked', senders == ""); $('#addaliasForwardsToNotAdvanced').prop('checked', senders == "");

View File

@ -234,7 +234,7 @@ def migration_13(env):
# 4. perform the migration # 4. perform the migration
users=m13.create_users(env, conn, ldap, ldap_base, ldap_users_base, ldap_domains_base) users=m13.create_users(env, conn, ldap, ldap_base, ldap_users_base, ldap_domains_base)
aliases=m13.create_aliases(conn, ldap, ldap_aliases_base) aliases=m13.create_aliases(env, conn, ldap, ldap_aliases_base)
permitted=m13.create_permitted_senders(conn, ldap, ldap_users_base, ldap_permitted_senders_base) permitted=m13.create_permitted_senders(conn, ldap, ldap_users_base, ldap_permitted_senders_base)
m13.populate_aliases(conn, ldap, users, aliases) m13.populate_aliases(conn, ldap, users, aliases)

View File

@ -107,7 +107,7 @@ def create_users(env, conn, ldapconn, ldap_base, ldap_users_base, ldap_domains_b
return users return users
def create_aliases(conn, ldapconn, aliases_base): def create_aliases(env, conn, ldapconn, aliases_base):
# iterate through sqlite 'aliases' table and create ldap # iterate through sqlite 'aliases' table and create ldap
# aliases but without members. returns a map of alias->dn # aliases but without members. returns a map of alias->dn
aliases={} aliases={}
@ -122,10 +122,19 @@ def create_aliases(conn, ldapconn, aliases_base):
else: else:
cn="%s" % uuid.uuid4() cn="%s" % uuid.uuid4()
dn="cn=%s,%s" % (cn, aliases_base) dn="cn=%s,%s" % (cn, aliases_base)
print("adding alias %s" % alias) description="Mail group %s" % alias
if alias.startswith("postmaster@") or \
alias.startswith("hostmaster@") or \
alias.startswith("abuse@") or \
alias.startswith("admin@") or \
alias == "administrator@" + env['PRIMARY_HOSTNAME']:
description = "Required alias"
print("adding alias %s" % alias)
ldapconn.add(dn, ['mailGroup'], { ldapconn.add(dn, ['mailGroup'], {
"mail": alias, "mail": alias,
"description": "Mail group %s" % alias "description": description
}) })
aliases[alias] = dn aliases[alias] = dn
return aliases return aliases

View File

@ -43,7 +43,8 @@ installed_state_capture() {
echo "Unable to get aliases: rc=$? err=$REST_ERROR" 1>&2 echo "Unable to get aliases: rc=$? err=$REST_ERROR" 1>&2
return 2 return 2
fi fi
echo "$REST_OUTPUT" > "$state_dir/aliases.json" # ignore/exclude the alias description field
echo "$REST_OUTPUT" | grep -v '"description":' > "$state_dir/aliases.json"
# record dns config # record dns config
H2 "record dns details" H2 "record dns details"
@ -84,7 +85,7 @@ installed_state_compare() {
H2 "Aliases" H2 "Aliases"
output="$(diff "$s1/aliases.json" "$s2/aliases.json" 2>&1)" output="$(diff "$s1/aliases.json" "$s2/aliases.json" 2>&1)"
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
change="true" changed="true"
echo "ALIASES ARE DIFFERENT!" echo "ALIASES ARE DIFFERENT!"
echo "$output" echo "$output"
else else

View File

@ -40,6 +40,7 @@ suite_start() {
let SUITE_COUNT_SKIPPED=0 let SUITE_COUNT_SKIPPED=0
let SUITE_COUNT_TOTAL=0 let SUITE_COUNT_TOTAL=0
SUITE_NAME="$1" SUITE_NAME="$1"
SUITE_START=$(date +%s)
OUTDIR="$BASE_OUTPUTDIR/$SUITE_NAME" OUTDIR="$BASE_OUTPUTDIR/$SUITE_NAME"
mkdir -p "$OUTDIR" mkdir -p "$OUTDIR"
echo "" echo ""
@ -50,7 +51,8 @@ suite_start() {
suite_end() { suite_end() {
suite_cleanup "$@" suite_cleanup "$@"
echo "Suite $SUITE_NAME finished" local SUITE_END=$(date +%s)
echo "Suite $SUITE_NAME finished ($(elapsed_pretty $SUITE_START $SUITE_END))"
let OVERALL_SUCCESSES+=$SUITE_COUNT_SUCCESS let OVERALL_SUCCESSES+=$SUITE_COUNT_SUCCESS
let OVERALL_FAILURES+=$SUITE_COUNT_FAILURE let OVERALL_FAILURES+=$SUITE_COUNT_FAILURE
let OVERALL_SKIPPED+=$SUITE_COUNT_SKIPPED let OVERALL_SKIPPED+=$SUITE_COUNT_SKIPPED