]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
dict-ldap: dict_ldap_map_settings - Var expand values
authorMarco Bettini <marco.bettini@open-xchange.com>
Fri, 25 Oct 2024 16:26:19 +0000 (16:26 +0000)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 17 Jan 2025 08:40:01 +0000 (10:40 +0200)
src/lib-dict-backend/dict-ldap-settings.c
src/lib-dict-backend/dict-ldap-settings.h
src/lib-dict-backend/dict-ldap.c

index 6f03e1043615873866f297a1164cd1d7cc04ec7a..2c9e5f4275870cddf2613f39f8c3628b482f8b15 100644 (file)
 
 /* <settings checks> */
 #include "ldap-settings-parse.h"
+
+static bool
+dict_ldap_map_settings_post_check(void *set, pool_t pool, const char **error_r);
+
 /* </settings checks> */
 
 #undef DEF
@@ -27,13 +31,11 @@ static const struct setting_define dict_ldap_map_setting_defines[] = {
        DEF(STR, pattern),
        DEFN(STR, base, ldap_base),
        DEFN(ENUM, scope, ldap_scope),
-       DEF(BOOLLIST, values),
        SETTING_DEFINE_LIST_END
 };
 
 static const struct dict_ldap_map_settings dict_ldap_map_default_settings = {
        .pattern = "",
-       .values = ARRAY_INIT,
        .base = "",
        .scope = "subtree:onelevel:base",
 };
@@ -71,6 +73,30 @@ const struct setting_parser_info dict_ldap_map_pre_setting_parser_info = {
        .pool_offset1 = 1 + offsetof(struct dict_ldap_map_pre_settings, pool),
 };
 
+#undef DEF
+#define DEF(type, name) \
+       SETTING_DEFINE_STRUCT_##type("dict_map_"#name, name, struct dict_ldap_map_post_settings)
+
+static const struct setting_define dict_ldap_map_post_setting_defines[] = {
+       DEF(STR, value),
+       SETTING_DEFINE_LIST_END
+};
+
+static const struct dict_ldap_map_post_settings dict_ldap_map_post_default_settings = {
+       .value = "",
+};
+
+const struct setting_parser_info dict_ldap_map_post_setting_parser_info = {
+       .name = "dict_ldap_map_post",
+
+       .defines = dict_ldap_map_post_setting_defines,
+       .defaults = &dict_ldap_map_post_default_settings,
+       .check_func = dict_ldap_map_settings_post_check,
+
+       .struct_size = sizeof(struct dict_ldap_map_post_settings),
+       .pool_offset1 = 1 + offsetof(struct dict_ldap_map_post_settings, pool),
+};
+
 #undef DEF
 #define DEF(type, name) \
        SETTING_DEFINE_STRUCT_##type("ldap_"#name, name, struct dict_ldap_settings)
@@ -95,9 +121,57 @@ const struct setting_parser_info dict_ldap_setting_parser_info = {
        .pool_offset1 = 1 + offsetof(struct dict_ldap_settings, pool),
 };
 
