dovecot-2.2: acl: Moved acl_rights array from vfile-specific cod...

dovecot at dovecot.org dovecot at dovecot.org
Sat Jan 18 00:33:56 EET 2014


details:   http://hg.dovecot.org/dovecot-2.2/rev/018c66251db6
changeset: 17115:018c66251db6
user:      Timo Sirainen <tss at iki.fi>
date:      Fri Jan 17 17:33:47 2014 -0500
description:
acl: Moved acl_rights array from vfile-specific code to generic struct acl_object.

diffstat:

 src/plugins/acl/acl-api-private.h          |   12 +
 src/plugins/acl/acl-api.c                  |  170 +++++++++++++++++++++
 src/plugins/acl/acl-backend-vfile-update.c |   17 +-
 src/plugins/acl/acl-backend-vfile.c        |  225 ++--------------------------
 src/plugins/acl/acl-backend-vfile.h        |    3 -
 5 files changed, 214 insertions(+), 213 deletions(-)

diffs (truncated from 616 to 300 lines):

diff -r 498fcb82fcb6 -r 018c66251db6 src/plugins/acl/acl-api-private.h
--- a/src/plugins/acl/acl-api-private.h	Fri Jan 17 16:23:49 2014 -0500
+++ b/src/plugins/acl/acl-api-private.h	Fri Jan 17 17:33:47 2014 -0500
@@ -67,6 +67,9 @@
 struct acl_object {
 	struct acl_backend *backend;
 	char *name;
+
+	pool_t rights_pool;
+	ARRAY(struct acl_rights) rights;
 };
 
 struct acl_object_list_iter {
@@ -78,6 +81,12 @@
 
 extern const char *const all_mailbox_rights[];
 
+struct acl_object_list_iter *
+acl_default_object_list_init(struct acl_object *aclobj);
+int acl_default_object_list_next(struct acl_object_list_iter *iter,
+				 struct acl_rights *rights_r);
+void acl_default_object_list_deinit(struct acl_object_list_iter *iter);
+
 const char *const *
 acl_backend_mask_get_names(struct acl_backend *backend,
 			   const struct acl_mask *mask, pool_t pool);
@@ -95,6 +104,7 @@
 int acl_rights_parse_line(const char *line, pool_t pool,
 			  struct acl_rights *rights_r, const char **error_r);
 int acl_rights_cmp(const struct acl_rights *r1, const struct acl_rights *r2);
+void acl_rights_sort(struct acl_object *aclobj);
 
 const char *const *
 acl_right_names_parse(pool_t pool, const char *acl, const char **error_r);
@@ -105,5 +115,7 @@
 			    const char *const **rightsp,
 			    const char *const *modify_rights,
 			    enum acl_modify_mode modify_mode);
+void acl_object_rebuild_cache(struct acl_object *aclobj);
+void acl_object_remove_all_access(struct acl_object *aclobj);
 
 #endif
diff -r 498fcb82fcb6 -r 018c66251db6 src/plugins/acl/acl-api.c
--- a/src/plugins/acl/acl-api.c	Fri Jan 17 16:23:49 2014 -0500
+++ b/src/plugins/acl/acl-api.c	Fri Jan 17 17:33:47 2014 -0500
@@ -185,6 +185,43 @@
         iter->aclobj->backend->v.object_list_deinit(iter);
 }
 
+struct acl_object_list_iter *
+acl_default_object_list_init(struct acl_object *aclobj)
+{
+	struct acl_object_list_iter *iter;
+
+	iter = i_new(struct acl_object_list_iter, 1);
+	iter->aclobj = aclobj;
+
+	if (!array_is_created(&aclobj->rights)) {
+		/* we may have the object cached, but we don't have all the
+		   rights read into memory */
+		acl_cache_flush(aclobj->backend->cache, aclobj->name);
+	}
+
+	if (aclobj->backend->v.object_refresh_cache(aclobj) < 0)
+		iter->failed = TRUE;
+	return iter;
+}
+
+int acl_default_object_list_next(struct acl_object_list_iter *iter,
+				 struct acl_rights *rights_r)
+{
+	const struct acl_rights *rights;
+
+	if (iter->idx == array_count(&iter->aclobj->rights))
+		return 0;
+
+	rights = array_idx(&iter->aclobj->rights, iter->idx++);
+	*rights_r = *rights;
+	return 1;
+}
+
+void acl_default_object_list_deinit(struct acl_object_list_iter *iter)
+{
+	i_free(iter);
+}
+
 struct acl_mailbox_list_context *
 acl_backend_nonowner_lookups_iter_init(struct acl_backend *backend)
 {
@@ -399,6 +436,35 @@
 	return null_strcmp(r1->identifier, r2->identifier);
 }
 
+void acl_rights_sort(struct acl_object *aclobj)
+{
+	struct acl_rights *rights;
+	unsigned int i, dest, count;
+
+	if (!array_is_created(&aclobj->rights))
+		return;
+
+	array_sort(&aclobj->rights, acl_rights_cmp);
+
+	/* merge identical identifiers */
+	rights = array_get_modifiable(&aclobj->rights, &count);
+	for (dest = 0, i = 1; i < count; i++) {
+		if (acl_rights_cmp(&rights[i], &rights[dest]) == 0) {
+			/* add i's rights to dest and delete i */
+			acl_right_names_merge(aclobj->rights_pool,
+					      &rights[dest].rights,
+					      rights[i].rights, FALSE);
+			acl_right_names_merge(aclobj->rights_pool,
+					      &rights[dest].neg_rights,
+					      rights[i].neg_rights, FALSE);
+		} else {
+			if (++dest != i)
+				rights[dest] = rights[i];
+		}
+	}
+	if (++dest != count)
+		array_delete(&aclobj->rights, dest, count - dest);
+}
 
 bool acl_rights_has_nonowner_lookup_changes(const struct acl_rights *rights)
 {
@@ -633,3 +699,107 @@
 	}
 	return old_rights[i] != NULL || new_rights[i] != NULL;
 }
