- [kopano](src/kopano/plugin/movetopublicldap.py) Add sample config file for the kopano-dagent python plugin movetopublicldap.

master v1.2.7
mlan 2021-01-08 18:09:55 +01:00
parent b9c2529745
commit 6e32101634
8 changed files with 90 additions and 42 deletions

View File

@ -1,6 +1,7 @@
# 1.2.7
- [kopano](src/kopano/plugin/movetopublicldap.py) Add LDAP support to the [Move to public](https://documentation.kopano.io/kopanocore_administrator_manual/special_kc_configurations.html#move-to-public ) kopano-dagent python plugin.
- [kopano](src/kopano/plugin/movetopublicldap.py) Add sample config file for the kopano-dagent python plugin movetopublicldap.
# 1.2.6

View File

@ -444,13 +444,13 @@ The [Mobile Device Management](https://documentation.kopano.io/webapp_mdm_manual
There are two type of stores (folders containing communication elements); private and public stores. There can only be one public store. It is the Kopano dagent that places incoming messages into mail boxes, that is the private and public stores.
Public folders are managed by the system admin and not by individual users. Users have them mapped automatically. The public folders can be synced via [Exchange ActiveSync (EAS)](https://wiki.z-hub.io/display/ZP/Sharing+folders+and+Read-only) .
Public folders are configured by the system admin. Users have them mapped automatically. The shared and public folders can be synced with mobile devices via [Exchange ActiveSync (EAS)](https://wiki.z-hub.io/display/ZP/Sharing+folders+and+Read-only). Each user can manage which shared or public folders to sync with her mobile devices by using the [Mobile Device Management](#mobile-device-management) WebApp plugin.
With the current Kopano implementation, delivering to the public store is configured separately from normal user management. There is the [move to public](https://documentation.kopano.io/kopanocore_administrator_manual/special_kc_configurations.html#move-to-public) plugin which moves incoming messages to a folder in the public store. It has a static configuration and does not support LDAP lookup.
### Move to public with LDAP lookup
The `mlan/kopano` image include a extended version of the move to public plugin which use LDAP lookup, instead of a static file based lookup. When the plugin [move to public with LDAP](src/kopano/plugin/movetopublicldap.py) is enabled, `DAGENT_PLUGINS=movetopublicldap.py`, the kopano dagent will do two LDAP queries. The first is to search for an entry/user with matching email address. The second, introduced with this plugin, is to get the public folder from this entry. If found the message will be delivered to the public folder otherwise it will be delivered to the mailbox of the user.
The `mlan/kopano` image include a extended version of the move to public plugin which use LDAP lookup, instead of a static file based lookup. When the plugin [move to public with LDAP](src/kopano/plugin/movetopublicldap.py) is enabled, `DAGENT_PLUGINS=movetopublicldap`, the kopano dagent will do two LDAP queries. The first is to search for an entry/user with matching email address. The second, introduced with this plugin, is to get the public folder from this entry. If found the message will be delivered to the public folder otherwise it will be delivered to the mailbox of the user.
Lets demonstrate how delivery to a public folder is configured. With this LDAP entry:
@ -466,11 +466,11 @@ mail: public@example.com
kopanoAccount: 1
kopanoHidden: 1
kopanoSharedStoreOnly: 1
kopanoResourceType: publicStore:Public Stores/public
kopanoResourceType: publicFolder:Public Stores/public
```
messages to `public@example.com` will be delivered to the public store in `Public Stores/public`.
The central [attribute](https://documentation.kopano.io/kopanocore_administrator_manual/appendix_b.html#appendix-b-ldap-attribute-description) is `kopanoResourceType: publicStore:Public Stores/public`. It contains a token and a folder name. The token match is case sensitive and there must be a colon `:` separating
The central [attribute](https://documentation.kopano.io/kopanocore_administrator_manual/appendix_b.html#appendix-b-ldap-attribute-description) is `kopanoResourceType: publicFolder:Public Stores/public`. It contains a token and a folder name. The token match is case sensitive and there must be a colon `:` separating
the token and the public folder name. The folder name can contain space and
sub folders, which are distinguished using a forward slash `/`.
@ -479,17 +479,17 @@ The LDAP attribute holding the token and the token itself have the following
default values, which can be modified in `/etc/kopano/movetopublicldap.cfg` if desired.
```yaml
ldap_public_store_attribute = kopanoResourceType
ldap_public_store_attribute_token = publicStore
ldap_public_folder_attribute = kopanoResourceType
ldap_public_folder_attribute_token = publicFolder
```
As with other parameters, environment variables can be used to define them: `LDAP_PUBLIC_STORE_ATTRIBUTE=kopanoResourceType` and `LDAP_PUBLIC_STORE_ATTRIBUTE_TOKEN=publicStore`.
As with other parameters, environment variables can be used to define them: `LDAP_PUBLIC_FOLDER_ATTRIBUTE=kopanoResourceType` and `LDAP_PUBLIC_FOLDER_ATTRIBUTE_TOKEN=publicFolder`.
## Shared folders
Users can share folders when sufficient permission have been granted. When logged into WebApp with an administrative account (`kopanoAdmin: 1`) you can modify the permissions on users shares and folders. Users can then, when logged into WebApp, open the inbox of other users by selecting `Open Shared Mails`.
The [impersonation](https://wiki.z-hub.io/display/ZP/Impersonation) mechanism allow such shared folders to be synced over [Exchange ActiveSync (EAS)](https://wiki.z-hub.io/display/ZP/Sharing+folders+and+Read-only) too.
The [impersonation](https://wiki.z-hub.io/display/ZP/Impersonation) mechanism allow such shared folders to be synced over [Exchange ActiveSync (EAS)](https://wiki.z-hub.io/display/ZP/Sharing+folders+and+Read-only) too. Each user can manage which shared or public folders to sync with her mobile devices by using the [Mobile Device Management](#mobile-device-management) WebApp plugin.
## Crontab

View File

@ -179,7 +179,7 @@ mail: $(LDAP_TEST_PUB)@$(MAIL_DOMAIN)
kopanoAccount: 1
kopanoHidden: 1
kopanoSharedStoreOnly: 1
kopanoResourceType: publicStore:Public Stores/public
kopanoResourceType: publicFolder:Public Stores/public
endef
export LDIF_ADD_STO

View File

@ -27,7 +27,7 @@ services:
- LDAP_GROUP_TYPE_ATTRIBUTE_VALUE=${LDAP_GROUPOBJ-posixGroup}
- LDAP_GROUPMEMBERS_ATTRIBUTE_TYPE=dn
- LDAP_PROPMAP=
- DAGENT_PLUGINS=movetopublicldap.py
- DAGENT_PLUGINS=movetopublicldap
- MYSQL_DATABASE=${MYSQL_DATABASE-kopano}
- MYSQL_USER=${MYSQL_USER-kopano}
- MYSQL_PASSWORD=${MYSQL_PASSWORD-secret}

View File

@ -18,7 +18,7 @@ DOCKER_MAN5_DIR=${DOCKER_MAN5_DIR-/usr/share/man/man5/}
DOCKER_APPL_SSL_CERT=${DOCKER_APPL_SSL_CERT-$DOCKER_APPL_SSL_DIR/cert.pem}
DOCKER_APPL_SSL_KEY=${DOCKER_APPL_SSL_KEY-$DOCKER_APPL_SSL_DIR/priv_key.pem}
DOCKER_LDAP_PMAP_FILE=${DOCKER_LDAP_PMAP_FILE-/usr/share/kopano/ldap.propmap.cfg}
DOCKER_LDAP_SERVICES=${DOCKER_LDAP_SERVICES-archiver dagent gateway ical ldap movetopublicldap search server spamd spooler}
DOCKER_LDAP_SERVICES=${DOCKER_LDAP_SERVICES-archiver dagent gateway ical ldap search server spamd spooler}
sqlstate_cfg_file=$DOCKER_CONF_DIR2/backend/sqlstatemachine/config.php
zpush_cfg_file=$DOCKER_CONF_DIR2/config.php
webapp_cfg_file=$DOCKER_CONF_DIR1/webapp/config.php
@ -30,7 +30,7 @@ webapp_smime_cfg_file=$DOCKER_CONF_DIR1/webapp/config-smime.php
# CLI commands
#
list_parms() {
local services="$DOCKER_LDAP_SERVICES"
local services="$DOCKER_LDAP_SERVICES $DAGENT_PLUGINS"
[ $# -ge 0 ] && services="$@"
for service in $services; do
[ $# -ne 1 ] && echo "# $service"
@ -50,7 +50,7 @@ list_parms() {
#
kopano_apply_envvars_core() {
kopano_enable_envvars_plugin
for service in $DOCKER_LDAP_SERVICES; do
for service in $DOCKER_LDAP_SERVICES $DAGENT_PLUGINS; do
kopano_apply_envvars_cfg $service
done
}
@ -130,7 +130,7 @@ kopano_get_envvars_ext() {
local vars
case $service in
movetopublicldap)
vars="ldap_public_store_attribute ldap_public_store_attribute_token"
vars="ldap_public_folder_attribute ldap_public_folder_attribute_token"
;;
esac
echo $vars | tr "[:lower:]" "[:upper:]" | tr " " "\n" | sort -u
@ -143,9 +143,16 @@ kopano_enable_envvars_plugin() {
for service in dagent spooler; do
local envvar=${service^^}_PLUGINS
for plugin in ${!envvar}; do
dc_log 5 "Enabling ${service} python plugin ${plugin}"
ln -sf /usr/share/kopano-${service}/python/plugins/${plugin} /var/lib/kopano/${service}/plugins
export ${service^^}_PLUGIN_ENABLED=yes
local pyfile=$(find /usr/share/kopano* -name ${plugin}.py)
if [ -z "$pyfile" ]; then
dc_log 5 "Unable to locate ${service} python plugin ${plugin}"
else
dc_log 5 "Enabling ${service} python plugin ${plugin}"
mkdir -p $DOCKER_CONF_DIR1/${service}
ln -sf ${pyfile} $DOCKER_CONF_DIR1/${service}
export ${service^^}_PLUGIN_PATH=$DOCKER_CONF_DIR1/${service}
export ${service^^}_PLUGIN_ENABLED=yes
fi
done
done
}

View File

@ -0,0 +1,32 @@
# movetopublicldap.cfg
#
# Move mail to public ldap configuration
#
# This configuration file is used by the dagent plugin 'movetopublicldap.py'
# The default location is '/etc/kopano/movetopublicldap.cfg'
#
#
# The plugin reads two coniguration files.
# First it looks for parameters in '/etc/kopano/ldap.cfg' allowing
# common conifuration parameters to be kep tin on place.
# Second it reads '/etc/kopano/movetopublicldap.cfg'
#
#
# Parameters that can be set in both files:
#
# ldap_uri =
# ldap_starttls = no
# ldap_search_base =
# ldap_bind_user =
# ldap_bind_passwd =
# ldap_user_unique_attribute = uid
#
#
# Parameters that can only be set in ovetopublicldap.cfg:
#
# ldap_public_folder_attribute = kopanoResourceType
# ldap_public_folder_attribute_token = publicFolder
#

View File

@ -8,13 +8,13 @@ store. If folders are missing they will be created.
A LDAP entry including:
kopanoAccount: 1
kopanoResourceType: publicStore:<public folder>
kopanoResourceType: publicFolder:<public folder>
will have its email delivered to the public store in <public folder>.
The token match is case sensitive and there must be a colon ':' separating
the token and the public folder name. The folder name can contain space and
sub folders, which are distinguished using forward slash '/'.
So if we have 'kopanoResourceType: publicStore:Public Stores/public' emails will
So if we have 'kopanoResourceType: publicFolder:Public Stores/public' emails will
be delivered to 'Public Folders/Public Stores/public'.
The parameters in /etc/kopano/ldap.cfg will be used for the LDAP query.
@ -22,8 +22,8 @@ The LDAP attribute holding the token and the token itself have the following
default values, which can be modified in /etc/kopano/movetopublicldap.cfg
if desired.
ldap_public_store_attribute = kopanoResourceType
ldap_public_store_attribute_token = publicStore
ldap_public_folder_attribute = kopanoResourceType
ldap_public_folder_attribute_token = publicFolder
"""
from sys import hexversion
@ -37,6 +37,7 @@ from plugintemplates import IMapiDAgentPlugin, MP_CONTINUE, MP_STOP_SUCCESS
from zconfig import ZConfigParser
import configparser
import ldap
import os.path
class KConfigParser(ZConfigParser):
""" Extends zconfig.ZConfigParser to also allow !directive in cfg files """
@ -55,12 +56,13 @@ class MoveToPublic(IMapiDAgentPlugin):
DEFAULTCONFIG = {
'ldap_uri': None,
'ldap_starttls': "no",
'ldap_search_base': None,
'ldap_bind_user': None,
'ldap_bind_passwd': None,
'ldap_user_unique_attribute': "uid",
'ldap_public_store_attribute': "kopanoResourceType",
'ldap_public_store_attribute_token': "publicStore"
'ldap_public_folder_attribute': "kopanoResourceType",
'ldap_public_folder_attribute_token': "publicFolder"
}
def __init__(self, logger):
@ -72,34 +74,40 @@ class MoveToPublic(IMapiDAgentPlugin):
options = [opt.split('_', 1)[1] for opt in defaultconfig.keys()]
config = None
for configfile in configfiles:
if not config:
config = KConfigParser(configfile, defaultconfig)
if os.path.isfile(configfile):
self.logger.logDebug("*--- Reading file {}".format(configfile))
if not config:
config = KConfigParser(configfile, defaultconfig)
else:
config = KConfigParser(configfile, config.options())
else:
config = KConfigParser(configfile, config.options())
self.logger.logDebug("*--- Can't find file {}".format(configfile))
self.config = config.getdict('ldap',options)
self.logger.logDebug("*--- Config list {}".format(self.config))
return self.config
def searchfilter(self, recipient):
""" (&(uid=recipient)(kopanoResourceType=publicStore:*)) """
""" (&(uid=recipient)(kopanoResourceType=publicFolder:*)) """
return ("(&({}={})({}={}:*))"
.format(self.config['user_unique_attribute'],
recipient,
self.config['public_store_attribute'],
self.config['public_store_attribute_token']))
self.config['public_folder_attribute'],
self.config['public_folder_attribute_token']))
def searchquery(self, recipient):
""" Query a LDAP/AD driectory server to lookup recipient using
search_base and return public_store_attribute
search_base and return public_folder_attribute
"""
if (self.config['uri'] is None):
if not self.config or not self.config['uri']:
self.logger.logError(("!--- ldap_uri is not defined."
" Please check {}" .format(self.CONFIGFILES[0])))
" Please check {}" .format(self.CONFIGFILES)))
return None
else:
l = ldap.initialize(self.config['uri'])
try:
l.protocol_version = ldap.VERSION3
if self.config['starttls'] == 'yes':
l.start_tls_s()
l.simple_bind_s(self.config['bind_user'] or u'', \
self.config['bind_passwd'] or u'')
except ldap.SERVER_DOWN as e:
@ -108,7 +116,7 @@ class MoveToPublic(IMapiDAgentPlugin):
return None
except ldap.INVALID_CREDENTIALS as e:
self.logger.logError(("!--- Invalid LDAP credentials {}"
" Please check {}" .format(e, self.CONFIGFILES[0])))
" Please check {}" .format(e, self.CONFIGFILES)))
l.unbind_s()
return None
except ldap.LDAPError as e:
@ -118,19 +126,19 @@ class MoveToPublic(IMapiDAgentPlugin):
try:
result = l.search_s(self.config['search_base'], \
ldap.SCOPE_SUBTREE, self.searchfilter(recipient), \
[self.config['public_store_attribute']])
[self.config['public_folder_attribute']])
except ldap.LDAPError as e:
self.logger.logError("!--- LDAPError {}".format(e))
l.unbind_s()
return result
def publicfolder(self, recipient):
""" Check for ldap_public_store_attribute_token and return folder """
""" Check for ldap_public_folder_attribute_token and return folder """
destination_folder = []
result = self.searchquery(recipient)
if result:
tokenandfolder = (result[0][1]
.get(self.config['public_store_attribute'])[0].decode('utf-8'))
.get(self.config['public_folder_attribute'])[0].decode('utf-8'))
if tokenandfolder:
destination_folder = tokenandfolder.split(':')[1]
if destination_folder:

View File

@ -25,9 +25,9 @@ class ldapstores():
'ldap_bind_passwd': None,
'ldap_user_unique_attribute': "uid",
'ldap_user_search_filter': "(kopanoAccount=1)",
# 'ldap_user_search_filter': "(&(kopanoAccount=1)(kopanoResourceType=publicStore:*))",
'ldap_public_store_attribute': "kopanoResourceType",
'ldap_public_store_attribute_token': "publicStore"
# 'ldap_user_search_filter': "(&(kopanoAccount=1)(kopanoResourceType=publicFolder:*))",
'ldap_public_folder_attribute': "kopanoResourceType",
'ldap_public_folder_attribute_token': "publicFolder"
}
def __init__(self, configfile = '/etc/kopano/ldap.cfg'):
@ -58,7 +58,7 @@ class ldapstores():
ldap_result_id = l.search(self.config['search_base'], \
ldap.SCOPE_SUBTREE, self.config['user_search_filter'], \
[self.config['user_unique_attribute'], \
self.config['public_store_attribute']])
self.config['public_folder_attribute']])
results = []
while 1:
result_type, result_data = l.result(ldap_result_id, 0)
@ -77,11 +77,11 @@ class ldapstores():
public = []
for store in stores:
recipient = store[1].get(self.config['user_unique_attribute'])
tokenandfolder = store[1].get(self.config['ldap_public_store_attribute'])
tokenandfolder = store[1].get(self.config['ldap_public_folder_attribute'])
if tokenandfolder:
token = tokenandfolder.split(':')[0]
destination_folder = tokenandfolder.split(':')[1]
if (token == self.config['ldap_public_store_attribute_token']);
if (token == self.config['ldap_public_folder_attribute_token']);
public[recipient] = destination_folder
return public