summaryrefslogtreecommitdiffstats
path: root/roles/common
diff options
context:
space:
mode:
Diffstat (limited to 'roles/common')
-rw-r--r--roles/common/files/etc/ldap/schema/fripost.ldif179
-rw-r--r--roles/common/files/var/lib/ldap/fripost/DB_CONFIG5
-rw-r--r--roles/common/handlers/main.yml3
-rw-r--r--roles/common/tasks/ldap.yml66
-rw-r--r--roles/common/tasks/main.yml6
-rw-r--r--roles/common/templates/etc/ldap/database.ldif.j2487
6 files changed, 746 insertions, 0 deletions
diff --git a/roles/common/files/etc/ldap/schema/fripost.ldif b/roles/common/files/etc/ldap/schema/fripost.ldif
new file mode 100644
index 0000000..6ec55dc
--- /dev/null
+++ b/roles/common/files/etc/ldap/schema/fripost.ldif
@@ -0,0 +1,179 @@
+# Fripost's LDAP schema
+# Copyright 2013 Guilhem Moulin <guilhem@fripost.org>
+#
+# Licensed under the GNU GPL version 3 or higher.
+
+# Load this file with
+#
+# ldapadd -Y EXTERNAL -H ldapi:/// -f fripost.ldif
+#
+# It will load the schema. To perform modifications, the easiest way is to
+#
+# * Save the database: slapcat -b 'o=mailHosting,dc=fripost,dc=dev' > /tmp/db.ldif
+# * Save the configuration: slapcat -n0 > /tmp/config.ldif
+# * Backup slap.d: cp -a /etc/ldap/slapd.d/ /tmp/slap.d_back
+# * Edit the schema in /tmp/config.ldif
+# * Load the new config: mkdir -m 0700 /tmp/slapd.d_new && slapadd -F /tmp/slapd.d_new -n0 -l /tmp/config.ldif
+# * Stop slapd: /etc/init.d/slapd stop
+# * Load the new config: rm -rf /etc/ldap/slapd.d/ && mv /tmp/slapd.d_new /etc/ldap/slapd.d && chown -R openldap:openldap /etc/ldap/slapd.d
+# * Create indexes: sudo -u openldap slapindex -b 'o=mailHosting,dc=fripost,dc=dev'
+# * Start slapd: /etc/init.d/slapd start
+# If it fails, remove the existing database and see what's wrong
+# rm -rf /var/lib/ldap/dev/* && sudo -u openldap slapadd -b 'o=mailHosting,dc=fripost,dc=org' -l /tmp/db.ldif
+#
+#
+# /!\ WARN: All modification to the ACL should be reflected to the test
+# /!\ suite as well!
+#
+#
+# References:
+# - http://courier.svn.sourceforge.net/svnroot/courier/trunk/courier-authlib/authldap.schema
+# - http://www.qmail-ldap.org/wiki/index.php/Qmail.schema
+# - http://www.wanderingbarque.com/howtos/mailserver/mailserver.html
+
+
+# 1.3.6.1.4.1.40011 Fripost's OID
+# 1.3.6.1.4.1.40011.1
+# 1.3.6.1.4.1.40011.1.2 fripost LDAP Elements
+# 1.3.6.1.4.1.40011.1.2.1 AttributeTypes
+# 1.3.6.1.4.1.40011.1.2.2 ObjectClasses
+# 1.3.6.1.4.1.40011.1.2.3 Syntax Definitions
+
+# This schema depends on:
+# - core.schema
+# - cosine.schema
+# - nis.schema
+
+
+dn: cn=fripost-master,cn=schema,cn=config
+objectClass: olcSchemaConfig
+#
+# Attributes: 1.3.6.1.4.1.40011.1.1
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.1 NAME 'fvd'
+ DESC 'A virtual mail domain'
+ EQUALITY caseIgnoreIA5Match
+ SUBSTR caseIgnoreIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.2 NAME 'fvl'
+ DESC 'The local part of a virtual user, alias, list or list command'
+ EQUALITY caseIgnoreIA5Match
+ SUBSTR caseIgnoreIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
+#
+# This is redundant since we always use DNs of the form
+# fvl=localpart,fvd=domainpart.tld,...
+# (But Postfix doesn't allow the use of '%u' and '%d' from the query in
+# its 'result_format'.)
+# It is a priori insecure to allow arbitrary values here since users
+# will modify this value themselves, however our Postfix will only
+# accept well-formed values, enforced by a custom filter:
+# query_filter = (&...(fripostLocalAlias=%u#%d))
+# result_attribute = fripostLocalAlias
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.3 NAME 'fripostLocalAlias'
+ DESC 'A local alias, typically localpart#domainpart.tld'
+ EQUALITY caseIgnoreIA5Match
+ SUBSTR caseIgnoreIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{64} SINGLE-VALUE )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.4 NAME 'fripostMaildrop'
+ DESC 'An email address the virtual alias should be mapped to'
+ EQUALITY caseIgnoreIA5Match
+ SUBSTR caseIgnoreIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+#
+# We are creating a new attribute, optional in virtual domains and
+# users, because the presence index should *not* apply to the
+# mandatory attribute above.
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.5 NAME 'fripostOptionalMaildrop'
+ DESC 'An optional email address for catch-all aliases on domains and users'
+ EQUALITY caseIgnoreIA5Match
+ SUBSTR caseIgnoreIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.6 NAME 'fripostIsStatusActive'
+ DESC 'When present, a token locking the entry in an inactive state'
+ EQUALITY booleanMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.7 NAME 'fripostPendingToken'
+ DESC 'Is the entry pending?'
+ EQUALITY caseExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{64} SINGLE-VALUE )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.8 NAME 'fripostUserQuota'
+ DESC 'The quota on a user e.g., "50MB"'
+ EQUALITY caseExactMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32} SINGLE-VALUE )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.9 NAME 'fripostCanAddDomain'
+ DESC 'A user/domain that can add domains'
+ SUP distinguishedName )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.10 NAME 'fripostCanAddAlias'
+ DESC 'A user/domain that can add aliases under the parent domain'
+ SUP distinguishedName )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.11 NAME 'fripostCanAddList'
+ DESC 'A user/domain that can add lists under the parent domain'
+ SUP distinguishedName )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.12 NAME 'fripostOwner'
+ DESC 'A user that owns under parent domain'
+ SUP distinguishedName )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.13 NAME 'fripostPostmaster'
+ DESC 'A user that is a postmaster of the parent domain'
+ SUP distinguishedName )
+#
+olcAttributeTypes: ( 1.3.6.1.4.1.40011.1.2.1.14 NAME 'fripostListManager'
+ DESC 'The list manager'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{64} SINGLE-VALUE )
+#
+#
+# Objects: 1.3.6.1.4.1.40011.1.2
+#
+olcObjectClasses: ( 1.3.6.1.4.1.40011.1.2.1 NAME 'FripostVirtual'
+ AUXILIARY
+ DESC 'Virtual mail hosting'
+ MAY ( fripostCanAddDomain ) )
+#
+olcObjectClasses: ( 1.3.6.1.4.1.40011.1.2.2 NAME 'FripostVirtualDomain'
+ SUP top STRUCTURAL
+ DESC 'Virtual domain'
+ MUST ( fvd $ fripostIsStatusActive )
+ MAY ( fripostCanAddAlias $ fripostCanAddList $
+ fripostOwner $ fripostPostmaster $
+ fripostOptionalMaildrop $ description ) )
+#
+# | TODO: add limits here
+olcObjectClasses: ( 1.3.6.1.4.1.40011.1.2.3 NAME 'FripostVirtualUser'
+ SUP top STRUCTURAL
+ DESC 'Virtual user'
+ MUST ( fvl $ userPassword $ fripostIsStatusActive )
+ MAY ( fripostUserQuota $ fripostOptionalMaildrop $ description) )
+#
+olcObjectClasses: ( 1.3.6.1.4.1.40011.1.2.4 NAME 'FripostVirtualAlias'
+ SUP top STRUCTURAL
+ DESC 'Virtual alias'
+ MUST ( fvl $ fripostMaildrop $ fripostIsStatusActive )
+ MAY ( fripostOwner $ description ) )
+#
+olcObjectClasses: ( 1.3.6.1.4.1.40011.1.2.5 NAME 'FripostVirtualList'
+ SUP top STRUCTURAL
+ DESC 'Virtual list'
+ MUST ( fvl $ fripostListManager $ fripostIsStatusActive $ fripostLocalAlias )
+ MAY ( fripostOwner $ description ) )
+#
+olcObjectClasses: ( 1.3.6.1.4.1.40011.1.2.6 NAME 'FripostVirtualListCommand'
+ SUP top STRUCTURAL
+ DESC 'Virtual list command'
+ MUST ( fvl $ fripostLocalAlias ) )
+#
+olcObjectClasses: ( 1.3.6.1.4.1.40011.1.2.7 NAME 'FripostPendingEntry'
+ SUP top AUXILIARY
+ DESC 'Virtual pending entry'
+ MAY ( fripostPendingToken ) )
diff --git a/roles/common/files/var/lib/ldap/fripost/DB_CONFIG b/roles/common/files/var/lib/ldap/fripost/DB_CONFIG
new file mode 100644
index 0000000..0bd4e58
--- /dev/null
+++ b/roles/common/files/var/lib/ldap/fripost/DB_CONFIG
@@ -0,0 +1,5 @@
+set_cachesize 0 5242880 1
+# 5MB cachesize, allow defragmentation
+set_lk_max_objects 1500
+set_lk_max_locks 1500
+set_lk_max_lockers 1500
diff --git a/roles/common/handlers/main.yml b/roles/common/handlers/main.yml
index 54643ed..51b19f7 100644
--- a/roles/common/handlers/main.yml
+++ b/roles/common/handlers/main.yml
@@ -35,3 +35,6 @@
- name: Reload Postfix
service: name=postfix state=reloaded
+
+- name: Restart slapd
+ service: name=slapd state=restarted
diff --git a/roles/common/tasks/ldap.yml b/roles/common/tasks/ldap.yml
new file mode 100644
index 0000000..b1ced49
--- /dev/null
+++ b/roles/common/tasks/ldap.yml
@@ -0,0 +1,66 @@
+- name: Install OpenLDAP
+ apt: pkg={{ item }}
+ with_items:
+ - slapd
+ - ldap-utils
+ - ldapvi
+ - db-util
+ - python-ldap
+
+# Upon install slapd create and populate a database under /var/lib/ldap.
+# We clear it up and create a children directory to get finer-grain
+# control.
+- name: Clear empty /var/lib/ldap
+ # Don't remove the database (and fail) if it contains something else
+ # than its suffix or cn=admin,...
+ openldap: dbdirectory=/var/lib/ldap ignoredn=cn=admin
+ state=absent
+
+- name: Create directory /var/lib/ldap/fripost
+ file: path=/var/lib/ldap/fripost
+ owner=openldap group=openldap
+ state=directory
+ mode=0700
+
+- name: Copy /var/lib/ldap/fripost/DB_CONFIG
+ copy: src=var/lib/ldap/fripost/DB_CONFIG
+ dest=/var/lib/ldap/fripost/DB_CONFIG
+ owner=openldap group=openldap
+ mode=0600
+ notify:
+ # Not sure if required
+ - Restart slapd
+
+- name: Create directory /etc/ldap/fripost
+ file: path=/etc/ldap/fripost
+ owner=root group=root
+ state=directory
+ mode=0755
+
+- name: Copy fripost database definition
+ template: src=etc/ldap/database.ldif.j2
+ dest=/etc/ldap/fripost/database.ldif
+ owner=root group=root
+ mode=0600
+
+- name: Copy fripost schema
+ copy: src=etc/ldap/schema/fripost.ldif
+ dest=/etc/ldap/schema/fripost.ldif
+ owner=root group=root
+ mode=0644
+
+- name: Create fripost database and load the schema
+ openldap: target=/etc/ldap/{{ item }} state=present
+ with_items:
+ - fripost/database.ldif
+ - schema/fripost.ldif
+
+- name: Load LDAP modules
+ openldap: module={{ item }}.la state=present
+ with_items:
+ # TODO only if provider
+ - syncprov
+ # TODO only if writable
+ - constraint
+
+# TODO: authz constraint syncprov syncrepl
diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml
index 81ef705..ed84cb5 100644
--- a/roles/common/tasks/main.yml
+++ b/roles/common/tasks/main.yml
@@ -15,3 +15,9 @@
when: "'MDA' in group_names or
'webmail' in group_names or
'backup' in group_names"
+- include: ldap.yml tags=slapd,ldap
+ when: "'MDA' in group_names or
+ 'MSA' in group_names or
+ 'lists' in group_names or
+ 'LDAP-producer' in group_names or
+ 'MX' in group_names"
diff --git a/roles/common/templates/etc/ldap/database.ldif.j2 b/roles/common/templates/etc/ldap/database.ldif.j2
new file mode 100644
index 0000000..fc4ce4b
--- /dev/null
+++ b/roles/common/templates/etc/ldap/database.ldif.j2
@@ -0,0 +1,487 @@
+# Fripost's LDAP database definition
+# Copyright 2013 Guilhem Moulin <guilhem@fripost.org>
+#
+# Licensed under the GNU GPL version 3 or higher.
+
+dn: olcDatabase=hdb,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcHdbConfig
+olcDbDirectory: /var/lib/ldap/fripost
+olcSuffix: o=mailHosting,dc=fripost,dc=org
+olcLastMod: TRUE
+olcDbCheckpoint: 512 15
+# Require LDAPv3 protocol and authentication prior to directory
+# operations.
+olcRequires: LDAPv3 authc
+# We don't want to give "canAdd{Alias,List}" write access to alias/list
+# attributes.
+olcAddContentAcl: FALSE
+# The root user has all rights on the whole database (when SASL-binding
+# on a UNIX socket).
+olcRootDN: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
+#
+#
+########################################################################
+########################################################################
+# Performance considerations
+#
+# To reindex an existing database, you have to
+# * Stop slapd sudo service slapd stop
+# * Reindex su openldap -c "slapindex -b 'o=mailHosting,dc=fripost,dc=org'"
+# * Restart slapd sudo service slapd start
+#
+# References
+# - https://wiki.zimbra.com/wiki/OpenLDAP_Performance_Tuning_5.0
+# - http://www.openldap.org/doc/admin24/tuning.html
+# - http://www.openldap.org/faq/data/cache/42.html
+# - http://www.openldap.org/faq/data/cache/136.html
+# - http://www.zytrax.com/books/ldap/apa/indeces.html
+#
+olcDbIndex: objectClass eq
+# Let us make Postfix's life easier. TODO: only if MX, lists.f.o, MDA, etc.
+olcDbIndex: fripostIsStatusActive,fvd,fvl,fripostLocalAlias eq
+olcDbIndex: fripostOptionalMaildrop pres
+# SyncProv/SyncRepl specific indexing. TODO: only if SyncProv/SyncRepl
+olcDbIndex: entryCSN,entryUUID eq
+#
+#
+#
+# 1. On single- and dual-core systems, change the maximum number of
+# threads to 8. (The default, 16, is fine for 4- and 8-core systems.)
+#
+# dn: cn=config
+# changetype: modify
+# add: olcThreads
+# olcThreads: 8
+#
+#
+# 2. It may be a good idea to modify DB_CONFIG, depending on the output
+# of
+#
+# db_stat -mh /var/lib/ldap/fripost | head -16
+#
+# (For optimal performance, the Requested pages found in the cache
+# should be above 95%, and the dirty/clean pages forced from the cache
+# should be 0.)
+#
+# and
+#
+# db_stat -ch /var/lib/ldap/fripost | head -16
+#
+# (For optimal performance, usage should be within 85% of the configured
+# values.)
+#
+#
+########################################################################
+########################################################################
+# Access control
+# /!\ WARN: All modification to the ACL should be reflected to the test
+# /!\ suite as well!
+#
+# References:
+# - http://www.openldap.org/doc/admin24/access-control.html
+# - http://www.openldap.org/faq/data/cache/189.html
+# - http://www.openldap.org/faq/data/cache/1140.html
+# - http://www.openldap.org/faq/data/cache/1133.html
+# - man 5 slapd.access
+#
+#
+########################################################################
+# Most common services: Postfix, Amavis, SASLauth, Dovecot
+# (Most used ACLs are cheaper when written first.)
+#
+# Postfix have read access to the attribute they need.
+olcAccess: to dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ attrs=entry,objectClass,fvd,fvl,fripostMaildrop,fripostOptionalMaildrop,fripostLocalAlias
+ filter=(&(|(objectClass=FripostVirtualDomain)(objectClass=FripostVirtualUser)(objectClass=FripostVirtualAlias)(objectClass=FripostVirtualList)(objectClass=FripostVirtualListCommand))(!(objectClass=FripostPendingEntry))(!(fripostIsStatusActive=FALSE)))
+ by dn.exact="cn=Postfix,ou=services,o=mailHosting,dc=fripost,dc=org" =rsd
+ by users =0 break
+# Search lists and domain owners
+olcAccess: to dn.exact="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ attrs=entry
+ by dn.exact="cn=Postfix,ou=services,o=mailHosting,dc=fripost,dc=org" =s
+ by dn.exact="gidNumber=8+uidNumber=8,cn=peercred,cn=external,cn=auth" =s
+ by users =0 break
+#
+# Search domain owners / postmasters
+olcAccess: to dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ attrs=entry,objectClass,fvd,fvl,fripostPostmaster,fripostOwner
+ filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry))(!(fripostIsStatusActive=FALSE)))
+ by dn.exact="gidNumber=8+uidNumber=8,cn=peercred,cn=external,cn=auth" =rsd
+ by users =0 break
+#
+# Anonymous can authenticate into the services. (But not read or write the password.)
+olcAccess: to dn.one="ou=services,o=mailHosting,dc=fripost,dc=org"
+ attrs=userPassword
+ by realanonymous =xd
+#
+# That's necessary for SASL proxy Authorize the web application.
+olcAccess: to dn.exact="cn=AdminWebPanel,ou=services,o=mailHosting,dc=fripost,dc=org"
+ attrs=entry,objectClass,authzTo
+ by realanonymous =x
+#
+# 1. The WebPanel itself cannot bind, read or write passwords. This
+# guarantees that, if an attacker gains its priviledge, it will *not* be
+# able to change user passwords (which would allow him/her to read every
+# emails). This is a trick to tackle the absence of 'realgroup'.
+# 2. Anonymous users can bind.
+# 3. Users can change their password (but not read it).
+# 4. The postmaster of a domain can change (replace) his/her users' password (but not read it).
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualUser)
+ attrs=userPassword
+ by realdn.exact="uid=AdminWebPanel@fripost.org,cn=auth" =0
+ by realanonymous =xd
+ by realself =w
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" =w
+ by dn.onelevel="ou=managers,o=mailHosting,dc=fripost,dc=org" =w
+#
+# A catch-all, to be sure that noone else have access to the passwords.
+olcAccess: to dn.subtree="o=mailHosting,dc=fripost,dc=org"
+ attrs=userPassword
+ by * =0
+#
+#
+########################################################################
+# Virtual subtree, pending token and general access
+#
+# 1. Users need further access. We use a set to deny all access to non-users without
+# having a need for an expensive LDAP search (URL) in the AuthzTo.
+# /!\ The objectClass "FripostVirtualUser" is case-sensitive in this case!
+# 2,3. Services that need particular access on the tree.
+# 4. Managers have read/write access to the "virtual" subtree.
+olcAccess: to dn.subtree="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ by set.exact="user/objectClass & [FripostVirtualUser]" =0 break
+ by dn.exact="cn=CreateList,ou=services,o=mailHosting,dc=fripost,dc=org" =0 break
+ by dn.exact="cn=DeletePendingEntries,ou=services,o=mailHosting,dc=fripost,dc=org" =0 break
+ by dn.onelevel="ou=managers,o=mailHosting,dc=fripost,dc=org" =wrscd
+#
+# Only the domain Postmasters and Owners can delete the 'pending' status on domains.
+olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(&(objectClass=FripostVirtualDomain)(objectClass=FripostPendingEntry))
+ attrs=objectClass val=FripostPendingEntry
+ by dnattr=fripostPostmaster =z break
+ by dnattr=fripostOwner =z break
+ by * =0 break
+#
+# The list creation service can delete the 'pending' status on lists and list commands.
+olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(&(|(objectClass=FripostVirtualList)(objectClass=FripostVirtualListCommand))(objectClass=FripostPendingEntry))
+ attrs=objectClass val=FripostPendingEntry
+ by dn.exact="cn=CreateList,ou=services,o=mailHosting,dc=fripost,dc=org" =z break
+ by * +0 break
+#
+# ObjectClass is a public attribute: everyone can read and search it.
+olcAccess: to dn.subtree="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ attrs=objectClass
+ by * +rscd
+#
+# The pending token is not public, but domain owner and postmasters can check their and
+# delete it (if the token matches, but the check is done on the library side).
+olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(&(objectClass=FripostVirtualDomain)(objectClass=FripostPendingEntry))
+ attrs=fripostPendingToken
+ by dnattr=fripostPostmaster =zcd break
+ by dnattr=fripostOwner =zcd break
+ by * +0 break
+#
+# The list creation service can delete the 'pending' status on lists and list commands.
+olcAccess: to dn.regex="^fvl=[^,]+,fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(&(|(objectClass=FripostVirtualList)(objectClass=FripostVirtualListCommand))(objectClass=FripostPendingEntry))
+ attrs=fripostPendingToken
+ by dn.exact="cn=CreateList,ou=services,o=mailHosting,dc=fripost,dc=org" +z
+ by * +0
+#
+# The cleaning service can list the (expired) pending entries and delete them.
+olcAccess: to dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ filter=(objectClass=FripostPendingEntry)
+ attrs=entry
+ by dn.exact="cn=DeletePendingEntries,ou=services,o=mailHosting,dc=fripost,dc=org" =zrd break
+ by * =0 break
+#
+# One can search search everywhere in the virtual tree.
+olcAccess: to dn.subtree="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ attrs=entry
+ by dn.exact="cn=DeletePendingEntries,ou=services,o=mailHosting,dc=fripost,dc=org" +s
+ by * =s break
+#
+# We're giving away create/delete access on the children attributes, but we will be carefull
+# with the 'entry' permissions.
+olcAccess: to dn.base="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ filter=(objectClass=FripostVirtual)
+ attrs=children
+ by dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org" =w
+ by dn.exact="cn=DeletePendingEntries,ou=services,o=mailHosting,dc=fripost,dc=org" =z
+olcAccess: to dn.one="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ filter=(objectClass=FripostVirtualDomain)
+ attrs=children
+ by dn.exact="cn=DeletePendingEntries,ou=services,o=mailHosting,dc=fripost,dc=org" =z
+ by * break
+olcAccess: to dn.one="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ filter=(&(objectClass=FripostVirtualDomain)(!(objectClass=FripostPendingEntry)))
+ attrs=children
+ by dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org" =w
+#
+# The cleaning service needs to know when entries have been created.
+olcAccess: to dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ filter=(objectClass=FripostPendingEntry)
+ attrs=createTimestamp
+ by dn.exact="cn=DeletePendingEntries,ou=services,o=mailHosting,dc=fripost,dc=org" =s
+#
+# Users can use these in filters (e.g., to list the entries they have created).
+olcAccess: to dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ filter=(|(objectClass=FripostVirtualDomain)(objectClass=FripostVirtualUser)(objectClass=FripostVirtualAlias)(objectClass=FripostVirtualList))
+ attrs=fripostOwner,fripostPostmaster,fripostCanAddAlias,fripostCanAddList
+ by dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org" =s break
+#
+#
+########################################################################
+# Virtual subtree, domains
+#
+# 1. The postmaster of a domain can give (or take back) people the right to create
+# aliases.
+# 2,3. People that can create aliases can list the members of the group.
+olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(objectClass=FripostVirtualDomain)
+ attrs=fripostCanAddAlias
+ by dnattr=fripostPostmaster =wrscd
+ by dnattr=fripostOwner =rscd
+ by set.exact="this/fripostCanAddAlias & (user | user/-1)" =rscd
+#
+# 1. The postmaster of a domain can give (or take back) people the right to create lists.
+# 2,3. People that can create lists can list the members of the group.
+olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(objectClass=FripostVirtualDomain)
+ attrs=fripostCanAddList
+ by dnattr=fripostPostmaster =wrscd
+ by dnattr=fripostOwner =rscd
+ by set.exact="this/fripostCanAddList & (user | user/-1)" =rscd
+#
+# 1-3. Noone (but the managers) can appoint domain Owners or Postmasters.
+# But people that can create aliases and lists can list the members of their group.
+olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(objectClass=FripostVirtualDomain)
+ attrs=fripostOwner,fripostPostmaster
+ by dnattr=fripostOwner =rscd
+ by dnattr=fripostPostmaster =rscd
+ by set.exact="(this/fripostCanAddAlias | this/fripostCanAddList) & (user | user/-1)" =rscd
+ by dn.onelevel,expand="$0" +d
+ by * +0
+#
+# 1. Domain owners can edit their entry's attributes.
+# 2. So can domain postmasters.
+# 3. Domain users can read the public domain attributes.
+# 4. So can users with "canAddAlias" or "canAddList" access.
+olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(objectClass=FripostVirtualDomain)
+ attrs=fvd,fripostIsStatusActive,description
+ by dnattr=fripostOwner =wrscd
+ by dnattr=fripostPostmaster =wrscd
+ by dn.onelevel,expand="$0" =rscd
+ by set.exact="(this/fripostCanAddAlias | this/fripostCanAddList) & (user | user/-1)" =rscd
+#
+# 1. Domain owners can edit their entry's attributes.
+# 2. So can domain postmasters.
+olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(objectClass=FripostVirtualDomain)
+ attrs=@fripostVirtualDomain
+ by dnattr=fripostOwner =wrscd
+ by dnattr=fripostPostmaster =wrscd
+ by * +0
+#
+# Users with "addDomain" access can create new entries, but only if
+# there is a pending token.
+olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(&(objectClass=FripostVirtualDomain)(objectClass=FripostPendingEntry)(fripostPendingToken=*))
+ attrs=entry
+ by set.exact="this/-1/fripostCanAddDomain & (user | user/-1)" +a break
+ by * +0 break
+#
+# 1. Domain owners can delete their domain (and read the entry).
+# 2. So can domain postmasters.
+# 3. Domain users can read the domain entry (but not delete it).
+# 4. So can users with "canAddAlias" or "canAddList" rights.
+olcAccess: to dn.regex="^fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ filter=(objectClass=FripostVirtualDomain)
+ attrs=entry
+ by dnattr=fripostOwner +zrd
+ by dnattr=fripostPostmaster +zrd
+ by dn.onelevel,expand="$0" +rd
+ by set.exact="(this/fripostCanAddAlias | this/fripostCanAddList) & (user | user/-1)" +rd
+ by dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org" +0
+#
+# Reserved local parts are reserved. /!\ The case must be insensitive
+# - postmaster: RFC 822, appendix C.6
+# - abuse: RFC 2142, section 4
+olcAccess: to dn.regex="^fvl=(postmaster|abuse),fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org$"
+ by * =0
+#
+#
+########################################################################
+# Virtual subtree, users
+#
+# Users and their postmaster can read the quota (but not change it).
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualUser)
+ attrs=fripostUserQuota
+ by self =rscd
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" =rscd
+#
+# 1. Users can modify their own entry.
+# 2. So can their postmasters.
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualUser)
+ attrs=@FripostVirtualUser
+ by self =wrscd
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" =wrscd
+#
+# 1. Users can read their entry (but not delete it).
+# 2. Postmasters can create users (but not delete them).
+# (Provided that they have +a access to the parent's "children" attribute.)
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualUser)
+ attrs=entry
+ by self +rd
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" +ard
+#
+#
+########################################################################
+# Virtual subtree, aliases
+#
+# 1. The alias owner can list the ownership of the entry.
+# 2. The domain owner can add/delete/change the ownership of the entry.
+# 3. So can the domain postmasters.
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualAlias)
+ attrs=fripostOwner
+ by dnattr=fripostOwner =rscd continue
+ by group/FripostVirtualDomain/fripostOwner.expand="$1" =wrscd
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" =wrscd
+ by * +0
+#
+# 1. The alias owners can edit the rest of their entry's attributes.
+# 2. So can the domain owners.
+# 3. So can the domain postmasters.
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualAlias)
+ attrs=@FripostVirtualAlias
+ by dnattr=fripostOwner =wrscd
+ by group/FripostVirtualDomain/fripostOwner.expand="$1" =wrscd
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" =wrscd
+#
+# 1. The alias owners can read and delete the entry.
+# 2. So can the domain owner.
+# 3. So can the domain postmaster.
+# 4. Users with "canAddAlias" access (either explicitely, or as a wildcard) for the domain can create aliases for that domain.
+# (But *not* delete them, unless also owner.)
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualAlias)
+ attrs=entry
+ by dnattr=fripostOwner +zrd continue
+ by group/FripostVirtualDomain/fripostOwner.expand="$1" +wrd
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" +wrd
+ by set.exact="this/-1/fripostCanAddAlias & (user | user/-1)" +a
+ by dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org" +0
+#
+#
+########################################################################
+# Virtual subtree, lists
+#
+# 1. The list owner can list the ownership of the entry.
+# 2. The domain owner can add/delete/change the ownership of the entry.
+# 3. So can the domain postmasters.
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualList)
+ attrs=fripostOwner
+ by dnattr=fripostOwner =rscd continue
+ by group/FripostVirtualDomain/fripostOwner.expand="$1" =wrscd
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" =wrscd
+ by * +0
+#
+# 1. The list owner read (but not edit) the transport-related attributes.
+# 2. So can the domain ower.
+# 3. So can the domain postmaster.
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualList)
+ attrs=fripostListManager
+ by dnattr=fripostOwner =rscd
+ by group/FripostVirtualDomain/fripostOwner.expand="$1" =rscd
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" =rscd
+#
+# Local aliases are for internal use only.
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualList)
+ attrs=fripostLocalAlias
+ by * =0
+#
+# 1. The list owners can edit their entry's attributes.
+# 2. So can the domain owners.
+# 3. So can the domain postmasters.
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualList)
+ attrs=@FripostVirtualList
+ by dnattr=fripostOwner =wrscd
+ by group/FripostVirtualDomain/fripostOwner.expand="$1" =wrscd
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" =wrscd
+#
+# 1. The domain owner can create and delete lists, but only those with a 'pending' status
+# 2. So can the domain postmaster.
+# 3. The list owner can delete pending lists.
+# 4. The entry creator can delete pending lists (needed to be able to rollback).
+# 5. People with "canAddList" access can create lists, but only with a 'pending' status.
+# 6. The list creation service can search and browse the entry.
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(&(objectClass=FripostVirtualList)(objectClass=FripostPendingEntry))
+ attrs=entry
+ by group/FripostVirtualDomain/fripostOwner.expand="$1" +w break
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" +w break
+ by dnattr=fripostOwner +z continue
+ by dnattr=creatorsName +z continue
+ by set.exact="this/-1/fripostCanAddList & (user | user/-1)" +a break
+ by dn.exact="cn=CreateList,ou=services,o=mailHosting,dc=fripost,dc=org" +rd
+ by * +0 break
+#
+# 1. The domain owner can create and delete list commands, but only those with a 'pending' status
+# 2. So can the domain postmaster.
+# 3. The entry creator can delete pending list commands (needed to be able to rollback).
+# 4. People with "canAddList" access can create list commands, but only with a 'pending' status.
+# 5. The list creation service can search and browse the entry.
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(&(objectClass=FripostVirtualListCommand)(objectClass=FripostPendingEntry))
+ attrs=entry
+ by group/FripostVirtualDomain/fripostOwner.expand="$1" +w
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" +w
+ by dnattr=creatorsName +z continue
+ by set.exact="this/-1/fripostCanAddList & (user | user/-1)" +a
+ by dn.exact="cn=CreateList,ou=services,o=mailHosting,dc=fripost,dc=org" +rd
+ by * +0
+#
+# 1. The list owners can read the entry.
+# 2. So can the domain's Owner.
+# 3. So can the domain's Postmaster.
+olcAccess: to dn.regex="^fvl=[^,]+,(fvd=[^,]+,ou=virtual,o=mailHosting,dc=fripost,dc=org)$"
+ filter=(objectClass=FripostVirtualList)
+ attrs=entry
+ by dnattr=fripostOwner +rd
+ by group/FripostVirtualDomain/fripostOwner.expand="$1" +rd
+ by group/FripostVirtualDomain/fripostPostmaster.expand="$1" +rd
+ by * +0
+#
+#
+########################################################################
+# Catchall
+#
+# Users with "canAddDomain" access can see that they have the right
+# to create domains.
+olcAccess: to dn.base="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ filter=(objectClass=FripostVirtual)
+ attrs=entry
+ by dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org" +rd
+olcAccess: to dn.base="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ filter=(objectClass=FripostVirtual)
+ attrs=fripostCanAddDomain
+ by set.exact="this/fripostCanAddDomain & (user | user/-1)" =rscd
+# Catch the break above
+olcAccess: to dn.subtree="ou=virtual,o=mailHosting,dc=fripost,dc=org"
+ by dn.children="ou=virtual,o=mailHosting,dc=fripost,dc=org" +0
+# vim: set filetype=ldif :