The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
mod.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 35f236aab2b414ba45eca78ecf0cf48db8c27768 $
19 *
20 * @brief Utility functions used in the module.
21 * @file mod.c
22 *
23 * @author Aaron Hurt (ahurt@anbcs.com)
24 * @copyright 2013-2014 The FreeRADIUS Server Project.
25 */
26RCSID("$Id: 35f236aab2b414ba45eca78ecf0cf48db8c27768 $")
27
28#define LOG_PREFIX "couchbase"
29
30#include <freeradius-devel/json/base.h>
31#include <freeradius-devel/server/base.h>
32#include <freeradius-devel/server/map.h>
33
34#include "mod.h"
35#include "couchbase.h"
36
37/** Delete a connection pool handle and free related resources
38 *
39 * Destroys the underlying Couchbase connection handle freeing any related
40 * resources and closes the socket connection.
41 *
42 * @param chandle The connection handle to destroy.
43 * @return 0.
44 */
46{
47 lcb_t cb_inst = chandle->handle; /* couchbase instance */
48
49 /* destroy/free couchbase instance */
50 lcb_destroy(cb_inst);
51
52 /* return */
53 return 0;
54}
55
56/** Delete a object built by mod_build_api_opts()
57 *
58 * Release the underlying mod_build_api_opts() objects
59 *
60 * @param inst The module instance.
61 * @return 0.
62 */
64{
65 couchbase_opts_t *opts = inst->api_opts;
66
67 if (!opts) return 0;
68
69 DEBUG("Releasing the couchbase api options");
70
71 for (; opts != NULL; opts = opts->next) {
72 TALLOC_FREE(opts->key);
73 TALLOC_FREE(opts->val);
74 }
75
76 TALLOC_FREE(opts);
77
78 /* return */
79 return 0;
80}
81
82/** Build a couchbase_opts_t structure from the configuration "couchbase_api" list
83 *
84 * Parse the "couchbase_api" list from the module configuration file and store this
85 * as a couchbase_opts_t object (key/value list).
86 *
87 * @param conf Configuration list.
88 * @param inst The module instance.
89 * @return
90 * - 0 on success.
91 * - -1 on failure.
92 */
94{
95 CONF_SECTION *cs; /* module config list */
96 CONF_ITEM *ci; /* config item */
97 CONF_PAIR *cp; /* config pair */
98 couchbase_opts_t *entry = NULL; /* couchbase api options */
99
100 /* find opts list */
101 cs = cf_section_find(conf, "opts", NULL);
102
103 /* check list */
104 if (!cs) return 0;
105
106 /* parse libcouchbase_opts list */
107 cf_log_debug(cs, "opts {");
108
109 for (ci = cf_item_next(cs, NULL); ci != NULL; ci = cf_item_next(cs, ci)) {
110 /*
111 * Ignore things we don't care about.
112 */
113 if (!cf_item_is_pair(ci)) {
114 continue;
115 }
116
117 /* get value pair from item */
118 cp = cf_item_to_pair(ci);
119
120 /* create opts object */
121 if (!entry) {
122 entry = talloc_zero(inst, couchbase_opts_t);
123 inst->api_opts = entry;
124 } else {
125 entry->next = talloc_zero(inst->api_opts, couchbase_opts_t);
126 entry = entry->next;
127 }
128 entry->next = NULL;
129 entry->key = talloc_typed_strdup(entry, cf_pair_attr(cp));
130 entry->val = talloc_typed_strdup(entry, cf_pair_value(cp));
131
132 /* debugging */
133 cf_log_debug(cs, "\t%s = \"%s\"", entry->key, entry->val);
134 }
135
136 cf_log_debug(cs, "}");
137
138 /* return */
139 return 0;
140}
141
142/** Create a new connection pool handle
143 *
144 * Create a new connection to Couchbase within the pool and initialize
145 * information associated with the connection instance.
146 *
147 * @param ctx The connection parent context.
148 * @param instance The module instance.
149 * @param timeout Maximum time to establish the connection.
150 * @return
151 * - New connection handle.
152 * - NULL on error.
153 */
154void *mod_conn_create(TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout)
155{
156 rlm_couchbase_t *inst = talloc_get_type_abort(instance, rlm_couchbase_t); /* module instance pointer */
157 rlm_couchbase_handle_t *chandle = NULL; /* connection handle pointer */
158 cookie_t *cookie = NULL; /* couchbase cookie */
159 lcb_t cb_inst; /* couchbase connection instance */
160 lcb_error_t cb_error; /* couchbase error status */
161 couchbase_opts_t const *opts = inst->api_opts; /* couchbase extra API settings */
162
163 /* create instance */
164 cb_error = couchbase_init_connection(&cb_inst, inst->server, inst->bucket, inst->username,
165 inst->password, fr_time_delta_to_sec(timeout), opts);
166
167 /* check couchbase instance */
168 if (cb_error != LCB_SUCCESS) {
169 ERROR("failed to initiate couchbase connection: %s (0x%x)",
170 lcb_strerror(NULL, cb_error), cb_error);
171 /* destroy/free couchbase instance */
172 lcb_destroy(cb_inst);
173 /* fail */
174 return NULL;
175 }
176
177 /* allocate memory for couchbase connection instance abstraction */
178 chandle = talloc_zero(ctx, rlm_couchbase_handle_t);
179 talloc_set_destructor(chandle, _mod_conn_free);
180
181 /* allocate cookie off handle */
182 cookie = talloc_zero(chandle, cookie_t);
183
184 /* init tokener error and json object */
185 cookie->jerr = json_tokener_success;
186 cookie->jobj = NULL;
187
188 /* populate handle */
189 chandle->cookie = cookie;
190 chandle->handle = cb_inst;
191
192 /* return handle struct */
193 return chandle;
194}
195
196/** Check the health of a connection handle
197 *
198 * Attempt to determine the state of the Couchbase connection by requesting
199 * a cluster statistics report. Mark the connection as failed if the request
200 * returns anything other than success.
201 *
202 * @param opaque The module instance (currently unused).
203 * @param connection The connection handle.
204 * @return
205 * - 0 on success (alive).
206 * - -1 on failure (unavailable).
207 */
208int mod_conn_alive(UNUSED void *opaque, void *connection)
209{
210 rlm_couchbase_handle_t *chandle = connection; /* connection handle pointer */
211 lcb_t cb_inst = chandle->handle; /* couchbase instance */
212 lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error status */
213
214 /* attempt to get server stats */
215 if ((cb_error = couchbase_server_stats(cb_inst, NULL)) != LCB_SUCCESS) {
216 /* log error */
217 ERROR("failed to get couchbase server stats: %s (0x%x)",
218 lcb_strerror(NULL, cb_error), cb_error);
219 /* error out */
220 return -1;
221 }
222 return 0;
223}
224
225/** Build a JSON object map from the configuration "map" list
226 *
227 * Parse the "map" list from the module configuration file and store this
228 * as a JSON object (key/value list) in the module instance. This map will be
229 * used to lookup and map attributes for all incoming accounting requests.
230 *
231 * @param conf Configuration list.
232 * @param inst The module instance.
233 * @return
234 * - 0 on success.
235 * - -1 on failure.
236 */
238{
239 CONF_SECTION *cs; /* module config list */
240 CONF_ITEM *ci; /* config item */
241 CONF_PAIR *cp; /* config pair */
242 const char *attribute, *element; /* attribute and element names */
243
244 /* find update list */
245 cs = cf_section_find(conf, "update", NULL);
246
247 /* backwards compatibility */
248 if (!cs) {
249 cs = cf_section_find(conf, "map", NULL);
250 WARN("found deprecated 'map' list - please change to 'update'");
251 }
252
253 /* check list */
254 if (!cs) {
255 ERROR("failed to find 'update' list in config");
256 /* fail */
257 return -1;
258 }
259
260 /* create attribute map object */
261 inst->map = json_object_new_object();
262
263 /* parse update list */
264 for (ci = cf_item_next(cs, NULL); ci != NULL; ci = cf_item_next(cs, ci)) {
265 /* validate item */
266 if (!cf_item_is_pair(ci)) {
267 ERROR("failed to parse invalid item in 'update' list");
268 /* free map */
269 if (inst->map) {
270 json_object_put(inst->map);
271 }
272 /* fail */
273 return -1;
274 }
275
276 /* get value pair from item */
277 cp = cf_item_to_pair(ci);
278
279 /* get pair name (attribute name) */
280 attribute = cf_pair_attr(cp);
281
282 /* get pair value (element name) */
283 element = cf_pair_value(cp);
284
285 /* add pair name and value */
286 json_object_object_add(inst->map, attribute, json_object_new_string(element));
287
288 /* debugging */
289 DEBUG3("added attribute '%s' to element '%s' mapping", attribute, element);
290 }
291
292 /* debugging */
293 DEBUG3("built attribute to element mapping %s", json_object_to_json_string(inst->map));
294
295 /* return */
296 return 0;
297}
298
299/** Map attributes to JSON element names
300 *
301 * Attempt to map the passed attribute name to the configured JSON element
302 * name using the JSON object map mod_build_attribute_element_map().
303 *
304 * @param name The character name of the requested attribute.
305 * @param map The JSON object map to use for the lookup.
306 * @param buf The buffer where the given element will be stored if found.
307 * @return
308 * - 0 on success.
309 * - -1 on failure.
310 */
311int mod_attribute_to_element(const char *name, json_object *map, void *buf)
312{
313 json_object *j_value; /* json object values */
314
315 /* clear buffer */
316 memset((char *) buf, 0, MAX_KEY_SIZE);
317
318 /* attempt to map attribute */
319 if (json_object_object_get_ex(map, name, &j_value)) {
320 /* copy and check size */
321 if (strlcpy(buf, json_object_get_string(j_value), MAX_KEY_SIZE) >= MAX_KEY_SIZE) {
322 /* oops ... this value is bigger than our buffer ... error out */
323 ERROR("json map value larger than MAX_KEY_SIZE - %d", MAX_KEY_SIZE);
324 /* return fail */
325 return -1;
326 }
327 /* looks good */
328 return 0;
329 }
330
331 /* debugging */
332 DEBUG("skipping attribute with no map entry - %s", name);
333
334 /* default return */
335 return -1;
336}
337
338/** Build value pairs from the passed JSON object and add to the request
339 *
340 * Parse the passed JSON object and create value pairs that will be injected into
341 * the given request for authorization.
342 *
343 * Example JSON document structure:
344 * @code{.json}
345 * {
346 * "docType": "raduser",
347 * "userName": "test",
348 * "control": {
349 * "SHA-Password": {
350 * "value": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
351 * "op": ":="
352 * }
353 * },
354 * "reply": {
355 * "Reply-Message": {
356 * "value": "Hidey Ho!",
357 * "op": "="
358 * }
359 * }
360 * }
361 * @endcode
362 *
363 * @param[in] ctx to allocate maps in.
364 * @param[in] out Cursor to append maps to.
365 * @param[in] request The request to which the generated pairs should be added.
366 * @param[in] json The JSON object representation of the user document.
367 * @param[in] list The pair list fr_request_attr_control or fr_request_attr_reply
368 * @return
369 * - 1 if no section found.
370 * - 0 on success.
371 * - <0 on error.
372 */
373int mod_json_object_to_map(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, json_object *json, fr_dict_attr_t const *list)
374{
375 json_object *list_obj;
376 char const *list_name = list->name;
377
378 /*
379 * Check for a section matching the specified list
380 */
381 if (!json_object_object_get_ex(json, list_name, &list_obj)) {
382 RDEBUG2("Couldn't find \"%s\" key in json object - Not adding value pairs for this attribute list",
383 list_name);
384
385 return 1;
386 }
387
388 /*
389 * Check the key representing the list is a JSON object
390 */
391 if (!json_object_is_type(list_obj, json_type_object)) {
392 RERROR("Invalid json type for \"%s\" key - Attribute lists must be json objects", list_name);
393
394 return -1;
395 }
396
397 fr_dcursor_tail(out); /* Wind to the end */
398
399 /*
400 * Loop through the keys in this object.
401 *
402 * Where attr_name is the key, and attr_value_obj is
403 * the object containing the attributes value and
404 * operator.
405 */
406 json_object_object_foreach(list_obj, attr_name, attr_value_obj) {
407 json_object *value_obj, *op_obj;
408 fr_dict_attr_t const *da;
409 fr_token_t op;
410
411 if (!json_object_is_type(attr_value_obj, json_type_object)) {
412 REDEBUG("Invalid json type for \"%s\" key - Attributes must be json objects", attr_name);
413
414 error:
415 fr_dcursor_free_list(out); /* Free any maps we added */
416 return -1;
417 }
418
419 RDEBUG3("Parsing %s - \"%s\" : { %s }", list_name,
420 attr_name, json_object_to_json_string(attr_value_obj));
421
422 /*
423 * Check we have a value key
424 */
425 if (!json_object_object_get_ex(attr_value_obj, "value", &value_obj)) {
426 REDEBUG("Missing \"value\" key in: %s - \"%s\" : { %s }", list_name,
427 attr_name, json_object_to_json_string(attr_value_obj));
428
429 goto error;
430 }
431
432 /*
433 * Parse the operator and check its valid
434 */
435 if (json_object_object_get_ex(attr_value_obj, "op", &op_obj)) {
436 char const *op_str;
437
438 op_str = json_object_get_string(op_obj);
439 if (!op_str) {
440 bad_op:
441 REDEBUG("Invalid \"op\" key in: %s - \"%s\" : { %s }", list_name,
442 attr_name, json_object_to_json_string(attr_value_obj));
443
444 goto error;
445 }
446
448 if (!fr_assignment_op[op] && !fr_comparison_op[op]) goto bad_op;
449 } else {
450 op = T_OP_SET; /* The default */
451 }
452
453 /*
454 * Lookup the string attr_name in the
455 * request dictionary.
456 */
457 da = fr_dict_attr_by_name(NULL, fr_dict_root(request->dict), attr_name);
458 if (!da) {
459 RPERROR("Invalid attribute \"%s\"", attr_name);
460 goto error;
461 }
462
463 /*
464 * Create a map representing the operation
465 */
466 {
467 fr_value_box_t tmp = { .type = FR_TYPE_NULL };
468 map_t *map;
469
470 if (fr_json_object_to_value_box(ctx, &tmp, value_obj, da, true) < 0) {
471 bad_value:
472 RPERROR("Failed parsing value for \"%s\"", attr_name);
473 goto error;
474 }
475
476 if (fr_value_box_cast_in_place(ctx, &tmp, da->type, da) < 0) {
477 fr_value_box_clear(&tmp);
478 goto bad_value;
479 }
480
481 if (map_afrom_value_box(ctx, &map,
482 attr_name, T_BARE_WORD,
483 &(tmpl_rules_t){
484 .attr = {
485 .dict_def = request->dict,
486 .list_def = list
487 }
488 },
489 op,
490 &tmp, true) < 0) {
491 fr_value_box_clear(&tmp);
492 goto bad_value;
493 }
494
496 }
497 }
498
499 return 0;
500}
501
502/** Convert value pairs to json objects
503 *
504 * Take the passed value pair and convert it to a json-c JSON object..
505 *
506 * @param request The request object.
507 * @param vp The value pair to convert.
508 * @return A JSON object.
509 */
511{
512 char value[255]; /* radius attribute value */
513
514 /* add this attribute/value pair to our json output */
515 {
516 unsigned int i;
517
518 switch (vp->vp_type) {
519 case FR_TYPE_UINT32:
520 i = vp->vp_uint32;
521 goto print_int;
522
523 case FR_TYPE_UINT16:
524 i = vp->vp_uint16;
525 goto print_int;
526
527 case FR_TYPE_UINT8:
528 i = vp->vp_uint8;
529
530 print_int:
531 /* skip if we have flags */
532 if (vp->da->flags.has_value) break;
533#ifdef HAVE_JSON_OBJECT_NEW_INT64
534 /* debug */
535 RDEBUG3("creating new int64 for unsigned 32 bit int/byte/short '%s'", vp->da->name);
536 /* return as 64 bit int - JSON spec does not support unsigned ints */
537 return json_object_new_int64(i);
538#else
539 /* debug */
540 RDEBUG3("creating new int for unsigned 32 bit int/byte/short '%s'", vp->da->name);
541 /* return as 64 bit int - JSON spec does not support unsigned ints */
542 return json_object_new_int(i);
543#endif
544
545 case FR_TYPE_INT32:
546#ifdef HAVE_JSON_OBJECT_NEW_INT64
547 /* debug */
548 RDEBUG3("creating new int64 for signed 32 bit integer '%s'", vp->da->name);
549 /* return as 64 bit int - json-c represents all ints as 64 bits internally */
550 return json_object_new_int64(vp->vp_int32);
551#else
552 RDEBUG3("creating new int for signed 32 bit integer '%s'", vp->da->name);
553 /* return as signed int */
554 return json_object_new_int(vp->vp_int32);
555#endif
556
557 case FR_TYPE_UINT64:
558#ifdef HAVE_JSON_OBJECT_NEW_INT64
559 /* debug */
560 RDEBUG3("creating new int64 for 64 bit integer '%s'", vp->da->name);
561 /* return as 64 bit int - because it is a 64 bit int */
562 return json_object_new_int64(vp->vp_uint64);
563#else
564 /* warning */
565 RWARN("skipping 64 bit integer attribute '%s' - please upgrade json-c to 0.10+", vp->da->name);
566 break;
567#endif
568
569 default:
570 /* silence warnings - do nothing */
571 break;
572 }
573 }
574
575 /* keep going if not set above */
576 switch (vp->vp_type) {
577 case FR_TYPE_STRING:
578 /* debug */
579 RDEBUG3("assigning string '%s' as string", vp->da->name);
580 /* return string value */
581 return json_object_new_string(vp->vp_strvalue);
582
583 default:
584 /* debug */
585 RDEBUG3("assigning unhandled '%s' as string", vp->da->name);
586 /* get standard value */
588 /* return string value from above */
589 return json_object_new_string(value);
590 }
591}
592
593/** Ensure accounting documents always contain a valid timestamp
594 *
595 * Inspect the given JSON object representation of an accounting document
596 * fetched from Couchbase and ensuse it contains a valid (non NULL) timestamp value.
597 *
598 * @param json JSON object representation of an accounting document.
599 * @param vps The value pairs associated with the current accounting request.
600 * @return
601 * - 0 on success.
602 * - -1 on failure.
603 */
604int mod_ensure_start_timestamp(json_object *json, fr_pair_list_t *vps)
605{
606 json_object *j_value; /* json object value */
607 struct tm tm; /* struct to hold event time */
608 time_t ts = 0; /* values to hold time in seconds */
609 fr_pair_t *vp; /* values to hold value pairs */
610 char value[255]; /* store radius attribute values and our timestamp */
611
612 /* get our current start timestamp from our json body */
613 if (json_object_object_get_ex(json, "startTimestamp", &j_value) == 0) {
614 /* debugging ... this shouldn't ever happen */
615 DEBUG("failed to find 'startTimestamp' in current json body");
616 /* return */
617 return -1;
618 }
619
620 /* check for null value */
621 if (json_object_get_string(j_value) != NULL) {
622 /* already set - nothing left to do */
623 return 0;
624 }
625
626 /* get current event timestamp */
627 if ((vp = fr_pair_find_by_da(vps, NULL, attr_event_timestamp)) != NULL) {
628 /* get seconds value from attribute */
629 ts = fr_unix_time_to_sec(vp->vp_date);
630 } else {
631 /* debugging */
632 DEBUG("failed to find event timestamp in current request");
633 /* return */
634 return -1;
635 }
636
637 /* clear value */
638 memset(value, 0, sizeof(value));
639
640 /* get elapsed session time */
641 if ((vp = fr_pair_find_by_da(vps, NULL, attr_acct_session_time)) != NULL) {
642 /* calculate diff */
643 ts = (ts - vp->vp_uint32);
644 /* calculate start time */
645 size_t length = strftime(value, sizeof(value), "%b %e %Y %H:%M:%S %Z", localtime_r(&ts, &tm));
646 /* check length */
647 if (length > 0) {
648 /* debugging */
649 DEBUG("calculated start timestamp: %s", value);
650 /* store new value in json body */
651 json_object_object_add_ex(json, "startTimestamp", json_object_new_string(value),
652 JSON_C_OBJECT_KEY_IS_CONSTANT);
653 } else {
654 /* debugging */
655 DEBUG("failed to format calculated timestamp");
656 /* return */
657 return -1;
658 }
659 }
660
661 /* default return */
662 return 0;
663}
664
665/** Handle client value processing for client_map_section()
666 *
667 * @param out Character output
668 * @param cp Configuration pair
669 * @param data The client data
670 * @return
671 * - 0 on success.
672 * - -1 on failure.
673 */
674static int _get_client_value(char **out, CONF_PAIR const *cp, void *data)
675{
676 json_object *j_value;
677
678 if (!json_object_object_get_ex((json_object *)data, cf_pair_value(cp), &j_value)) {
679 *out = NULL;
680 return 0;
681 }
682
683 if (!j_value) return -1;
684
685 *out = talloc_strdup(NULL, json_object_get_string(j_value));
686 if (!*out) return -1;
687
688 return 0;
689}
690
691/** Load client entries from Couchbase client documents on startup
692 *
693 * This function executes the view defined in the module configuration and loops
694 * through all returned rows. The view is called with "stale=false" to ensure the
695 * most accurate data available when the view is called. This will force an index
696 * rebuild on this design document in Couchbase. However, since this function is only
697 * run once at server startup this should not be a concern.
698 *
699 * @param inst The module instance.
700 * @param tmpl Default values for new clients.
701 * @param map The client attribute configuration list.
702 * @return
703 * - 0 on success.
704 * - -1 on failure.
705 */
707{
708 rlm_couchbase_handle_t *handle = NULL; /* connection pool handle */
709 char vpath[256], vid[MAX_KEY_SIZE], vkey[MAX_KEY_SIZE]; /* view path and fields */
710 char error[512]; /* view error return */
711 int idx = 0; /* row array index counter */
712 int retval = 0; /* return value */
713 lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error holder */
714 json_object *json, *j_value; /* json object holders */
715 json_object *jrows = NULL; /* json object to hold view rows */
716 CONF_SECTION *client; /* freeradius config list */
717 fr_client_t *c; /* freeradius client */
718
719 /* get handle */
720 handle = fr_pool_connection_get(inst->pool, NULL);
721
722 /* check handle */
723 if (!handle) return -1;
724
725 /* set couchbase instance */
726 lcb_t cb_inst = handle->handle;
727
728 /* set cookie */
729 cookie_t *cookie = handle->cookie;
730
731 /* build view path */
732 snprintf(vpath, sizeof(vpath), "%s?stale=false", inst->client_view);
733
734 /* query view for document */
735 cb_error = couchbase_query_view(cb_inst, cookie, vpath, NULL);
736
737 /* check error and object */
738 if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
739 /* log error */
740 ERROR("failed to execute view request or parse return");
741 /* set return */
742 retval = -1;
743 /* return */
744 goto free_and_return;
745 }
746
747 /* debugging */
748 DEBUG3("cookie->jobj == %s", json_object_to_json_string(cookie->jobj));
749
750 /* check for error in json object */
751 if (json_object_object_get_ex(cookie->jobj, "error", &json)) {
752 /* build initial error buffer */
753 strlcpy(error, json_object_get_string(json), sizeof(error));
754 /* get error reason */
755 if (json_object_object_get_ex(cookie->jobj, "reason", &json)) {
756 /* append divider */
757 strlcat(error, " - ", sizeof(error));
758 /* append reason */
759 strlcat(error, json_object_get_string(json), sizeof(error));
760 }
761 /* log error */
762 ERROR("view request failed with error: %s", error);
763 /* set return */
764 retval = -1;
765 /* return */
766 goto free_and_return;
767 }
768
769 /* check for document id in return */
770 if (!json_object_object_get_ex(cookie->jobj, "rows", &json)) {
771 /* log error */
772 ERROR("failed to fetch rows from view payload");
773 /* set return */
774 retval = -1;
775 /* return */
776 goto free_and_return;
777 }
778
779 /* get and hold rows */
780 jrows = json_object_get(json);
781
782 /* free cookie object */
783 if (cookie->jobj) {
784 json_object_put(cookie->jobj);
785 cookie->jobj = NULL;
786 }
787
788 /* debugging */
789 DEBUG3("jrows == %s", json_object_to_json_string(jrows));
790
791 /* check for valid row value */
792 if (!json_object_is_type(jrows, json_type_array) || json_object_array_length(jrows) < 1) {
793 /* log error */
794 ERROR("no valid rows returned from view: %s", vpath);
795 /* set return */
796 retval = -1;
797 /* return */
798 goto free_and_return;
799 }
800
801 /* loop across all row elements */
802 for (idx = 0; (size_t)idx < (size_t)json_object_array_length(jrows); idx++) {
803 /* fetch current index */
804 json = json_object_array_get_idx(jrows, idx);
805
806 /* get view id */
807 if (json_object_object_get_ex(json, "id", &j_value)) {
808 /* clear view id */
809 memset(vid, 0, sizeof(vid));
810 /* copy and check length */
811 if (strlcpy(vid, json_object_get_string(j_value), sizeof(vid)) >= sizeof(vid)) {
812 ERROR("id from row longer than MAX_KEY_SIZE (%d)",
814 continue;
815 }
816 } else {
817 WARN("failed to fetch id from row - skipping");
818 continue;
819 }
820
821 /* get view key */
822 if (json_object_object_get_ex(json, "key", &j_value)) {
823 /* clear view key */
824 memset(vkey, 0, sizeof(vkey));
825 /* copy and check length */
826 if (strlcpy(vkey, json_object_get_string(j_value), sizeof(vkey)) >= sizeof(vkey)) {
827 ERROR("key from row longer than MAX_KEY_SIZE (%d)",
829 continue;
830 }
831 } else {
832 WARN("failed to fetch key from row - skipping");
833 continue;
834 }
835
836 /* fetch document */
837 cb_error = couchbase_get_key(cb_inst, cookie, vid);
838
839 /* check error and object */
840 if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
841 /* log error */
842 ERROR("failed to execute get request or parse return");
843 /* set return */
844 retval = -1;
845 /* return */
846 goto free_and_return;
847 }
848
849 /* debugging */
850 DEBUG3("cookie->jobj == %s", json_object_to_json_string(cookie->jobj));
851
852 /* allocate conf list */
853 client = tmpl ? cf_section_dup(NULL, NULL, tmpl, "client", vkey, true) :
854 cf_section_alloc(NULL, NULL, "client", vkey);
855
856 if (client_map_section(client, map, _get_client_value, cookie->jobj) < 0) {
857 /* free config section */
858 talloc_free(client);
859 /* set return */
860 retval = -1;
861 /* return */
862 goto free_and_return;
863 }
864
865 /*
866 * @todo These should be parented from something.
867 */
868 c = client_afrom_cs(NULL, client, false, 0);
869 if (!c) {
870 ERROR("failed to allocate client");
871 /* free config section */
872 talloc_free(client);
873 /* set return */
874 retval = -1;
875 /* return */
876 goto free_and_return;
877 }
878
879 /*
880 * Client parents the CONF_SECTION which defined it.
881 */
882 talloc_steal(c, client);
883
884 /* attempt to add client */
885 if (!client_add(NULL, c)) {
886 ERROR("failed to add client '%s' from '%s', possible duplicate?", vkey, vid);
887 /* free client */
888 client_free(c);
889 /* set return */
890 retval = -1;
891 /* return */
892 goto free_and_return;
893 }
894
895 /* debugging */
896 DEBUG("client '%s' added", c->longname);
897
898 /* free json object */
899 if (cookie->jobj) {
900 json_object_put(cookie->jobj);
901 cookie->jobj = NULL;
902 }
903 }
904
905 free_and_return:
906
907 /* free rows */
908 if (jrows) {
909 json_object_put(jrows);
910 }
911
912 /* free json object */
913 if (cookie->jobj) {
914 json_object_put(cookie->jobj);
915 cookie->jobj = NULL;
916 }
917
918 /* release handle */
919 if (handle) fr_pool_connection_release(inst->pool, NULL, handle);
920
921 /* return */
922 return retval;
923}
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition cf_util.c:632
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1028
CONF_SECTION * cf_section_dup(TALLOC_CTX *ctx, CONF_SECTION *parent, CONF_SECTION const *cs, char const *name1, char const *name2, bool copy_meta)
Duplicate a configuration section.
Definition cf_util.c:928
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:664
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1594
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1578
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
Definition cf_util.h:140
#define cf_log_debug(_cf, _fmt,...)
Definition cf_util.h:292
#define cf_item_next(_ci, _curr)
Definition cf_util.h:92
lcb_error_t couchbase_query_view(lcb_t instance, const void *cookie, const char *path, const char *post)
Query a Couchbase design document view.
Definition couchbase.c:377
lcb_error_t couchbase_server_stats(lcb_t instance, const void *cookie)
Request Couchbase server statistics.
Definition couchbase.c:254
lcb_error_t couchbase_init_connection(lcb_t *instance, const char *host, const char *bucket, const char *user, const char *pass, lcb_uint32_t timeout, const couchbase_opts_t *opts)
Initialize a Couchbase connection instance.
Definition couchbase.c:196
lcb_error_t couchbase_get_key(lcb_t instance, const void *cookie, const char *key)
Retrieve a document by key from Couchbase.
Definition couchbase.c:325
Couchbase wrapper function prototypes and datatypes.
json_object * jobj
JSON objects handled by the json-c library.
Definition couchbase.h:45
HIDDEN fr_dict_attr_t const * attr_event_timestamp
HIDDEN fr_dict_attr_t const * attr_acct_session_time
char * val
Value for the key used in lcb_cntl_string().
Definition couchbase.h:69
char * key
Key value for lcb_cntl_string().
Definition couchbase.h:68
enum json_tokener_error jerr
Error values produced by the json-c library.
Definition couchbase.h:47
couchbase_opts_t * next
Linked list.
Definition couchbase.h:70
Information relating to the parsing of Couchbase document payloads.
Definition couchbase.h:44
static void * fr_dcursor_tail(fr_dcursor_t *cursor)
Wind cursor to the tail item in the list.
Definition dcursor.h:259
static int fr_dcursor_insert(fr_dcursor_t *cursor, void *v)
Insert directly after the current item.
Definition dcursor.h:435
static void fr_dcursor_free_list(fr_dcursor_t *cursor)
Free the current item and all items after it.
Definition dcursor.h:663
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition dict_util.c:3263
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2400
Test enumeration values.
Definition dict_test.h:92
int fr_json_object_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *out, json_object *object, fr_dict_attr_t const *enumv, bool tainted)
Convert json object to fr_value_box_t.
Definition json.c:96
char const * longname
Client identifier.
Definition client.h:87
Describes a host allowed to send packets to the server.
Definition client.h:80
#define DEBUG3(_fmt,...)
Definition log.h:266
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RWARN(fmt,...)
Definition log.h:297
#define RERROR(fmt,...)
Definition log.h:298
#define RPERROR(fmt,...)
Definition log.h:302
int map_afrom_value_box(TALLOC_CTX *ctx, map_t **out, char const *lhs, fr_token_t lhs_quote, tmpl_rules_t const *lhs_rules, fr_token_t op, fr_value_box_t *rhs, bool steal_rhs_buffs)
Convert a value box to a map.
Definition map.c:1269
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
unsigned long int size_t
struct tm * localtime_r(time_t const *l_clock, struct tm *result)
Definition missing.c:163
int mod_attribute_to_element(const char *name, json_object *map, void *buf)
Map attributes to JSON element names.
Definition mod.c:311
static int _get_client_value(char **out, CONF_PAIR const *cp, void *data)
Handle client value processing for client_map_section()
Definition mod.c:674
int mod_free_api_opts(rlm_couchbase_t *inst)
Delete a object built by mod_build_api_opts()
Definition mod.c:63
int mod_build_api_opts(CONF_SECTION *conf, rlm_couchbase_t *inst)
Build a couchbase_opts_t structure from the configuration "couchbase_api" list.
Definition mod.c:93
int mod_load_client_documents(rlm_couchbase_t *inst, CONF_SECTION *tmpl, CONF_SECTION *map)
Load client entries from Couchbase client documents on startup.
Definition mod.c:706
int mod_build_attribute_element_map(CONF_SECTION *conf, rlm_couchbase_t *inst)
Build a JSON object map from the configuration "map" list.
Definition mod.c:237
int mod_ensure_start_timestamp(json_object *json, fr_pair_list_t *vps)
Ensure accounting documents always contain a valid timestamp.
Definition mod.c:604
int mod_conn_alive(UNUSED void *opaque, void *connection)
Check the health of a connection handle.
Definition mod.c:208
int mod_json_object_to_map(TALLOC_CTX *ctx, fr_dcursor_t *out, request_t *request, json_object *json, fr_dict_attr_t const *list)
Build value pairs from the passed JSON object and add to the request.
Definition mod.c:373
void * mod_conn_create(TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout)
Create a new connection pool handle.
Definition mod.c:154
static int _mod_conn_free(rlm_couchbase_handle_t *chandle)
Delete a connection pool handle and free related resources.
Definition mod.c:45
json_object * mod_value_pair_to_json_object(request_t *request, fr_pair_t *vp)
Convert value pairs to json objects.
Definition mod.c:510
Function prototypes and datatypes used in the module.
void * cookie
Couchbase cookie (cookie_u cookie_t).
Definition mod.h:73
#define MAX_KEY_SIZE
Definition mod.h:38
void * handle
Real couchbase instance.
Definition mod.h:72
Couchbase instance specific information.
Definition mod.h:71
The main module instance.
Definition mod.h:44
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:693
void fr_pool_connection_release(fr_pool_t *pool, request_t *request, void *conn)
Release a connection.
Definition pool.c:1407
void * fr_pool_connection_get(fr_pool_t *pool, request_t *request)
Reserve a connection in the connection pool.
Definition pool.c:1392
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define WARN(fmt,...)
Definition radclient.h:47
static rs_t * conf
Definition radsniff.c:53
static char const * name
#define FR_SBUFF_OUT(_start, _len_or_end)
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:341
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_client_t * client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, CONF_SECTION *server_cs, size_t extra)
Allocate a new client from a config section.
Definition client.c:708
int client_map_section(CONF_SECTION *out, CONF_SECTION const *map, client_value_cb_t func, void *data)
Create a client CONF_SECTION using a mapping section to map values from a result set to client attrib...
Definition client.c:626
void client_free(fr_client_t *client)
Free a client.
Definition client.c:98
bool client_add(fr_client_list_t *clients, fr_client_t *client)
Add a client to a fr_client_list_t.
Definition client.c:187
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
size_t strlcat(char *dst, char const *src, size_t siz)
Definition strlcat.c:35
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
Value pair map.
Definition map.h:77
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:445
static int64_t fr_unix_time_to_sec(fr_unix_time_t delta)
Definition time.h:506
static int64_t fr_time_delta_to_sec(fr_time_delta_t delta)
Definition time.h:647
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
const bool fr_assignment_op[T_TOKEN_LAST]
Definition token.c:168
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:33
const bool fr_comparison_op[T_TOKEN_LAST]
Definition token.c:198
enum fr_token fr_token_t
@ T_INVALID
Definition token.h:39
@ T_BARE_WORD
Definition token.h:120
@ T_OP_SET
Definition token.h:84
ssize_t fr_pair_print_value_quoted(fr_sbuff_t *out, fr_pair_t const *vp, fr_token_t quote)
Print the value of an attribute to a string.
Definition pair_print.c:53
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:3572
void fr_value_box_clear(fr_value_box_t *data)
Clear/free any existing value and metadata.
Definition value.c:3723
static fr_slen_t data
Definition value.h:1265
static size_t char ** out
Definition value.h:997