+
+static void apply_owner_default_rights(struct acl_object *aclobj)
+{
+	struct acl_rights_update ru;
+	const char *null = NULL;
+
+	memset(&ru, 0, sizeof(ru));
+	ru.modify_mode = ACL_MODIFY_MODE_REPLACE;
+	ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE;
+	ru.rights.id_type = ACL_ID_OWNER;
+	ru.rights.rights = aclobj->backend->default_rights;
+	ru.rights.neg_rights = &null;
+	acl_cache_update(aclobj->backend->cache, aclobj->name, &ru);
+}
+
+void acl_object_rebuild_cache(struct acl_object *aclobj)
+{
+	struct acl_rights_update ru;
+	enum acl_modify_mode add_mode;
+	const struct acl_rights *rights, *prev_match = NULL;
+	unsigned int i, count;
+	bool first_global = TRUE;
+
+	acl_cache_flush(aclobj->backend->cache, aclobj->name);
+
+	if (!array_is_created(&aclobj->rights))
+		return;
+
+	/* 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);
+	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 */
+		ru.modify_mode = rights[i].rights == NULL ?
+			ACL_MODIFY_MODE_REMOVE : add_mode;
+		ru.neg_modify_mode = rights[i].neg_rights == NULL ?
+			ACL_MODIFY_MODE_REMOVE : add_mode;
+		ru.rights = rights[i];
+		if (rights[i].global && first_global) {
+			/* first global: reset negative ACLs so local ACLs
+			   can't mess things up via them */
+			first_global = FALSE;
+			ru.neg_modify_mode = ACL_MODIFY_MODE_REPLACE;
+		}
+		acl_cache_update(aclobj->backend->cache, aclobj->name, &ru);
+	}
+}
+
+void acl_object_remove_all_access(struct acl_object *aclobj)
+{
+	static const char *null = NULL;
+	struct acl_rights rights;
+
+	memset(&rights, 0, sizeof(rights));
+	rights.id_type = ACL_ID_ANYONE;
+	rights.rights = &null;
+	array_append(&aclobj->rights, &rights, 1);
+
+	rights.id_type = ACL_ID_OWNER;
+	rights.rights = &null;
+	array_append(&aclobj->rights, &rights, 1);
+}
diff -r 498fcb82fcb6 -r 018c66251db6 src/plugins/acl/acl-backend-vfile-update.c
--- a/src/plugins/acl/acl-backend-vfile-update.c	Fri Jan 17 16:23:49 2014 -0500
+++ b/src/plugins/acl/acl-backend-vfile-update.c	Fri Jan 17 17:33:47 2014 -0500
@@ -56,7 +56,7 @@
 }
 
 static bool
-vfile_object_modify_right(struct acl_object_vfile *aclobj, unsigned int idx,
+vfile_object_modify_right(struct acl_object *aclobj, unsigned int idx,
 			  const struct acl_rights_update *update)
 {
 	struct acl_rights *right;
@@ -78,7 +78,7 @@
 }
 
 static bool
-vfile_object_add_right(struct acl_object_vfile *aclobj, unsigned int idx,
+vfile_object_add_right(struct acl_object *aclobj, unsigned int idx,
 		       const struct acl_rights_update *update)
 {
 	struct acl_rights right;
@@ -129,7 +129,7 @@
 }
 
 static int
-acl_backend_vfile_update_write(struct acl_object_vfile *aclobj,
+acl_backend_vfile_update_write(struct acl_object *aclobj,
 			       int fd, const char *path)
 {
 	struct ostream *output;
@@ -193,7 +193,8 @@
 int acl_backend_vfile_object_update(struct acl_object *_aclobj,
 				    const struct acl_rights_update *update)
 {
-	struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj;
+	struct acl_object_vfile *aclobj =
+		(struct acl_object_vfile *)_aclobj;
 	struct acl_backend_vfile *backend =
 		(struct acl_backend_vfile *)_aclobj->backend;
 	struct acl_backend_vfile_validity *validity;
@@ -212,11 +213,11 @@
 	if (fd == -1)
 		return -1;
 
-	if (!array_bsearch_insert_pos(&aclobj->rights, &update->rights,
+	if (!array_bsearch_insert_pos(&_aclobj->rights, &update->rights,
 				      acl_rights_cmp, &i))
-		changed = vfile_object_add_right(aclobj, i, update);
+		changed = vfile_object_add_right(_aclobj, i, update);
 	else
-		changed = vfile_object_modify_right(aclobj, i, update);
+		changed = vfile_object_modify_right(_aclobj, i, update);
 	if (!changed) {
 		file_dotlock_delete(&dotlock);
 		return 0;
@@ -228,7 +229,7 @@
 
 	/* ACLs were really changed, write the new ones */
 	path = file_dotlock_get_lock_path(dotlock);
-	if (acl_backend_vfile_update_write(aclobj, fd, path) < 0) {
+	if (acl_backend_vfile_update_write(_aclobj, fd, path) < 0) {
 		file_dotlock_delete(&dotlock);
 		acl_cache_flush(_aclobj->backend->cache, _aclobj->name);
 		return -1;
diff -r 498fcb82fcb6 -r 018c66251db6 src/plugins/acl/acl-backend-vfile.c
--- a/src/plugins/acl/acl-backend-vfile.c	Fri Jan 17 16:23:49 2014 -0500
+++ b/src/plugins/acl/acl-backend-vfile.c	Fri Jan 17 17:33:47 2014 -0500


More information about the dovecot-cvs mailing list