summaryrefslogtreecommitdiff
path: root/module/consumer.cc
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2009-07-07 20:05:29 +0000
committerStef Walter <stef@memberwebs.com>2009-07-07 20:05:29 +0000
commit2989ee8b72ddb3995e5a4686c988385d05493365 (patch)
tree566819988818d2478a556394d2a7243d8f1a8b1f /module/consumer.cc
parent2622d0eb7d32ae035d9c04d92de4e61588b1950e (diff)
Implement simple AX attribute exchange.
* Does not yet handle setting attributes from the cookie.
Diffstat (limited to 'module/consumer.cc')
-rw-r--r--module/consumer.cc255
1 files changed, 219 insertions, 36 deletions
diff --git a/module/consumer.cc b/module/consumer.cc
index 49620db..32db8cb 100644
--- a/module/consumer.cc
+++ b/module/consumer.cc
@@ -26,6 +26,12 @@ using opkele::secret_t;
using std::string;
using std::vector;
+typedef std::list<std::string> string_list;
+
+/* -----------------------------------------------------------------------------
+ * UTILITY
+ */
+
class LockShared
{
public:
@@ -35,6 +41,102 @@ public:
{ sid_shared_unlock (); }
};
+
+static int
+parse_number (const string& str)
+{
+ char *end = NULL;
+ int result = strtoul (str.c_str(), &end, 10);
+ if (!end || *end != '\0')
+ result = 0;
+ return result;
+}
+
+static string
+format_number (int num)
+{
+ char buf[64];
+ snprintf (buf, sizeof (buf), "%d", num);
+ return string(buf);
+}
+
+static void
+parse_query_string (const char *qs, params_t &params)
+{
+ string pair, key, value;
+ const char *at;
+
+ if (qs == NULL)
+ return;
+
+ while (*qs != 0) {
+ at = strchr (qs, '&');
+ if (at == NULL)
+ at = qs + strlen (qs);
+ pair = string(qs, at);
+ string::size_type loc = pair.find('=', 0);
+ if (loc != string::npos) {
+ key = pair.substr (0, loc);
+ value = pair.substr (loc + 1);
+ } else {
+ key = pair;
+ value = "";
+ }
+
+ params[opkele::util::url_decode(key)] = opkele::util::url_decode(value);
+ if (*at == 0)
+ break;
+ qs = at + 1;
+ }
+}
+
+static void
+filter_prefixed_params (params_t &params, params_t &openid, const string& prefix)
+{
+ /*
+ * Expects message to have openid. prefix present, and strips
+ * openid. prefix from output
+ */
+
+ for (params_t::iterator it = params.begin(); it != params.end(); ) {
+ const string& name = it->first;
+ if (name.find (prefix) == 0) {
+ openid[name.substr(prefix.length())] = it->second;
+
+ /* We erase an increment together, must use post-increment operator */
+ params.erase (it++);
+ } else {
+ /* Did not match, just go to next element */
+ ++it;
+ }
+ }
+}
+
+static void
+filter_listed_params (const params_t &params, params_t &listed, const string& list)
+{
+ /* Expects message to have openid. prefix stripped from params */
+
+ if(!params.has_param(list))
+ return;
+
+ string slist = params.get_param(list);
+ string::size_type pos = 0;
+ for (;;) {
+ string::size_type at = slist.find(',', pos);
+ string name = slist.substr(pos, at - pos);
+ if(params.has_param(name))
+ listed[name] = params.get_param(name);
+ if(at == string::npos)
+ return;
+ pos = at + 1;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * CONSUMER CLASS
+ */
+
class Consumer :
public prequeue_RP
{
@@ -204,55 +306,132 @@ Consumer::check_nonce(const string& server, const string& nonce)
*/
static void
-filter_openid_params (params_t &params, params_t &openid)
+request_ax_attributes (sid_request_t *req, openid_message_t &message, sid_attribute_t *attrs)
{
- for (params_t::iterator it = params.begin(); it != params.end(); ) {
- const string& name = it->first;
- if (name.find ("openid.") == 0) {
- openid[name.substr(7)] = it->second;
+ sid_attribute_t *attr;
- /* We erase an increment together, must use post-increment operator */
- params.erase (it++);
- } else {
- /* Did not match, just go to next element */
- ++it;
+ /* Request attributes for attribute exchange */
+ if (!attrs)
+ return;
+
+ message["ns.ax"] = "http://openid.net/srv/ax/1.0";
+ message["ax.mode"] = "fetch_request";
+
+ string required, available;
+ for (attr = attrs; attr; attr = attr->next) {
+
+ /* Request the attribute */
+ string field("ax.type.");
+ field.append(attr->alias);
+ message[field] = attr->url;
+
+ /* Add to our reqired list */
+ string& list = attr->required ? required : available;
+ if(!list.empty())
+ list.append(",");
+ list.append(attr->alias);
+
+ /* How many to request? */
+ if (attr->count != 1) {
+ field.assign("ax.count.");
+ field.append(attr->alias);
+ if (attr->count == 0)
+ message[field] = "unlimited";
+ else
+ message[field] = format_number (attr->count);
}
}
+
+ if(!required.empty())
+ message["ax.required"] = required;
+ if(!available.empty())
+ message["ax.if_available"] = available;
}
static void
-parse_query_string (const char *qs, params_t &params)
+process_ax_values (sid_request_t *req, sid_attribute_t *attr, const string_list& values)
{
- string pair, key, value;
- const char *at;
+ const char** array = new const char*[values.size() + 1];
+ if (!array) {
+ sid_request_log_error (req, "out of memory", NULL);
+ return;
+ }
- if (qs == NULL)
+ int i = 0;
+ for(string_list::const_iterator it = values.begin(); it != values.end(); ++it, ++i)
+ array[i] = it->c_str();
+ array[i] = NULL;
+
+ sid_request_attribute_values (req, attr, array);
+ delete [] array;
+}
+
+static void
+parse_ax_attributes (sid_request_t *req, params_t &message, sid_attribute_t *attrs)
+{
+ /* Build a list of all signed params */
+ params_t checked;
+ filter_listed_params(message, checked, "signed");
+
+ /* Look through and find ax namespace */
+ string prefix;
+ for (params_t::iterator it = checked.begin(); it != checked.end(); ++it) {
+ if(it->first.find("ns.") == 0 && it->second == "http://openid.net/srv/ax/1.0") {
+ prefix = it->first.substr(3) + ".";
+ break;
+ }
+ }
+
+ if(prefix.empty())
return;
- while (*qs != 0) {
- at = strchr (qs, '&');
- if (at == NULL)
- at = qs + strlen (qs);
- pair = string(qs, at);
- string::size_type loc = pair.find('=', 0);
- if (loc != string::npos) {
- key = pair.substr (0, loc);
- value = pair.substr (loc + 1);
+ /* Look through and find all aliases */
+ params_t aliases;
+ string type_prefix = prefix + "type.";
+ for (params_t::iterator it = checked.begin(); it != checked.end(); ++it) {
+ if(it->first.find(type_prefix) == 0)
+ aliases[it->second] = it->first.substr(type_prefix.length());
+ }
+
+ if(aliases.empty())
+ return;
+
+ /* Now pull out the values for the attributes we want */
+ string_list values;
+ for(sid_attribute_t *attr = attrs; attr; attr = attr->next) {
+ if(!aliases.has_param(attr->url))
+ continue;
+
+ string alias = aliases.get_param(attr->url);
+ values.clear();
+
+ /* See if there's a count attribute */
+ string field = prefix + "count." + alias;
+ if(checked.has_param(field)) {
+
+ /* Get each value in turn */
+ int count = parse_number (checked.get_param(field));
+ for(int i = 0; i < count; ++i) {
+ field = prefix + "value." + alias + "." + format_number (i + 1);
+ if (checked.has_param (field))
+ values.push_back (checked.get_param (field));
+ }
+
+ /* No count, just a single value */
} else {
- key = pair;
- value = "";
+ field = prefix + "value." + alias;
+ if(checked.has_param(field))
+ values.push_back(checked.get_param(field));
}
- params[opkele::util::url_decode(key)] = opkele::util::url_decode(value);
- if (*at == 0)
- break;
- qs = at + 1;
+ /* Send off values */
+ process_ax_values(req, attr, values);
}
}
static void
-begin_auth (sid_request_t *req, Consumer &consumer, params_t &params,
- const string& trust_root, const string &identity)
+begin_auth (sid_request_t *req, Consumer &consumer, const string& trust_root,
+ const string &identity, sid_attribute_t *attributes)
{
params_t result;
string redirect;
@@ -261,6 +440,7 @@ begin_auth (sid_request_t *req, Consumer &consumer, params_t &params,
openid_message_t cm;
consumer.initiate (identity);
consumer.checkid_ (cm, opkele::mode_checkid_setup, consumer.get_this_url(), trust_root);
+ request_ax_attributes (req, cm, attributes);
redirect = cm.append_query (consumer.get_endpoint().uri);
} catch (failed_xri_resolution &ex) {
@@ -295,12 +475,14 @@ begin_auth (sid_request_t *req, Consumer &consumer, params_t &params,
}
static void
-complete_auth (sid_request_t *req, Consumer &consumer, params_t &params)
+complete_auth (sid_request_t *req, Consumer &consumer, params_t &params,
+ sid_attribute_t *attributes)
{
try {
consumer.id_res(params);
string identity = consumer.get_claimed_id();
sid_request_authenticated (req, identity.c_str());
+ parse_ax_attributes(req, params, attributes);
} catch (exception &ex) {
sid_request_respond (req, 500, NULL, NULL);
sid_request_log_error (req, "error while completing authentication", ex.what());
@@ -315,7 +497,8 @@ cancelled_auth (sid_request_t *req, Consumer &consumer, params_t &params)
extern "C" void
sid_consumer_authenticate(sid_request_t *req, sid_storage_t *store,
- const char *trust_root, const char *identity)
+ const char *trust_root, const char *identity,
+ sid_attribute_t *attributes)
{
params_t params;
params_t openid;
@@ -324,7 +507,7 @@ sid_consumer_authenticate(sid_request_t *req, sid_storage_t *store,
const char *qs = sid_request_qs (req);
parse_query_string (qs, params);
- filter_openid_params (params, openid);
+ filter_prefixed_params (params, openid, "openid.");
string url = sid_request_url (req, 1);
if (!params.empty())
@@ -333,7 +516,7 @@ sid_consumer_authenticate(sid_request_t *req, sid_storage_t *store,
/* Returning (hopefully successful) authentication */
if (openid.has_param("assoc_handle")) {
- complete_auth (req, consumer, openid);
+ complete_auth (req, consumer, openid, attributes);
/* Returning cancelled authentication */
} else if (openid.has_param("mode") && openid.get_param("mode") == "cancel") {
@@ -343,6 +526,6 @@ sid_consumer_authenticate(sid_request_t *req, sid_storage_t *store,
} else {
if (!trust_root)
trust_root = sid_request_url (req, 0);
- begin_auth (req, consumer, params, trust_root, identity);
+ begin_auth (req, consumer, trust_root, identity, attributes);
}
}