dovecot-1.2: acl: Fixed the logic of merging multiple ACLs.

dovecot at dovecot.org dovecot at dovecot.org
Fri Oct 1 17:46:41 EEST 2010


details:   http://hg.dovecot.org/dovecot-1.2/rev/fd607e10e75d
changeset: 9617:fd607e10e75d
user:      Timo Sirainen <tss at iki.fi>
date:      Fri Oct 01 15:37:19 2010 +0100
description:
acl: Fixed the logic of merging multiple ACLs.

diffstat:

 src/plugins/acl/acl-api.h           |   3 +
 src/plugins/acl/acl-backend-vfile.c |  62 ++++++++++++++++++++++---------
 src/plugins/acl/acl-backend.c       |  21 ++++++++++
 src/plugins/acl/acl-cache.c         |  36 +-----------------
 4 files changed, 70 insertions(+), 52 deletions(-)

diffs (198 lines):

diff -r 9e824012da57 -r fd607e10e75d src/plugins/acl/acl-api.h
--- a/src/plugins/acl/acl-api.h	Mon Jan 25 20:40:25 2010 +0200
+++ b/src/plugins/acl/acl-api.h	Fri Oct 01 15:37:19 2010 +0100
@@ -110,6 +110,9 @@
 /* Returns index for the right name. If it doesn't exist, it's created. */
 unsigned int acl_backend_lookup_right(struct acl_backend *backend,
 				      const char *right);
+/* Returns TRUE if acl_rights matches backend user. */
+bool acl_backend_rights_match_me(struct acl_backend *backend,
+				 const struct acl_rights *rights);
 
 /* List mailboxes that have lookup right to some non-owners. */
 struct acl_mailbox_list_context *
diff -r 9e824012da57 -r fd607e10e75d src/plugins/acl/acl-backend-vfile.c
--- a/src/plugins/acl/acl-backend-vfile.c	Mon Jan 25 20:40:25 2010 +0200
+++ b/src/plugins/acl/acl-backend-vfile.c	Fri Oct 01 15:37:19 2010 +0100
@@ -742,7 +742,7 @@
 		array_delete(&aclobj->rights, dest, count - dest);
 }
 
-static void apply_owner_rights(struct acl_object *_aclobj)
+static void apply_owner_default_rights(struct acl_object *_aclobj)
 {
 	struct acl_rights_update ru;
 	const char *null = NULL;
@@ -762,9 +762,9 @@
 	struct acl_object *_aclobj = &aclobj->aclobj;
 	struct acl_rights_update ru;
 	enum acl_modify_mode add_mode;
-	const struct acl_rights *rights;
+	const struct acl_rights *rights, *prev_match = NULL;
 	unsigned int i, count;
-	bool owner_applied, first_global = TRUE;
+	bool first_global = TRUE;
 
 	acl_cache_flush(_aclobj->backend->cache, _aclobj->name);
 
@@ -772,26 +772,54 @@
 		return;
 
 	ns = mailbox_list_get_namespace(_aclobj->backend->list);
-	owner_applied = ns->type != NAMESPACE_PRIVATE;
 
+	/* Rights are sorted by their 1) locals first, globals next,
+	   2) acl_id_type. We'll apply only the rights matching ourself.
+
+	   Every time acl_id_type or local/global changes, the new ACLs will
+	   replace all of the existing ACLs. Basically this means that if
+	   user belongs to multiple matching groups or group-overrides, their
+	   ACLs are merged. In all other situations the ACLs are replaced
+	   (because there aren't duplicate rights entries and a user can't
+	   match multiple usernames). */
 	memset(&ru, 0, sizeof(ru));
 	rights = array_get(&aclobj->rights, &count);
-	for (i = 0; i < count; i++) {
-		if (!owner_applied &&
-		    (rights[i].id_type >= ACL_ID_OWNER || rights[i].global)) {
-			owner_applied = TRUE;
-			if (rights[i].id_type != ACL_ID_OWNER) {
-				/* owner rights weren't explicitly specified.
-				   replace all the current rights  */
-				apply_owner_rights(_aclobj);
-			}
+	if (!acl_backend_user_is_owner(_aclobj->backend))
+		i = 0;
+	else {
+		/* we're the owner. skip over all rights entries until we
+		   reach ACL_ID_OWNER or higher, or alternatively when we
+		   reach a global ACL (even ACL_ID_ANYONE overrides owner's
+		   rights if it's global) */
+		for (i = 0; i < count; i++) {
+			if (rights[i].id_type >= ACL_ID_OWNER ||
+			    rights[i].global)
+				break;
 		}
+		apply_owner_default_rights(_aclobj);
+		/* now continue applying the rest of the rights,
+		   if there are any */
+	}
+	for (; i < count; i++) {
+		if (!acl_backend_rights_match_me(_aclobj->backend, &rights[i]))
+			continue;
+
+		if (prev_match == NULL ||
+		    prev_match->id_type != rights[i].id_type ||
+		    prev_match->global != rights[i].global) {
+			/* replace old ACLs */
+			add_mode = ACL_MODIFY_MODE_REPLACE;
+		} else {
+			/* merging to existing ACLs */
+			i_assert(rights[i].id_type == ACL_ID_GROUP ||
+				 rights[i].id_type == ACL_ID_GROUP_OVERRIDE);
+			add_mode = ACL_MODIFY_MODE_ADD;
+		}
+		prev_match = &rights[i];
+
 		/* If [neg_]rights is NULL it needs to be ignored.
 		   The easiest way to do that is to just mark it with
 		   REMOVE mode */
-		add_mode = i > 0 && rights[i-1].id_type == rights[i].id_type &&
-			rights[i-1].global == rights[i].global ?
-			ACL_MODIFY_MODE_ADD : ACL_MODIFY_MODE_REPLACE;
 		ru.modify_mode = rights[i].rights == NULL ?
 			ACL_MODIFY_MODE_REMOVE : add_mode;
 		ru.neg_modify_mode = rights[i].neg_rights == NULL ?
@@ -805,8 +833,6 @@
 		}
 		acl_cache_update(_aclobj->backend->cache, _aclobj->name, &ru);
 	}
-	if (!owner_applied && count > 0)
-		apply_owner_rights(_aclobj);
 }
 
 static int acl_backend_vfile_object_refresh_cache(struct acl_object *_aclobj)
diff -r 9e824012da57 -r fd607e10e75d src/plugins/acl/acl-backend.c
--- a/src/plugins/acl/acl-backend.c	Mon Jan 25 20:40:25 2010 +0200
+++ b/src/plugins/acl/acl-backend.c	Fri Oct 01 15:37:19 2010 +0100
@@ -128,6 +128,27 @@
 		       sizeof(const char *), bsearch_strcmp) != NULL;
 }
 
