[asterisk-commits] dlee: branch dlee/ari-authn r392859 - in /team/dlee/ari-authn/res: ./ stasis_...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Jun 25 13:41:40 CDT 2013


Author: dlee
Date: Tue Jun 25 13:41:38 2013
New Revision: 392859

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=392859
Log:
ARI command line

Added:
    team/dlee/ari-authn/res/stasis_http/cli.c   (with props)
    team/dlee/ari-authn/res/stasis_http/config.c   (with props)
    team/dlee/ari-authn/res/stasis_http/internal.h   (with props)
Modified:
    team/dlee/ari-authn/res/Makefile
    team/dlee/ari-authn/res/res_stasis_http.c

Modified: team/dlee/ari-authn/res/Makefile
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-authn/res/Makefile?view=diff&rev=392859&r1=392858&r2=392859
==============================================================================
--- team/dlee/ari-authn/res/Makefile (original)
+++ team/dlee/ari-authn/res/Makefile Tue Jun 25 13:41:38 2013
@@ -80,5 +80,8 @@
 $(if $(filter res_parking,$(EMBEDDED_MODS)),modules.link,res_parking.so): $(subst .c,.o,$(wildcard parking/*.c))
 $(subst .c,.o,$(wildcard parking/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_parking)
 
+res_stasis_http.so: stasis_http/cli.o stasis_http/config.o
+stasis_http/cli.o stasis_http/config.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http)
+
 # Dependencies for res_stasis_http_*.so are generated, so they're in this file
 include stasis_http.make

Modified: team/dlee/ari-authn/res/res_stasis_http.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-authn/res/res_stasis_http.c?view=diff&rev=392859&r1=392858&r2=392859
==============================================================================
--- team/dlee/ari-authn/res/res_stasis_http.c (original)
+++ team/dlee/ari-authn/res/res_stasis_http.c Tue Jun 25 13:41:38 2013
@@ -75,236 +75,31 @@
 	<support_level>core</support_level>
  ***/
 
-/*** DOCUMENTATION
-	<configInfo name="res_stasis_http" language="en_US">
-		<synopsis>HTTP binding for the Stasis API</synopsis>
-		<configFile name="stasis_http.conf">
-			<configObject name="global">
-				<synopsis>Global configuration settings</synopsis>
-				<configOption name="enabled">
-					<synopsis>Enable/disable the stasis-http module</synopsis>
-				</configOption>
-				<configOption name="pretty">
-					<synopsis>Responses from stasis-http are formatted to be human readable</synopsis>
-				</configOption>
-			</configObject>
-
-			<configObject name="user">
-				<synopsis>Per-user configuration settings</synopsis>
-				<configOption name="read_only">
-					<synopsis>When set to yes, user is only authorized for read-only requests</synopsis>
-				</configOption>
-				<configOption name="allow_api_key">
-					<synopsis>When set to yes, user may authenticate by appending ?api_key=username+password to their requests.</synopsis>
-				</configOption>
-				<configOption name="password">
-					<synopsis>Crypted or plaintext password (see password_format)</synopsis>
-				</configOption>
-				<configOption name="password_format">
-					<synopsis>password_format may be set to plain (the default) or crypt. When set to crypt, crypt(3) is used to validate the password. A crypted password can be generated using mkpasswd -m sha-512. When set to plain, the password is in plaintext</synopsis>
-				</configOption>
-			</configObject>
-		</configFile>
-	</configInfo>
-***/
-
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
+#include "asterisk/astobj2.h"
 #include "asterisk/module.h"
 #include "asterisk/paths.h"
 #include "asterisk/stasis_http.h"
-#include "asterisk/config_options.h"
+#include "stasis_http/internal.h"
 
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
-/*! \brief Global configuration options for stasis http. */
-struct conf_global_options {
-	/*! Enabled by default, disabled if false. */
-	int enabled;
-	/*! Encoding format used during output (default compact). */
-	enum ast_json_encoding_format format;
-};
-
-/*! \brief Password format */
-enum password_format {
-	/*! \brief Plaintext password */
-	PASSWORD_FORMAT_PLAIN,
-	/*! crypt(3) password */
-	PASSWORD_FORMAT_CRYPT,
-};
-
-struct conf_user_options {
-	/*! Username for authentication */
-	char username[128];
-	/*! User's password. If 256 seems like a lot, a crypt SHA-512 has over
-	 *  100 characters */
-	char password[256];
-	/*! Format for the password field */
-	enum password_format password_format;
-	/*! If true, user cannot execute change operations */
-	int read_only;
-	/*! If true, user allowed to authenticate with ?api_key=user+password */
-	int allow_api_key;
-};
-
-static void user_dtor(void *obj)
-{
-	struct conf_user_options *user = obj;
-
-	ast_debug(3, "Disposing of user %s", user->username);
-}
-
-static void *user_alloc(const char *cat)
-{
-	RAII_VAR(struct conf_user_options *, user, NULL, ao2_cleanup);
-	const char *username = strchr(cat, '-') + 1;
-
-	if (!username) {
-		ast_log(LOG_ERROR, "Invalid user category '%s'\n", cat);
-		return NULL;
-	}
-
-	ast_debug(3, "Allocating user %s\n", cat);
-
-	user = ao2_alloc(sizeof(*user), user_dtor);
-	if (!user) {
-		return NULL;
-	}
-
-	ao2_ref(user, +1);
-	return user;
-}
-
-static int user_sort_cmp(const void *obj_left, const void *obj_right, int flags)
-{
-	const struct conf_user_options *user_left = obj_left;
-	const struct conf_user_options *user_right = obj_right;
-	const char *right_key =
-		(flags & OBJ_KEY) ? obj_right : user_right->username;
-
-	return strcmp(user_left->username, right_key);
-}
-
-static void *user_find(struct ao2_container *tmp_container,
-	const char *category)
-{
-	return ao2_find(tmp_container, category, OBJ_KEY);
-}
-
-/*! \brief All configuration options for stasis http. */
-struct conf {
-	/*! The general section configuration options. */
-	struct conf_global_options *global;
-	/*! Configured users */
-	struct ao2_container *users;
-};
-
-/*! \brief Locking container for safe configuration access. */
-static AO2_GLOBAL_OBJ_STATIC(confs);
-
-/*! \brief Mapping of the stasis http conf struct's globals to the
- *         general context in the config file. */
-static struct aco_type global_option = {
-	.type = ACO_GLOBAL,
-	.name = "global",
-	.item_offset = offsetof(struct conf, global),
-	.category = "^general$",
-	.category_match = ACO_WHITELIST,
-};
-
-static struct aco_type *global_options[] = ACO_TYPES(&global_option);
-
-static struct aco_type user_option = {
-	.type = ACO_ITEM,
-	.name = "user",
-	.category_match = ACO_WHITELIST,
-	.category = "^user-.+$",
-	.item_alloc = user_alloc,
-	.item_find = user_find,
-	.item_offset = offsetof(struct conf, users),
-};
-
-static struct aco_type *user_options[] = ACO_TYPES(&user_option);
-
-/*! \brief Disposes of the stasis http conf object */
-static void conf_destructor(void *obj)
-{
-	struct conf *cfg = obj;
-	ao2_cleanup(cfg->global);
-}
-
-/*! \brief Creates the statis http conf object. */
-static void *conf_alloc(void)
-{
-	RAII_VAR(struct conf *, cfg, NULL, ao2_cleanup);
-
-	cfg = ao2_alloc(sizeof(*cfg), conf_destructor);
-	if (!cfg) {
-		return NULL;
-	}
-
-	cfg->global = ao2_alloc(sizeof(*cfg->global), NULL);
-	if (!cfg->global) {
-		return NULL;
-	}
-
-	cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
-		AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL);
-
-	ao2_ref(cfg, +1);
-	return cfg;
-}
-
-/*! \brief The conf file that's processed for the module. */
-static struct aco_file conf_file = {
-	/*! The config file name. */
-	.filename = "stasis_http.conf",
-	/*! The mapping object types to be processed. */
-	.types = ACO_TYPES(&global_option, &user_option),
-};
-
-CONFIG_INFO_STANDARD(cfg_info, confs, conf_alloc,
-		     .files = ACO_FILES(&conf_file));
-
-/*! \brief Encoding format handler converts from boolean to enum. */
-static int encoding_format_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
-{
-	struct conf_global_options *global = obj;
-
-	if (!strcasecmp(var->name, "pretty")) {
-		global->format = ast_true(var->value) ? AST_JSON_PRETTY : AST_JSON_COMPACT;
-	} else {
-		return -1;
-	}
-
-	return 0;
-}
-
-static int password_format_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
-{
-	struct conf_user_options *user = obj;
-
-	if (strcasecmp(var->value, "plain") == 0) {
-		user->password_format = PASSWORD_FORMAT_PLAIN;
-	} else if (strcasecmp(var->value, "crypt") == 0) {
-		user->password_format = PASSWORD_FORMAT_CRYPT;
-	} else {
-		return -1;
-	}
-
-	return 0;
-}
-
 /*! \brief Helper function to check if module is enabled. */
-static char is_enabled(void)
-{
-	RAII_VAR(struct conf *, cfg, ao2_global_obj_ref(confs), ao2_cleanup);
-
-	return cfg->global->enabled;
+static int is_enabled(void)
+{
+	RAII_VAR(struct ari_conf *, cfg, ari_config_get(), ao2_cleanup);
+	return cfg->general->enabled;
+}
+
+static enum ast_json_encoding_format json_format(void)
+{
+	RAII_VAR(struct ari_conf *, cfg, ari_config_get(), ao2_cleanup);
+	return cfg->general->format;
 }
 
 /*! Lock for \ref root_handler */
@@ -890,7 +685,6 @@
 				struct ast_variable *get_params,
 				struct ast_variable *headers)
 {
-	RAII_VAR(struct conf *, cfg, ao2_global_obj_ref(confs), ao2_cleanup);
 	RAII_VAR(struct ast_str *, response_headers, ast_str_create(40), ast_free);
 	RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
 	struct stasis_http_response response = {};
@@ -938,7 +732,8 @@
 	if (response.message && !ast_json_is_null(response.message)) {
 		ast_str_append(&response_headers, 0,
 			       "Content-type: application/json\r\n");
-		if (ast_json_dump_str_format(response.message, &response_body, cfg->global->format) != 0) {
+		if (ast_json_dump_str_format(response.message, &response_body, 
+				json_format()) != 0) {
 			/* Error encoding response */
 			response.response_code = 500;
 			response.response_text = "Internal Server Error";
@@ -974,41 +769,27 @@
 {
 	ast_mutex_init(&root_handler_lock);
 
-	root_handler = root_handler_create();
+	/* root_handler may have been built during a declined load */
+	if (!root_handler) {
+		root_handler = root_handler_create();
+	}
 	if (!root_handler) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
 
-	if (aco_info_init(&cfg_info)) {
-		aco_info_destroy(&cfg_info);
+	/* alloc_failed_message may have been built during a declined load */
+	if (!alloc_failed_message) {
+		alloc_failed_message = ast_json_pack(
+			"{s: s}", "message", "Allocation failed");
+	}
+	if (!alloc_failed_message) {
+		/* Ironic */
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
+	if (ari_config_init() != 0) {
 		return AST_MODULE_LOAD_DECLINE;
 	}
-
-	aco_option_register(&cfg_info, "enabled", ACO_EXACT, global_options,
-		"yes", OPT_BOOL_T, 1,
-		FLDSET(struct conf_global_options, enabled));
-	aco_option_register_custom(&cfg_info, "pretty", ACO_EXACT,
-		global_options, "no",  encoding_format_handler, 0);
-
-	aco_option_register(&cfg_info, "read_only", ACO_EXACT, user_options,
-		"no", OPT_BOOL_T, 1,
-		FLDSET(struct conf_user_options, read_only));
-	aco_option_register(&cfg_info, "allow_api_key", ACO_EXACT, user_options,
-		"no", OPT_BOOL_T, 1,
-		FLDSET(struct conf_user_options, allow_api_key));
-	aco_option_register(&cfg_info, "password", ACO_EXACT, user_options,
-		"", OPT_CHAR_ARRAY_T, 0,
-		FLDSET(struct conf_user_options, password));
-	aco_option_register_custom(&cfg_info, "password_format", ACO_EXACT,
-		user_options, "plain",  password_format_handler, 0);
-
-	if (aco_process_config(&cfg_info, 0)) {
-		aco_info_destroy(&cfg_info);
-		return AST_MODULE_LOAD_DECLINE;
-	}
-
-	alloc_failed_message = ast_json_pack(
-		"{s: s}", "message", "Allocation failed");
 
 	if (is_enabled()) {
 		ast_debug(3, "ARI enabled\n");
@@ -1017,26 +798,31 @@
 		ast_debug(3, "ARI disabled\n");
 	}
 
+	if (ari_cli_register() != 0) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
 static int unload_module(void)
 {
-	ast_json_unref(alloc_failed_message);
-	alloc_failed_message = NULL;
+	ari_cli_unregister();
 
 	if (is_enabled()) {
 		ast_debug(3, "Disabling ARI\n");
 		ast_http_uri_unlink(&http_uri);
 	}
 
-	aco_info_destroy(&cfg_info);
-	ao2_global_obj_release(confs);
+	ari_config_destroy();
 
 	ao2_cleanup(root_handler);
 	root_handler = NULL;
 	ast_mutex_destroy(&root_handler_lock);
 
+	ast_json_unref(alloc_failed_message);
+	alloc_failed_message = NULL;
+
 	return 0;
 }
 
@@ -1044,7 +830,7 @@
 {
 	char was_enabled = is_enabled();
 
-	if (aco_process_config(&cfg_info, 1)) {
+	if (ari_config_reload() != 0) {
 		return AST_MODULE_LOAD_DECLINE;
 	}
 

Added: team/dlee/ari-authn/res/stasis_http/cli.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-authn/res/stasis_http/cli.c?view=auto&rev=392859
==============================================================================
--- team/dlee/ari-authn/res/stasis_http/cli.c (added)
+++ team/dlee/ari-authn/res/stasis_http/cli.c Tue Jun 25 13:41:38 2013
@@ -1,0 +1,229 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Command line for ARI.
+ * \author David M. Lee, II <dlee at digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/astobj2.h"
+#include "asterisk/cli.h"
+#include "internal.h"
+
+static char *ari_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	RAII_VAR(struct ari_conf *, conf, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ari show status";
+		e->usage =
+			"Usage: ari show status\n"
+			"       Shows all ARI settings\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	default:
+		break;
+	}
+
+	if (a->argc != 3) {
+		return CLI_SHOWUSAGE;
+	}
+
+	conf = ari_config_get();
+
+	if (!conf) {
+		ast_cli(a->fd, "Error getting ARI configuration\n");
+		return CLI_FAILURE;
+	}
+
+	ast_cli(a->fd, "ARI Status:\n");
+	ast_cli(a->fd, "Enabled: %s\n", AST_CLI_YESNO(conf->general->enabled));
+	ast_cli(a->fd, "Output format: ");
+	switch (conf->general->format) {
+	case AST_JSON_COMPACT:
+		ast_cli(a->fd, "compact");
+		break;
+	case AST_JSON_PRETTY:
+		ast_cli(a->fd, "pretty");
+		break;
+	}
+	ast_cli(a->fd, "\n");
+	ast_cli(a->fd, "User count: %d\n", ao2_container_count(conf->users));
+	return CLI_SUCCESS;
+}
+
+static int show_users_cb(void *obj, void *arg, int flags)
+{
+	struct ari_conf_user *user = obj;
+	struct ast_cli_args *a = arg;
+
+	ast_cli(a->fd, "%-*s  %-*s  %s\n",
+		4, AST_CLI_YESNO(user->read_only),
+		7, AST_CLI_YESNO(user->allow_api_key),
+		user->username);
+	return 0;
+}
+
+static char *ari_show_users(struct ast_cli_entry *e, int cmd,
+	struct ast_cli_args *a)
+{
+	RAII_VAR(struct ari_conf *, conf, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ari show users";
+		e->usage =
+			"Usage: ari show users\n"
+			"       Shows all ARI users\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	default:
+		break;
+	}
+
+	if (a->argc != 3) {
+		return CLI_SHOWUSAGE;
+	}
+
+	conf = ari_config_get();
+	if (!conf) {
+		ast_cli(a->fd, "Error getting ARI configuration\n");
+		return CLI_FAILURE;
+	}
+
+	ast_cli(a->fd, "r/o?  API key  Username\n");
+	ast_cli(a->fd, "----  -------  --------\n");
+
+	ao2_callback(conf->users, OBJ_NODATA, show_users_cb, a);
+
+	return CLI_SUCCESS;
+}
+
+struct user_complete {
+	/*! Nth user to search for */
+	int state;
+	/*! Which user currently on */
+	int which;
+};
+
+static int complete_ari_user_search(void *obj, void *arg, void *data, int flags)
+{
+	struct user_complete *search = data;
+
+	if (++search->which > search->state) {
+		return CMP_MATCH;
+	}
+	return 0;
+}
+
+static char *complete_ari_user(struct ast_cli_args *a)
+{
+	RAII_VAR(struct ari_conf *, conf, NULL, ao2_cleanup);
+	RAII_VAR(struct ari_conf_user *, user, NULL, ao2_cleanup);
+
+	struct user_complete search = {
+		.state = a->n,
+	};
+
+	conf = ari_config_get();
+	if (!conf) {
+		ast_cli(a->fd, "Error getting ARI configuration\n");
+		return CLI_FAILURE;
+	}
+
+	user = ao2_callback_data(conf->users,
+		ast_strlen_zero(a->word) ? 0 : OBJ_PARTIAL_KEY,
+		complete_ari_user_search, (char*)a->word, &search);
+
+	return user ? ast_strdup(user->username) : NULL;
+}
+
+static char *complete_ari_show_user(struct ast_cli_args *a)
+{
+	if (a->pos == 3) {
+		return complete_ari_user(a);
+	}
+
+	return NULL;
+}
+
+static char *ari_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	RAII_VAR(struct ari_conf *, conf, NULL, ao2_cleanup);
+	RAII_VAR(struct ari_conf_user *, user, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "ari show user";
+		e->usage =
+			"Usage: ari show user <username>\n"
+			"       Shows a specific ARI user\n";
+		return NULL;
+	case CLI_GENERATE:
+		return complete_ari_show_user(a);
+	default:
+		break;
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	conf = ari_config_get();
+
+	if (!conf) {
+		ast_cli(a->fd, "Error getting ARI configuration\n");
+		return CLI_FAILURE;
+	}
+
+	user = ao2_find(conf->users, a->argv[3], OBJ_KEY);
+	if (!user) {
+		ast_cli(a->fd, "User '%s' not found\n", a->argv[3]);
+		return CLI_SUCCESS;
+	}
+
+	ast_cli(a->fd, "Username: %s\n", user->username);
+	ast_cli(a->fd, "Read only?: %s\n", AST_CLI_YESNO(user->read_only));
+	ast_cli(a->fd, "Allow api_key?: %s\n",
+		AST_CLI_YESNO(user->allow_api_key));
+
+	return CLI_SUCCESS;
+}
+
+
+static struct ast_cli_entry cli_ari[] = {
+	AST_CLI_DEFINE(ari_show, "Show ARI settings"),
+	AST_CLI_DEFINE(ari_show_users, "List ARI users"),
+	AST_CLI_DEFINE(ari_show_user, "List single ARI user"),
+};
+
+int ari_cli_register(void) {
+	return ast_cli_register_multiple(cli_ari, ARRAY_LEN(cli_ari));
+}
+
+void ari_cli_unregister(void) {
+	ast_cli_unregister_multiple(cli_ari, ARRAY_LEN(cli_ari));
+}

Propchange: team/dlee/ari-authn/res/stasis_http/cli.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/dlee/ari-authn/res/stasis_http/cli.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/dlee/ari-authn/res/stasis_http/cli.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/dlee/ari-authn/res/stasis_http/config.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-authn/res/stasis_http/config.c?view=auto&rev=392859
==============================================================================
--- team/dlee/ari-authn/res/stasis_http/config.c (added)
+++ team/dlee/ari-authn/res/stasis_http/config.c Tue Jun 25 13:41:38 2013
@@ -1,0 +1,280 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Config framework stuffz for ARI.
+ * \author David M. Lee, II <dlee at digium.com>
+ */
+
+/*** DOCUMENTATION
+	<configInfo name="res_stasis_http" language="en_US">
+		<synopsis>HTTP binding for the Stasis API</synopsis>
+		<configFile name="stasis_http.conf">
+			<configObject name="general">
+				<synopsis>General configuration settings</synopsis>
+				<configOption name="enabled">
+					<synopsis>Enable/disable the stasis-http module</synopsis>
+				</configOption>
+				<configOption name="pretty">
+					<synopsis>Responses from stasis-http are formatted to be human readable</synopsis>
+				</configOption>
+			</configObject>
+
+			<configObject name="user">
+				<synopsis>Per-user configuration settings</synopsis>
+				<configOption name="read_only">
+					<synopsis>When set to yes, user is only authorized for read-only requests</synopsis>
+				</configOption>
+				<configOption name="allow_api_key">
+					<synopsis>When set to yes, user may authenticate by appending ?api_key=username+password to their requests.</synopsis>
+				</configOption>
+				<configOption name="password">
+					<synopsis>Crypted or plaintext password (see password_format)</synopsis>
+				</configOption>
+				<configOption name="password_format">
+					<synopsis>password_format may be set to plain (the default) or crypt. When set to crypt, crypt(3) is used to validate the password. A crypted password can be generated using mkpasswd -m sha-512. When set to plain, the password is in plaintext</synopsis>
+				</configOption>
+			</configObject>
+		</configFile>
+	</configInfo>
+***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/config_options.h"
+#include "internal.h"
+
+/*! \brief Locking container for safe configuration access. */
+static AO2_GLOBAL_OBJ_STATIC(confs);
+
+/*! \brief Mapping of the stasis http conf struct's globals to the
+ *         general context in the config file. */
+static struct aco_type general_option = {
+	.type = ACO_GLOBAL,
+	.name = "global",
+	.item_offset = offsetof(struct ari_conf, general),
+	.category = "^general$",
+	.category_match = ACO_WHITELIST,
+};
+
+static struct aco_type *general_options[] = ACO_TYPES(&general_option);
+
+/*! \brief Encoding format handler converts from boolean to enum. */
+static int encoding_format_handler(const struct aco_option *opt,
+	struct ast_variable *var, void *obj)
+{
+	struct ari_conf_general *general = obj;
+
+	if (!strcasecmp(var->name, "pretty")) {
+		general->format = ast_true(var->value) ?
+			AST_JSON_PRETTY : AST_JSON_COMPACT;
+	} else {
+		return -1;
+	}
+
+	return 0;
+}
+
+/*! \brief Parses the ari_password_format enum from a config file */
+static int password_format_handler(const struct aco_option *opt,
+	struct ast_variable *var, void *obj)
+{
+	struct ari_conf_user *user = obj;
+
+	if (strcasecmp(var->value, "plain") == 0) {
+		user->password_format = ARI_PASSWORD_FORMAT_PLAIN;
+	} else if (strcasecmp(var->value, "crypt") == 0) {
+		user->password_format = ARI_PASSWORD_FORMAT_CRYPT;
+	} else {
+		return -1;
+	}
+
+	return 0;
+}
+
+static void user_dtor(void *obj)
+{
+	struct ari_conf_user *user = obj;
+	ast_debug(3, "Disposing of user %s\n", user->username);
+}
+
+static void *user_alloc(const char *cat)
+{
+	RAII_VAR(struct ari_conf_user *, user, NULL, ao2_cleanup);
+	const char *username = strchr(cat, '-') + 1;
+
+	if (!username) {
+		ast_log(LOG_ERROR, "Invalid user category '%s'\n", cat);
+		return NULL;
+	}
+
+	if (strlen(username) + 1 > ARRAY_LEN(user->username)) {
+		ast_log(LOG_ERROR, "User name too long '%s'\n", username);
+		return NULL;
+	}
+
+	ast_debug(3, "Allocating user %s\n", cat);
+
+	user = ao2_alloc(sizeof(*user), user_dtor);
+	if (!user) {
+		return NULL;
+	}
+
+	strncpy(user->username, username, ARRAY_LEN(user->username));
+
+	ao2_ref(user, +1);
+	return user;
+}
+
+static int user_sort_cmp(const void *obj_left, const void *obj_right, int flags)
+{
+	const struct ari_conf_user *user_left = obj_left;
+
+	if (flags & OBJ_PARTIAL_KEY) {
+		const char *key_right = obj_right;
+		return strncasecmp(user_left->username, key_right,
+			strlen(key_right));
+	} else if (flags & OBJ_KEY) {
+		const char *key_right = obj_right;
+		return strcasecmp(user_left->username, key_right);
+	} else {
+		const struct ari_conf_user *user_right = obj_right;
+		const char *key_right = user_right->username;
+		return strcasecmp(user_left->username, key_right);
+	}
+}
+
+static void *user_find(struct ao2_container *tmp_container,
+	const char *category)
+{
+	return ao2_find(tmp_container, category, OBJ_KEY);
+}
+
+static struct aco_type user_option = {
+	.type = ACO_ITEM,
+	.name = "user",
+	.category_match = ACO_WHITELIST,
+	.category = "^user-.+$",
+	.item_alloc = user_alloc,
+	.item_find = user_find,
+	.item_offset = offsetof(struct ari_conf, users),
+};
+
+static struct aco_type *user[] = ACO_TYPES(&user_option);
+
+/*! \brief Disposes of the stasis http conf object */
+static void conf_destructor(void *obj)
+{
+	struct ari_conf *cfg = obj;
+	ao2_cleanup(cfg->general);
+	ao2_cleanup(cfg->users);
+}
+
+/*! \brief Creates the statis http conf object. */
+static void *conf_alloc(void)
+{
+	RAII_VAR(struct ari_conf *, cfg, NULL, ao2_cleanup);
+
+	cfg = ao2_alloc(sizeof(*cfg), conf_destructor);
+	if (!cfg) {
+		return NULL;
+	}
+
+	cfg->general = ao2_alloc(sizeof(*cfg->general), NULL);
+	if (!cfg->general) {
+		return NULL;
+	}
+
+	cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
+		AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL);
+
+	ao2_ref(cfg, +1);
+	return cfg;
+}
+
+/*! \brief The conf file that's processed for the module. */
+static struct aco_file conf_file = {
+	/*! The config file name. */
+	.filename = "stasis_http.conf",
+	/*! The mapping object types to be processed. */
+	.types = ACO_TYPES(&general_option, &user_option),
+};
+
+CONFIG_INFO_STANDARD(cfg_info, confs, conf_alloc,
+		     .files = ACO_FILES(&conf_file));
+
+int ari_config_init(void)
+{
+	if (aco_info_init(&cfg_info)) {
+		aco_info_destroy(&cfg_info);
+		return -1;
+	}
+
+	aco_option_register(&cfg_info, "enabled", ACO_EXACT, general_options,
+		"yes", OPT_BOOL_T, 1,
+		FLDSET(struct ari_conf_general, enabled));
+	aco_option_register_custom(&cfg_info, "pretty", ACO_EXACT,
+		general_options, "no",  encoding_format_handler, 0);
+
+	aco_option_register(&cfg_info, "read_only", ACO_EXACT, user,
+		"no", OPT_BOOL_T, 1,
+		FLDSET(struct ari_conf_user, read_only));
+	aco_option_register(&cfg_info, "allow_api_key", ACO_EXACT, user,
+		"no", OPT_BOOL_T, 1,
+		FLDSET(struct ari_conf_user, allow_api_key));
+	aco_option_register(&cfg_info, "password", ACO_EXACT, user,
+		"", OPT_CHAR_ARRAY_T, 0,
+		FLDSET(struct ari_conf_user, password));
+	aco_option_register_custom(&cfg_info, "password_format", ACO_EXACT,
+		user, "plain",  password_format_handler, 0);
+
+	if (aco_process_config(&cfg_info, 0)) {
+		aco_info_destroy(&cfg_info);
+		return -1;
+	}
+
+	return 0;
+}
+
+int ari_config_reload(void)
+{
+	switch (aco_process_config(&cfg_info, 1)) {
+	case ACO_PROCESS_OK:
+	case ACO_PROCESS_UNCHANGED:
+		return 0;
+	case ACO_PROCESS_ERROR:
+		return -1;
+	}
+
+	return -1;
+}
+
+void ari_config_destroy(void)
+{
+	aco_info_destroy(&cfg_info);
+	ao2_global_obj_release(confs);
+}
+
+struct ari_conf *ari_config_get(void)
+{
+	return ao2_global_obj_ref(confs);
+}
+

Propchange: team/dlee/ari-authn/res/stasis_http/config.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/dlee/ari-authn/res/stasis_http/config.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/dlee/ari-authn/res/stasis_http/config.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/dlee/ari-authn/res/stasis_http/internal.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ari-authn/res/stasis_http/internal.h?view=auto&rev=392859
==============================================================================
--- team/dlee/ari-authn/res/stasis_http/internal.h (added)
+++ team/dlee/ari-authn/res/stasis_http/internal.h Tue Jun 25 13:41:38 2013
@@ -1,0 +1,119 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#ifndef STASIS_HTTP_INTERNAL_H_
+#define STASIS_HTTP_INTERNAL_H_
+
+/*! \file
+ *
+ * \brief Internal API's for res_stasis_http.
+ * \author David M. Lee, II <dlee at digium.com>
+ */
+
+#include "asterisk/json.h"
+
+/*! @{ */
+
+/*!
+ * \brief Register CLI commands for ARI.
+ *
+ * \return 0 on success.
+ * \return Non-zero on error.
+ */
+int ari_cli_register(void);
+
+/*!
+ * \brief Unregister CLI commands for ARI.
+ */
+void ari_cli_unregister(void);
+
+/*! @} */
+
+/*! @{ */
+
+struct ari_conf_general;
+
+/*! \brief All configuration options for stasis http. */
+struct ari_conf {
+	/*! The general section configuration options. */
+	struct ari_conf_general *general;
+	/*! Configured users */
+	struct ao2_container *users;
+};
+
+/*! \brief Global configuration options for stasis http. */
+struct ari_conf_general {
+	/*! Enabled by default, disabled if false. */
+	int enabled;
+	/*! Encoding format used during output (default compact). */
+	enum ast_json_encoding_format format;
+};
+
+/*! \brief Password format */
+enum ari_password_format {
+	/*! \brief Plaintext password */
+	ARI_PASSWORD_FORMAT_PLAIN,
+	/*! crypt(3) password */
+	ARI_PASSWORD_FORMAT_CRYPT,
+};
+
+/*! \brief Per-user configuration options */
+struct ari_conf_user {
+	/*! Username for authentication */
+	char username[128];
+	/*! User's password. If 256 seems like a lot, a crypt SHA-512 has over
+	 *  100 characters */
+	char password[256];
+	/*! Format for the password field */
+	enum ari_password_format password_format;
+	/*! If true, user cannot execute change operations */
+	int read_only;
+	/*! If true, user allowed to authenticate with ?api_key=user+password */
+	int allow_api_key;
+};
+
+/*!
+ * \brief Initialize the ARI configuration
+ */
+int ari_config_init(void);
+
+/*!
+ * \brief Reload the ARI configuration
+ */
+int ari_config_reload(void);
+
+/*!
+ * \brief Destroy the ARI configuration
+ */
+void ari_config_destroy(void);
+
+/*!
+ * \brief Get the current ARI configuration.
+ *
+ * This is an immutable object, so don't modify it. It is AO2 managed, so
+ * ao2_cleanup() when you're done with it.
+ *
+ * \return ARI configuration object.
+ * \return \c NULL on error.
+ */
+struct ari_conf *ari_config_get(void);
+
+/*! @} */
+
+
+#endif /* STASIS_HTTP_INTERNAL_H_ */

Propchange: team/dlee/ari-authn/res/stasis_http/internal.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/dlee/ari-authn/res/stasis_http/internal.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/dlee/ari-authn/res/stasis_http/internal.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain




More information about the asterisk-commits mailing list