summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--test/.gitignore2
-rw-r--r--test/Makefile.am20
-rw-r--r--test/OP-db.sql25
-rw-r--r--test/OP.cc409
4 files changed, 452 insertions, 4 deletions
diff --git a/test/.gitignore b/test/.gitignore
index d07884c..3d88495 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -5,3 +5,5 @@
5/idiscover 5/idiscover
6/RP.cgi 6/RP.cgi
7/RP-db.cc 7/RP-db.cc
8/OP.cgi
9/OP-db.cc
diff --git a/test/Makefile.am b/test/Makefile.am
index 61e3787..8fedf48 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,4 +1,4 @@
1noinst_PROGRAMS = test idiscover RP.cgi 1noinst_PROGRAMS = test idiscover RP.cgi OP.cgi
2 2
3AM_CPPFLAGS=${CPPFLAGS_DEBUG} 3AM_CPPFLAGS=${CPPFLAGS_DEBUG}
4DEFAULT_INCLUDES = -I${top_builddir} 4DEFAULT_INCLUDES = -I${top_builddir}
@@ -8,8 +8,8 @@ test_SOURCES = test.cc
8test_LDADD = ${top_builddir}/lib/libopkele.la 8test_LDADD = ${top_builddir}/lib/libopkele.la
9 9
10EXTRA_DIST= \ 10EXTRA_DIST= \
11 sqlite.h \ 11 sqlite.h kingate_openid_message.h \
12 RP-db.sql 12 RP-db.sql OP-db.sql
13 13
14idiscover_SOURCES = idiscover.cc 14idiscover_SOURCES = idiscover.cc
15idiscover_LDADD = ${top_builddir}/lib/libopkele.la 15idiscover_LDADD = ${top_builddir}/lib/libopkele.la
@@ -31,8 +31,20 @@ RP-db.cc: RP-db.sql
31 echo ';' \ 31 echo ';' \
32 ) >$@ 32 ) >$@
33 33
34OP_cgi_SOURCES = OP.cc
35nodist_OP_cgi_SOURCES = OP-db.cc
36OP_cgi_LDADD = ${RP_cgi_LDADD}
37OP_cgi_CFLAGS = ${RP_cgi_CFLAGS}
38
39OP-db.cc: OP-db.sql
40 ( \
41 echo 'const char * __OP_db_bootstrap = ' && \
42 sed -e 's/^/"/' -e 's/$$/"/' $< && \
43 echo ';' \
44 ) >$@
45
34clean-local: 46clean-local:
35 rm -f RP-db.cc 47 rm -f RP-db.cc OP-db.cc
36 48
37endif #HAVE_UUID 49endif #HAVE_UUID
38endif #HAVE_KINGATE 50endif #HAVE_KINGATE
diff --git a/test/OP-db.sql b/test/OP-db.sql
new file mode 100644
index 0000000..424b5ff
--- a/dev/null
+++ b/test/OP-db.sql
@@ -0,0 +1,25 @@
1CREATE TABLE assoc (
2 a_op text,
3 a_handle text NOT NULL,
4 a_type text DEFAULT 'HMAC-SHA1',
5 a_ctime text NOT NULL,
6 a_etime text NOT NULL,
7 a_secret text NOT NULL,
8 a_stateless integer NOT NULL DEFAULT 0,
9 a_itime integer,
10 UNIQUE(a_op,a_handle)
11);
12
13CREATE TABLE nonces (
14 n_once text NOT NULL PRIMARY KEY,
15 n_itime integer
16);
17
18CREATE TABLE setup (
19 s_password text
20);
21
22CREATE TABLE ht_sessions (
23 hts_id text NOT NULL PRIMARY KEY,
24 authorized integer NOT NULL DEFAULT 0
25);
diff --git a/test/OP.cc b/test/OP.cc
new file mode 100644
index 0000000..1196c0c
--- a/dev/null
+++ b/test/OP.cc
@@ -0,0 +1,409 @@
1#include <uuid/uuid.h>
2#include <iostream>
3#include <cassert>
4#include <string>
5#include <ext/algorithm>
6using namespace std;
7#include <kingate/exception.h>
8#include <kingate/plaincgi.h>
9#include <kingate/cgi_gateway.h>
10#include <opkele/exception.h>
11#include <opkele/util.h>
12#include <opkele/uris.h>
13#include <opkele/extension.h>
14#include <opkele/association.h>
15#include <opkele/debug.h>
16#include <opkele/verify_op.h>
17
18#include "sqlite.h"
19#include "kingate_openid_message.h"
20
21static const string get_self_url(const kingate::cgi_gateway& gw) {
22 bool s = gw.has_meta("SSL_PROTOCOL_VERSION");
23 string rv = s?"https://":"http://";
24 rv += gw.http_request_header("Host");
25 const string& port = gw.get_meta("SERVER_PORT");
26 if( port!=(s?"443":"80") ) {
27 rv += ':'; rv += port;
28 }
29 rv += gw.get_meta("REQUEST_URI");
30 string::size_type q = rv.find('?');
31 if(q!=string::npos)
32 rv.erase(q);
33 return rv;
34}
35
36class opdb_t : public sqlite3_t {
37 public:
38 opdb_t()
39 : sqlite3_t("/tmp/OP.db") {
40 assert(_D);
41 char **resp; int nr,nc; char *errm;
42 if(sqlite3_get_table(
43 _D, "SELECT a_op FROM assoc LIMIT 0",
44 &resp,&nr,&nc,&errm)!=SQLITE_OK) {
45 extern const char *__OP_db_bootstrap;
46 DOUT_("Bootstrapping DB");
47 if(sqlite3_exec(_D,__OP_db_bootstrap,NULL,NULL,&errm)!=SQLITE_OK)
48 throw opkele::exception(OPKELE_CP_ string("Failed to boostrap SQLite database: ")+errm);
49 }else
50 sqlite3_free_table(resp);
51 }
52};
53
54class example_op_t : public opkele::verify_op {
55 public:
56 kingate::cgi_gateway& gw;
57 opdb_t db;
58 kingate::cookie htc;
59
60
61 example_op_t(kingate::cgi_gateway& gw)
62 : gw(gw) {
63 try {
64 htc = gw.cookies.get_cookie("htop_session");
65 sqlite3_mem_t<char*> S = sqlite3_mprintf(
66 "SELECT 1 FROM ht_sessions WHERE hts_id=%Q",
67 htc.get_value().c_str());
68 sqlite3_table_t T; int nr,nc;
69 db.get_table(S,T,&nr,&nc);
70 if(nr<1)
71 throw kingate::exception_notfound(CODEPOINT,"forcing cookie generation");
72 }catch(kingate::exception_notfound& kenf) {
73 uuid_t uuid; uuid_generate(uuid);
74 htc = kingate::cookie("htop_session",opkele::util::encode_base64(uuid,sizeof(uuid)));
75 sqlite3_mem_t<char*> S = sqlite3_mprintf(
76 "INSERT INTO ht_sessions (hts_id) VALUES (%Q)",
77 htc.get_value().c_str());
78 db.exec(S);
79 }
80 }
81
82 void set_authorized(bool a) {
83 sqlite3_mem_t<char*>
84 S = sqlite3_mprintf(
85 "UPDATE ht_sessions"
86 " SET authorized=%d"
87 " WHERE hts_id=%Q",
88 (int)a,htc.get_value().c_str());
89 db.exec(S);
90 }
91 bool get_authorized() {
92 sqlite3_mem_t<char*>
93 S = sqlite3_mprintf(
94 "SELECT authorized"
95 " FROM ht_sessions"
96 " WHERE hts_id=%Q",
97 htc.get_value().c_str());
98 sqlite3_table_t T; int nr,nc;
99 db.get_table(S,T,&nr,&nc);
100 assert(nr==1); assert(nc=1);
101 return opkele::util::string_to_long(T.get(1,0,nc));
102 }
103
104 ostream& cookie_header(ostream& o) const {
105 o << "Set-Cookie: " << htc.set_cookie_header() << "\n";
106 return o;
107 }
108
109 opkele::assoc_t alloc_assoc(const string& type,size_t klength,bool sl) {
110 uuid_t uuid; uuid_generate(uuid);
111 string a_handle = opkele::util::encode_base64(uuid,sizeof(uuid));
112 opkele::secret_t a_secret;
113 generate_n(
114 back_insert_iterator<opkele::secret_t>(a_secret),klength,
115 rand );
116 string ssecret; a_secret.to_base64(ssecret);
117 time_t now = time(0);
118 int expires_in = sl?3600*2:3600*24*7*2;
119 sqlite3_mem_t<char*>
120 S = sqlite3_mprintf(
121 "INSERT INTO assoc"
122 " (a_handle,a_type,a_ctime,a_etime,a_secret,a_stateless)"
123 " VALUES ("
124 " %Q,%Q,datetime('now'),"
125 " datetime('now','+%d seconds'),"
126 " %Q,%d );",
127 a_handle.c_str(), type.c_str(),
128 expires_in,
129 ssecret.c_str(), sl );
130 db.exec(S);
131 return opkele::assoc_t(new opkele::association(
132 "",
133 a_handle, type, a_secret,
134 now+expires_in, sl ));
135 }
136
137 opkele::assoc_t retrieve_assoc(const string& h) {
138 sqlite3_mem_t<char*>
139 S = sqlite3_mprintf(
140 "SELECT"
141 " a_handle,a_type,a_secret,a_stateless,"
142 " strftime('%%s',a_etime) AS a_etime,"
143 " a_itime"
144 " FROM assoc"
145 " WHERE a_handle=%Q AND a_itime IS NULL"
146 " AND datetime('now') < a_etime"
147 " LIMIT 1",
148 h.c_str() );
149 sqlite3_table_t T;
150 int nr,nc;
151 db.get_table(S,T,&nr,&nc);
152 if(nr<1)
153 throw opkele::failed_lookup(OPKELE_CP_
154 "couldn't retrieve valid unexpired assoc");
155 assert(nr==1); assert(nc==6);
156 opkele::secret_t secret; opkele::util::decode_base64(T.get(1,2,nc),secret);
157 return opkele::assoc_t(new opkele::association(
158 "", h, T.get(1,1,nc), secret,
159 strtol(T.get(1,4,nc),0,0),
160 strtol(T.get(1,3,nc),0,0) ));
161 }
162
163 string& alloc_nonce(string& nonce,bool stateless) {
164 uuid_t uuid; uuid_generate(uuid);
165 nonce += opkele::util::encode_base64(uuid,sizeof(uuid));
166 sqlite3_mem_t<char*>
167 S = sqlite3_mprintf(
168 "INSERT INTO nonces"
169 " (n_once) VALUES (%Q)",
170 nonce.c_str() );
171 db.exec(S);
172 return nonce;
173 }
174 bool check_nonce(const string& nonce) {
175 sqlite3_mem_t<char*>
176 S = sqlite3_mprintf(
177 "SELECT 1"
178 " FROM nonces"
179 " WHERE n_once=%Q AND n_itime IS NULL",
180 nonce.c_str());
181 sqlite3_table_t T;
182 int nr,nc;
183 db.get_table(S,T,&nr,&nc);
184 return nr>=1;
185 }
186 void invalidate_nonce(const string& nonce) {
187 sqlite3_mem_t<char*>
188 S = sqlite3_mprintf(
189 "UPDATE nonces"
190 " SET n_itime=datetime('now')"
191 " WHERE n_once=%Q",
192 nonce.c_str());
193 db.exec(S);
194 }
195
196 const string get_op_endpoint() const {
197 return get_self_url(gw);
198 }
199
200};
201
202int main(int argc,char *argv[]) {
203 try {
204 kingate::plaincgi_interface ci;
205 kingate::cgi_gateway gw(ci);
206 string op;
207 try { op = gw.get_param("op"); }catch(kingate::exception_notfound&) { }
208 string message;
209 if(op=="set_password") {
210 example_op_t OP(gw);
211 string password = gw.get_param("password");
212 sqlite3_mem_t<char*>
213 Sget = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1");
214 sqlite3_table_t T; int nr,nc;
215 OP.db.get_table(Sget,T,&nr,&nc);
216 if(nr>=1)
217 throw opkele::exception(OPKELE_CP_ "Password already set");
218 sqlite3_mem_t<char*>
219 Sset = sqlite3_mprintf(
220 "INSERT INTO setup (s_password) VALUES (%Q)",
221 password.c_str());
222 OP.db.exec(Sset);
223 op.clear();
224 message = "password set";
225 }else if(op=="login") {
226 example_op_t OP(gw);
227 string password = gw.get_param("password");
228 sqlite3_mem_t<char*>
229 Sget = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1");
230 sqlite3_table_t T; int nr,nc;
231 OP.db.get_table(Sget,T,&nr,&nc);
232 if(nr<1)
233 throw opkele::exception(OPKELE_CP_ "no password set");
234 if(password!=T.get(1,0,nc))
235 throw opkele::exception(OPKELE_CP_ "wrong password");
236 OP.set_authorized(true);
237 op.clear();
238 message = "logged in";
239 OP.cookie_header(cout);
240 }else if(op=="logout") {
241 example_op_t OP(gw);
242 OP.set_authorized(false);
243 op.clear();
244 message = "logged out";
245 }
246 string om;
247 try { om = gw.get_param("openid.mode"); }catch(kingate::exception_notfound&) { }
248 if(op=="xrds") {
249 cout <<
250 "Content-type: application/xrds+xml\n\n"
251 "<?xml version='1.0' encoding='utf-8'?>"
252 "<xrds:XRDS xmlns:xrds='xri://$xrds' xmlns='xri://$xrd*($v*2.0)'>"
253 "<XRD>"
254 "<Service>"
255 "<Type>" STURI_OPENID20 "</Type>"
256 "<URI>" << get_self_url(gw) << "</URI>"
257 "</Service>";
258 if(gw.has_param("idsel")){
259 cout <<
260 "<Service>"
261 "<Type>" STURI_OPENID20_OP "</Type>"
262 "<URI>" << get_self_url(gw) << "</URI>";
263 }
264 cout <<
265 "</XRD>"
266 "</xrds:XRDS>";
267 }else if(op=="id_res" || op=="cancel") {
268 kingate_openid_message_t inm(gw);
269 example_op_t OP(gw);
270 if(gw.get_param("hts_id")!=OP.htc.get_value())
271 throw opkele::exception(OPKELE_CP_ "toying around, huh?");
272 OP.checkid_(inm,0);
273 OP.cookie_header(cout);
274 opkele::openid_message_t om;
275 if(op=="id_res") {
276 if(!OP.get_authorized())
277 throw opkele::exception(OPKELE_CP_ "not logged in");
278 if(OP.is_id_select()) {
279 OP.select_identity( get_self_url(gw), get_self_url(gw) );
280 }
281 cout <<
282 "Status: 302 Going back to RP with id_res\n"
283 "Location: " << OP.id_res(om).append_query(OP.get_return_to())
284 << "\n\n";
285 }else{
286 cout <<
287 "Status: 302 Going back to RP with cancel\n"
288 "Location: " << OP.cancel(om).append_query(OP.get_return_to())
289 << "\n\n";
290 }
291 om.to_keyvalues(clog);
292 }else if(om=="associate") {
293 kingate_openid_message_t inm(gw);
294 opkele::openid_message_t oum;
295 example_op_t OP(gw);
296 OP.associate(oum,inm);
297 cout << "Content-type: text/plain\n\n";
298 oum.to_keyvalues(cout);
299 }else if(om=="checkid_setup") {
300 kingate_openid_message_t inm(gw);
301 example_op_t OP(gw);
302 OP.checkid_(inm,0);
303 OP.cookie_header(cout) <<
304 "Content-type: text/html\n"
305 "\n"
306
307 "<html>"
308 "<head>"
309 "<title>test OP: confirm authentication</title>"
310 "</head>"
311 "<body>"
312 "realm: " << OP.get_realm() << "<br/>"
313 "return_to: " << OP.get_return_to() << "<br/>"
314 "claimed_id: " << OP.get_claimed_id() << "<br/>"
315 "identity: " << OP.get_identity() << "<br/>";
316 if(OP.is_id_select()) {
317 OP.select_identity( get_self_url(gw), get_self_url(gw) );
318 cout <<
319 "selected claimed_id: " << OP.get_claimed_id() << "<br/>"
320 "selected identity: " << OP.get_identity() << "<br/>";
321 }
322 cout <<
323 "<form method='post'>";
324 inm.to_htmlhiddens(cout);
325 cout <<
326 "<input type='hidden' name='hts_id'"
327 " value='" << opkele::util::attr_escape(OP.htc.get_value()) << "'/>"
328 "<input type='submit' name='op' value='id_res'/>"
329 "<input type='submit' name='op' value='cancel'/>"
330 "</form>"
331 "</body>"
332 "</html>";
333 }else if(om=="check_authentication") {
334 kingate_openid_message_t inm(gw);
335 example_op_t OP(gw);
336 opkele::openid_message_t oum;
337 OP.check_authentication(oum,inm);
338 cout << "Content-type: text/plain\n\n";
339 oum.to_keyvalues(cout);
340 oum.to_keyvalues(clog);
341 }else{
342 example_op_t OP(gw);
343 string idsel;
344 if(gw.has_param("idsel"))
345 idsel = "&idsel=idsel";
346 OP.cookie_header(cout) <<
347 "Content-type: text/html\n"
348 "X-XRDS-Location: " << get_self_url(gw) << "?op=xrds" << idsel << "\n"
349 "\n"
350
351 "<html>"
352 "<head>"
353 "<title>test OP</title>"
354 "<link rel='openid.server' href='" << get_self_url(gw) << "'/>"
355 "</head>"
356 "<body>"
357 "test openid 2.0 endpoint"
358 "<br/>"
359 "<a href='" << get_self_url(gw) << "?op=xrds" << idsel << "'>XRDS document</a>"
360 "<br/>"
361 "<h1>" << message << "</h1>";
362 sqlite3_mem_t<char*>
363 S = sqlite3_mprintf("SELECT s_password FROM setup LIMIT 1");
364 sqlite3_table_t T; int nr,nc;
365 OP.db.get_table(S,T,&nr,&nc);
366 if(nr<1) {
367 cout <<
368 "<form method='post'>"
369 "set password "
370 "<input type='hidden' name='op' value='set_password'/>"
371 "<input type='password' name='password' value=''/>"
372 "<input type='submit' name='submit' value='submit'/>"
373 "</form>";
374 }else if(OP.get_authorized()) {
375 cout <<
376 "<br/>"
377 "<a href='" << get_self_url(gw) << "?op=logout'>logout</a>";
378 }else{
379 cout <<
380 "<form method='post'>"
381 "login "
382 "<input type='hidden' name='op' value='login'/>"
383 "<input type='password' name='password' value=''/>"
384 "<input type='submit' name='submit' value='submit'/>"
385 "</form>";
386 }
387 cout << "</body>";
388 }
389#ifdef OPKELE_HAVE_KONFORKA
390 }catch(konforka::exception& e) {
391#else
392 }catch(std::exception& e){
393#endif
394 DOUT_("Oops: " << e.what());
395 cout << "Content-Type: text/plain\n\n"
396 "Exception:\n"
397 " what: " << e.what() << endl;
398#ifdef OPKELE_HAVE_KONFORKA
399 cout << " where: " << e.where() << endl;
400 if(!e._seen.empty()) {
401 cout << " seen:" << endl;
402 for(list<konforka::code_point>::const_iterator
403 i=e._seen.begin();i!=e._seen.end();++i) {
404 cout << " " << i->c_str() << endl;
405 }
406 }
407#endif
408 }
409}