diff --git a/management/daemon.py b/management/daemon.py index b7bf2a66..b37e33bb 100755 --- a/management/daemon.py +++ b/management/daemon.py @@ -203,6 +203,7 @@ def mail_aliases(): def mail_aliases_add(): return add_mail_alias( request.form.get('address', ''), + request.form.get('description', ''), request.form.get('forwards_to', ''), request.form.get('permitted_senders', ''), env, diff --git a/management/mailconfig.py b/management/mailconfig.py index 7b2d68ed..1e6cd79f 100755 --- a/management/mailconfig.py +++ b/management/mailconfig.py @@ -312,7 +312,8 @@ def get_mail_aliases(env, as_map=False): # { dn: {string}, # mail: {string} # forward_tos: {array of string}, - # permited_senders: {array of string} + # permited_senders: {array of string}, + # description: {string} # } # 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 } # 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 # key=email(lowercase), value=(email, forward-tos, permitted-senders). @@ -352,7 +353,8 @@ def get_mail_aliases(env, as_map=False): "dn": alias["dn"], "mail": alias_email, "forward_tos": forward_tos, - "permitted_senders": allowed_senders + "permitted_senders": allowed_senders, + "description": alias["description"][0] } if not as_map: @@ -382,7 +384,8 @@ def get_mail_aliases_ex(env): # address_display: "name@domain.tld", # full Unicode # forwards_to: ["user1@domain.com", "receiver-only1@domain.com", ...], # 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) 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 + forwards_to=alias["forward_tos"] + permitted_senders=alias["permitted_senders"] + description=alias["description"] domain = get_domain(address) required = (address in required_aliases) - + # add to list if not domain in domains: domains[domain] = { "domain": domain, "aliases": [], } + domains[domain]["aliases"].append({ "address": address, "address_display": prettify_idn_email_address(address), - "forwards_to": [prettify_idn_email_address(r.strip()) for r in forwards_to.split(",")], - "permitted_senders": [prettify_idn_email_address(s.strip()) for s in permitted_senders.split(",")] if permitted_senders is not None else None, + "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] if permitted_senders is not None and len(permitted_senders)>0 else None, "required": required, + "description": description }) + # Sort domains. domains = [domains[domain] for domain in utils.sort_domains(domains.keys(), env)] @@ -857,10 +871,11 @@ def convert_rfc822MailMember(env, conn, dn, mail): 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. # # 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 # 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. @@ -958,20 +973,30 @@ def add_mail_alias(address, forwards_to, permitted_senders, env, update_if_exist # save to db 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: return ("Alias already exists (%s)." % address, 400) cn="%s" % uuid.uuid4() dn="cn=%s,%s" % (cn, env.LDAP_ALIASES_BASE) - if address.startswith('@') and \ - len(validated_forwards_to)==1 and \ - validated_forwards_to[0].startswith('@'): - description = "Domain alias %s->%s" % (address, validated_forwards_to[0]) - elif address.startswith('@'): - description = "Catch-all for %s" % address - else: - description ="Mail group %s" % address + if not description: + # supply a default description for new entries that did not + # specify one + if not existing_alias: + if address.startswith('@') and \ + len(validated_forwards_to)==1 and \ + validated_forwards_to[0].startswith('@'): + 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 = { "mail": address, "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, - ['member', 'rfc822MailMember' ], + ['member', 'rfc822MailMember', 'description' ], ['mailGroup'], attrs) if op == 'modify': @@ -1104,7 +1129,7 @@ def kick(env, mail_result=None): # Doesn't exist. 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 - 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 results.append("added alias %s (=> %s)\n" % (address, administrator)) diff --git a/management/templates/aliases.html b/management/templates/aliases.html index 848fcf49..52711850 100644 --- a/management/templates/aliases.html +++ b/management/templates/aliases.html @@ -35,6 +35,15 @@ +
+ +
+ +
+ An optional description of the alias +
+
+
@@ -81,6 +90,7 @@ Alias
Forwards To Permitted Senders + Comment @@ -103,6 +113,7 @@ +
@@ -170,6 +181,7 @@ function show_aliases() { n.find('td.forwardsTo').append($("
").text(alias.forwards_to[j])) for (var j = 0; j < (alias.permitted_senders ? alias.permitted_senders.length : 0); j++) n.find('td.senders').append($("
").text(alias.permitted_senders[j])) + n.find('td.description').append($("
").text(alias.description)); $('#alias_table tbody').append(n); } } @@ -208,6 +220,7 @@ var is_alias_add_update = false; function do_add_alias() { var title = (!is_alias_add_update) ? "Add Alias" : "Update Alias"; var form_address = $("#addaliasAddress").val(); + var form_description = $("#addaliasDescription").val(); var form_forwardsto = $("#addaliasForwardsTo").val(); var form_senders = ($('#addaliasForwardsToAdvanced').prop('checked') ? $("#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', address: form_address, + description: form_description, forwards_to: form_forwardsto, permitted_senders: form_senders }, @@ -237,9 +251,10 @@ function do_add_alias() { function aliases_reset_form() { $("#addaliasAddress").prop('disabled', false); - $("#addaliasAddress").val('') - $("#addaliasForwardsTo").val('') - $("#addaliasSenders").val('') + $("#addaliasAddress").val(''); + $("#addaliasDescription").val(''); + $("#addaliasForwardsTo").val(''); + $("#addaliasSenders").val(''); $('#alias-cancel').addClass('hidden'); $('#add-alias-button').text('Add Alias'); is_alias_add_update = false; @@ -247,6 +262,7 @@ function aliases_reset_form() { function aliases_edit(elem) { 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 senderdivs = $(elem).parents('tr').find('.senders div'); var forwardsTo = ""; @@ -264,6 +280,7 @@ function aliases_edit(elem) { $('#alias-cancel').removeClass('hidden'); $("#addaliasAddress").prop('disabled', true); $("#addaliasAddress").val(address); + $("#addaliasDescription").val(description); $("#addaliasForwardsTo").val(forwardsTo); $('#addaliasForwardsToAdvanced').prop('checked', senders != ""); $('#addaliasForwardsToNotAdvanced').prop('checked', senders == ""); diff --git a/setup/migrate.py b/setup/migrate.py index a3912001..dae96799 100755 --- a/setup/migrate.py +++ b/setup/migrate.py @@ -234,7 +234,7 @@ def migration_13(env): # 4. perform the migration 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) m13.populate_aliases(conn, ldap, users, aliases) diff --git a/setup/migration_13.py b/setup/migration_13.py index 81cdd9b4..303ba68d 100644 --- a/setup/migration_13.py +++ b/setup/migration_13.py @@ -107,7 +107,7 @@ def create_users(env, conn, ldapconn, ldap_base, ldap_users_base, ldap_domains_b 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 # aliases but without members. returns a map of alias->dn aliases={} @@ -122,10 +122,19 @@ def create_aliases(conn, ldapconn, aliases_base): else: cn="%s" % uuid.uuid4() 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'], { "mail": alias, - "description": "Mail group %s" % alias + "description": description }) aliases[alias] = dn return aliases diff --git a/tests/lib/installed-state.sh b/tests/lib/installed-state.sh index 3e3eacf0..84e81bc6 100644 --- a/tests/lib/installed-state.sh +++ b/tests/lib/installed-state.sh @@ -43,7 +43,8 @@ installed_state_capture() { echo "Unable to get aliases: rc=$? err=$REST_ERROR" 1>&2 return 2 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 H2 "record dns details" @@ -84,7 +85,7 @@ installed_state_compare() { H2 "Aliases" output="$(diff "$s1/aliases.json" "$s2/aliases.json" 2>&1)" if [ $? -ne 0 ]; then - change="true" + changed="true" echo "ALIASES ARE DIFFERENT!" echo "$output" else diff --git a/tests/suites/_init.sh b/tests/suites/_init.sh index 5466ad3b..25d8afe1 100644 --- a/tests/suites/_init.sh +++ b/tests/suites/_init.sh @@ -40,6 +40,7 @@ suite_start() { let SUITE_COUNT_SKIPPED=0 let SUITE_COUNT_TOTAL=0 SUITE_NAME="$1" + SUITE_START=$(date +%s) OUTDIR="$BASE_OUTPUTDIR/$SUITE_NAME" mkdir -p "$OUTDIR" echo "" @@ -50,7 +51,8 @@ suite_start() { suite_end() { 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_FAILURES+=$SUITE_COUNT_FAILURE let OVERALL_SKIPPED+=$SUITE_COUNT_SKIPPED