summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--include/Makefile.am3
-rw-r--r--include/opkele/sreg.h206
-rw-r--r--lib/Makefile.am5
-rw-r--r--lib/sreg.cc124
4 files changed, 336 insertions, 2 deletions
diff --git a/include/Makefile.am b/include/Makefile.am
index 72931eb..4b9b02a 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -6,7 +6,8 @@ nobase_include_HEADERS = \
6 opkele/exception.h \ 6 opkele/exception.h \
7 opkele/server.h \ 7 opkele/server.h \
8 opkele/consumer.h \ 8 opkele/consumer.h \
9 opkele/extension.h 9 opkele/extension.h \
10 opkele/sreg.h
10EXTRA_DIST = \ 11EXTRA_DIST = \
11 opkele/data.h \ 12 opkele/data.h \
12 opkele/util.h 13 opkele/util.h
diff --git a/include/opkele/sreg.h b/include/opkele/sreg.h
new file mode 100644
index 0000000..6713ef7
--- a/dev/null
+++ b/include/opkele/sreg.h
@@ -0,0 +1,206 @@
1#ifndef __OPKELE_SREG_H
2#define __OPKELE_SREG_H
3
4/**
5 * @file
6 * @brief Simple registration extension
7 */
8
9#include <opkele/extension.h>
10
11/**
12 * @brief the main opkele namespace
13 */
14namespace opkele {
15 using std::map;
16
17 /**
18 * OpenID simple registration extension implementation
19 * http://openid.net/specs/openid-simple-registration-extension-1_0.html
20 */
21 class sreg_t : public extension_t {
22 public:
23 /**
24 * sreg fields enumeration
25 */
26 enum fieldbit_t {
27 /**
28 * Any UTF-8 string that the End User wants to use as a nickname.
29 */
30 field_nickname = 1,
31 /**
32 * The email address of the End User as specified in section 3.4.1 of [RFC2822]
33 */
34 field_email = 2,
35 /**
36 * UTF-8 string free text representation of the End User's full name.
37 */
38 field_fullname = 4,
39 /**
40 * The End User's date of birth as YYYY-MM-DD. Any values whose
41 * representation uses fewer than the specified number of
42 * digits should be zero-padded. The length of this value MUST
43 * always be 10. If the End User user does not want to reveal
44 * any particular component of this value, it MUST be set to
45 * zero.
46 *
47 * For instance, if a End User wants to specify that his date
48 * of birth is in 1980, but not the month or day, the value
49 * returned SHALL be "1980-00-00".
50 */
51 field_dob = 8,
52 /**
53 * Alias to field_dob
54 */
55 field_birthdate = field_dob,
56 /**
57 * The End User's gender, "M" for male, "F" for female.
58 */
59 field_gender = 16,
60 /**
61 * Alias to field_gender
62 */
63 field_sex = field_gender,
64 /**
65 * UTF-8 string free text that SHOULD conform to the End User's
66 * country's postal system.
67 */
68 field_postcode = 32,
69 /**
70 * The End User's country of residence as specified by ISO3166
71 */
72 field_country = 64,
73 /**
74 * End User's preferred language as specified by ISO639
75 */
76 field_language = 128,
77 /**
78 * ASCII string from TimeZone database
79 *
80 * For example, "Europe/Paris" or "America/Los_Angeles".
81 */
82 field_timezone = 256,
83 /**
84 * All fields bits combined
85 */
86 fields_ALL = 511,
87 /**
88 * No fields
89 */
90 fields_NONE = 0
91 };
92 /**
93 * Bitmask for fields which, if absent from the response, will
94 * prevent the Consumer from completing the registration without
95 * End User interation.
96 */
97 long fields_required;
98 /**
99 * Bitmask for fields that will be used by the Consumer, but whose
100 * absence will not prevent the registration from completing.
101 */
102 long fields_optional;
103 /**
104 * A URL which the Consumer provides to give the End User a place
105 * to read about the how the profile data will be used. The
106 * Identity Provider SHOULD display this URL to the End User if it
107 * is given.
108 */
109 string policy_url;
110
111 /**
112 * Bitmask for fields present in response
113 */
114 long has_fields;
115 /**
116 * Container type for response fields values
117 */
118 typedef map<fieldbit_t,string> response_t;
119 /**
120 * Response contents
121 */
122 response_t response;
123
124 /**
125 * Fields bitmask to send in response
126 */
127 long fields_response;
128
129 /**
130 * Consumer constructor.
131 * @param fr required fields
132 * @see fields_required
133 * @param fo optional fields
134 * @see fields_optional
135 * @param pu policy url
136 * @see policy_url
137 */
138 sreg_t(long fr=fields_NONE,long fo=fields_NONE,const string& pu="")
139 : fields_required(fr), fields_optional(fo), policy_url(pu), has_fields(0) { }
140
141 /**
142 * Implementation of consumer's checkid hook
143 */
144 virtual void checkid_hook(params_t& p,const string& identity);
145 /**
146 * Implementation of consumer's id_res hook
147 */
148 virtual void id_res_hook(const params_t& p,const params_t& sp,const string& identity);
149 /**
150 * Implementation of server's checkid_hook
151 */
152 virtual void checkid_hook(const params_t& pin,params_t& pout);
153
154 /**
155 * Check and see if we have value for some particular field.
156 * @param fb field in question
157 * @see fieldbit_t
158 * @return true if the value is available
159 */
160 bool has_field(fieldbit_t fb) const { return has_fields&fb; }
161
162 /**
163 * Retrieve the value for a field.
164 * @param fb field in question
165 * @see fieldbit_t
166 * @return field value
167 * @throw failed_lookup if no data avaialble
168 */
169 const string& get_field(fieldbit_t fb) const;
170
171 /**
172 * Set the value for a field.
173 * @param fb field in question
174 * @see fieldbit_t
175 * @param fv field value
176 */
177 void set_field(fieldbit_t fb,const string& fv);
178
179 /**
180 * Remove the value for a field.
181 * @param fb field in question
182 * @see fieldbit_t
183 */
184 void reset_field(fieldbit_t fb);
185
186 /**
187 * Reset field data
188 */
189 void clear();
190
191 /**
192 * Function called after parsing sreg request to set up response
193 * fields. The default implementation tries to send as much fields
194 * as we have. The function is supposed to set the data and
195 * fields_response.
196 * @see fields_response
197 * @param pin input request parameters with "openid." prefix
198 * @param pout output request parameters without "openid." prefix.
199 * @see checkid_hook(const params_t&,params_t&)
200 */
201 virtual void setup_response(const params_t& pin,params_t& pout);
202
203 };
204}
205
206#endif /* __OPKELE_SREG_H */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 69c749e..783f2ab 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -22,4 +22,7 @@ libopkele_la_SOURCES = \
22 data.cc \ 22 data.cc \
23 consumer.cc \ 23 consumer.cc \
24 exception.cc \ 24 exception.cc \
25 extension.cc 25 extension.cc \
26 sreg.cc
27libopkele_la_LDFLAGS = \
28 -version-info 1:0:0
diff --git a/lib/sreg.cc b/lib/sreg.cc
new file mode 100644
index 0000000..08e66b7
--- a/dev/null
+++ b/lib/sreg.cc
@@ -0,0 +1,124 @@
1#include <opkele/exception.h>
2#include <opkele/sreg.h>
3#include <algorithm>
4
5namespace opkele {
6 using std::find;
7
8 static const struct _sreg_field {
9 const char *fieldname;
10 sreg_t::fieldbit_t fieldbit;
11 }fields[] = {
12 { "nickname", sreg_t::field_nickname },
13 { "email", sreg_t::field_email },
14 { "fullname", sreg_t::field_fullname },
15 { "dob", sreg_t::field_dob },
16 { "gender", sreg_t::field_gender },
17 { "postcode", sreg_t::field_postcode },
18 { "country", sreg_t::field_country },
19 { "language", sreg_t::field_language },
20 { "timezone", sreg_t::field_timezone }
21 };
22 # define fields_BEGINfields
23# define fields_END &fields[sizeof(fields)/sizeof(*fields)]
24 typedef const struct _sreg_field *fields_iterator;
25
26 bool operator==(const struct _sreg_field& fd,const string& fn) {
27 return fd.fieldname==fn;
28 }
29
30 void sreg_t::checkid_hook(params_t& p,const string& identity) {
31 string fr, fo;
32 for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
33 if(f->fieldbit&fields_required) {
34 if(!fr.empty()) fr+=",";
35 fr += f->fieldname;
36 }
37 if(f->fieldbit&fields_optional) {
38 if(!fo.empty()) fo+=",";
39 fo += f->fieldname;
40 }
41 }
42 if(!fr.empty()) p["sreg.required"]=fr;
43 if(!fo.empty()) p["sreg.optional"]=fo;
44 if(!policy_url.empty()) p["sreg.policy_url"]=policy_url;
45 }
46
47 void sreg_t::id_res_hook(const params_t& p,const params_t& sp,const string& identity) {
48 clear();
49 for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
50 string fn = "sreg."; fn+=f->fieldname;
51 if(!sp.has_param(fn)) continue;
52 has_fields |= f->fieldbit;
53 response[f->fieldbit]=sp.get_param(fn);
54 }
55 }
56
57 const string& sreg_t::get_field(fieldbit_t fb) const {
58 response_t::const_iterator i = response.find(fb);
59 if(i==response.end())
60 throw failed_lookup(OPKELE_CP_ "no field data available");
61 return i->second;
62 }
63
64 void sreg_t::set_field(fieldbit_t fb,const string& fv) {
65 response[fb] = fv;
66 has_fields |= fb;
67 }
68
69 void sreg_t::reset_field(fieldbit_t fb) {
70 has_fields &= ~fb;
71 response.erase(fb);
72 }
73
74 void sreg_t::clear() {
75 has_fields = 0; response.clear();
76 }
77
78 static long fields_list_to_bitmask(string& fl) {
79 long rv = 0;
80 while(!fl.empty()) {
81 string::size_type co = fl.find(',');
82 string fn;
83 if(co==string::npos) {
84 fn = fl; fl.erase();
85 }else{
86 fn = fl.substr(0,co); fl.erase(0,co+1);
87 }
88 fields_iterator f = find(fields_BEGIN,fields_END,fn);
89 if(f!=fields_END)
90 rv |= f->fieldbit;
91 }
92 return rv;
93 }
94
95 void sreg_t::checkid_hook(const params_t& pin,params_t& pout) {
96 fields_optional = 0; fields_required = 0; policy_url.erase();
97 fields_response = 0;
98 try {
99 string fl = pin.get_param("openid.sreg.required");
100 fields_required = fields_list_to_bitmask(fl);
101 }catch(failed_lookup&) { }
102 try {
103 string fl = pin.get_param("openid.sreg.optional");
104 fields_optional = fields_list_to_bitmask(fl);
105 }catch(failed_lookup&) { }
106 try {
107 policy_url = pin.get_param("openid.sreg.policy_url");
108 }catch(failed_lookup&) { }
109 setup_response(pin,pout);
110 fields_response &= has_fields;
111 for(fields_iterator f=fields_BEGIN;f<fields_END;++f) {
112 if(!(f->fieldbit&fields_response)) continue;
113 if(!pout["signed"].empty())
114 pout["signed"] +=',';
115 string pn = "sreg."; pn += f->fieldname;
116 pout["signed"] += pn;
117 pout[pn] = get_field(f->fieldbit);
118 }
119 }
120
121 void sreg_t::setup_response(const params_t& pin,params_t& pout) {
122 fields_response = (fields_required|fields_optional)&has_fields;
123 }
124}