mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2026-03-05 15:57:23 +01:00
Merge branch 'main' of https://github.com/mail-in-a-box/mailinabox
Upstream is adding handling for utf8 domains by creating a domain alias @utf8 -> @idna. I'm deviating from this approach by setting multiple email address (idna and utf8) per user and alias where a domain contains non-ascii characters. The maildrop (mailbox) remains the same - all mail goes to the user's mailbox regardless of which email address was used. This is more in line with how other systems (eg. active directory), handle multiple email addresses for a single user. # Conflicts: # README.md # management/mailconfig.py # management/templates/index.html # setup/dns.sh # setup/mail-users.sh
This commit is contained in:
@@ -80,7 +80,13 @@ remote-control:
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "include: /etc/nsd/zones.conf" >> /etc/nsd/nsd.conf;
|
||||
# Create a directory for additional configuration directives, including
|
||||
# the zones.conf file written out by our management daemon.
|
||||
echo "include: /etc/nsd/nsd.conf.d/*.conf" >> /etc/nsd/nsd.conf;
|
||||
|
||||
# Remove the old location of zones.conf that we generate. It will
|
||||
# now be stored in /etc/nsd/nsd.conf.d.
|
||||
rm -f /etc/nsd/zones.conf
|
||||
|
||||
# Create DNSSEC signing keys.
|
||||
|
||||
|
||||
@@ -13,8 +13,13 @@ get_attribute_from_ldif() {
|
||||
local line
|
||||
while read line; do
|
||||
[ -z "$line" ] && break
|
||||
local v=$(awk "/^$attr:/ { print substr(\$0, length(\"$attr\")+3) }" <<<$line)
|
||||
[ ! -z "$v" ] && ATTR_VALUE+=( "$v" )
|
||||
local v=$(awk "/^$attr: / { print substr(\$0, length(\"$attr\")+3) }" <<<"$line")
|
||||
if [ ! -z "$v" ]; then
|
||||
ATTR_VALUE+=( "$v" )
|
||||
else
|
||||
v=$(awk "/^$attr:: / { print substr(\$0, length(\"$attr\")+4) }" <<<"$line")
|
||||
[ ! -z "$v" ] && ATTR_VALUE+=( $(base64 --decode --wrap=0 <<<"$v") )
|
||||
fi
|
||||
done <<< "$ldif"
|
||||
return 0
|
||||
}
|
||||
|
||||
122
setup/ldap.sh
122
setup/ldap.sh
@@ -295,8 +295,8 @@ schema_to_ldif() {
|
||||
local cn="$3" # schema common name, eg "postfix"
|
||||
local cat='cat'
|
||||
if [ ! -e "$schema" ]; then
|
||||
if [ -e "conf/$(basename $schema)" ]; then
|
||||
schema="conf/$(basename $schema)"
|
||||
if [ -e "conf/schema/$(basename $schema)" ]; then
|
||||
schema="conf/schema/$(basename $schema)"
|
||||
else
|
||||
cat="curl -s"
|
||||
fi
|
||||
@@ -316,56 +316,57 @@ EOF
|
||||
| sed 's/^\s*$/#/g' >> "$ldif"
|
||||
}
|
||||
|
||||
change_core_mail_syntax() {
|
||||
# output the ldif to change mail to utf8 from ia5 in the core schema
|
||||
get_attribute "cn=schema,cn=config" "(cn={0}core)" "olcAttributeTypes"
|
||||
case "${ATTR_VALUE[48]}" in
|
||||
*\'mail\'*caseIgnoreIA5Match* )
|
||||
newval=$(sed 's/SYNTAX[ \t][^ ]*/SYNTAX 1.3.6.1.4.1.1466.115.121.1.15/g' <<<"${ATTR_VALUE[48]}" |
|
||||
sed 's/caseIgnoreIA5Match/caseIgnoreMatch/g' |
|
||||
sed 's/caseIgnoreIA5SubstringsMatch/caseIgnoreSubstringsMatch/g')
|
||||
cat <<EOF
|
||||
dn: cn={0}core,cn=schema,cn=config
|
||||
changetype: modify
|
||||
delete: olcAttributeTypes
|
||||
olcAttributeTypes: ${ATTR_VALUE[48]}
|
||||
-
|
||||
add: olcAttributeTypes
|
||||
olcAttributeTypes: $newval
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
add_schemas() {
|
||||
# Add necessary schema's for MiaB operaion
|
||||
# Add necessary schema's for MiaB operaion
|
||||
#
|
||||
# First, apply rfc822MailMember from OpenLDAP's "misc"
|
||||
# schema. Don't apply the whole schema file because much is from
|
||||
# expired RFC's, and we just need rfc822MailMember
|
||||
local cn="misc"
|
||||
get_attribute "cn=schema,cn=config" "(&(cn={*}$cn)(objectClass=olcSchemaConfig))" "cn"
|
||||
if [ -z "$ATTR_DN" ]; then
|
||||
say_verbose "Adding '$cn' schema"
|
||||
cat "/etc/ldap/schema/misc.ldif" | awk 'BEGIN {C=0}
|
||||
/^(dn|objectClass|cn):/ { print $0; next }
|
||||
/^olcAttributeTypes:/ && /27\.2\.1\.15/ { print $0; C=1; next }
|
||||
/^(olcAttributeTypes|olcObjectClasses):/ { C=0; next }
|
||||
/^ / && C==1 { print $0 }' | ldapadd -Q -Y EXTERNAL -H ldapi:/// >/dev/null
|
||||
fi
|
||||
|
||||
# Next, apply the postfix schema from the ldapadmin project
|
||||
# (GPL)(*).
|
||||
# Note: the postfix schema originally came from the ldapadmin
|
||||
# project (GPL)(*), but has been modified to support the needs of
|
||||
# this project.
|
||||
# see: http://ldapadmin.org
|
||||
# http://ldapadmin.org/docs/postfix.schema
|
||||
# http://www.postfix.org/LDAP_README.html
|
||||
# (*) mailGroup modified to include rfc822MailMember
|
||||
local schema="http://ldapadmin.org/docs/postfix.schema"
|
||||
local cn="postfix"
|
||||
get_attribute "cn=schema,cn=config" "(&(cn={*}$cn)(objectClass=olcSchemaConfig))" "cn"
|
||||
if [ -z "$ATTR_DN" ]; then
|
||||
local ldif="/tmp/$cn.$$.ldif"
|
||||
schema_to_ldif "$schema" "$ldif" "$cn"
|
||||
sed -i 's/\$ member \$/$ member $ rfc822MailMember $/' "$ldif"
|
||||
say_verbose "Adding '$cn' schema"
|
||||
[ ${verbose:-0} -gt 1 ] && cat "$ldif"
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f "$ldif" >/dev/null
|
||||
rm -f "$ldif"
|
||||
fi
|
||||
|
||||
# apply the mfa-totp schema
|
||||
# this adds the totpUser class to store the totp secret
|
||||
local schema="mfa-totp.schema"
|
||||
local cn="mfa-totp"
|
||||
get_attribute "cn=schema,cn=config" "(&(cn={*}$cn)(objectClass=olcSchemaConfig))" "cn"
|
||||
if [ -z "$ATTR_DN" ]; then
|
||||
local ldif="/tmp/$cn.$$.ldif"
|
||||
schema_to_ldif "$schema" "$ldif" "$cn"
|
||||
say_verbose "Adding '$cn' schema"
|
||||
[ ${verbose:-0} -gt 1 ] && cat "$ldif"
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f "$ldif" >/dev/null
|
||||
rm -f "$ldif"
|
||||
fi
|
||||
for cn in "postfix" "mfa-totp" "namedProperties"; do
|
||||
schema="$cn.schema"
|
||||
get_attribute "cn=schema,cn=config" "(&(cn={*}$cn)(objectClass=olcSchemaConfig))" "cn"
|
||||
if [ -z "$ATTR_DN" ]; then
|
||||
local ldif="/tmp/$cn.$$.ldif"
|
||||
schema_to_ldif "$schema" "$ldif" "$cn"
|
||||
if [ "$cn" = "postfix" ]; then
|
||||
local ldif2="/tmp/$cn.$$-2.ldif"
|
||||
change_core_mail_syntax >"$ldif2"
|
||||
echo "" >>"$ldif2"
|
||||
sed 's/^dn: cn=postfix,\(.*\)$/dn: cn=postfix,\1\nchangetype: add/g' "$ldif" >> "$ldif2"
|
||||
rm "$ldif"
|
||||
ldif="$ldif2"
|
||||
fi
|
||||
say_verbose "Adding '$cn' schema"
|
||||
[ ${verbose:-0} -gt 1 ] && cat "$ldif"
|
||||
ldapadd -Q -Y EXTERNAL -H ldapi:/// -f "$ldif" >/dev/null
|
||||
rm -f "$ldif"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
@@ -504,7 +505,7 @@ add_indexes() {
|
||||
# Add the indexes
|
||||
get_attribute "$cdn" "(objectClass=*)" "olcDbIndex" base
|
||||
local attr
|
||||
for attr in mail maildrop mailaccess dc rfc822MailMember; do
|
||||
for attr in mail maildrop mailaccess dc dcIntl mailMember; do
|
||||
local type="eq" atype="" aindex=""
|
||||
[ "$attr" == "mail" ] && type="eq,sub"
|
||||
|
||||
@@ -661,6 +662,7 @@ process_cmdline() {
|
||||
elif [ "$1" == "-config" ]; then
|
||||
# Apply a certain configuration
|
||||
if [ "$2" == "server" ]; then
|
||||
add_schemas
|
||||
modify_global_config
|
||||
add_overlays
|
||||
add_indexes
|
||||
@@ -675,8 +677,23 @@ process_cmdline() {
|
||||
|
||||
elif [ "$1" == "-search" ]; then
|
||||
# search for email addresses, distinguished names and general
|
||||
# ldap filters
|
||||
debug_search "$2"
|
||||
# ldap filters. Args:
|
||||
# search filter [optional]
|
||||
# base dn [optional]
|
||||
# remaining args are attributes to output [optional]
|
||||
shift
|
||||
if [ $# -eq 0 ]; then
|
||||
debug_search "(objectclass=*)"
|
||||
else
|
||||
debug_search "$@"
|
||||
fi
|
||||
exit 0
|
||||
|
||||
elif [ "$1" == "-schema-to-ldif" ]; then
|
||||
cn="$2"
|
||||
output="${3:-/dev/stdout}"
|
||||
schema="$cn.schema"
|
||||
schema_to_ldif "$schema" "$output" "$cn"
|
||||
exit 0
|
||||
|
||||
elif [ "$1" == "-dumpdb" ]; then
|
||||
@@ -699,6 +716,11 @@ process_cmdline() {
|
||||
echo ""
|
||||
slapcat ${slapcat_args[@]} -s "$ATTR_DN" | grep -Ev "^$hide_attrs:"
|
||||
fi
|
||||
if [ "$s" == "all" -o "$s" == "schema" ]; then
|
||||
echo ""
|
||||
echo '--------------------------------'
|
||||
slapcat ${slapcat_args[@]} -s "cn=schema,cn=config" | grep -Ev "^$hide_attrs:"
|
||||
fi
|
||||
if [ "$s" == "all" -o "$s" == "frontend" ]; then
|
||||
echo ""
|
||||
echo '--------------------------------'
|
||||
@@ -716,7 +738,7 @@ process_cmdline() {
|
||||
if [ "$s" == "aliases" ]; then
|
||||
echo ""
|
||||
echo '--------------------------------'
|
||||
local attrs=(mail member mailRoutingAddress rfc822MailMember)
|
||||
local attrs=(mail member mailRoutingAddress mailMember namedProperty)
|
||||
[ ${verbose:-0} -gt 0 ] && attrs=()
|
||||
debug_search "(objectClass=mailGroup)" "$LDAP_ALIASES_BASE" ${attrs[@]}
|
||||
fi
|
||||
|
||||
@@ -63,7 +63,7 @@ base = ${LDAP_USERS_BASE}
|
||||
# filter below. If found, the user is authenticated against this dn
|
||||
# (a bind is attempted as that user). The attribute 'mail' is
|
||||
# multi-valued and contains all the user's email addresses. We use
|
||||
# maildrop as the dovecot mailbox address and forbid then from using
|
||||
# maildrop as the dovecot mailbox address and forbid them from using
|
||||
# it for authentication by excluding maildrop from the filter.
|
||||
pass_filter = (&(objectClass=mailUser)(mail=%u))
|
||||
pass_attrs = maildrop=user
|
||||
@@ -166,6 +166,7 @@ chmod 0640 /etc/postfix/sender-login-maps-aliases.cf
|
||||
# Check whether a destination email address exists, and to perform any
|
||||
# email alias rewrites in Postfix.
|
||||
tools/editconf.py /etc/postfix/main.cf \
|
||||
smtputf8_enable=no \
|
||||
virtual_mailbox_domains=ldap:/etc/postfix/virtual-mailbox-domains.cf \
|
||||
virtual_mailbox_maps=ldap:/etc/postfix/virtual-mailbox-maps.cf \
|
||||
virtual_alias_maps=ldap:/etc/postfix/virtual-alias-maps.cf \
|
||||
@@ -180,7 +181,7 @@ bind_dn = ${LDAP_POSTFIX_DN}
|
||||
bind_pw = ${LDAP_POSTFIX_PASSWORD}
|
||||
version = 3
|
||||
search_base = ${LDAP_DOMAINS_BASE}
|
||||
query_filter = (&(dc=%s)(businessCategory=mail))
|
||||
query_filter = (&(|(dc=%s)(dcIntl=%s))(businessCategory=mail))
|
||||
result_attribute = dc
|
||||
EOF
|
||||
chgrp postfix /etc/postfix/virtual-mailbox-domains.cf
|
||||
@@ -228,7 +229,6 @@ chmod 0640 /etc/postfix/virtual-mailbox-maps.cf
|
||||
# it might have just permitted_senders, skip any records with an
|
||||
# empty destination here so that other lower priority rules might match.
|
||||
|
||||
|
||||
#
|
||||
# This is the ldap version of aliases(5) but for virtual
|
||||
# addresses. Postfix queries this recursively to determine delivery
|
||||
@@ -242,7 +242,7 @@ bind_pw = ${LDAP_POSTFIX_PASSWORD}
|
||||
version = 3
|
||||
search_base = ${LDAP_USERS_BASE}
|
||||
query_filter = (mail=%s)
|
||||
result_attribute = maildrop, rfc822MailMember
|
||||
result_attribute = maildrop, mailMember
|
||||
special_result_attribute = member
|
||||
EOF
|
||||
chgrp postfix /etc/postfix/virtual-alias-maps.cf
|
||||
|
||||
@@ -187,6 +187,11 @@ def migration_13(env):
|
||||
db = os.path.join(env["STORAGE_ROOT"], 'mail/users.sqlite')
|
||||
shell("check_call", ["sqlite3", db, "CREATE TABLE mfa (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, type TEXT NOT NULL, secret TEXT NOT NULL, mru_token TEXT, label TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE);"])
|
||||
|
||||
def migration_14(env):
|
||||
# Add the "auto_aliases" table.
|
||||
db = os.path.join(env["STORAGE_ROOT"], 'mail/users.sqlite')
|
||||
shell("check_call", ["sqlite3", db, "CREATE TABLE auto_aliases (id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT NOT NULL UNIQUE, destination TEXT NOT NULL, permitted_senders TEXT);"])
|
||||
|
||||
###########################################################
|
||||
|
||||
|
||||
@@ -245,9 +250,77 @@ def migration_miabldap_1(env):
|
||||
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)
|
||||
|
||||
|
||||
ldap.unbind()
|
||||
conn.close()
|
||||
|
||||
|
||||
def migration_miabldap_2(env):
|
||||
# This migration step changes the ldap schema to support utf8 email
|
||||
#
|
||||
# possible states at this point:
|
||||
# miabldap was installed and is being upgraded
|
||||
# -> old pre-utf8 schema present
|
||||
# a miab install was present and step 1 upgaded it to miabldap
|
||||
# -> new utf8 schema present
|
||||
#
|
||||
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), "../management")))
|
||||
import ldap3
|
||||
from backend import connect
|
||||
import migration_14 as m14
|
||||
|
||||
# 1. get ldap site details
|
||||
ldapvars = load_env_vars_from_file(os.path.join(env["STORAGE_ROOT"], "ldap/miab_ldap.conf"), strip_quotes=True)
|
||||
ldap_domains_base = ldapvars.LDAP_DOMAINS_BASE
|
||||
ldap_aliases_base = ldapvars.LDAP_ALIASES_BASE
|
||||
ldap_users_base = ldapvars.LDAP_USERS_BASE
|
||||
|
||||
# connect before schema changes to ensure admin password works
|
||||
ldap = connect(ldapvars)
|
||||
|
||||
# 2. if this is a miab -> maibldap install, the new schema is
|
||||
# already in place and no schema changes are needed. however,
|
||||
# if this is a miabldap/1 to miabldap/2 migration, we must
|
||||
# upgrade the schema.
|
||||
ret = shell("check_output", [
|
||||
"ldapsearch",
|
||||
"-Q",
|
||||
"-Y", "EXTERNAL",
|
||||
"-H", "ldapi:///",
|
||||
"(&(objectClass=olcSchemaConfig)(cn={*}postfix))",
|
||||
"-b", "cn=schema,cn=config",
|
||||
"-LLL",
|
||||
"olcObjectClasses"
|
||||
])
|
||||
|
||||
if "rfc822MailMember" in ret:
|
||||
def ldif_change_fn(ldif):
|
||||
return ldif.replace("rfc822MailMember: ", "mailMember: ")
|
||||
# apply schema changes miabldap/1 -> miabldap/2
|
||||
ldap.unbind()
|
||||
print("Apply schema changes")
|
||||
m14.apply_schema_changes(env, ldapvars, ldif_change_fn)
|
||||
# reconnect
|
||||
ldap = connect(ldapvars)
|
||||
|
||||
# 3. migrate to utf8: users, aliases and domains
|
||||
print("Create utf8 entries for users and aliases having IDNA domains")
|
||||
m14.add_utf8_mail_addresses(env, ldap, ldap_users_base)
|
||||
|
||||
print("Add namedProperties objectclass to aliases")
|
||||
m14.add_namedProperties_objectclass(env, ldap, ldap_aliases_base)
|
||||
|
||||
print("Add mailDomain objectclass to domains")
|
||||
m14.add_mailDomain_objectclass(env, ldap, ldap_domains_base)
|
||||
|
||||
print("Mark required aliases with 'auto' property")
|
||||
m14.add_auto_tag(env, ldap, ldap_aliases_base)
|
||||
|
||||
print("Ensure all required aliases are created")
|
||||
m14.ensure_required_aliases(env, ldapvars, ldap)
|
||||
|
||||
ldap.unbind()
|
||||
|
||||
|
||||
def get_current_migration():
|
||||
ver = 0
|
||||
@@ -327,11 +400,20 @@ def run_miabldap_migrations():
|
||||
env = load_environment()
|
||||
|
||||
migration_id_file = os.path.join(env['STORAGE_ROOT'], 'mailinabox-ldap.version')
|
||||
migration_id = 0
|
||||
migration_id = None
|
||||
if os.path.exists(migration_id_file):
|
||||
with open(migration_id_file) as f:
|
||||
migration_id = f.read().strip();
|
||||
if migration_id.strip()=='': migration_id = None
|
||||
|
||||
if migration_id is None:
|
||||
if os.path.exists(os.path.join(env['STORAGE_ROOT'], 'mailinabox.version')):
|
||||
migration_id = 0
|
||||
else:
|
||||
print()
|
||||
print("%s file doesn't exists. Skipping migration..." % (migration_id_file,))
|
||||
return
|
||||
|
||||
ourver = int(migration_id)
|
||||
|
||||
while True:
|
||||
|
||||
232
setup/migration_14.py
Normal file
232
setup/migration_14.py
Normal file
@@ -0,0 +1,232 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- indent-tabs-mode: t; tab-width: 4; python-indent-offset: 4; -*-
|
||||
|
||||
#
|
||||
# helper functions for migration #14 / miabldap-migration #2
|
||||
#
|
||||
|
||||
import sys, os, ldap3, idna
|
||||
from utils import shell
|
||||
from mailconfig import (
|
||||
add_required_aliases,
|
||||
required_alias_names,
|
||||
get_mail_domains
|
||||
)
|
||||
|
||||
|
||||
def utf8_from_idna(domain_idna):
|
||||
try:
|
||||
return idna.decode(domain_idna.encode("ascii"))
|
||||
except (UnicodeError, idna.IDNAError):
|
||||
# Failed to decode IDNA, should never happen
|
||||
return domain_idna
|
||||
|
||||
|
||||
def apply_schema_changes(env, ldapvars, ldif_change_fn):
|
||||
# 1. save LDAP_BASE data to ldif
|
||||
slapd_conf = os.path.join(env["STORAGE_ROOT"], "ldap/slapd.d")
|
||||
fail_fn = os.path.join(env["STORAGE_ROOT"], "ldap/failed_migration.txt")
|
||||
ldif = shell("check_output", [
|
||||
"/usr/sbin/slapcat",
|
||||
"-F", slapd_conf,
|
||||
"-b", ldapvars.LDAP_BASE
|
||||
])
|
||||
|
||||
# 2. wipe out existing database configuration and database
|
||||
# 2a. set the creation parameters
|
||||
ORGANIZATION="Mail-In-A-Box"
|
||||
LDAP_DOMAIN="mailinabox"
|
||||
shell("check_output", [
|
||||
"/usr/bin/debconf-set-selections"
|
||||
], input=f'''slapd shared/organization string {ORGANIZATION}
|
||||
slapd slapd/domain string {LDAP_DOMAIN}
|
||||
slapd slapd/password1 password {ldapvars.LDAP_ADMIN_PASSWORD}
|
||||
slapd slapd/password2 password {ldapvars.LDAP_ADMIN_PASSWORD}
|
||||
'''.encode('utf-8')
|
||||
)
|
||||
|
||||
# 2b. recreate ldap config and database
|
||||
shell("check_call", [
|
||||
"/usr/sbin/dpkg-reconfigure",
|
||||
"--frontend=noninteractive",
|
||||
"slapd"
|
||||
])
|
||||
|
||||
# 2c. clear passwords from debconf
|
||||
shell("check_output", [
|
||||
"/usr/bin/debconf-set-selections"
|
||||
], input='''slapd slapd/password1 password
|
||||
slapd slapd/password2 password
|
||||
'''.encode('utf-8')
|
||||
)
|
||||
|
||||
# 3. make desired ldif changes
|
||||
# 3a. first, remove dc=mailinabox and
|
||||
# cn=admin,dc=mailinabox. they were both created during
|
||||
# dpkg-reconfigure and can't be readded
|
||||
entries = ldif.split("\n\n")
|
||||
keep = []
|
||||
removed = []
|
||||
remove = [
|
||||
"dn: " + ldapvars.LDAP_BASE,
|
||||
"dn: " + ldapvars.LDAP_ADMIN_DN
|
||||
]
|
||||
for entry in entries:
|
||||
dn = entry.split("\n")[0]
|
||||
if dn not in remove:
|
||||
keep.append(entry)
|
||||
else:
|
||||
removed.append(entry)
|
||||
|
||||
# 3b. call the given ldif change function
|
||||
ldif = ldif_change_fn("\n\n".join(keep))
|
||||
#ldif = ldif_change_fn(ldif)
|
||||
|
||||
# 4. re-create schemas and other config
|
||||
shell("check_call", [
|
||||
"setup/ldap.sh",
|
||||
"-v",
|
||||
"-config", "server"
|
||||
])
|
||||
|
||||
# 5. restore LDAP_BASE data
|
||||
code, ret = shell("check_output", [
|
||||
"/usr/sbin/slapadd",
|
||||
"-F", slapd_conf,
|
||||
"-b", ldapvars.LDAP_BASE,
|
||||
"-v",
|
||||
"-c"
|
||||
], input=ldif.encode('utf-8'), trap=True, capture_stderr=True)
|
||||
|
||||
if code != 0:
|
||||
try:
|
||||
with open(fail_fn, "w") as of:
|
||||
of.write("# slapadd -F %s -b %s -v -c\n" %
|
||||
(slapd_conf, ldapvars.LDAP_BASE))
|
||||
of.write(ldif)
|
||||
print("See saved data in %s" % fail_fn)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
raise ValueError("Could not restore data: exit code=%s: output=%s" % (code, ret))
|
||||
|
||||
|
||||
|
||||
def add_utf8_mail_addresses(env, ldap, ldap_users_base):
|
||||
# if the mail attribute of users or aliases is idna encoded, also
|
||||
# add a utf8 version of the address to the mail attribute so the
|
||||
# user or alias will be known by multiple addresses (idna and
|
||||
# utf8)
|
||||
pager = ldap.paged_search(ldap_users_base, "(|(objectClass=mailGroup)(objectClass=mailUser))", attributes=['mail'])
|
||||
changes = []
|
||||
for rec in pager:
|
||||
mail_idna_lc = []
|
||||
for addr in rec['mail']:
|
||||
mail_idna_lc = addr.lower()
|
||||
|
||||
changed = False
|
||||
new_mail = []
|
||||
for addr in rec['mail']:
|
||||
new_mail.append(addr)
|
||||
name = addr.split('@')[0]
|
||||
domain = addr.split('@', 1)[1]
|
||||
addr_utf8 = name + '@' + utf8_from_idna(domain)
|
||||
addr_utf8_lc = addr_utf8.lower()
|
||||
if addr_utf8 != addr and addr_utf8_lc not in mail_lc:
|
||||
new_mail.append(addr_utf8)
|
||||
print("Add '%s' for %s" % (addr_utf8, addr))
|
||||
changed = True
|
||||
if changed:
|
||||
changes.append({"rec":rec, "mail":new_mail})
|
||||
|
||||
for change in changes:
|
||||
ldap.modify_record(
|
||||
change["rec"],
|
||||
{ "mail": change["mail"] }
|
||||
)
|
||||
|
||||
|
||||
|
||||
def add_namedProperties_objectclass(env, ldap, ldap_aliases_base):
|
||||
# ensure every alias has a namedProperties objectClass attached
|
||||
pager = ldap.paged_search(ldap_aliases_base, "(&(objectClass=mailGroup)(!(objectClass=namedProperties)))", attributes=['objectClass'])
|
||||
changes = []
|
||||
for rec in pager:
|
||||
newoc = rec['objectClass'].copy()
|
||||
newoc.append('namedProperties')
|
||||
changelist = {
|
||||
'objectClass': newoc,
|
||||
}
|
||||
changes.append({'rec': rec, 'changelist': changelist})
|
||||
|
||||
for change in changes:
|
||||
ldap.modify_record(change['rec'], change['changelist'])
|
||||
|
||||
|
||||
def add_auto_tag(env, ldap, ldap_aliases_base):
|
||||
# add namedProperty=auto to existing required aliases
|
||||
# this step is needed to upgrade miabldap systems
|
||||
name_q = [
|
||||
"(mail=hostmaster@"+env['PRIMARY_HOSTNAME']+")"
|
||||
]
|
||||
for name in required_alias_names:
|
||||
name_q.append("(mail=%s@*)" % name)
|
||||
|
||||
q = [
|
||||
"(objectClass=mailGroup)",
|
||||
"(!(namedProperty=auto))",
|
||||
"(|%s)" % "".join(name_q)
|
||||
]
|
||||
pager = ldap.paged_search(
|
||||
ldap_aliases_base,
|
||||
"(&%s)" % "".join(q),
|
||||
attributes=['namedProperty']
|
||||
)
|
||||
changes = []
|
||||
for rec in pager:
|
||||
newval = rec["namedProperty"].copy()
|
||||
newval.append("auto")
|
||||
changes.append({"rec": rec, "namedProperty": newval})
|
||||
|
||||
for change in changes:
|
||||
ldap.modify_record(
|
||||
change["rec"],
|
||||
{"namedProperty": change["namedProperty"]}
|
||||
)
|
||||
|
||||
|
||||
|
||||
def add_mailDomain_objectclass(env, ldap, ldap_domains_base):
|
||||
# ensure every domain has a mailDomain objectClass attached
|
||||
pager = ldap.paged_search(ldap_domains_base, "(&(objectClass=domain)(!(objectClass=mailDomain)))", attributes=['objectClass', 'dc', 'dcIntl'])
|
||||
changes = []
|
||||
for rec in pager:
|
||||
newoc = rec['objectClass'].copy()
|
||||
newoc.append('mailDomain')
|
||||
changelist = {
|
||||
'objectClass': newoc,
|
||||
'dcIntl': [ utf8_from_idna(rec['dc'][0]) ]
|
||||
}
|
||||
changes.append({'rec': rec, 'changelist': changelist})
|
||||
|
||||
for change in changes:
|
||||
ldap.modify_record(change['rec'], change['changelist'])
|
||||
|
||||
|
||||
def ensure_required_aliases(env, ldapvars, ldap):
|
||||
# ensure every domain has its required aliases
|
||||
env_combined = env.copy()
|
||||
env_combined.update(ldapvars)
|
||||
errors = []
|
||||
for domain_idna in get_mail_domains(ldapvars):
|
||||
results = add_required_aliases(env_combined, ldap, domain_idna)
|
||||
for result in results:
|
||||
if isinstance(result, str):
|
||||
print(result)
|
||||
else:
|
||||
print("Error: %s" % result[0])
|
||||
errors.append(result[0])
|
||||
if len(errors)>0:
|
||||
raise ValueError("Some required aliases could not be added")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user