summaryrefslogtreecommitdiffabout
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--include/opkele/ax.h84
-rw-r--r--include/opkele/oauth_ext.h62
-rw-r--r--lib/ax.cc135
-rw-r--r--lib/oauth_ext.cc75
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() {
+ }
+}
+