+/* <settings checks> */
+
+static bool
+dict_ldap_map_settings_post_check(void *_set, pool_t pool,
+                                 const char **error_r ATTR_UNUSED)
+{
+       struct dict_ldap_map_post_settings *set = _set;
+       p_array_init(&set->values, pool, 1);
+       if (*set->value != '\0')
+               array_push_back(&set->values, &set->value);
+       return TRUE;
+}
+
+/* </settings checks> */
+
+static int ldap_parse_attributes(struct dict_ldap_map_settings *set,
+                                struct dict_ldap_map_post_settings *post,
+                                const char **error_r)
+{
+       const char *value;
+       p_array_init(&set->parsed_attributes, set->pool, 2);
+       array_foreach_elem(&post->values, value) {
+               struct var_expand_program *prog;
+
+               if (var_expand_program_create(value, &prog, error_r) < 0) {
+                       *error_r = t_strdup_printf("Invalid ldap_map_value %s: %s",
+                                                  value, *error_r);
+                       return -1;
+               }
+
+               const char *const *vars = var_expand_program_variables(prog);
+               for (; *vars != NULL; vars++) {
+                       const char *ldap_attr;
+                       if (!str_begins(*vars, "ldap:", &ldap_attr) &&
+                           !str_begins(*vars, "ldap_multi:", &ldap_attr))
+                               continue;
+
+                       /* When we free program, this name would be invalid,
+                          so dup it here. */
+                       ldap_attr = p_strdup(set->pool, ldap_attr);
+                       array_push_back(&set->parsed_attributes, &ldap_attr);
+               }
+               var_expand_program_free(&prog);
+       }
+       return 0;
+}
+
 static int
 dict_ldap_map_settings_postcheck(struct dict_ldap_map_settings *set,
                                 struct dict_ldap_map_pre_settings *pre,
+                                struct dict_ldap_map_post_settings *post,
                                 const char **error_r)
 {
        if (!str_begins_with(pre->filter, "(")) {
@@ -114,12 +188,9 @@ dict_ldap_map_settings_postcheck(struct dict_ldap_map_settings *set,
                return -1;
        }
 
-       if (array_is_empty(&set->values)) {
+       if (array_is_empty(&post->values)) {
                *error_r = "ldap_map_value not set";
                return -1;
-       } else {
-               array_append_zero(&set->values);
-               array_pop_back(&set->values);
        }
 
        if (ldap_parse_scope(set->scope, &set->parsed_scope) < 0) {
@@ -128,7 +199,7 @@ dict_ldap_map_settings_postcheck(struct dict_ldap_map_settings *set,
                return -1;
        }
 
-       return 0;
+       return ldap_parse_attributes(set, post, error_r);
 }
 
 static const char *pattern_read_name(const char **pattern)
@@ -204,26 +275,34 @@ dict_ldap_settings_parse_maps(struct event *event, struct dict_ldap_settings *se
        array_foreach_elem(&set->maps, name) {
                struct dict_ldap_map_settings *map = NULL;
                struct dict_ldap_map_pre_settings *pre = NULL;
+               struct dict_ldap_map_post_settings *post = NULL;
                if (settings_get_filter(event, "dict_map", name,
                                        &dict_ldap_map_setting_parser_info,
                                        0, &map, error_r) < 0 ||
                    settings_get_filter(event, "dict_map", name,
                                        &dict_ldap_map_pre_setting_parser_info,
                                        SETTINGS_GET_FLAG_NO_EXPAND,
-                                       &pre, error_r) < 0) {
+                                       &pre, error_r) < 0 ||
+                   settings_get_filter(event, "dict_map", name,
+                                       &dict_ldap_map_post_setting_parser_info,
+                                       SETTINGS_GET_FLAG_NO_EXPAND,
+                                       &post, error_r) < 0) {
                        *error_r = t_strdup_printf("Failed to get dict_map %s: %s",
                                                   name, *error_r);
                        settings_free(map);
                        settings_free(pre);
+                       settings_free(post);
                        return -1;
                }
 
-               if (dict_ldap_map_settings_postcheck(map, pre, error_r) < 0) {
+               if (dict_ldap_map_settings_postcheck(map, pre, post, error_r) < 0) {
                        settings_free(map);
                        settings_free(pre);
+                       settings_free(post);
                        return -1;
                }
                settings_free(pre);
+               settings_free(post);
 
                dict_ldap_settings_parse_pattern(map);
                chain_ref(set->pool, map->pool);
index fbdf4803cc1bbd2c317e7abaf4b48be123e252b8..ffae6e4f16e7f57c4d17469fbc8d6ce968c4d90f 100644 (file)
@@ -5,12 +5,13 @@ struct dict_ldap_map_settings {
        pool_t pool;
 
        const char *pattern;
-       ARRAY_TYPE(const_string) values;
        const char *base;
        const char *scope;
 
        /* parsed */
 
+       ARRAY_TYPE(const_string) parsed_attributes;
+
        /* attributes sorted by the position in parsed_pattern. */
        ARRAY_TYPE(const_string) parsed_pattern_keys;
        int parsed_scope;
@@ -24,6 +25,18 @@ struct dict_ldap_map_pre_settings {
        const char *filter;
 };
 
+struct dict_ldap_map_post_settings {
+       pool_t pool;
+       const char *value;
+
+       /* parsed */
+
+       /* This is preliminary support for supporting multiple values.
+          For now the array contains only the single value coming
+          from 'value' above. */
+       ARRAY_TYPE(const_string) values;
+};
+
 struct dict_ldap_settings {
        pool_t pool;
        ARRAY_TYPE(const_string) maps;
@@ -34,6 +47,7 @@ struct dict_ldap_settings {
 
 extern const struct setting_parser_info dict_ldap_map_setting_parser_info;
 extern const struct setting_parser_info dict_ldap_map_pre_setting_parser_info;
+extern const struct setting_parser_info dict_ldap_map_post_setting_parser_info;
 extern const struct setting_parser_info dict_ldap_setting_parser_info;
 
 int dict_ldap_settings_get(struct event *event,
index 196807e58e7bbb3bcbdb9ad7ed38f9c2291299bb..660ca47e0b33bc1cdadd3d638c9dbb29a5655f34 100644 (file)
 static const char *LDAP_ESCAPE_CHARS = "*,\\#+<>;\"()= ";
 
 struct ldap_dict;
+struct key_value {
+       /* pre lower-cased */
+       const char *key_lcase;
 
-struct dict_ldap_vars_context {
-       const struct dict_ldap_map_settings *set;
-       ARRAY_TYPE(const_string) values;
-       const char *username;
-       bool private;
+       const char *value;
 };
 
 struct dict_ldap_op {
        struct ldap_dict *dict;
        struct event *event;
        const struct dict_ldap_map_settings *map;
+       ARRAY_TYPE(const_string) pattern_values;
+       ARRAY(struct key_value) attribute_values;
+
+       const char *username;
+       bool private;
        pool_t pool;
        unsigned long txid;
        struct dict_lookup_result res;
@@ -238,27 +242,43 @@ ldap_dict_lookup_cb_values(const struct ldap_entry *entry, struct dict_ldap_op *
        e_debug(op->event, "got dn %s",
                ldap_entry_dn(entry));
 
-       ARRAY_TYPE(const_string) resp_values;
-       p_array_init(&resp_values, op->pool, array_count(&op->map->values) + 1);
-
        const char *attribute;
-       array_foreach_elem(&op->map->values, attribute) {
+       array_foreach_elem(&op->map->parsed_attributes, attribute) {
                const char *const *values = ldap_entry_get_attribute(entry, attribute);
                bool no_attribute = values == NULL;
                e_debug(op->event, "%s attribute %s",
                        no_attribute ? "dit not get" : "got", attribute);
-               if (no_attribute && array_is_empty(&resp_values))
-                       break;
-               const char *value = no_attribute ? "" : p_strdup(op->pool, values[0]);
+               if (no_attribute)
+                       continue;
+               struct key_value *kv = array_append_space(&op->attribute_values);
+               kv->key_lcase = p_strdup(op->pool, t_str_lcase(attribute));
+               kv->value = p_strdup(op->pool, values[0]);
+       }
+
+       struct dict_ldap_map_post_settings *post;
+       if (settings_get_filter(op->event, "dict_map", op->map->pattern,
+                               &dict_ldap_map_post_setting_parser_info,
+                               0, &post, &op->res.error) < 0) {
+               op->res.ret = -1;
+               return;
+       }
+
+       ARRAY_TYPE(const_string) resp_values;
+       p_array_init(&resp_values, op->pool, array_count(&post->values) + 1);
+
+       const char *value;
+       array_foreach_elem(&post->values, value) {
+               value = p_strdup(op->pool, value);
                array_push_back(&resp_values, &value);
        }
 
+       settings_free(post);
        array_append_zero(&resp_values);
        array_pop_back(&resp_values);
        bool got_values = array_not_empty(&resp_values);
        op->res.values = got_values ? array_front(&resp_values) : NULL;
        op->res.value = got_values ? op->res.values[0] : NULL;
-       op->res.ret = got_values ? 1 : 0;
+       op->res.ret = 1;
 }
 
 static void
@@ -356,6 +376,29 @@ void ldap_dict_atomic_inc(struct dict_transaction_context *ctx,
                           const char *key, long long diff);
 */
 
+static int
+ldap_dict_ldap_expand(const char *key, const char **value_r, void *_ctx,
+                     const char **error_r ATTR_UNUSED)
+{
+       struct dict_ldap_op *op = _ctx;
+       *value_r = "";
+
+       if (array_not_empty(&op->attribute_values)) {
+               key = t_str_lcase(key);
+
+               const struct key_value *attr;
+               array_foreach(&op->attribute_values, attr) {
+                       if (strcmp(key, attr->key_lcase) == 0) {
+                               *value_r = attr->value;
+                               return 0;
+                       }
+               }
+       }
+
+       *error_r = t_strdup_printf("ldap attribute %s not found", key);
+       return -1;
+}
+
 static int
 ldap_dict_pattern_expand(const char *key, const char **value_r, void *_ctx,
                         const char **error_r)
@@ -393,22 +436,15 @@ void ldap_dict_lookup_async(struct dict *dict,
        op->callback_ctx = context;
        op->txid = ctx->last_txid++;
        op->event = event_create(op->dict->dict.event);
+       op->private = str_begins_with(key, DICT_PATH_PRIVATE);
+       op->username = set->username;
+       op->map = ldap_dict_find_map(ctx, key, &op->pattern_values);
+       p_array_init(&op->attribute_values, op->pool, 2);
 
-       struct dict_ldap_vars_context *pattern_ctx =
-               p_new(op->pool, struct dict_ldap_vars_context, 1);
-       t_array_init(&pattern_ctx->values, 2);
-       pattern_ctx->private = str_begins_with(key, DICT_PATH_PRIVATE);
-       if (pattern_ctx->private)
-               pattern_ctx->username = set->username;
-
-       const struct dict_ldap_map_settings *map = pattern_ctx->set =
-               ldap_dict_find_map(ctx, key, &pattern_ctx->values);
-
-       if (map != NULL) {
-               op->map = map;
-
+       if (op->map != NULL) {
                static const struct var_expand_provider providers[] = {
                        { "pattern", ldap_dict_pattern_expand },
+                       { "ldap", ldap_dict_ldap_expand},
                        { NULL, NULL }
                };
 
@@ -424,11 +460,8 @@ void ldap_dict_lookup_async(struct dict *dict,
                params->context = op;
                params->table = table;
                event_set_ptr(op->event, SETTINGS_EVENT_VAR_EXPAND_PARAMS, params);
-               if (str_begins_with(key, DICT_PATH_PRIVATE))
-                       event_add_str(op->event, "user", set->username);
-
                struct dict_ldap_map_pre_settings *pre;
-               if (settings_get_filter(op->event, "dict_map", map->pattern,
+               if (settings_get_filter(op->event, "dict_map", op->map->pattern,
                                        &dict_ldap_map_pre_setting_parser_info,
                                        0, &pre, &op->res.error) < 0) {
 
@@ -441,12 +474,14 @@ void ldap_dict_lookup_async(struct dict *dict,
 
                /* build lookup */
                i_zero(&input);
-               input.base_dn = map->base;
-               input.scope = map->parsed_scope;
                input.filter = pre->filter;
+               input.base_dn = op->map->base;
+               input.scope = op->map->parsed_scope;
                /* Guaranteed to be NULL-terminated by
                   dict_ldap_map_settings_postcheck() */
-               input.attributes = array_front(&map->values);
+               input.attributes =
+                       array_is_empty(&op->map->parsed_attributes) ? NULL :
+                       array_front(&op->map->parsed_attributes);
                ctx->pending++;
                ldap_search_start(ctx->client, &input, ldap_dict_lookup_callback, op);
                settings_free(pre);