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.
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.
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
20 * @brief Utillity functions used in the module.
23 * @copyright 2013-2014 Aaron Hurt <ahurt@anbcs.com>
28 #include <freeradius-devel/radiusd.h>
30 #include <libcouchbase/couchbase.h>
31 #include <json/json.h>
34 #include "couchbase.h"
35 #include "jsonc_missing.h"
37 /* create new connection pool handle */
38 void *mod_conn_create(void *instance) {
39 rlm_couchbase_t *inst = instance; /* module instance pointer */
40 rlm_couchbase_handle_t *chandle = NULL; /* connection handle pointer */
41 cookie_t *cookie = NULL; /* couchbase cookie */
42 lcb_t cb_inst; /* couchbase connection instance */
43 lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error status */
46 cb_inst = couchbase_init_connection(inst->server, inst->bucket, inst->password);
48 /* check couchbase instance status */
49 if ((cb_error = lcb_get_last_error(cb_inst)) != LCB_SUCCESS) {
50 ERROR("rlm_couchbase: failed to initiate couchbase connection: %s (0x%x)", lcb_strerror(NULL, cb_error), cb_error);
51 /* destroy/free couchbase instance */
57 /* allocate memory for couchbase connection instance abstraction */
58 chandle = talloc_zero(inst, rlm_couchbase_handle_t);
59 cookie = talloc_zero(chandle, cookie_t);
61 /* initialize cookie error holder */
62 cookie->jerr = json_tokener_success;
64 /* populate handle with allocated structs */
65 chandle->cookie = cookie;
66 chandle->handle = cb_inst;
68 /* return handle struct */
72 /* verify valid couchbase connection handle */
73 int mod_conn_alive(UNUSED void *instance, void *handle) {
74 rlm_couchbase_handle_t *chandle = handle; /* connection handle pointer */
75 lcb_t cb_inst = chandle->handle; /* couchbase instance */
76 lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error status */
78 /* attempt to get server list */
79 const char *const *servers = lcb_get_server_list(cb_inst);
81 /* check error state and server list return */
82 if (((cb_error = lcb_get_last_error(cb_inst)) != LCB_SUCCESS) || (servers == NULL)) {
84 ERROR("rlm_couchbase: failed to get couchbase server topology: %s (0x%x)", lcb_strerror(NULL, cb_error), cb_error);
91 /* free couchbase instance handle and any additional context memory */
92 int mod_conn_delete(UNUSED void *instance, void *handle) {
93 rlm_couchbase_handle_t *chandle = handle; /* connection instance handle */
94 lcb_t cb_inst = chandle->handle; /* couchbase instance */
96 /* destroy/free couchbase instance */
100 talloc_free(chandle);
106 /* build json object for mapping radius attributes to json elements */
107 int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance) {
108 rlm_couchbase_t *inst = instance; /* our module instance */
109 CONF_SECTION *cs; /* module config section */
110 CONF_ITEM *ci; /* config item */
111 CONF_PAIR *cp; /* conig pair */
112 const char *attribute, *element; /* attribute and element names */
114 /* find map section */
115 cs = cf_section_sub_find(conf, "map");
119 ERROR("rlm_couchbase: failed to find 'map' section in config");
124 /* create attribute map object */
125 inst->map = json_object_new_object();
127 /* parse update section */
128 for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
130 if (!cf_item_is_pair(ci)) {
131 ERROR("rlm_couchbase: failed to parse invalid item in 'map' section");
134 json_object_put(inst->map);
140 /* get value pair from item */
141 cp = cf_itemtopair(ci);
143 /* get pair name (element name) */
144 element = cf_pair_attr(cp);
146 /* get pair value (attribute name) */
147 attribute = cf_pair_value(cp);
149 /* add pair name and value */
150 json_object_object_add(inst->map, attribute, json_object_new_string(element));
153 DEBUG("rlm_couchbase: added attribute '%s' to element '%s' map to object", attribute, element);
157 DEBUG("rlm_couchbase: built attribute to element map %s", json_object_to_json_string(inst->map));
163 /* map free radius attribute to user defined json element name */
164 int mod_attribute_to_element(const char *name, json_object *map, void *buf) {
165 json_object *jval; /* json object values */
168 memset((char *) buf, 0, MAX_KEY_SIZE);
170 /* attempt to map attribute */
171 if (json_object_object_get_ex(map, name, &jval)) {
172 int length; /* json value length */
173 /* get value length */
174 length = json_object_get_string_len(jval);
175 /* check buffer size */
176 if (length > MAX_KEY_SIZE -1) {
177 /* oops ... this value is bigger than our buffer ... error out */
178 ERROR("rlm_couchbase: json map value larger than MAX_KEY_SIZE - %d", MAX_KEY_SIZE);
182 /* copy string value to buffer */
183 strncpy(buf, json_object_get_string(jval), length);
190 DEBUG("rlm_couchbase: skipping attribute with no map entry - %s", name);
196 /* inject value pairs into given request
197 * that are defined in the passed json object
199 void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQUEST *request) {
200 json_object *jobj, *jval, *jop; /* json object pointers */
201 TALLOC_CTX *ctx; /* talloc context for pairmake */
202 VALUE_PAIR *vp, **ptr; /* value pair and value pair pointer for pairmake */
204 /* assign ctx and vps for pairmake based on section */
205 if (strcmp(section, "config") == 0) {
207 ptr = &(request->config_items);
208 } else if (strcmp(section, "reply") == 0) {
209 ctx = request->reply;
210 ptr = &(request->reply->vps);
212 /* log error - this shouldn't happen */
213 RERROR("invalid section passed for pairmake");
218 /* get config payload */
219 if (json_object_object_get_ex(json, section, &jobj)) {
220 /* make sure we have the correct type */
221 if (!json_object_is_type(jobj, json_type_object)) {
223 RERROR("invalid json type for '%s' section - sections must be json objects", section);
227 /* loop through object */
228 json_object_object_foreach(jobj, attribute, json_vp) {
229 /* check for appropriate type in value and op */
230 if (!json_object_is_type(json_vp, json_type_object)) {
232 RERROR("invalid json type for '%s' attribute - attributes must be json objects", attribute);
237 RDEBUG("parsing '%s' attribute: %s => %s", section, attribute, json_object_to_json_string(json_vp));
238 /* create pair from json object */
239 if (json_object_object_get_ex(json_vp, "value", &jval) &&
240 json_object_object_get_ex(json_vp, "op", &jop)) {
241 /* make correct pairs based on json object type */
242 switch (json_object_get_type(jval)) {
243 case json_type_double:
245 case json_type_string:
247 RDEBUG("adding '%s' attribute to '%s' section", attribute, section);
249 vp = pairmake(ctx, ptr, attribute, json_object_get_string(jval),
250 fr_str2int(fr_tokens, json_object_get_string(jop), 0));
253 RERROR("could not build value pair for '%s' attribute (%s)", attribute, fr_strerror());
258 case json_type_object:
259 case json_type_array:
260 /* log error - we want to handle these eventually */
261 RERROR("skipping unhandled nested json object or array value pair object");
264 /* log error - this shouldn't ever happen */
265 RERROR("skipping unhandled json type in value pair object");
270 RERROR("failed to get 'value' or 'op' element for '%s' attribute", attribute);
278 RDEBUG("couldn't find '%s' section in json object - not adding value pairs for this section", section);
284 /* convert freeradius value/pair to json object
285 * basic structure taken from freeradius function
286 * vp_prints_value_json in src/lib/print.c */
287 json_object *mod_value_pair_to_json_object(REQUEST *request, VALUE_PAIR *vp) {
288 char value[255]; /* radius attribute value */
290 /* add this attribute/value pair to our json output */
291 if (!vp->da->flags.has_tag) {
292 switch (vp->da->type) {
293 case PW_TYPE_INTEGER:
296 /* skip if we have flags */
297 if (vp->da->flags.has_value) break;
298 #ifdef HAVE_JSON_OBJECT_NEW_INT64
300 RDEBUG3("creating new int64 for unsigned 32 bit int/byte/short '%s'", vp->da->name);
301 /* return as 64 bit int - JSON spec does not support unsigned ints */
302 return json_object_new_int64(vp->vp_integer);
305 RDEBUG3("creating new int for unsigned 32 bit int/byte/short '%s'", vp->da->name);
306 /* return as 64 bit int - JSON spec does not support unsigned ints */
307 return json_object_new_int(vp->vp_integer);
311 #ifdef HAVE_JSON_OBJECT_NEW_INT64
313 RDEBUG3("creating new int64 for signed 32 bit integer '%s'", vp->da->name);
314 /* return as 64 bit int - json-c represents all ints as 64 bits internally */
315 return json_object_new_int64(vp->vp_signed);
317 RDEBUG3("creating new int for signed 32 bit integer '%s'", vp->da->name);
318 /* return as signed int */
319 return json_object_new_int(vp->vp_signed);
322 case PW_TYPE_INTEGER64:
323 #ifdef HAVE_JSON_OBJECT_NEW_INT64
325 RDEBUG3("creating new int64 for 64 bit integer '%s'", vp->da->name);
326 /* return as 64 bit int - because it is a 64 bit int */
327 return json_object_new_int64(vp->vp_integer64);
330 RWARN("skipping 64 bit integer attribute '%s' - please upgrade json-c to 0.10+", vp->da->name);
334 /* silence warnings - do nothing */
339 /* keep going if not set above */
340 switch (vp->da->type) {
343 RDEBUG3("assigning string '%s' as string", vp->da->name);
344 /* return string value */
345 return json_object_new_string(vp->vp_strvalue);
348 RDEBUG3("assigning unhandled '%s' as string", vp->da->name);
349 /* get standard value */
350 vp_prints_value(value, sizeof(value), vp, 0);
351 /* return string value from above */
352 return json_object_new_string(value);
357 /* check current value of start timestamp in json body and update if needed */
358 int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps) {
359 json_object *jval; /* json object value */
360 struct tm tm; /* struct to hold event time */
361 time_t ts = 0; /* values to hold time in seconds */
362 VALUE_PAIR *vp; /* values to hold value pairs */
363 char value[255]; /* store radius attribute values and our timestamp */
365 /* get our current start timestamp from our json body */
366 if (json_object_object_get_ex(json, "startTimestamp", &jval) == 0) {
367 /* debugging ... this shouldn't ever happen */
368 DEBUG("rlm_couchbase: failed to find start timestamp in current json body");
373 /* check the value */
374 if (strcmp(json_object_get_string(jval), "null") != 0) {
376 DEBUG("rlm_couchbase: start timestamp looks good - nothing to do");
377 /* already set - nothing else to do */
381 /* get current event timestamp */
382 if ((vp = pairfind(vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
383 /* get seconds value from attribute */
387 DEBUG("rlm_couchbase: failed to find event timestamp in current request");
393 memset(value, 0, sizeof(value));
395 /* get elapsed session time */
396 if ((vp = pairfind(vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY)) != NULL) {
398 ts = (ts - vp->vp_integer);
399 /* calculate start time */
400 size_t length = strftime(value, sizeof(value), "%b %e %Y %H:%M:%S %Z", localtime_r(&ts, &tm));
404 DEBUG("rlm_couchbase: calculated start timestamp: %s", value);
405 /* store new value in json body */
406 json_object_object_add(json, "startTimestamp", json_object_new_string(value));
409 DEBUG("rlm_couchbase: failed to format calculated timestamp");