-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 @@ | |||
1 | #ifndef __OPKELE_AX_H | ||
2 | #define __OPKELE_AX_H | ||
3 | |||
4 | /** | ||
5 | * @file | ||
6 | * @brief Attribute Exchange extension | ||
7 | */ | ||
8 | |||
9 | #include <opkele/extension.h> | ||
10 | |||
11 | namespace opkele { | ||
12 | |||
13 | /** | ||
14 | * OpenID simple registration extension implementation | ||
15 | * http://openid.net/specs/openid-simple-registration-extension-1_0.html | ||
16 | */ | ||
17 | class ax_t : public extension_t { | ||
18 | public: | ||
19 | /** special "count" value for add_attribute to request fetching "as many values as possible". */ | ||
20 | static const int UNLIMITED_COUNT = -1; | ||
21 | |||
22 | /** | ||
23 | * Optional URL for receiving future attribute updates. | ||
24 | * Set it before checkid_setup to send up the URL; read it after id_res to get it back. | ||
25 | */ | ||
26 | std::string update_url; | ||
27 | |||
28 | /** | ||
29 | * Consumer constructor. | ||
30 | */ | ||
31 | ax_t() : alias_count(0) { } | ||
32 | |||
33 | /** Adds an attribute to request during checkid_setup. */ | ||
34 | void add_attribute(const char *uri, bool required, const char *alias = NULL, int count = 1); | ||
35 | |||
36 | /** Returns an attribute fetched for the given type-uri during id_res. */ | ||
37 | std::string get_attribute(const char *uri, int index = 0); | ||
38 | /** Returns the number of values fetched for the given type-uri during id_res. */ | ||
39 | size_t get_attribute_count(const char *uri); | ||
40 | |||
41 | virtual void rp_checkid_hook(basic_openid_message& om); | ||
42 | virtual void rp_id_res_hook(const basic_openid_message& om, | ||
43 | const basic_openid_message& sp); | ||
44 | virtual void op_checkid_hook(const basic_openid_message& inm); | ||
45 | virtual void op_id_res_hook(basic_openid_message& oum); | ||
46 | |||
47 | virtual void checkid_hook(basic_openid_message& om); | ||
48 | virtual void id_res_hook(const basic_openid_message& om, | ||
49 | const basic_openid_message& sp); | ||
50 | virtual void checkid_hook(const basic_openid_message& inm, | ||
51 | basic_openid_message& oum); | ||
52 | |||
53 | /** | ||
54 | * Function called after parsing sreg request to set up response | ||
55 | * fields. The default implementation tries to send as much fields | ||
56 | * as we have. The function is supposed to set the data and | ||
57 | * fields_response. | ||
58 | * @see fields_response | ||
59 | * @param inm incoming openid message | ||
60 | * @param oum outgoing openid message | ||
61 | */ | ||
62 | virtual void setup_response(const basic_openid_message& inm, | ||
63 | basic_openid_message& oum); | ||
64 | |||
65 | virtual void setup_response(); | ||
66 | |||
67 | protected: | ||
68 | /** Stores attributes to request fetching during checkid_setup. */ | ||
69 | struct ax_attr_t { | ||
70 | std::string uri; | ||
71 | std::string alias; | ||
72 | bool required; | ||
73 | int count; | ||
74 | }; | ||
75 | std::vector<ax_attr_t> attrs; | ||
76 | unsigned int alias_count; // auto-incr counter for auto-named aliases | ||
77 | |||
78 | /** Stores results from fetch response during id_res. */ | ||
79 | std::map<std::string, std::vector<std::string> > response_attrs; | ||
80 | }; | ||
81 | } | ||
82 | |||
83 | #endif /* __OPKELE_SREG_H */ | ||
84 | |||
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 @@ | |||
1 | #ifndef __OPKELE_OAUTH_EXT_H | ||
2 | #define __OPKELE_OAUTH_EXT_H | ||
3 | |||
4 | /** | ||
5 | * @file | ||
6 | * @brief OAuth extension | ||
7 | */ | ||
8 | |||
9 | #include <opkele/extension.h> | ||
10 | |||
11 | namespace opkele { | ||
12 | |||
13 | /** | ||
14 | * OpenID OAuth extension | ||
15 | * http://step2.googlecode.com/svn/spec/openid_oauth_extension/latest/openid_oauth_extension.html | ||
16 | */ | ||
17 | class oauth_ext_t : public extension_t { | ||
18 | public: | ||
19 | std::string m_consumer, m_scope, m_request_token; | ||
20 | |||
21 | /** | ||
22 | * Consumer constructor. | ||
23 | * @param fr required fields | ||
24 | * @see fields_required | ||
25 | * @param fo optional fields | ||
26 | * @see fields_optional | ||
27 | * @param pu policy url | ||
28 | * @see policy_url | ||
29 | */ | ||
30 | oauth_ext_t(const char *consumer = "", const char *scope = "") : m_consumer(consumer), m_scope(scope) { } | ||
31 | |||
32 | virtual void rp_checkid_hook(basic_openid_message& om); | ||
33 | virtual void rp_id_res_hook(const basic_openid_message& om, | ||
34 | const basic_openid_message& sp); | ||
35 | virtual void op_checkid_hook(const basic_openid_message& inm); | ||
36 | virtual void op_id_res_hook(basic_openid_message& oum); | ||
37 | |||
38 | virtual void checkid_hook(basic_openid_message& om); | ||
39 | virtual void id_res_hook(const basic_openid_message& om, | ||
40 | const basic_openid_message& sp); | ||
41 | virtual void checkid_hook(const basic_openid_message& inm, | ||
42 | basic_openid_message& oum); | ||
43 | |||
44 | /** | ||
45 | * Function called after parsing sreg request to set up response | ||
46 | * fields. The default implementation tries to send as much fields | ||
47 | * as we have. The function is supposed to set the data and | ||
48 | * fields_response. | ||
49 | * @see fields_response | ||
50 | * @param inm incoming openid message | ||
51 | * @param oum outgoing openid message | ||
52 | */ | ||
53 | virtual void setup_response(const basic_openid_message& inm, | ||
54 | basic_openid_message& oum); | ||
55 | |||
56 | virtual void setup_response(); | ||
57 | |||
58 | }; | ||
59 | } | ||
60 | |||
61 | #endif /* __OPKELE_OAUTH_EXT_H */ | ||
62 | |||
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 @@ | |||
1 | #include <opkele/exception.h> | ||
2 | #include <opkele/ax.h> | ||
3 | #include <opkele/uris.h> | ||
4 | #include <opkele/util.h> | ||
5 | |||
6 | #include <map> | ||
7 | #include <string> | ||
8 | #include <vector> | ||
9 | |||
10 | using namespace std; | ||
11 | |||
12 | namespace opkele { | ||
13 | |||
14 | void ax_t::add_attribute(const char *uri, bool required, const char *alias /* = NULL */, int count /* = 1 */) { | ||
15 | assert(uri && *uri); | ||
16 | assert(count != 0); | ||
17 | |||
18 | ax_attr_t attr; | ||
19 | attr.uri = uri; | ||
20 | attr.required = required; | ||
21 | attr.count = count; | ||
22 | // if no alias is specified, generate one using an internal auto-incremented counter | ||
23 | attr.alias = alias ? alias : string("attr") + opkele::util::long_to_string(++alias_count); | ||
24 | |||
25 | attrs.push_back(attr); | ||
26 | } | ||
27 | |||
28 | void ax_t::rp_checkid_hook(basic_openid_message& om) { | ||
29 | if (attrs.size() == 0) return; // not asking for any attributes | ||
30 | |||
31 | string pfx = om.allocate_ns(OIURI_AX10,"ax"); | ||
32 | om.set_field(pfx+".mode", "fetch_request"); // only supports fetch_request for now | ||
33 | |||
34 | string required_fields, optional_fields; | ||
35 | for (size_t i = 0; i < attrs.size(); i++) { | ||
36 | // build up list of required/optional aliases | ||
37 | if (attrs[i].required) required_fields += (required_fields.empty() ? "" : ",") + attrs[i].alias; | ||
38 | else optional_fields += (optional_fields.empty() ? "" : ",") + attrs[i].alias; | ||
39 | |||
40 | om.set_field(pfx+".type."+attrs[i].alias, attrs[i].uri); | ||
41 | |||
42 | // only specify count if it's >1 or unlimited | ||
43 | if (attrs[i].count == UNLIMITED_COUNT) { | ||
44 | om.set_field(pfx+".count."+attrs[i].alias, "unlimited"); | ||
45 | } else if (attrs[i].count > 1) { | ||
46 | om.set_field(pfx+".count."+attrs[i].alias, opkele::util::long_to_string(attrs[i].count)); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | if (!required_fields.empty()) om.set_field(pfx+".required", required_fields); | ||
51 | if (!optional_fields.empty()) om.set_field(pfx+".if_available", optional_fields); | ||
52 | |||
53 | if (!update_url.empty()) om.set_field(pfx+".update_url", update_url); | ||
54 | } | ||
55 | |||
56 | void ax_t::checkid_hook(basic_openid_message& om) { | ||
57 | rp_checkid_hook(om); } | ||
58 | |||
59 | void ax_t::rp_id_res_hook(const basic_openid_message& om, | ||
60 | const basic_openid_message& sp) { | ||
61 | string pfx; | ||
62 | try { | ||
63 | pfx = om.find_ns(OIURI_AX10,"ax"); | ||
64 | }catch(failed_lookup&) { | ||
65 | return; | ||
66 | } | ||
67 | pfx += '.'; | ||
68 | |||
69 | // first look at all aliases and generate an internal uri->alias map | ||
70 | string fn; | ||
71 | map<string, string> aliases; | ||
72 | for (basic_openid_message::fields_iterator it = sp.fields_begin(); it != sp.fields_end(); ++it) { | ||
73 | fn = *it; | ||
74 | string type_pfx = pfx; type_pfx += "type."; | ||
75 | size_t pos = fn.find(type_pfx); | ||
76 | if (pos == string::npos) continue; | ||
77 | string alias = fn.substr(pos + type_pfx.size()); | ||
78 | aliases[sp.get_field(fn)] = alias; | ||
79 | } | ||
80 | |||
81 | // now for each alias, pull out the count and value(s) and store uri->[value1, ...] | ||
82 | for (map<string, string>::iterator it = aliases.begin(); it != aliases.end(); ++it) { | ||
83 | vector<string> values; | ||
84 | fn = pfx; fn += "count." + it->second; | ||
85 | if (sp.has_field(fn)) { | ||
86 | int count = opkele::util::string_to_long(sp.get_field(fn)); | ||
87 | for (int i = 1; i <= count; i++) { | ||
88 | fn = pfx; fn += "value." + it->second + "." + opkele::util::long_to_string(i); | ||
89 | values.push_back(sp.get_field(fn)); | ||
90 | } | ||
91 | } else { | ||
92 | fn = pfx; fn += "value." + it->second; | ||
93 | values.push_back(sp.get_field(fn)); | ||
94 | } | ||
95 | response_attrs[it->first] = values; | ||
96 | } | ||
97 | |||
98 | fn = pfx; fn += "update_url"; | ||
99 | if (sp.has_field(fn)) update_url = sp.get_field(fn); | ||
100 | } | ||
101 | |||
102 | string ax_t::get_attribute(const char *uri, int index /* = 0 */) { | ||
103 | if (response_attrs.find(uri) == response_attrs.end()) return ""; | ||
104 | return response_attrs[uri][index]; | ||
105 | } | ||
106 | |||
107 | size_t ax_t::get_attribute_count(const char *uri) { | ||
108 | if (response_attrs.find(uri) == response_attrs.end()) return 0; | ||
109 | return response_attrs[uri].size(); | ||
110 | } | ||
111 | |||
112 | void ax_t::id_res_hook(const basic_openid_message& om, | ||
113 | const basic_openid_message& sp) { | ||
114 | rp_id_res_hook(om,sp); } | ||
115 | |||
116 | void ax_t::op_checkid_hook(const basic_openid_message& inm) { | ||
117 | } | ||
118 | |||
119 | void ax_t::op_id_res_hook(basic_openid_message& oum) { | ||
120 | } | ||
121 | |||
122 | void ax_t::checkid_hook(const basic_openid_message& inm, | ||
123 | basic_openid_message& oum) { | ||
124 | op_checkid_hook(inm); | ||
125 | setup_response(inm,oum); | ||
126 | op_id_res_hook(oum); | ||
127 | } | ||
128 | |||
129 | void ax_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) { | ||
130 | setup_response(); | ||
131 | } | ||
132 | void ax_t::setup_response() { | ||
133 | } | ||
134 | } | ||
135 | |||
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 @@ | |||
1 | #include <opkele/exception.h> | ||
2 | #include <opkele/oauth_ext.h> | ||
3 | #include <opkele/uris.h> | ||
4 | #include <algorithm> | ||
5 | |||
6 | namespace opkele { | ||
7 | using std::find; | ||
8 | |||
9 | void oauth_ext_t::rp_checkid_hook(basic_openid_message& om) { | ||
10 | |||
11 | string pfx = om.allocate_ns(OIURI_OAUTH10,"oauth"); | ||
12 | //required: openid.oauth.consumer=www.plaxo.com | ||
13 | //optional: openid.oauth.scope=http://www.google.com/m8/feeds/ | ||
14 | if (m_consumer.empty()) throw bad_input(OPKELE_CP_ "Required consumer key is missing from OAuth extension"); | ||
15 | om.set_field(pfx+".consumer", m_consumer); | ||
16 | if (!m_scope.empty()) om.set_field(pfx+".scope", m_scope); | ||
17 | } | ||
18 | |||
19 | void oauth_ext_t::checkid_hook(basic_openid_message& om) { | ||
20 | rp_checkid_hook(om); } | ||
21 | |||
22 | void oauth_ext_t::rp_id_res_hook(const basic_openid_message& om, | ||
23 | const basic_openid_message& sp) { | ||
24 | string pfx; | ||
25 | try { | ||
26 | pfx = om.get_ns(OIURI_OAUTH10); | ||
27 | }catch(failed_lookup&) { | ||
28 | return; | ||
29 | } | ||
30 | pfx += '.'; | ||
31 | //required: openid.oauth.request_token=abcdefg | ||
32 | //optional: openid.oauth.consumer=www.plaxo.com | ||
33 | //optional: openid.oauth.scope=http://www.google.com/m8/feeds/ | ||
34 | string fn; | ||
35 | |||
36 | fn = pfx + "request_token"; | ||
37 | if (sp.has_field(fn)) { | ||
38 | m_request_token = sp.get_field(fn); | ||
39 | } else throw bad_input(OPKELE_CP_ "Missing required response field: "+fn); | ||
40 | |||
41 | fn = pfx + "consumer"; | ||
42 | if (sp.has_field(fn)) { | ||
43 | m_consumer = sp.get_field(fn); | ||
44 | } | ||
45 | |||
46 | fn = pfx + "scope"; | ||
47 | if (sp.has_field(fn)) { | ||
48 | m_scope = sp.get_field(fn); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | void oauth_ext_t::id_res_hook(const basic_openid_message& om, | ||
53 | const basic_openid_message& sp) { | ||
54 | rp_id_res_hook(om,sp); } | ||
55 | |||
56 | void oauth_ext_t::op_checkid_hook(const basic_openid_message& inm) { | ||
57 | } | ||
58 | |||
59 | void oauth_ext_t::op_id_res_hook(basic_openid_message& oum) { | ||
60 | } | ||
61 | |||
62 | void oauth_ext_t::checkid_hook(const basic_openid_message& inm, | ||
63 | basic_openid_message& oum) { | ||
64 | op_checkid_hook(inm); | ||
65 | setup_response(inm,oum); | ||
66 | op_id_res_hook(oum); | ||
67 | } | ||
68 | |||
69 | void oauth_ext_t::setup_response(const basic_openid_message& /* inm */,basic_openid_message& /* oum */) { | ||
70 | setup_response(); | ||
71 | } | ||
72 | void oauth_ext_t::setup_response() { | ||
73 | } | ||
74 | } | ||
75 | |||