URL: https://github.com/SSSD/sssd/pull/34
Author: pbrezina
 Title: #34: cache_req: move from switch to plugins
Action: opened

PR body:
"""
cache_req grown quite big from the original code and it turned out
that using switch statements to branch code for different cases
makes the code quite hard to read and further extend and any
modification to the logic itself is difficult.

This patch changes the switch statements to plugins with small
functions and separates logic into multiple modules. This gives
us better control over the code and improves readability and
maintainability while keeping code duplication to minimum.

At the moment only cache req user by name, id and upn is
implemented as a proof of concept. If you like it, I will
finish the rest, it won't take much time.
"""

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/34/head:pr34
git checkout pr34
From 936f358f1e4c5cd8296bebaf772fee587bda6c95 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <[email protected]>
Date: Tue, 27 Sep 2016 13:18:01 +0200
Subject: [PATCH] cache_req: move from switch to plugins

cache_req grown quite big from the original code and it turned out
that using switch statements to branch code for different cases
makes the code quite hard to read and further extend and any
modification to the logic itself is difficult.

This patch changes the switch statements to plugins with small
functions and separates logic into multiple modules. This gives
us better control over the code and improves readability and
maintainability while keeping code duplication to minimum.
---
 src/responder/common/cache_req/cache_req.c         | 526 +++++++++++++++++++++
 src/responder/common/cache_req/cache_req.h         |  57 +++
 src/responder/common/cache_req/cache_req_data.c    | 168 +++++++
 src/responder/common/cache_req/cache_req_plugin.h  | 173 +++++++
 src/responder/common/cache_req/cache_req_private.h |  82 ++++
 src/responder/common/cache_req/cache_req_search.c  | 388 +++++++++++++++
 .../cache_req/plugins/cache_req_user_by_id.c       | 124 +++++
 .../cache_req/plugins/cache_req_user_by_name.c     | 176 +++++++
 .../cache_req/plugins/cache_req_user_by_upn.c      | 124 +++++
 9 files changed, 1818 insertions(+)
 create mode 100644 src/responder/common/cache_req/cache_req.c
 create mode 100644 src/responder/common/cache_req/cache_req.h
 create mode 100644 src/responder/common/cache_req/cache_req_data.c
 create mode 100644 src/responder/common/cache_req/cache_req_plugin.h
 create mode 100644 src/responder/common/cache_req/cache_req_private.h
 create mode 100644 src/responder/common/cache_req/cache_req_search.c
 create mode 100644 src/responder/common/cache_req/plugins/cache_req_user_by_id.c
 create mode 100644 src/responder/common/cache_req/plugins/cache_req_user_by_name.c
 create mode 100644 src/responder/common/cache_req/plugins/cache_req_user_by_upn.c

diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
new file mode 100644
index 0000000..2449223
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req.c
@@ -0,0 +1,526 @@
+/*
+    Authors:
+        Pavel Březina <[email protected]>
+
+    Copyright (C) 2016 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <ldb.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "responder/common/cache_req/cache_req_private.h"
+
+static struct cache_req_plugin *
+cache_req_get_plugin(enum cache_req_type type)
+{
+    static struct cache_req_plugin *plugins[CACHE_REQ_SENTINEL] = {
+        &cache_req_user_by_name,
+        &cache_req_user_by_upn,
+        &cache_req_user_by_id,
+    };
+
+    if (type >= CACHE_REQ_SENTINEL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Bug: invalid plugin type!");
+        return NULL;
+    }
+
+    return NULL;
+}
+
+static void cache_req_set_plugin(struct cache_req *cr,
+                                 enum cache_req_type type)
+{
+    struct cache_req_plugin *plugin;
+
+    plugin = cache_req_get_plugin(type);
+    if (plugin == NULL) {
+        DEBUG(SSSDBG_FATAL_FAILURE, "Bug: unset plugin!");
+        return;
+    }
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr, "Setting \"%s\" plugin\n",
+                    plugin->name);
+
+    cr->reqname = plugin->name;
+    cr->dp_type = plugin->dp_type;
+    cr->plugin = plugin;
+
+    return;
+}
+
+static struct cache_req *
+cache_req_create(TALLOC_CTX *mem_ctx,
+                 struct resp_ctx *rctx,
+                 struct cache_req_data *data,
+                 struct sss_nc_ctx *ncache,
+                 int midpoint)
+{
+    struct cache_req *cr;
+
+    cr = talloc_zero(mem_ctx, struct cache_req);
+    if (cr == NULL) {
+        return NULL;
+    }
+
+    cr->rctx = rctx;
+    cr->data = data;
+    cr->ncache = ncache;
+    cr->midpoint = midpoint;
+    cr->req_start = time(NULL);
+
+    /* It is perfectly fine to just overflow here. */
+    cr->reqid = rctx->cache_req_num++;
+
+    cache_req_set_plugin(cr, data->type);
+
+    return cr;
+}
+
+static errno_t
+cache_req_set_name(struct cache_req *cr, const char *name)
+{
+    const char *dup_name;
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Setting name [%s]\n", name);
+
+    dup_name = talloc_strdup(cr->data, name);
+    if (dup_name == NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr, "Unable to set name!\n");
+        return ENOMEM;
+    }
+
+    talloc_zfree(cr->data->name.name);
+    cr->data->name.name = dup_name;
+
+    return EOK;
+}
+
+static errno_t
+cache_req_prepare_domain_data(struct cache_req *cr,
+                              struct sss_domain_info *domain)
+{
+    errno_t ret;
+
+    if (cr->plugin->prepare_domain_data_fn == NULL) {
+        return EOK;
+    }
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+                    "Preparing input data for domain [%s] rules\n",
+                    domain->name);
+
+    ret = cr->plugin->prepare_domain_data_fn(cr, cr->data, domain);
+    if (ret != EOK) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                        "Unable to prepare data [%d]: %s\n",
+                        ret, sss_strerror(ret));
+        return ret;
+    }
+
+    return EOK;
+}
+
+static errno_t
+cache_req_create_debug_name(struct cache_req *cr,
+                            struct sss_domain_info *domain)
+{
+    if (cr->plugin->create_debug_name_fn == NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                        "Bug: no create debug name function specified!\n");
+        return ERR_INTERNAL;
+    }
+
+    talloc_zfree(cr->debugobj);
+
+    cr->debugobj = cr->plugin->create_debug_name_fn(cr, cr->data, domain);
+    if (cr->debugobj == NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                        "Unable to create debug name!\n");
+        return ENOMEM;
+    }
+
+    return EOK;
+}
+
+static errno_t
+cache_req_set_domain(struct cache_req *cr,
+                     struct sss_domain_info *domain)
+{
+    errno_t ret;
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Using domain [%s]\n", domain->name);
+
+    ret = cache_req_prepare_domain_data(cr, domain);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    ret = cache_req_create_debug_name(cr, domain);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    cr->domain = domain;
+
+    return EOK;
+}
+
+static void cache_req_global_ncache_add(struct cache_req *cr)
+{
+    errno_t ret;
+
+    if (cr->plugin->global_ncache_add_fn == NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr,
+                        "This request type does not support "
+                        "global negative cache\n");
+        return;
+    }
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Adding [%s] to global "
+                    "negative cache\n", cr->debugobj);
+
+    ret = cr->plugin->global_ncache_add_fn(cr->ncache, cr->data);
+    if (ret != EOK) {
+        CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr,
+                        "Cannot set negative cache for [%s] [%d]: %s\n",
+                        cr->debugobj, ret, sss_strerror(ret));
+        /* not fatal */
+    }
+
+    return;
+}
+
+static bool
+cache_req_assume_upn(struct cache_req *cr)
+{
+    errno_t ret;
+
+    if (cr->plugin->allow_assume_upn == false
+            || cr->data->name.input == NULL
+            || strchr(cr->data->name.input, '@') == NULL) {
+        return false;
+    }
+
+    cache_req_set_plugin(cr, cr->plugin->upn_equivalent);
+
+    ret = cache_req_set_name(cr, cr->data->name.input);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "cache_req_set_name() failed\n");
+        return false;
+    }
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Assuming UPN [%s]\n",
+                    cr->data->name.input);
+
+    return true;
+}
+
+struct cache_req_state {
+    /* input data */
+    struct tevent_context *ev;
+    struct cache_req *cr;
+
+    /* work data */
+    struct ldb_result *result;
+    struct sss_domain_info *domain;
+    struct sss_domain_info *selected_domain;
+    bool check_next;
+};
+
+static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
+                                       struct tevent_req *req,
+                                       struct cache_req *cr,
+                                       const char *domain)
+{
+    struct tevent_req *subreq;
+
+    if (cr->data->name.input == NULL) {
+        /* Input was not name, there is no need to process it further. */
+        return EOK;
+    }
+
+    if (cr->plugin->parse_name == false || domain != NULL) {
+        /* We do not want to parse the name. */
+        return cache_req_set_name(cr, cr->data->name.input);
+    }
+
+    /* Parse name since it may contain a domain name. */
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+                    "Parsing input name [%s]\n", cr->data->name.input);
+
+    subreq = sss_parse_inp_send(mem_ctx, cr->rctx, cr->data->name.input);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
+        return ENOMEM;
+    }
+
+    tevent_req_set_callback(subreq, cache_req_input_parsed, req);
+
+    return EAGAIN;
+}
+
+struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct resp_ctx *rctx,
+                                  struct sss_nc_ctx *ncache,
+                                  int midpoint,
+                                  const char *domain,
+                                  struct cache_req_data *data)
+{
+    struct cache_req_state *state;
+    struct cache_req *cr;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct cache_req_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    state->ev = ev;
+    state->cr = cr = cache_req_create(state, rctx, data, ncache, midpoint);
+    if (state->cr == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "New request\n");
+
+    ret = cache_req_process_input(state, req, cr, domain);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = cache_req_select_domains(req, domain);
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+        tevent_req_post(req, ev);
+    } else if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+        tevent_req_post(req, ev);
+    }
+
+    return req;
+}
+
+static void cache_req_input_parsed(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct cache_req_state *state;
+    char *name;
+    char *domain;
+    bool maybe_upn;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct cache_req_state);
+
+    ret = sss_parse_inp_recv(subreq, state, &name, &domain);
+    switch (ret) {
+    case EOK:
+        ret = cache_req_set_name(state->cr, name);
+        if (ret != EOK) {
+            tevent_req_error(req, ret);
+            return;
+        }
+        break;
+    case ERR_DOMAIN_NOT_FOUND:
+        maybe_upn = cache_req_assume_upn(state->cr);
+        if (!maybe_upn) {
+            tevent_req_error(req, ret);
+            return;
+        }
+
+        domain = NULL;
+        break;
+    default:
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = cache_req_select_domains(req, domain);
+    if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+        return;
+    }
+}
+
+static errno_t cache_req_select_domains(struct tevent_req *req,
+                                        const char *domain)
+{
+    struct cache_req_state *state = NULL;
+
+    state = tevent_req_data(req, struct cache_req_state);
+
+    if (domain != NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                        "Performing a single domain search\n");
+
+        state->domain = responder_get_domain(state->cr->rctx, domain);
+        if (state->domain == NULL) {
+            return ERR_DOMAIN_NOT_FOUND;
+        }
+
+        state->check_next = false;
+    } else {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                        "Performing a multi-domain search\n");
+
+        state->domain = state->cr->rctx->domains;
+        state->check_next = true;
+    }
+
+    return cache_req_next_domain(req);
+}
+
+static errno_t cache_req_next_domain(struct tevent_req *req)
+{
+    struct cache_req_state *state;
+    struct tevent_req *subreq;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct cache_req_state);
+
+    while (state->domain != NULL) {
+       /* If it is a domainless search, skip domains that require fully
+        * qualified names instead. */
+        while (state->domain != NULL && state->check_next
+                && state->domain->fqnames
+                && state->cr->data->type != CACHE_REQ_USER_BY_CERT
+                && !state->cr->plugin->upn_lookup) {
+            state->domain = get_next_domain(state->domain, 0);
+        }
+
+        state->selected_domain = state->domain;
+
+        if (state->domain == NULL) {
+            break;
+        }
+
+        ret = cache_req_set_domain(state->cr, state->domain);
+        if (ret != EOK) {
+            return ret;
+        }
+
+        subreq = cache_req_search_send(state, state->ev, state->cr);
+        if (subreq == NULL) {
+            return ENOMEM;
+        }
+
+        tevent_req_set_callback(subreq, cache_req_done, req);
+
+        /* we will continue with the following domain the next time */
+        if (state->check_next) {
+            if (cache_req_is_upn(state->cr)
+                    || state->cr->data->type == CACHE_REQ_USER_BY_CERT ) {
+                state->domain = get_next_domain(state->domain, SSS_GND_DESCEND);
+            } else {
+                state->domain = get_next_domain(state->domain, 0);
+            }
+        }
+
+        return EAGAIN;
+    }
+
+    /* If the object searched has to be unique among all maintained domains,
+     * we have to add it into negative cache here when all domains have
+     * been searched. */
+    cache_req_global_ncache_add(state->cr);
+
+    return ENOENT;
+}
+
+static void cache_req_done(struct tevent_req *subreq)
+{
+    struct cache_req_state *state;
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct cache_req_state);
+
+    ret = cache_req_search_recv(state, subreq, &state->result);
+    talloc_zfree(subreq);
+    if (ret == EOK) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, "Finished: Success\n");
+        tevent_req_done(req);
+        return;
+    }
+
+    if (state->check_next == false) {
+        if (ret == ENOENT && cache_req_assume_upn(state->cr)) {
+            /* search by upn now */
+            cache_req_select_domains(req, NULL);
+            return;
+        }
+
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr, "Finished: Not found\n");
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = cache_req_next_domain(req);
+    if (ret != EAGAIN) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                        "Finished: Error %d: %s\n", ret, sss_strerror(ret));
+        tevent_req_error(req, ret);
+    }
+
+    return;
+}
+
+errno_t cache_req_recv(TALLOC_CTX *mem_ctx,
+                       struct tevent_req *req,
+                       struct ldb_result **_result,
+                       struct sss_domain_info **_domain,
+                       char **_name)
+{
+    struct cache_req_state *state = NULL;
+    char *name;
+
+    state = tevent_req_data(req, struct cache_req_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    if (_name != NULL) {
+        if (state->cr->data->name.lookup == NULL) {
+            *_name = NULL;
+        } else {
+            name = talloc_strdup(mem_ctx, state->cr->data->name.lookup);
+            if (name == NULL) {
+                return ENOMEM;
+            }
+
+            *_name = name;
+        }
+    }
+
+    if (_result != NULL) {
+        *_result = talloc_steal(mem_ctx, state->result);
+    }
+
+    if (_domain != NULL) {
+        *_domain = state->selected_domain;
+    }
+
+    return EOK;
+}
diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h
new file mode 100644
index 0000000..99d7bcc
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req.h
@@ -0,0 +1,57 @@
+/*
+    Authors:
+        Pavel Březina <[email protected]>
+
+    Copyright (C) 2016 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CACHE_REQ_H_
+#define _CACHE_REQ_H_
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "responder/common/negcache.h"
+
+enum cache_req_type {
+    CACHE_REQ_USER_BY_NAME,
+    CACHE_REQ_USER_BY_UPN,
+    CACHE_REQ_USER_BY_ID,
+    CACHE_REQ_GROUP_BY_NAME,
+    CACHE_REQ_GROUP_BY_ID,
+    CACHE_REQ_INITGROUPS,
+    CACHE_REQ_INITGROUPS_BY_UPN,
+    CACHE_REQ_USER_BY_CERT,
+    CACHE_REQ_USER_BY_FILTER,
+    CACHE_REQ_GROUP_BY_FILTER,
+    CACHE_REQ_OBJECT_BY_SID,
+    CACHE_REQ_SENTINEL
+};
+
+struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct resp_ctx *rctx,
+                                  struct sss_nc_ctx *ncache,
+                                  int midpoint,
+                                  const char *domain,
+                                  struct cache_req_data *data);
+
+errno_t cache_req_recv(TALLOC_CTX *mem_ctx,
+                       struct tevent_req *req,
+                       struct ldb_result **_result,
+                       struct sss_domain_info **_domain,
+                       char **_name);
+
+#endif /* _CACHE_REQ_H_ */
diff --git a/src/responder/common/cache_req/cache_req_data.c b/src/responder/common/cache_req/cache_req_data.c
new file mode 100644
index 0000000..67774bb
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_data.c
@@ -0,0 +1,168 @@
+/*
+    Authors:
+        Pavel Březina <[email protected]>
+
+    Copyright (C) 2016 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+
+#include "responder/common/cache_req/cache_req_private.h"
+
+static struct cache_req_data *
+cache_req_data_create(TALLOC_CTX *mem_ctx,
+                      enum cache_req_type type,
+                      struct cache_req_data *input)
+{
+    struct cache_req_data *data;
+    errno_t ret;
+
+    data = talloc_zero(mem_ctx, struct cache_req_data);
+    if (data == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+        return NULL;
+    }
+
+    data->type = type;
+
+    switch (type) {
+    case CACHE_REQ_USER_BY_NAME:
+    case CACHE_REQ_USER_BY_UPN:
+    case CACHE_REQ_GROUP_BY_NAME:
+    case CACHE_REQ_USER_BY_FILTER:
+    case CACHE_REQ_GROUP_BY_FILTER:
+    case CACHE_REQ_INITGROUPS:
+    case CACHE_REQ_INITGROUPS_BY_UPN:
+        if (input->name.input == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL!\n");
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+
+        data->name.input = talloc_strdup(data, input->name.input);
+        if (data->name.input == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        break;
+    case CACHE_REQ_USER_BY_CERT:
+        if (input->cert == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Bug: certificate cannot be NULL!\n");
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+
+        data->cert = talloc_strdup(data, input->cert);
+        if (data->cert == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        break;
+    case CACHE_REQ_USER_BY_ID:
+    case CACHE_REQ_GROUP_BY_ID:
+        if (input->id == 0) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Bug: id cannot be 0!\n");
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+
+        data->id = input->id;
+        break;
+    case CACHE_REQ_OBJECT_BY_SID:
+        if (input->sid == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Bug: SID cannot be NULL!\n");
+            ret = ERR_INTERNAL;
+            goto done;
+        }
+
+        data->sid = talloc_strdup(data, input->sid);
+        if (data->sid == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        break;
+    }
+
+    if (input->attrs != NULL) {
+        data->attrs = dup_string_list(data, input->attrs);
+        if (data->attrs == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        talloc_zfree(data);
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create cache_req data "
+              "[%d]: %s\n", ret, sss_strerror(ret));
+        return NULL;
+    }
+
+    return data;
+}
+
+struct cache_req_data *
+cache_req_data_name(TALLOC_CTX *mem_ctx,
+                    enum cache_req_type type,
+                    const char *name)
+{
+    struct cache_req_data input = {0};
+
+    input.name.input = name;
+
+    return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_id(TALLOC_CTX *mem_ctx,
+                  enum cache_req_type type,
+                  uint32_t id)
+{
+    struct cache_req_data input = {0};
+
+    input.id = id;
+
+    return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_cert(TALLOC_CTX *mem_ctx,
+                    enum cache_req_type type,
+                    const char *cert)
+{
+    struct cache_req_data input = {0};
+
+    input.cert = cert;
+
+    return cache_req_data_create(mem_ctx, type, &input);
+}
+
+struct cache_req_data *
+cache_req_data_sid(TALLOC_CTX *mem_ctx,
+                   enum cache_req_type type,
+                   const char *sid,
+                   const char **attrs)
+{
+    struct cache_req_data input = {0};
+
+    input.sid = sid;
+    input.attrs = attrs;
+
+    return cache_req_data_create(mem_ctx, type, &input);
+}
diff --git a/src/responder/common/cache_req/cache_req_plugin.h b/src/responder/common/cache_req/cache_req_plugin.h
new file mode 100644
index 0000000..02952f8
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_plugin.h
@@ -0,0 +1,173 @@
+/*
+    Authors:
+        Pavel Březina <[email protected]>
+
+    Copyright (C) 2016 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CACHE_REQ_PLUGIN_H_
+#define _CACHE_REQ_PLUGIN_H_
+
+#include "responder/common/cache_req/cache_req_private.h"
+
+enum cache_object_status {
+    CACHE_OBJECT_VALID,
+    CACHE_OBJECT_EXPIRED,
+    CACHE_OBJECT_MISSING,
+    CACHE_OBJECT_MIDPOINT
+};
+
+/**
+ * Create an object debug name that is used in debug messages to identify
+ * this object.
+ *
+ * @return EOK If everything went fine.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_prepare_domain_data_fn)(struct cache_req *cr,
+                                    struct cache_req_data *data,
+                                    struct sss_domain_info *domain);
+
+/**
+ * Create an object debug name that is used in debug messages to identify
+ * this object.
+ *
+ * @return Debug name or NULL in case of an error.
+ **/
+typedef const char *
+(*cache_req_create_debug_name_fn)(TALLOC_CTX *mem_ctx,
+                                  struct cache_req_data *data,
+                                  struct sss_domain_info *domain);
+
+/**
+ * Check if an object is stored in negative cache.
+ *
+ * @return EOK    If the object is not found.
+ * @return EEXIST If the object is found in negative cache.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_ncache_check_fn)(struct sss_nc_ctx *ncache,
+                             struct sss_domain_info *domain,
+                             struct cache_req_data *data);
+
+/**
+ * Add an object into negative cache.
+ *
+ * @return EOK If everything went fine.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_ncache_add_fn)(struct sss_nc_ctx *ncache,
+                           struct sss_domain_info *domain,
+                           struct cache_req_data *data);
+
+/**
+ * Add an object into global negative cache.
+ *
+ * @return EOK If everything went fine.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_global_ncache_add_fn)(struct sss_nc_ctx *ncache,
+                                  struct cache_req_data *data);
+
+/**
+ * Lookup object in sysdb.
+ *
+ * @return EOK    If the object is found.
+ * @return ENOENT If the object is not found.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_lookup_fn)(TALLOC_CTX *mem_ctx,
+                       struct cache_req *cr,
+                       struct cache_req_data *data,
+                       struct sss_domain_info *domain,
+                       struct ldb_result **_result);
+
+/**
+ * Return parameters for Data Provider request.
+ *
+ * @return EOK If everything went fine.
+ * @return Other errno code in case of an error.
+ */
+typedef errno_t
+(*cache_req_dpreq_params_fn)(TALLOC_CTX *mem_ctx,
+                             struct cache_req *cr,
+                             struct ldb_result *result,
+                             const char **_string,
+                             uint32_t *_id,
+                             const char **_flag);
+
+struct cache_req_plugin {
+    /**
+     * Plugin name.
+     */
+    const char *name;
+
+    /**
+     * Data provider request type.
+     */
+    enum sss_dp_acct_type dp_type;
+
+    /**
+     * True if input name should be parsed for domain.
+     */
+    bool parse_name;
+
+    /**
+     * True if this plugin looks up by UPN.
+     */
+    bool upn_lookup;
+
+    /**
+     * True if this plugin can be swapped for equivalent search with UPN.
+     */
+    bool allow_assume_upn;
+    enum cache_req_type upn_equivalent;
+
+    /**
+     * True if we always contact data provider.
+     */
+    bool bypass_cache;
+
+    /**
+     * True if only one result is expected.
+     */
+    bool only_one_result;
+
+    /**
+     * Expiration timestamp attribute name.
+     */
+    const char *attr_expiration;
+
+    /* Operations */
+    cache_req_prepare_domain_data_fn prepare_domain_data_fn;
+    cache_req_create_debug_name_fn create_debug_name_fn;
+    cache_req_global_ncache_add_fn global_ncache_add_fn;
+    cache_req_ncache_check_fn ncache_check_fn;
+    cache_req_ncache_add_fn ncache_add_fn;
+    cache_req_lookup_fn lookup_fn;
+    cache_req_dpreq_params_fn dpreq_params_fn;
+};
+
+extern struct cache_req_plugin cache_req_user_by_name;
+extern struct cache_req_plugin cache_req_user_by_upn;
+extern struct cache_req_plugin cache_req_user_by_id;
+
+#endif /* _CACHE_REQ_PLUGIN_H_ */
diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
new file mode 100644
index 0000000..b2979e8
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_private.h
@@ -0,0 +1,82 @@
+/*
+    Authors:
+        Pavel Březina <[email protected]>
+
+    Copyright (C) 2016 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CACHE_REQ_PRIVATE_H_
+#define _CACHE_REQ_PRIVATE_H_
+
+#include <stdint.h>
+
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+#define CACHE_REQ_DEBUG(level, cr, fmt, ...) \
+    DEBUG(level, "CR [%s #%u]: " fmt, \
+          (cr)->reqname, (cr)->reqid, ##__VA_ARGS__)
+
+struct cache_req {
+    /* Provided input. */
+    struct cache_req_data *data;
+
+    struct cache_req_plugin *plugin;
+    struct resp_ctx *rctx;
+    struct sss_nc_ctx *ncache;
+    int midpoint;
+
+    /* Data Provider request type resolved from @type.
+     * FIXME: This is currently needed for data provider calls. We should
+     * refactor responder_dp.c to get rid of this member. */
+    enum sss_dp_acct_type dp_type;
+
+    /* Domain related informations. */
+    struct sss_domain_info *domain;
+
+    /* Debug information */
+    uint32_t reqid;
+    const char *reqname;
+    const char *debugobj;
+
+    /* Time when the request started. Useful for by-filter lookups */
+    time_t req_start;
+};
+
+/* Input data. */
+struct cache_req_data {
+    enum cache_req_type type;
+    struct {
+        const char *input;  /* Original input. */
+        const char *name;   /* Parsed name or UPN. */
+        const char *lookup; /* Converted per domain rules. */
+    } name;
+    uint32_t id;
+    const char *cert;
+    const char *sid;
+    const char **attrs;
+};
+
+struct tevent_req *
+cache_req_search_send(TALLOC_CTX *mem_ctx,
+                      struct tevent_context *ev,
+                      struct cache_req *cr);
+
+errno_t cache_req_search_recv(TALLOC_CTX *mem_ctx,
+                              struct tevent_req *req,
+                              struct ldb_result **_result);
+
+#endif /* _CACHE_REQ_PRIVATE_H_ */
diff --git a/src/responder/common/cache_req/cache_req_search.c b/src/responder/common/cache_req/cache_req_search.c
new file mode 100644
index 0000000..01a403b
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_search.c
@@ -0,0 +1,388 @@
+/*
+    Authors:
+        Pavel Březina <[email protected]>
+
+    Copyright (C) 2016 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <ldb.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "util/util.h"
+#include "responder/common/cache_req/cache_req_private.h"
+
+static errno_t cache_req_search_ncache(struct cache_req *cr)
+{
+    errno_t ret;
+
+    if (cr->plugin->ncache_check_fn == NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr,
+                        "This request type does not support negative cache\n");
+        return EOK;
+    }
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+                    "Checking negative cache for [%s]\n",
+                    cr->debugobj);
+
+    ret = cr->plugin->ncache_check_fn(cr->ncache, cr->domain, cr->data);
+    if (ret == EEXIST) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+                        "[%s] does not exist (negative cache)\n",
+                        cr->debugobj);
+        return ENOENT;
+    } else if (ret != EOK) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                        "Unable to check negative cache [%d]: %s\n",
+                        ret, sss_strerror(ret));
+        return ret;
+    }
+
+    return EOK;
+}
+
+static void cache_req_search_ncache_add(struct cache_req *cr)
+{
+    errno_t ret;
+
+    if (cr->plugin->ncache_add_fn == NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr,
+                        "This request type does not support negative cache\n");
+        return;
+    }
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr, "Adding [%s] to negative cache\n",
+                    cr->debugobj);
+
+    ret = cr->plugin->ncache_add_fn(cr->ncache, cr->domain, cr->data);
+    if (ret != EOK) {
+        CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr,
+                        "Cannot set negative cache for [%s] [%d]: %s\n",
+                        cr->debugobj, ret, sss_strerror(ret));
+        /* not fatal */
+    }
+
+    return;
+}
+
+static errno_t cache_req_search_cache(TALLOC_CTX *mem_ctx,
+                                      struct cache_req *cr,
+                                      struct ldb_result **_result)
+{
+    errno_t ret;
+
+    if (cr->plugin->lookup_fn == NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                        "Bug: No cache lookup function specified\n");
+        return ERR_INTERNAL;
+    }
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+                    "Looking up [%s] in cache\n",
+                    cr->debugobj);
+
+    ret = cr->plugin->lookup_fn(mem_ctx, cr, cr->data, cr->domain, _result);
+    if (ret == ENOENT) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+                        "Object [%s] was not found in cache\n",
+                        cr->debugobj);
+        return ENOENT;
+    } else if (ret != EOK) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                        "Unable to lookup [%s] in cache\n",
+                        ret, sss_strerror(ret));
+        return ret;
+    } else if (cr->plugin->only_one_result && *_result->count > 1) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                        "Multiple objects were found when "
+                        "only one was expected!\n");
+        return ERR_INTERNAL;
+    }
+
+    return EOK;
+}
+
+static errno_t cache_req_dpreq_params(TALLOC_CTX *mem_ctx,
+                                      struct cache_req *cr,
+                                      struct ldb_result *result,
+                                      const char **_string,
+                                      uint32_t *_id,
+                                      const char **_flag)
+{
+    errno_t ret;
+
+    if (cr->plugin->dpreq_params_fn == NULL) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                        "Bug: No dpreq params function specified\n");
+        return ERR_INTERNAL;
+    }
+
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_INTERNAL, cr,
+                    "Creating DP request parameters\n");
+
+    ret = cr->plugin->dpreq_params_fn(mem_ctx, cr, result, _string, _id, _flag);
+    if (ret != EOK) {
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, cr,
+                        "Unable to create DP request parameters [%d]: %s\n",
+                        ret, sss_strerror(ret));
+        return ret;
+    }
+
+    return EOK;
+}
+
+static void cache_req_search_process_dp(TALLOC_CTX *mem_ctx,
+                                        struct tevent_req *subreq,
+                                        struct cache_req *cr)
+{
+    char *err_msg;
+    dbus_uint16_t err_maj;
+    dbus_uint32_t err_min;
+    errno_t ret;
+
+    ret = sss_dp_get_account_recv(mem_ctx, subreq, &err_maj, &err_min, &err_msg);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        CACHE_REQ_DEBUG(SSSDBG_OP_FAILURE, cr,
+                        "Could not get account info [%d]: %s\n",
+                        ret, sss_strerror(ret));
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+                        "Due to an error we will return cached data\n");
+    }
+
+    if (err_maj) {
+        CACHE_REQ_DEBUG(SSSDBG_OP_FAILURE, cr,
+                        "Data Provider Error: %u, %u, %s\n",
+                        (unsigned int)err_maj, (unsigned int)err_min, err_msg);
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+                        "Due to an error we will return cached data\n");
+    }
+
+    return;
+}
+
+static enum cache_object_status
+cache_req_expiration_status(struct cache_req *cr,
+                            struct ldb_result *result)
+{
+    time_t expire;
+    errno_t ret;
+
+    if (result == NULL || result->count == 0 || cr->plugin->bypass_cache) {
+        return CACHE_OBJECT_MISSING;
+    }
+
+    expire = ldb_msg_find_attr_as_uint64(result->msgs[0],
+                                         cr->plugin->attr_expiration, 0);
+
+    ret = sss_cmd_check_cache(result->msgs[0], cr->midpoint, expire);
+    if (ret == EOK) {
+        return CACHE_OBJECT_VALID;
+    } else if (ret == EAGAIN) {
+        return CACHE_OBJECT_MIDPOINT;
+    }
+
+    return CACHE_OBJECT_EXPIRED;
+}
+
+struct cache_req_search_state {
+    /* input data */
+    struct tevent_context *ev;
+    struct resp_ctx *rctx;
+    struct cache_req *cr;
+
+    /* output data */
+    struct ldb_result *result;
+};
+
+static void cache_req_search_dp(struct tevent_req *req);
+static void cache_req_search_oob_done(struct tevent_req *subreq);
+static void cache_req_search_done(struct tevent_req *subreq);
+
+struct tevent_req *
+cache_req_search_send(TALLOC_CTX *mem_ctx,
+                      struct tevent_context *ev,
+                      struct cache_req *cr)
+{
+    struct cache_req_search_state *state;
+    enum cache_object_status status;
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct cache_req_search_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
+        return NULL;
+    }
+
+    state->ev = ev;
+    state->cr = cr;
+
+    ret = cache_req_search_ncache(cr);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = cache_req_search_cache(state, cr, &state->result);
+    if (ret != EOK && ret != ENOENT) {
+        goto done;
+    }
+
+    status = cache_req_expiration_status(cr, state->result);
+    if (status == CACHE_OBJECT_VALID) {
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, cr,
+                        "Returning [%s] from cache\n", cr->debugobj);
+        ret = EOK;
+        goto done;
+    }
+
+    ret = cache_req_search_dp(req, status);
+    if (ret != EAGAIN) {
+        goto done;
+    }
+
+    return req;
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static errno_t cache_req_search_dp(struct tevent_req *req,
+                                   enum cache_object_status status)
+{
+    struct cache_req_search_state *state;
+    struct tevent_req *subreq;
+    const char *extra_flag;
+    const char *search_str;
+    uint32_t search_id;
+    errno_t ret;
+
+    state = tevent_req_data(req, struct cache_req_search_state);
+
+    ret = cache_req_dpreq_params(state, state->cr, state->result,
+                                 &search_str, &search_id, &extra_flag);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    switch (status) {
+    case CACHE_OBJECT_MIDPOINT:
+        /* Out of band update. The calling function will return the cached
+         * entry immediately. We need to use rctx so the request is not
+         * removed when state is freed. */
+
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                        "Performing midpoint cache update of [%s]\n",
+                        state->cr->debugobj);
+
+        subreq = sss_dp_get_account_send(state->cr->rctx, state->cr->rctx,
+                                         state->cr->domain, true,
+                                         state->cr->dp_type,
+                                         search_str, search_id, extra_flag);
+        if (subreq == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory sending out-of-band "
+                                       "data provider request\n");
+            /* This is non-fatal, so we'll continue here */
+        } else {
+            tevent_req_set_callback(subreq, cache_req_search_oob_done, req);
+        }
+
+        return EOK;
+    case CACHE_OBJECT_EXPIRED:
+    case CACHE_OBJECT_MISSING:
+        CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                        "Looking up [%s] in data provider\n",
+                        state->cr->debugobj);
+
+        subreq = sss_dp_get_account_send(state, state->cr->rctx,
+                                         state->cr->domain, true,
+                                         state->cr->dp_type,
+                                         search_str, search_id, extra_flag);
+        if (subreq == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Out of memory sending data provider request\n");
+            return ENOMEM;
+        }
+
+        tevent_req_set_callback(subreq, cache_req_search_done, req);
+        return EAGAIN;
+    default:
+        /* error */
+        CACHE_REQ_DEBUG(SSSDBG_CRIT_FAILURE, state->cr,
+                        "Unexpected status [%d]\n", status);
+        return ret;
+    }
+}
+
+static void cache_req_search_oob_done(struct tevent_req *subreq)
+{
+    DEBUG(SSSDBG_TRACE_INTERNAL, "Out of band request finished\n");
+    talloc_zfree(subreq);
+
+    return;
+}
+
+static void cache_req_search_done(struct tevent_req *subreq)
+{
+    struct cache_req_search_state *state;
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct cache_req_search_state);
+
+    cache_req_search_process_dp(state, subreq, state->cr);
+
+    /* Get result from cache again. */
+    ret = cache_req_search_cache(state, state->cr, &state->result);
+    if (ret == ENOENT) {
+        cache_req_search_ncache_add(state->cr);
+        tevent_req_error(req, ENOENT);
+        return;
+    } else if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
+                    "Returning updated object [%s]\n", state->cr->debugobj);
+
+    tevent_req_done(req);
+    return;
+}
+
+errno_t cache_req_search_recv(TALLOC_CTX *mem_ctx,
+                              struct tevent_req *req,
+                              struct ldb_result **_result)
+{
+    struct cache_req_search_state *state = NULL;
+    state = tevent_req_data(req, struct cache_req_search_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *_result = talloc_steal(mem_ctx, state->result);
+
+    return EOK;
+}
diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
new file mode 100644
index 0000000..c16bc8f
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
@@ -0,0 +1,124 @@
+/*
+    Authors:
+        Pavel Březina <[email protected]>
+
+    Copyright (C) 2016 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static const char *
+cache_req_user_by_id_create_debug_name(TALLOC_CTX *mem_ctx,
+                                         struct cache_req_data *data,
+                                         struct sss_domain_info *domain)
+{
+    return talloc_asprintf(mem_ctx, "UID:%d@%s", data->id, domain->name);
+}
+
+static errno_t
+cache_req_user_by_id_ncache_check(struct sss_nc_ctx *ncache,
+                                    struct sss_domain_info *domain,
+                                    struct cache_req_data *data)
+{
+    return sss_ncache_check_uid(ncache, NULL, data->id);
+}
+
+static errno_t
+cache_req_user_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
+                                       struct cache_req_data *data)
+{
+    return sss_ncache_set_uid(ncache, false, NULL, data->id);
+}
+
+static errno_t
+cache_req_user_by_id_lookup(TALLOC_CTX *mem_ctx,
+                              struct cache_req *cr,
+                              struct cache_req_data *data,
+                              struct sss_domain_info *domain,
+                              struct ldb_result **_result)
+{
+    return sysdb_getpwuid_with_views(mem_ctx, domain, data->id, _result);
+}
+
+static errno_t
+cache_req_user_by_id_dpreq_params(TALLOC_CTX *mem_ctx,
+                                    struct cache_req *cr,
+                                    struct ldb_result *result,
+                                    const char **_string,
+                                    uint32_t *_id,
+                                    const char **_flag)
+{
+    uint32_t id;
+    errno_t ret;
+
+    *_id = cr->data->id;
+    *_string = NULL;
+    *_flag = NULL;
+
+    if (!DOM_HAS_VIEWS(cr->domain)) {
+        return EOK;
+    }
+
+    /* We must search with views. */
+    if (result == NULL || result->count == 0) {
+        *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+        return EOK;
+    }
+
+    /* If domain has views we will try to user original values instead of the
+     * overridden ones. This is a must for the LOCAL view since we can't look
+     * it up otherwise. But it is also a shortcut for non-local views where
+     * we will not fail over to the overridden value. */
+
+    id = ldb_msg_find_attr_as_uint64(result->msgs[0], SYSDB_UIDNUM, 0);
+    if (id == 0) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Bug: id cannot be 0\n");
+        *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+        return EOK;
+    }
+
+    /* Now we have the original name and id. We don't have to search with
+     * views unless some error occurred. */
+    *_id = id;
+
+    return EOK;
+}
+
+struct cache_req_plugin cache_req_user_by_id = {
+    .name = "User by ID",
+    .dp_type = SSS_DP_USER,
+    .parse_name = false,
+    .upn_lookup = false,
+    .allow_assume_upn = false,
+    .upn_equivalent = CACHE_REQ_SENTINEL,
+    .bypass_cache = false,
+    .only_one_result = true,
+    .attr_expiration = SYSDB_CACHE_EXPIRE,
+
+    .prepare_domain_data_fn = NULL,
+    .create_debug_name_fn = cache_req_user_by_id_create_debug_name,
+    .global_ncache_add_fn = NULL,
+    .ncache_check_fn = cache_req_user_by_id_ncache_check,
+    .global_ncache_add_fn = cache_req_user_by_id_global_ncache_add,
+    .lookup_fn = cache_req_user_by_id_lookup,
+    .dpreq_params_fn = cache_req_user_by_id_dpreq_params
+};
diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_name.c b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
new file mode 100644
index 0000000..5f111da
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_name.c
@@ -0,0 +1,176 @@
+/*
+    Authors:
+        Pavel Březina <[email protected]>
+
+    Copyright (C) 2016 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_user_by_name_prepare_domain_data(struct cache_req *cr,
+                                           struct cache_req_data *data,
+                                           struct sss_domain_info *domain)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char *name;
+    errno_t ret;
+
+    if (cr->data->name.name == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed name is NULL?\n");
+        ret = ERR_INTERNAL;
+        goto done;
+    }
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    name = sss_get_cased_name(tmp_ctx, cr->data->name.name,
+                              domain->case_sensitive);
+    if (name == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    name = sss_reverse_replace_space(tmp_ctx, name, cr->rctx->override_space);
+    if (name == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    name = sss_create_internal_fqname(tmp_ctx, name, domain->name);
+    if (name == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    talloc_zfree(data->name.lookup);
+    data->name.lookup = talloc_steal(data, name);
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static const char *
+cache_req_user_by_name_create_debug_name(TALLOC_CTX *mem_ctx,
+                                         struct cache_req_data *data,
+                                         struct sss_domain_info *domain)
+{
+    return talloc_asprintf(mem_ctx, "%s", data->name.lookup, domain->name);
+}
+
+static errno_t
+cache_req_user_by_name_ncache_check(struct sss_nc_ctx *ncache,
+                                    struct sss_domain_info *domain,
+                                    struct cache_req_data *data)
+{
+    return sss_ncache_check_user(ncache, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_name_ncache_add(struct sss_nc_ctx *ncache,
+                                  struct sss_domain_info *domain,
+                                  struct cache_req_data *data)
+{
+    return sss_ncache_set_user(ncache, false, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_name_lookup(TALLOC_CTX *mem_ctx,
+                              struct cache_req *cr,
+                              struct cache_req_data *data,
+                              struct sss_domain_info *domain,
+                              struct ldb_result **_result)
+{
+    return sysdb_getpwnam_with_views(mem_ctx, domain, data->name.lookup,
+                                     _result);
+}
+
+static errno_t
+cache_req_user_by_name_dpreq_params(TALLOC_CTX *mem_ctx,
+                                    struct cache_req *cr,
+                                    struct ldb_result *result,
+                                    const char **_string,
+                                    uint32_t *_id,
+                                    const char **_flag)
+{
+    struct ldb_result *user;
+    const char *name;
+    errno_t ret;
+
+    *_id = 0;
+    *_string = cr->data->name.lookup;
+    *_flag = NULL;
+
+    if (!DOM_HAS_VIEWS(cr->domain)) {
+        return EOK;
+    }
+
+    /* We must search with views. */
+    if (result == NULL || result->count == 0) {
+        *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+        return EOK;
+    }
+
+    /* If domain has views we will try to user original values instead of the
+     * overridden ones. This is a must for the LOCAL view since we can't look
+     * it up otherwise. But it is also a shortcut for non-local views where
+     * we will not fail over to the overridden value. */
+
+    name = ldb_msg_find_attr_as_string(result->msgs[0], SYSDB_NAME, NULL);
+    if (name == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL\n");
+        *_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
+        return EOK;
+    }
+
+    /* Now we have the original name and id. We don't have to search with
+     * views unless some error occurred. */
+    *_string = talloc_steal(mem_ctx, name);
+
+    return EOK;
+}
+
+struct cache_req_plugin cache_req_user_by_name = {
+    .name = "User by name",
+    .dp_type = SSS_DP_USER,
+    .parse_name = true,
+    .upn_lookup = false,
+    .allow_assume_upn = true,
+    .upn_equivalent = CACHE_REQ_USER_BY_UPN,
+    .bypass_cache = false,
+    .only_one_result = true,
+    .attr_expiration = SYSDB_CACHE_EXPIRE,
+
+    .prepare_domain_data_fn = cache_req_user_by_name_prepare_domain_data,
+    .create_debug_name_fn = cache_req_user_by_name_create_debug_name,
+    .global_ncache_add_fn = NULL,
+    .ncache_check_fn = cache_req_user_by_name_ncache_check,
+    .ncache_add_fn = cache_req_user_by_name_ncache_add,
+    .lookup_fn = cache_req_user_by_name_lookup,
+    .dpreq_params_fn = cache_req_user_by_name_dpreq_params
+};
diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c
new file mode 100644
index 0000000..f26e96c
--- /dev/null
+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_upn.c
@@ -0,0 +1,124 @@
+/*
+    Authors:
+        Pavel Březina <[email protected]>
+
+    Copyright (C) 2016 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <ldb.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "responder/common/cache_req/cache_req_plugin.h"
+
+static errno_t
+cache_req_user_by_upn_prepare_domain_data(struct cache_req *cr,
+                                           struct cache_req_data *data,
+                                           struct sss_domain_info *domain)
+{
+    const char *name;
+    errno_t ret;
+
+    if (cr->data->name.name == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Bug: parsed UPN is NULL?\n");
+        return ERR_INTERNAL;
+    }
+
+    /* When looking up UPNs we don't want to reverse-replace spaces,
+     * just search whatever the user passed in. strdup the name so we
+     * can safely steal it later.
+     */
+    name = talloc_strdup(data, cr->data->name.name);
+    if (name == NULL) {
+        return ENOMEM;
+    }
+
+    talloc_zfree(data->name.lookup);
+    data->name.lookup = talloc_steal(data, name);
+
+    return EOK;
+}
+
+static const char *
+cache_req_user_by_upn_create_debug_name(TALLOC_CTX *mem_ctx,
+                                         struct cache_req_data *data,
+                                         struct sss_domain_info *domain)
+{
+    return talloc_asprintf(mem_ctx, "%s", data->name.lookup, domain->name);
+}
+
+static errno_t
+cache_req_user_by_upn_ncache_check(struct sss_nc_ctx *ncache,
+                                    struct sss_domain_info *domain,
+                                    struct cache_req_data *data)
+{
+    return sss_ncache_check_user(ncache, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_upn_ncache_add(struct sss_nc_ctx *ncache,
+                                  struct sss_domain_info *domain,
+                                  struct cache_req_data *data)
+{
+    return sss_ncache_set_user(ncache, false, domain, data->name.lookup);
+}
+
+static errno_t
+cache_req_user_by_upn_lookup(TALLOC_CTX *mem_ctx,
+                              struct cache_req *cr,
+                              struct cache_req_data *data,
+                              struct sss_domain_info *domain,
+                              struct ldb_result **_result)
+{
+    return sysdb_getpwupn(mem_ctx, domain, data->name.lookup, _result);
+}
+
+static errno_t
+cache_req_user_by_upn_dpreq_params(TALLOC_CTX *mem_ctx,
+                                    struct cache_req *cr,
+                                    struct ldb_result *result,
+                                    const char **_string,
+                                    uint32_t *_id,
+                                    const char **_flag)
+{
+    *_id = 0;
+    *_string = cr->data->name.lookup;
+    *_flag = EXTRA_NAME_IS_UPN;
+
+    return EOK;
+}
+
+struct cache_req_plugin cache_req_user_by_upn = {
+    .name = "User by UPN",
+    .dp_type = SSS_DP_USER,
+    .parse_name = false,
+    .upn_lookup = true,
+    .allow_assume_upn = false,
+    .upn_equivalent = CACHE_REQ_SENTINEL,
+    .bypass_cache = false,
+    .only_one_result = true,
+    .attr_expiration = SYSDB_CACHE_EXPIRE,
+
+    .prepare_domain_data_fn = cache_req_user_by_upn_prepare_domain_data,
+    .create_debug_name_fn = cache_req_user_by_upn_create_debug_name,
+    .global_ncache_add_fn = NULL,
+    .ncache_check_fn = cache_req_user_by_upn_ncache_check,
+    .ncache_add_fn = cache_req_user_by_upn_ncache_add,
+    .lookup_fn = cache_req_user_by_upn_lookup,
+    .dpreq_params_fn = cache_req_user_by_upn_dpreq_params
+};
_______________________________________________
sssd-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to