Mainly cosmetic changes

master
Daniel Berteaud 5 years ago
parent 665f663846
commit 1fa3ccb2d7
  1. 34
      zmldapsync/README.md
  2. 278
      zmldapsync/zmldapsync.pl
  3. 0
      zmldapsync/zmldapsync.yml

@ -14,7 +14,7 @@ The goals are :
## Configuration
The configuration is stored in a single file in YAML format. The script will look for a config at /opt/zimbra/conf/ldap_sync.yml or trhe one specified in the --config argument.
The configuration is stored in a single file in YAML format. The script will look for a config at /opt/zimbra/conf/zmldapsync.yml or trhe one specified in the --config argument.
The config has two main section :
@ -70,23 +70,36 @@ domains:
groups:
base: ou=groups,dc=corp2,dc=com
# A more complete example, which shows all the available settings
# A more complete example, which shows all the available settings, with their meaning
corp3.net:
ldap:
# List of LDAP servers to try (in order)
servers:
- ldap://ldap1.corp3.net:389
- ldap://ldap3.corp3.net:389
# Use starttls/ Do not set this if using ldaps:// URI
start_tls: True
# Optional bind DN and bind password for searches
bind_dn: CN=Zimbra,OU=Apps,DC=corp3,DC=net
bind_pass: 'p@ssw0rd'
# the schema used. Can be ad, rfc2307, rfc2307bis or simply ldap.
# ad, rfc2307 and rfc2307bis provides default values for attribute mapping. ldap is when you want
# a complete control, and you'll have to configure the mapping yourself
schema: ad
type: ad
users:
# Base DN where to look for users
base: OU=People,DC=corp3,DC=net
# Filter to look for users
filter: '(&(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Role_Mail,OU=Roles,DC=corp3,DC=net)(mail=*))'
# The attribute which uniquely identify a user. Usually either uid or sAMAccountName
# This attribute will be used as the user name in Zimbra (with the domain appended)
key: sAMAccountName
# The attribute for the main email address
mail_attr: mail
# The attribute for email aliases
alias_attr: otherMailbox
# A dict of attribute to map from external LDAP to Zimbra.
# The format is ext_attr: zimbra_attr
attr_map:
displayName: displayName
description: description
@ -103,18 +116,31 @@ domains:
title: title
company: company
groups:
# The base DN where to look for groups
base: OU=Groups,DC=corp3,DC=net
# An optional filter to apply to group searches
filter: (objectClass=group)
# The atribute which uniquely identify a group. Usually cn
# This attribute will be used as the distribution list name in Zimbra (with the domain appended)
key: cn
# The attribute which lists the group members
members_attr: member
# Are the members listed as full DN, or simply usernames (like memberUid with posixGroups)
members_as_dn: True
# The attribute for the main email address
mail_attr: mail
# The attribute for email aliases
alias_attr: null
# A dict of attribute to map from external LDAP to Zimbra.
# The format is ext_attr: zimbra_attr
attr_map:
displayName: displayName
description: description
zimbra:
# Should zmldapsync create the domain if missing ?
create_if_missing: False
# If the domain in Zimbra exists but is not configured
# for external auth (either LDAP or AD), should this script configure it ?
setup_ldap_auth: True
```
@ -122,6 +148,6 @@ domains:
Once a configuration file is ready, the script can be called with the following command line arguments :
* --config : path to the config file (defaults to /opt/zimbra/conf/ldap_sync.yml)
* --config : path to the config file (defaults to /opt/zimbra/conf/zmldapsync.yml)
* --quiet : will not print anything except errors
* --verbose : prints aditional info during the sync

@ -92,7 +92,11 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
]
);
if ( $zim_domain_search->code ) {
handle_error( $domain, 'Zimbra domain lookup', $zim_domain_search->error );
handle_error(
$domain,
'Zimbra domain lookup',
$zim_domain_search->error
);
next DOMAIN
}
@ -100,13 +104,23 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
if ( scalar $zim_domain_search->entries == 0 ) {
if ( yaml_bool($conf->{domains}->{$domain}->{zimbra}->{create_if_missing}) ) {
log_info( "Creating domain $domain" );
ZmClient::sendZmprovRequest( "createDomain $domain " . build_domain_attrs($conf->{domains}->{$domain}) );
ZmClient::sendZmprovRequest( "createDomain $domain " .
build_domain_attrs($conf->{domains}->{$domain})
);
} else {
handle_error( $domain, 'Zimbra domain lookup', "Domain $domain doesn't exist in Zimbra");
handle_error(
$domain,
'Zimbra domain lookup',
"Domain $domain doesn't exist in Zimbra"
);
next DOMAIN;
}
} elsif ( scalar $zim_domain_search->entries gt 1 ) {
handle_error( $domain, 'Zimbra domain lookup', "Found several matches for domain $domain" );
handle_error(
$domain,
'Zimbra domain lookup',
"Found several matches for domain $domain"
);
next DOMAIN;
}
@ -114,11 +128,18 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
my $domain_entry = ldap2hashref( $zim_domain_search, 'zimbraDomainName' )->{$domain};
# Check if auth is set to ad or ldap
if ( not defined $domain_entry->{zimbraAuthMech} or $domain_entry->{zimbraAuthMech} !~ m/^ad|ldap$/i) {
if (
not defined $domain_entry->{zimbraAuthMech} or
$domain_entry->{zimbraAuthMech} !~ m/^ad|ldap$/i
) {
if ( yaml_bool( $conf->{domains}->{$domain}->{zimbra}->{setup_ldap_auth} ) ) {
send_zmprov_cmd( "modifyDomain $domain " . build_domain_attrs( $conf->{domains}->{$domain} ) );
} else {
handle_error( $domain, 'Domain external auth check', "domain $domain must be configured for LDAP or AD authentication first" );
handle_error(
$domain,
'Domain external auth check',
"domain $domain must be configured for LDAP or AD authentication first"
);
next DOMAIN;
}
}
@ -128,16 +149,22 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
filter => "(&(objectClass=zimbraDomain)(zimbraDomainAliasTargetId=" . $domain_entry->{zimbraId} . "))"
);
if ( $zim_domain_alias_search->code ) {
handle_error( $domain, 'Zimbra domain alias lookup', $zim_domain_alias_search->error );
handle_error(
$domain,
'Zimbra domain alias lookup',
$zim_domain_alias_search->error
);
next DOMAIN;
}
$domain_entry->{zimbraDomainAliases} = [];
foreach my $alias ( $zim_domain_alias_search->entries ) {
push @{ $domain_entry->{zimbraDomainAliases} }, $alias->get_value('zimbraDomainName');
push @{ $domain_entry->{zimbraDomainAliases} },
$alias->get_value('zimbraDomainName');
}
log_verbose( "Trying to connect to " . join( ' or ', @{ $conf->{domains}->{$domain}->{ldap}->{servers} } ) );
log_verbose( "Trying to connect to " .
join( ' or ', @{ $conf->{domains}->{$domain}->{ldap}->{servers} } ) );
my $ext_ldap = Net::LDAP->new( [ @{ $conf->{domains}->{$domain}->{ldap}->{servers} } ] );
if ( not $ext_ldap ) {
@ -185,14 +212,21 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
]
);
if ( $zim_aliases_search->code ) {
handle_error( $domain, 'Zimbra user and distribution lists alias lookup', $zim_aliases_search->error );
handle_error(
$domain,
'Zimbra user and distribution lists alias lookup',
$zim_aliases_search->error
);
next DOMAIN;
}
$zim_aliases->{$domain_alias} = ldap2hashref( $zim_aliases_search, 'uid' );
}
log_verbose( "Searching for potential users in " . $conf->{domains}->{$domain}->{users}->{base} . " matching filter " . $conf->{domains}->{$domain}->{users}->{filter} );
log_verbose( "Searching for potential users in " .
$conf->{domains}->{$domain}->{users}->{base} .
" matching filter " .
$conf->{domains}->{$domain}->{users}->{filter} );
# List of attributes to fetch from LDAP
# First, we want all the attributes which are mapped to Zimbra fields
@ -207,6 +241,7 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
push $fetch_attrs, $conf->{domains}->{$domain}->{users}->{$_};
}
# Now we can run the lookup
my $ext_user_search = $ext_ldap->search(
base => $conf->{domains}->{$domain}->{users}->{base},
filter => $conf->{domains}->{$domain}->{users}->{filter},
@ -214,11 +249,16 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
);
if ( $ext_user_search->code ) {
handle_error( $domain, 'External LDAP user lookup', $ext_user_search->error );
handle_error(
$domain,
'External LDAP user lookup',
$ext_user_search->error
);
next DOMAIN;
}
log_verbose( "Found " . scalar $ext_user_search->entries . " users in external LDAP" );
log_verbose( "Found " . scalar $ext_user_search->entries .
" users in external LDAP" );
log_verbose( "Searching for users in Zimbra" );
@ -231,23 +271,37 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
'(mail=' . $zim_ldap->global->get_value('zimbraAmavisQuarantineAccount') . ')' .
'(uid=galsync*)(uid=admin))))',
attrs => [
( map { $conf->{domains}->{$domain}->{users}->{attr_map}->{$_} } keys $conf->{domains}->{$domain}->{users}->{attr_map} ),
( 'uid', 'zimbraAccountStatus', 'zimbraAuthLdapExternalDn', 'zimbraMailAlias', 'mail', 'zimbraNotes' )
( map { $conf->{domains}->{$domain}->{users}->{attr_map}->{$_} }
keys $conf->{domains}->{$domain}->{users}->{attr_map} ),
( 'uid',
'zimbraAccountStatus',
'zimbraAuthLdapExternalDn',
'zimbraMailAlias',
'mail',
'zimbraNotes' )
]
);
if ( $zim_user_search->code ) {
handle_error( $domain, 'Zimbra users lookup', $zim_user_search->error );
handle_error(
$domain,
'Zimbra users lookup',
$zim_user_search->error
);
next DOMAIN;
}
log_verbose( "Found " . scalar $zim_user_search->entries . " users in Zimbra" );
log_verbose( "Found " . scalar $zim_user_search->entries .
" users in Zimbra" );
log_verbose( "Comparing the accounts" );
my $ext_users = ldap2hashref(
$ext_user_search,
$conf->{domains}->{$domain}->{users}->{key},
( $conf->{domains}->{$domain}->{users}->{mail_attr}, $conf->{domains}->{$domain}->{users}->{alias_attr} )
(
$conf->{domains}->{$domain}->{users}->{mail_attr},
$conf->{domains}->{$domain}->{users}->{alias_attr}
)
);
my $zim_users = ldap2hashref(
$zim_user_search,
@ -264,14 +318,17 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
# User exists in Zimbra, lets check its attribute are up to date
foreach my $attr ( keys $conf->{domains}->{$domain}->{users}->{attr_map} ) {
if ( not defined $ext_users->{$user}->{$attr} and not defined $zim_users->{$user}->{$conf->{domains}->{$domain}->{users}->{attr_map}->{$attr}} ) {
if ( not defined $ext_users->{$user}->{$attr} and
not defined $zim_users->{$user}->{$conf->{domains}->{$domain}->{users}->{attr_map}->{$attr}} ) {
# Attr does not exist in external LDAP and in Zimbra, no need to continue comparing them
next;
}
if ( $conf->{domains}->{$domain}->{users}->{attr_map}->{$attr} ne 'sn' and not defined $ext_users->{$user}->{$attr} ) {
if ( $conf->{domains}->{$domain}->{users}->{attr_map}->{$attr} ne 'sn' and
not defined $ext_users->{$user}->{$attr} ) {
# If the attribute doesn't exist in external LDAP, we must remove it from Zimbra.
# Except for sn which is mandatory in Zimbra
$attrs .= '-' . $conf->{domains}->{$domain}->{users}->{attr_map}->{$attr} . " '" . $zim_users->{$user}->{$conf->{domains}->{$domain}->{users}->{attr_map}->{$attr}} . "' ";
$attrs .= '-' . $conf->{domains}->{$domain}->{users}->{attr_map}->{$attr} . " " .
zim_attr_value( $zim_users->{$user}->{$conf->{domains}->{$domain}->{users}->{attr_map}->{$attr}} );
} elsif (
( $conf->{domains}->{$domain}->{users}->{attr_map}->{$attr} ne 'sn' and
$ext_users->{$user}->{$attr} ne ( $zim_users->{$user}->{$conf->{domains}->{$domain}->{users}->{attr_map}->{$attr}} || '' )
@ -280,12 +337,12 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
defined $ext_users->{$user}->{$attr} and
$ext_users->{$user}->{$attr} ne ( $zim_users->{$user}->{$conf->{domains}->{$domain}->{users}->{attr_map}->{$attr}} || '' )
) {
$attrs .= " " . $conf->{domains}->{$domain}->{users}->{attr_map}->{$attr} . " " . zim_attr_value( $ext_users->{$user}->{$attr} );
$attrs .= " " . $conf->{domains}->{$domain}->{users}->{attr_map}->{$attr} . " " .
zim_attr_value( $ext_users->{$user}->{$attr} );
log_verbose( "Attribute $attr for user $user changed from " .
$zim_users->{$user}->{$conf->{domains}->{$domain}->{users}->{attr_map}->{$attr}} .
" to " .
$ext_users->{$user}->{$attr}
);
$ext_users->{$user}->{$attr} );
}
}
@ -308,7 +365,8 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
foreach my $attr ( keys $conf->{domains}->{$domain}->{users}->{attr_map} ) {
next if (not defined $ext_users->{$user}->{$attr} or $ext_users->{$user}->{$attr} eq '');
$attrs .= ' ' . $conf->{domains}->{$domain}->{users}->{attr_map}->{$attr} . " " . zim_attr_value( $ext_users->{$user}->{$attr} );
$attrs .= ' ' . $conf->{domains}->{$domain}->{users}->{attr_map}->{$attr} . " " .
zim_attr_value( $ext_users->{$user}->{$attr} );
}
# The password won't be used because Zimbra is set to use external LDAP/AD auth
# But better to set it to a random value
@ -318,11 +376,10 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
my @ext_aliases = ();
foreach my $mail_attr ( qw( mail_attr alias_attr ) ) {
next if (
not defined $conf->{domains}->{$domain}->{users}->{$mail_attr} or
not defined $ext_users->{$user}->{$conf->{domains}->{$domain}->{users}->{$mail_attr}}
);
push @ext_aliases, @{ $ext_users->{$user}->{$conf->{domains}->{$domain}->{users}->{$mail_attr}} };
next if ( not defined $conf->{domains}->{$domain}->{users}->{$mail_attr} or
not defined $ext_users->{$user}->{$conf->{domains}->{$domain}->{users}->{$mail_attr}} );
push @ext_aliases,
@{ $ext_users->{$user}->{$conf->{domains}->{$domain}->{users}->{$mail_attr}} };
}
@ext_aliases = sort @ext_aliases;
@ -335,23 +392,27 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
}
# On each sync, we register the list of LDAP aliases into Zimbra's LDAP in the zimbraNotes attribute
# We can compare if it has changed, and and/remove the aliases accordingly
# We can compare if it has changed, and add/remove the aliases accordingly
# This is not very clean, but at least allows the script to be "stateless"
# and only relies on LDAP content on both sides
# and only relies on LDAP content on both sides. If only zimbraAlias objectClass allowed zimbraNotes attribute
# it'd be easier
my $ext_prev_aliases = parse_zimbra_notes( $zim_users->{$user}->{zimbraNotes} || '' )->{LDAP_Aliases};
my @ext_prev_aliases = ( defined $ext_prev_aliases ) ? sort @{ $ext_prev_aliases } : ();
my $alias_diff = Array::Diff->diff( \@ext_prev_aliases, \@ext_aliases );
foreach my $alias ( @{ $alias_diff->deleted } ) {
my ( $al, $dom ) = split /\@/, $alias;
next if ( not defined $zim_aliases->{$dom} or not defined $zim_aliases->{$dom}->{$al} );
log_verbose( "Removing LDAP alias $alias from user $user as it doesn't exist in LDAP anymore" );
next if ( not defined $zim_aliases->{$dom} or
not defined $zim_aliases->{$dom}->{$al} );
log_verbose( "Removing LDAP alias $alias from user $user " .
"as it doesn't exist in LDAP anymore" );
send_zmprov_cmd( "removeAccountAlias $user\@$domain $alias" );
}
my $note = $sync_from_ldap . "|LDAP_Aliases=" . join(',', @ext_aliases);
if ( $note ne ($zim_users->{$user}->{zimbraNotes} || '') ) {
send_zmprov_cmd( "modifyAccount $user\@$domain zimbraNotes " . zim_attr_value( $note ) );
send_zmprov_cmd( "modifyAccount $user\@$domain zimbraNotes " .
zim_attr_value( $note ) );
}
}
@ -364,12 +425,13 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
$zim_users->{$user}->{zimbraNotes} =~ m/^$sync_from_ldap/ and
defined $zim_users->{$user}->{zimbraAccountStatus} and
$zim_users->{$user}->{zimbraAccountStatus} =~ m/^active|lockout$/ ) {
log_verbose( "User $user doesn't exist in external LDAP anymore, locking it in Zimbra" );
log_verbose( "User $user doesn't exist in external LDAP anymore, " .
"locking it in Zimbra" );
send_zmprov_cmd( "modifyAccount $user\@$domain zimbraAccountStatus locked" );
}
}
# Now, we try to sync groups in external LDAP into distribution list in Zimbra
# Now, we try to sync groups in external LDAP into distribution lists in Zimbra
if ( defined $conf->{domains}->{$domain}->{groups} ) {
log_verbose( "Searching for potential groups in " .
@ -397,7 +459,8 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
next DOMAIN;
}
log_verbose( "Found " . scalar $ext_group_search->entries . " groups in external LDAP" );
log_verbose( "Found " . scalar $ext_group_search->entries .
" groups in external LDAP" );
log_verbose( "Searching for distribution lists in Zimbra" );
@ -406,23 +469,37 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
base => 'ou=people,' . $domain_entry->{dn},
filter => "(objectClass=zimbraDistributionList)",
attrs => [
( map { $conf->{domains}->{$domain}->{groups}->{attr_map}->{$_} } keys $conf->{domains}->{$domain}->{groups}->{attr_map} ),
( 'uid', 'zimbraDistributionListSubscriptionPolicy', 'zimbraDistributionListUnsubscriptionPolicy',
'zimbraMailForwardingAddress', 'zimbraNotes', 'zimbraMailStatus', 'mail' )
( map { $conf->{domains}->{$domain}->{groups}->{attr_map}->{$_} }
keys $conf->{domains}->{$domain}->{groups}->{attr_map} ),
(
'uid',
'zimbraDistributionListSubscriptionPolicy',
'zimbraDistributionListUnsubscriptionPolicy',
'zimbraMailForwardingAddress',
'zimbraNotes',
'zimbraMailStatus',
'mail'
)
]
);
if ( $zim_dl_search->code ) {
handle_error( $domain, 'Zimbra distribution lists lookup', $zim_dl_search->error );
handle_error(
$domain,
'Zimbra distribution lists lookup',
$zim_dl_search->error
);
next DOMAIN;
}
log_verbose( "Found " . scalar $zim_dl_search->entries . " distribution list(s) in Zimbra" );
log_verbose( "Found " . scalar $zim_dl_search->entries .
" distribution list(s) in Zimbra" );
log_verbose( "Comparing groups with distribution lists" );
my $ext_groups = ldap2hashref(
$ext_group_search,
$conf->{domains}->{$domain}->{groups}->{key},
( $conf->{domains}->{$domain}->{groups}->{members_attr},
(
$conf->{domains}->{$domain}->{groups}->{members_attr},
$conf->{domains}->{$domain}->{groups}->{mail_attr},
$conf->{domains}->{$domain}->{groups}->{alias_attr}
)
@ -434,53 +511,49 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
);
# Build a dn2id hashref to lookup users or groups by their DN
my $dn2id = {};
$dn2id->{$ext_users->{$_}->{dn}} = $_ foreach ( keys $ext_users );
my $dn2id = {};
$dn2id->{$ext_users->{$_}->{dn}} = $_ foreach ( keys $ext_users );
$dn2id->{$ext_groups->{$_}->{dn}} = $_ foreach ( keys $ext_groups );
# First loop, check if every group in LDAP exist as a DL in Zimbra
# First loop, check if every group in LDAP exists as a DL in Zimbra
foreach my $group ( keys $ext_groups ) {
if ( defined $zim_dl->{$group} ) {
# A group match an existing DL, we must check its attributes
my $attrs = '';
foreach my $attr ( keys $conf->{domains}->{$domain}->{groups}->{attr_map} ) {
if (
not defined $ext_groups->{$group}->{$attr} and
not defined $zim_dl->{$group}->{$conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr}}
) {
if ( not defined $ext_groups->{$group}->{$attr} and
not defined $zim_dl->{$group}->{$conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr}} ) {
# Attr does not exist in external LDAP and in Zimbra, not need to continue
next;
} elsif ( not defined $ext_groups->{$group}->{$attr} ) {
# Attr doesn't exist in external LDAP, but exists in Zimbra. We must remove it
$attrs = ' -' . $conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr} . " " . zim_attr_value( $zim_dl->{$group}->{$conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr}} );
$attrs = ' -' . $conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr} . " " .
zim_attr_value( $zim_dl->{$group}->{$conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr}} );
} elsif ( $ext_groups->{$group}->{$attr} ne $zim_dl->{$group}->{$conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr}} ) {
# Attr exists in both but doesn't match
$attrs .= " " . $conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr} . " " . zim_attr_value( $ext_groups->{$group}->{$attr} );
log_verbose( $ext_groups->{$group}->{$attr} . " vs " . $zim_dl->{$group}->{$conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr}} );
$attrs .= " " . $conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr} . " " .
zim_attr_value( $ext_groups->{$group}->{$attr} );
log_verbose( $ext_groups->{$group}->{$attr} . " vs " .
$zim_dl->{$group}->{$conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr}} );
}
}
# Users cannot subscribe or unsubscribe from LDAP group
if (
not defined $zim_dl->{$group}->{zimbraDistributionListSubscriptionPolicy} or
$zim_dl->{$group}->{zimbraDistributionListSubscriptionPolicy} ne 'REJECT'
) {
if ( not defined $zim_dl->{$group}->{zimbraDistributionListSubscriptionPolicy} or
$zim_dl->{$group}->{zimbraDistributionListSubscriptionPolicy} ne 'REJECT' ) {
$attrs .= " zimbraDistributionListSubscriptionPolicy REJECT";
}
if (
not defined $zim_dl->{$group}->{zimbraDistributionListUnsubscriptionPolicy} or
$zim_dl->{$group}->{zimbraDistributionListUnsubscriptionPolicy} ne 'REJECT'
) {
if ( not defined $zim_dl->{$group}->{zimbraDistributionListUnsubscriptionPolicy} or
$zim_dl->{$group}->{zimbraDistributionListUnsubscriptionPolicy} ne 'REJECT' ) {
$attrs .= " zimbraDistributionListUnsubscriptionPolicy REJECT";
}
# If the group in LDAP has a mail defined, enable mail delivery in Zimbra. Else, disable it
my $mail_status = ( defined $ext_groups->{$group}->{$conf->{domains}->{$domain}->{groups}->{mail_attr}} ) ? 'enabled' : 'disabled';
if (
not defined $zim_dl->{$group}->{zimbraMailStatus} or
$zim_dl->{$group}->{zimbraMailStatus} ne $mail_status
) {
my $mail_status = ( defined $ext_groups->{$group}->{$conf->{domains}->{$domain}->{groups}->{mail_attr}} ) ?
'enabled' : 'disabled';
if ( not defined $zim_dl->{$group}->{zimbraMailStatus} or
$zim_dl->{$group}->{zimbraMailStatus} ne $mail_status ) {
$attrs .= " zimbraMailStatus $mail_status";
}
@ -495,11 +568,15 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
log_verbose( "Found a new group : $group. Creating it in Zimbra" );
my $attrs = '';
foreach my $attr ( keys $conf->{domains}->{$domain}->{groups}->{attr_map} ) {
next if (not defined $ext_groups->{$group}->{$attr} or $ext_groups->{$group}->{$attr} eq '');
$attrs .= ' ' . $conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr} . " " . zim_attr_value( $ext_groups->{$group}->{$attr} );
next if ( not defined $ext_groups->{$group}->{$attr} or
$ext_groups->{$group}->{$attr} eq '');
$attrs .= ' ' . $conf->{domains}->{$domain}->{groups}->{attr_map}->{$attr} . " " .
zim_attr_value( $ext_groups->{$group}->{$attr} );
}
$attrs .= " zimbraDistributionListUnsubscriptionPolicy REJECT zimbraDistributionListSubscriptionPolicy REJECT";
my $mail_status = ( defined $ext_groups->{$group}->{$conf->{domains}->{$domain}->{groups}->{mail_attr}} ) ? 'enabled' : 'disabled';
$attrs .= " zimbraDistributionListUnsubscriptionPolicy REJECT"
$attrs .= " zimbraDistributionListSubscriptionPolicy REJECT";
my $mail_status = ( defined $ext_groups->{$group}->{$conf->{domains}->{$domain}->{groups}->{mail_attr}} ) ?
'enabled' : 'disabled';
$attrs .= " zimbraMailStatus $mail_status";
send_zmprov_cmd( "createDistributionList $group\@$domain $attrs" );
}
@ -509,30 +586,40 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
my @ext_members = ();
if ( not yaml_bool( $conf->{domains}->{$domain}->{groups}->{members_as_dn} ) ) {
# If members are not listed as full DN, but by uid, simply concat it with the domain
foreach my $member ( $ext_groups->{$group}->{$conf->{domains}->{$domain}->{groups}->{members_attr}} ) {
next if ( not defined $ext_users->{$member} and not defined $ext_groups->{$member} );
next if ( not defined $ext_users->{$member} and
not defined $ext_groups->{$member} );
push @ext_members, $member . '@' . $domain;
}
} else {
# If members are listed as full DN, we need to lookup in the dn2id we prepared earlier
foreach my $member ( @{ $ext_groups->{$group}->{$conf->{domains}->{$domain}->{groups}->{members_attr}} } ) {
next if not defined $dn2id->{$member};
next if ( not defined $dn2id->{$member} );
push @ext_members, $dn2id->{$member} . '@' . $domain;
}
}
@ext_members = sort @ext_members;
my @zim_members = (defined $zim_dl->{$group}->{zimbraMailForwardingAddress} ) ? sort @{$zim_dl->{$group}->{zimbraMailForwardingAddress}} : ();
my @zim_members = ( defined $zim_dl->{$group}->{zimbraMailForwardingAddress} ) ?
sort @{$zim_dl->{$group}->{zimbraMailForwardingAddress}} : ();
# Now we can compare members for this group in external LDAP and Zimbra
my $diff = Array::Diff->diff( \@ext_members, \@zim_members );
if ( scalar @{ $diff->deleted } gt 0 ){
send_zmprov_cmd( "addDistributionListMember $group\@$domain " . join (' ', @{ $diff->deleted } ) );
send_zmprov_cmd( "addDistributionListMember $group\@$domain " .
join (' ', @{ $diff->deleted } ) );
}
if ( scalar @{ $diff->added } gt 0 ) {
send_zmprov_cmd( "removeDistributionListMember $group\@$domain $_ ") foreach ( @{ $diff->added } );
send_zmprov_cmd( "removeDistributionListMember $group\@$domain $_")
foreach ( @{ $diff->added } );
}
my @ext_aliases = ();
foreach my $mail_attr ( qw( mail_attr alias_attr ) ) {
next if (not defined $conf->{domains}->{$domain}->{groups}->{$mail_attr} or not defined $ext_groups->{$group}->{$conf->{domains}->{$domain}->{groups}->{$mail_attr}} );
push @ext_aliases, @{ $ext_groups->{$group}->{$conf->{domains}->{$domain}->{groups}->{$mail_attr}} };
next if ( not defined $conf->{domains}->{$domain}->{groups}->{$mail_attr} or
not defined $ext_groups->{$group}->{$conf->{domains}->{$domain}->{groups}->{$mail_attr}} );
push @ext_aliases,
@{ $ext_groups->{$group}->{$conf->{domains}->{$domain}->{groups}->{$mail_attr}} };
}
foreach my $alias ( @ext_aliases ) {
next if ( not alias_matches_domain( $alias, $domain_entry ) );
@ -548,22 +635,27 @@ DOMAIN: foreach my $domain ( keys $conf->{domains} ) {
foreach my $alias ( @{ $alias_diff->deleted } ) {
my ( $al, $dom ) = split /\@/, $alias;
next if ( not defined $zim_aliases->{$dom} or not defined $zim_aliases->{$dom}->{$al} );
log_verbose( "Removing LDAP alias $alias from distribution list $group as it doesn't exist in LDAP anymore" );
next if ( not defined $zim_aliases->{$dom} or
not defined $zim_aliases->{$dom}->{$al} );
log_verbose( "Removing LDAP alias $alias from distribution list $group " .
"as it doesn't exist in LDAP anymore" );
send_zmprov_cmd( "removeDistributionListAlias $group\@$domain $alias" );
}
my $note = $sync_from_ldap . "|LDAP_Aliases=" . join(',', @ext_aliases);
if ( $note ne ($zim_dl->{$group}->{zimbraNotes} || '') ) {
send_zmprov_cmd( "modifyDistributionList $group\@$domain zimbraNotes " . zim_attr_value( $note ) );
send_zmprov_cmd( "modifyDistributionList $group\@$domain zimbraNotes " .
zim_attr_value( $note ) );
}
}
# Now, look at all the distribution list which were created from LDAP but doesn't exist anymore in LDAP
foreach my $dl ( keys $zim_dl ) {
next if ( not defined $zim_dl->{$dl}->{zimbraNotes} or $zim_dl->{$dl}->{zimbraNotes} !~ m/^$sync_from_ldap/ );
next if ( not defined $zim_dl->{$dl}->{zimbraNotes} or
$zim_dl->{$dl}->{zimbraNotes} !~ m/^$sync_from_ldap/ );
next if ( defined $ext_groups->{$dl} );
log_verbose( "Group $dl doesn't exist in LDAP anymore, removing the corresponding distribution list" );
log_verbose( "Group $dl doesn't exist in LDAP anymore, " .
"removing the corresponding distribution list" );
send_zmprov_cmd( "deleteDistributionList $dl\@$domain" );
}
}
@ -633,7 +725,9 @@ sub handle_error {
},
body_str => "LDAP synchronisation for domain $domain failed at step '$step'. The error was\n$err\n",
);
my $transport = Email::Sender::Transport::Sendmail->new({ sendmail => '/opt/zimbra/common/sbin/sendmail' });
my $transport = Email::Sender::Transport::Sendmail->new({
sendmail => '/opt/zimbra/common/sbin/sendmail'
});
sendmail( $mail, { transport => $transport } );
}
$exit = 255;
@ -642,7 +736,7 @@ sub handle_error {
# ldap2hashref takes three args
# * An LDAP search result
# * The attribute used as the key of objects
# * an optional array of attributes we want as an array, even if there's a single value
# * An optional array of attributes we want as an array, even if there's a single value
# It'll return a hashref. The key will be unaccentuated and lower cased.
sub ldap2hashref {
@ -675,17 +769,21 @@ sub yaml_bool {
# Takes the domain conf hashref as only arg
sub build_domain_attrs {
my $domain_conf = shift;
my $attrs = "zimbraAuthMech " . zim_attr_value( $domain_conf->{ldap}->{type} );
$attrs .= " zimbraAuthMechAdmin " . zim_attr_value( $domain_conf->{ldap}->{type} );
if ( defined $domain_conf->{ldap}->{bind_dn} and defined $domain_conf->{ldap}->{bind_pass} ) {
my $type = ( $domain_conf->{ldap}->{schema} eq =~ m/^ad/i ) ? 'ad' : 'ldap';
my $attrs = "zimbraAuthMech " . zim_attr_value( $type );
$attrs .= " zimbraAuthMechAdmin " . zim_attr_value( $type );
if ( defined $domain_conf->{ldap}->{bind_dn} and
defined $domain_conf->{ldap}->{bind_pass} ) {
$attrs .= " zimbraAuthLdapSearchBindDn " . zim_attr_value( $domain_conf->{ldap}->{bind_dn} );
$attrs .= " zimbraAuthLdapSearchBindPassword " . zim_attr_value( $domain_conf->{ldap}->{bind_pass} );
}
# if ( defined $domain_conf->{users}->{filter} ) {
# $attrs = " zimbraAuthLdapSearchFilter " . zim_attr_value( "(&(|(" . $domain_conf->{users}->{key} . "=%u)(" . $domain_conf->{users}->{key} . "=%n))(" . $domain_conf->{users}->{filter} . ")" );
# }
$attrs .= " zimbraAuthLdapURL " . join( ' +zimbraAuthLdapURL', zim_attr_value( $domain_conf->{ldap}->{servers} ) );
if ( defined $domain_conf->{ldap}->{start_tls} and yaml_bool( $domain_conf->{ldap}->{start_tls} ) ) {
$attrs .= " zimbraAuthLdapURL " .
join( ' +zimbraAuthLdapURL', zim_attr_value( $domain_conf->{ldap}->{servers} ) );
if ( defined $domain_conf->{ldap}->{start_tls} and
yaml_bool( $domain_conf->{ldap}->{start_tls} ) ) {
$attrs .= " zimbraAuthLdapStartTlsEnabled TRUE";
} else {
$attrs .= " zimbraAuthLdapStartTlsEnabled FALSE";
Loading…
Cancel
Save