-rw-r--r-- | include/opkele/ax.h | 84 | ||||
-rw-r--r-- | include/opkele/oauth_ext.h | 62 | ||||
-rw-r--r-- | lib/ax.cc | 135 | ||||
-rw-r--r-- | lib/oauth_ext.cc | 75 |
4 files changed, 356 insertions, 0 deletions
diff --git a/include/opkele/ax.h b/include/opkele/ax.h new file mode 100644 index 0000000..996d756 --- a/dev/null +++ b/include/opkele/ax.h @@ -0,0 +1,84 @@ +#ifndef __OPKELE_AX_H +#define __OPKELE_AX_H + +/** + * @file + * @brief Attribute Exchange extension + */ + +#include <opkele/extension.h> + +namespace opkele { + + /** + * OpenID simple registration extension implementation + * http://openid.net/specs/openid-simple-registration-extension-1_0.html + */ + class ax_t : public extension_t { + public: + /** special "count" value for add_attribute to request fetching "as many values as possible". */ + static const int UNLIMITED_COUNT = -1; + + /** + * Optional URL for receiving future attribute updates. + * Set it before checkid_setup to send up the URL; read it after id_res to get it back. + */ + std::string update_url; + + /** + * Consumer constructor. + */ + ax_t() : alias_count(0) { } + + /** Adds an attribute to request during checkid_setup. */ + void add_attribute(const char *uri, bool required, const char *alias = NULL, int count = 1); + + /** Returns an attribute fetched for the given type-uri during id_res. */ + std::string get_attribute(const char *uri, int index = 0); + /** Returns the number of values fetched for the given type-uri during id_res. */ + size_t get_attribute_count(const char *uri); + + virtual void rp_checkid_hook(basic_openid_message& om); + virtual void rp_id_res_hook(const basic_openid_message& om, + const basic_openid_message& sp); + virtual void op_checkid_hook(const basic_openid_message& inm); + virtual void op_id_res_hook(basic_openid_message& oum); + + virtual void checkid_hook(basic_openid_message& om); + virtual void id_res_hook(const basic_openid_message& om, + const basic_openid_message& sp); + virtual void checkid_hook(const basic_openid_message& inm, + basic_openid_message& oum); + + /** + * Function called after parsing sreg request to set up response + * fields. The default implementation tries to send as much fields + * as we have. The function is supposed to set the data and + * fields_response. + * @see fields_response + * @param inm incoming openid message + * @param oum outgoing openid message + */ + virtual void setup_response(const basic_openid_message& inm, + basic_openid_message& oum); + + virtual void setup_response(); + + protected: + /** Stores attributes to request fetching during checkid_setup. */ + struct ax_attr_t { + std::string uri; + std::string alias; + bool required; + int count; + }; + std::vector<ax_attr_t> attrs; + unsigned int alias_count; // auto-incr counter for auto-named aliases + + /** Stores results from fetch response during id_res. */ + std::map<std::string, std::vector<std::string> > response_attrs; + }; +} + +#endif /* __OPKELE_SREG_H */ + diff --git a/include/opkele/oauth_ext.h b/include/opkele/oauth_ext.h new file mode 100644 index 0000000..37a826b --- a/dev/null +++ b/include/opkele/oauth_ext.h @@ -0,0 +1,62 @@ +#ifndef __OPKELE_OAUTH_EXT_H +#define __OPKELE_OAUTH_EXT_H + +/** + * @file + * @brief OAuth extension + */ + +#include <opkele/extension.h> + +namespace opkele { + + /** + * OpenID OAuth extension + * http://step2.googlecode.com/svn/spec/openid_oauth_extension/latest/openid_oauth_extension.html + */ + class oauth_ext_t : public extension_t { + public: + std::string m_consumer, m_scope, m_request_token; + + /** + * Consumer constructor. + * @param fr required fields + * @see fields_required + * @param fo optional fields + * @see fields_optional + * @param pu policy url + * @see policy_url + */ + oauth_ext_t(const char *consumer = "", const char *scope = "") : m_consumer(consumer), m_scope(scope) { } + + virtual void rp_checkid_hook(basic_openid_message& om); + virtual void rp_id_res_hook(const basic_openid_message& om, + const basic_openid_message& sp); + virtual void op_checkid_hook(const basic_openid_message& inm); + virtual void op_id_res_hook(basic_openid_message& oum); + + virtual void checkid_hook(basic_openid_message& om); + virtual void id_res_hook(const basic_openid_message& om, + const basic_openid_message& sp); + virtual void checkid_hook(const basic_openid_message& inm, + basic_openid_message& oum); + + /** + * Function called after parsing sreg request to set up response + * fields. The default implementation tries to send as much fields + * as we have. The function is supposed to set the data and + * fields_response. + * @see fields_response + * @param inm incoming openid message + * @param oum outgoing openid message + */ + virtual void setup_response(const basic_openid_message& inm, + basic_openid_message& oum); + + virtual void setup_response(); + + }; +} + +#endif /* __OPKELE_OAUTH_EXT_H */ + diff --git a/lib/ax.cc b/lib/ax.cc new file mode 100644 index 0000000..ccdb706 --- a/dev/null +++ b/lib/ax.cc @@ -0,0 +1,135 @@ +#include <opkele/exception.h> +#include <opkele/ax.h> +#include <opkele/uris.h> +#include <opkele/util.h> + +#include <map> +#include <string> +#include <vector> + +using namespace std; + +namespace opkele { + + void ax_t::add_attribute(const char *uri, bool required, const char *alias /* = NULL */, int count /* = 1 */) { + assert(uri && *uri); + assert(count != 0); + + ax_attr_t attr; + attr.uri = uri; + attr.required = required; + attr.count = count; + // if no alias is specified, generate one using an internal auto-incremented counter + attr.alias = alias ? alias : string("attr") + opkele::util::long_to_string(++alias_count); + + attrs.push_back(attr); + } + + void ax_t::rp_checkid_hook(basic_openid_message& om) { + if (attrs.size() == 0) return; // not asking for any attributes + + string pfx = om.allocate_ns(OIURI_AX10,"ax"); + om.set_field(pfx+".mode", "fetch_request"); // only supports fetch_request for now + + string required_fields, optional_fields; + for (size_t i = 0; i < attrs.size(); i++) { + // build up list of required/optional aliases + if (attrs[i].required) required_fields += (required_fields.empty() ? "" : ",") + attrs[i].alias; + else optional_fields += (optional_fields.empty() ? "" : ",") + attrs[i].alias; + + om.set_field(pfx+".type."+attrs[i].alias, attrs[i].uri); + + // only specify count if it's >1 or unlimited + if (attrs[i].count == UNLIMITED_COUNT) { + om.set_field(pfx+".count."+attrs[i].alias, "unlimited"); + } else if (attrs[i].count > 1) { + om.set_field(pfx+".count."+attrs[i].alias, opkele::util::long_to_string(attrs[i].count)); + } + } + + if (!required_fields.empty()) om.set_field(pfx+".required", required_fields); + if (!optional_fields.empty()) om.set_field(pfx+".if_available", optional_fields); + + if (!update_url.empty()) om.set_field(pfx+".update_url", update_url); + } + + void ax_t::checkid_hook(basic_openid_message& om) { + rp_checkid_hook(om); } + + void ax_t::rp_id_res_hook(const basic_openid_message& om, + const basic_openid_message& sp) { + string pfx; + try { + pfx = om.find_ns(OIURI_AX10,"ax"); + }catch(failed_lookup&) { + return; + } + pfx += '.'; + + // first look at all aliases and generate an internal uri->alias map + string fn; + map<string, string> aliases; + for (basic_openid_message::fields_iterator it = sp.fields_begin(); it != sp.fields_end(); ++it) { + fn = *it; + string type_pfx = pfx; type_pfx += "type."; + size_t pos = fn.find(type_pfx); + if (pos == string::npos) continue; + string alias = fn.substr(pos + type_pfx.size()); + aliases[sp.get_field(fn)] = alias; + } + + // now for each alias, pull out the count and value(s) and store uri->[value1, ...] + for (map<string, string>::iterator it = aliases.begin(); it != aliases.end(); ++it) { + vector<string> values; + fn = pfx; fn += "count." + it->second; + if (sp.has_field(fn)) { + int count = opkele::util::string_to_long(sp.get_field(fn)); + for (int i = 1; i <= count; i++) { + fn = pfx; fn += "value." + it->second + "." + opkele::util::long_to_string(i); + values.push_back(sp.get_field(fn)); + } + } else { + fn = pfx; fn += "value." + it->second; + values.push_back(sp.get_field(fn)); + } + response_attrs[it->first] = values; + } + + fn = pfx; fn += "update_url"; + if (sp.has_field(fn)) update_url = sp.get_field(fn); + } + + string ax_t::get_attribute(const char *uri, int index /* = 0 */) { + if (response_attrs.find(uri) == response_attrs.end()) return ""; + return response_attrs[uri][index]; + } + + size_t ax_t::get_attribute_count(const char *uri) { + if (response_attrs.find(uri) == response_attrs.end()) return 0; + return response_attrs[uri].size(); + } + + void ax_t::id_res_hook(const basic_openid_message& om, + const basic_openid_message& sp) { + rp_id_res_hook(om,sp); } + + void ax_t::op_checkid_hook(const basic_openid_message& inm) { + } + + void ax_t::op_id_res_hook(basic_openid_message& oum) { + } + + void ax_t::checkid_hook(const basic_openid_message& inm, + basic_openid_message& oum) { + op_checkid_hook(inm); + setup_response(inm,oum); + op_id_res_hook(oum); + } + + void ax_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) { + setup_response(); + } + void ax_t::setup_response() { + } +} + diff --git a/lib/oauth_ext.cc b/lib/oauth_ext.cc new file mode 100644 index 0000000..b728854 --- a/dev/null +++ b/lib/oauth_ext.cc @@ -0,0 +1,75 @@ +#include <opkele/exception.h> +#include <opkele/oauth_ext.h> +#include <opkele/uris.h> +#include <algorithm> + +namespace opkele { + using std::find; + + void oauth_ext_t::rp_checkid_hook(basic_openid_message& om) { + + string pfx = om.allocate_ns(OIURI_OAUTH10,"oauth"); + //required: openid.oauth.consumer=www.plaxo.com + //optional: openid.oauth.scope=http://www.google.com/m8/feeds/ + if (m_consumer.empty()) throw bad_input(OPKELE_CP_ "Required consumer key is missing from OAuth extension"); + om.set_field(pfx+".consumer", m_consumer); + if (!m_scope.empty()) om.set_field(pfx+".scope", m_scope); + } + + void oauth_ext_t::checkid_hook(basic_openid_message& om) { + rp_checkid_hook(om); } + + void oauth_ext_t::rp_id_res_hook(const basic_openid_message& om, + const basic_openid_message& sp) { + string pfx; + try { + pfx = om.get_ns(OIURI_OAUTH10); + }catch(failed_lookup&) { + return; + } + pfx += '.'; + //required: openid.oauth.request_token=abcdefg + //optional: openid.oauth.consumer=www.plaxo.com + //optional: openid.oauth.scope=http://www.google.com/m8/feeds/ + string fn; + + fn = pfx + "request_token"; + if (sp.has_field(fn)) { + m_request_token = sp.get_field(fn); + } else throw bad_input(OPKELE_CP_ "Missing required response field: "+fn); + + fn = pfx + "consumer"; + if (sp.has_field(fn)) { + m_consumer = sp.get_field(fn); + } + + fn = pfx + "scope"; + if (sp.has_field(fn)) { + m_scope = sp.get_field(fn); + } + } + + void oauth_ext_t::id_res_hook(const basic_openid_message& om, + const basic_openid_message& sp) { + rp_id_res_hook(om,sp); } + + void oauth_ext_t::op_checkid_hook(const basic_openid_message& inm) { + } + + void oauth_ext_t::op_id_res_hook(basic_openid_message& oum) { + } + + void oauth_ext_t::checkid_hook(const basic_openid_message& inm, + basic_openid_message& oum) { + op_checkid_hook(inm); + setup_response(inm,oum); + op_id_res_hook(oum); + } + + void oauth_ext_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) { + setup_response(); + } + void oauth_ext_t::setup_response() { + } +} + |