+bool acl_backend_rights_match_me(struct acl_backend *backend,
+				 const struct acl_rights *rights)
+{
+	switch (rights->id_type) {
+	case ACL_ID_ANYONE:
+		return TRUE;
+	case ACL_ID_AUTHENTICATED:
+		return acl_backend_user_is_authenticated(backend);
+	case ACL_ID_GROUP:
+	case ACL_ID_GROUP_OVERRIDE:
+		return acl_backend_user_is_in_group(backend, rights->identifier);
+	case ACL_ID_USER:
+		return acl_backend_user_name_equals(backend, rights->identifier);
+	case ACL_ID_OWNER:
+		return acl_backend_user_is_owner(backend);
+	case ACL_ID_TYPE_COUNT:
+		break;
+	}
+	i_unreached();
+}
+
 unsigned int acl_backend_lookup_right(struct acl_backend *backend,
 				      const char *right)
 {
diff -r 9e824012da57 -r fd607e10e75d src/plugins/acl/acl-cache.c
--- a/src/plugins/acl/acl-cache.c	Mon Jan 25 20:40:25 2010 +0200
+++ b/src/plugins/acl/acl-cache.c	Fri Oct 01 15:37:19 2010 +0100
@@ -284,9 +284,8 @@
 	return obj_cache;
 }
 
-static void
-acl_cache_update_rights(struct acl_cache *cache, const char *objname,
-			const struct acl_rights_update *update)
+void acl_cache_update(struct acl_cache *cache, const char *objname,
+		      const struct acl_rights_update *update)
 {
 	struct acl_object_cache *obj_cache;
 	bool created;
@@ -310,37 +309,6 @@
 				     &obj_cache->my_neg_rights);
 }
 
-void acl_cache_update(struct acl_cache *cache, const char *objname,
-		      const struct acl_rights_update *update)
-{
-	switch (update->rights.id_type) {
-	case ACL_ID_ANYONE:
-		acl_cache_update_rights(cache, objname, update);
-		break;
-	case ACL_ID_AUTHENTICATED:
-		if (acl_backend_user_is_authenticated(cache->backend))
-			acl_cache_update_rights(cache, objname, update);
-		break;
-	case ACL_ID_GROUP:
-	case ACL_ID_GROUP_OVERRIDE:
-		if (acl_backend_user_is_in_group(cache->backend,
-						 update->rights.identifier))
-			acl_cache_update_rights(cache, objname, update);
-		break;
-	case ACL_ID_USER:
-		if (acl_backend_user_name_equals(cache->backend,
-						 update->rights.identifier))
-			acl_cache_update_rights(cache, objname, update);
-		break;
-	case ACL_ID_OWNER:
-		if (acl_backend_user_is_owner(cache->backend))
-			acl_cache_update_rights(cache, objname, update);
-		break;
-	case ACL_ID_TYPE_COUNT:
-		i_unreached();
-	}
-}
-
 void acl_cache_set_validity(struct acl_cache *cache, const char *objname,
 			    const void *validity)
 {


More information about the dovecot-cvs mailing list