Mercurial > dovecot > original-hg > dovecot-1.2
changeset 9617:fd607e10e75d HEAD
acl: Fixed the logic of merging multiple ACLs.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 01 Oct 2010 15:37:19 +0100 |
parents | 9e824012da57 |
children | d0500786b046 |
files | src/plugins/acl/acl-api.h src/plugins/acl/acl-backend-vfile.c src/plugins/acl/acl-backend.c src/plugins/acl/acl-cache.c |
diffstat | 4 files changed, 70 insertions(+), 52 deletions(-) [+] |
line wrap: on
line diff
--- 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 *
--- 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)
--- 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) {
--- 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) {