1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
|
#include <uuid/uuid.h>
#include <iostream>
#include <cassert>
#include <cstdlib>
#include <stdexcept>
#include <string>
#include <set>
#include <iterator>
using namespace std;
#include <kingate/exception.h>
#include <kingate/plaincgi.h>
#include <kingate/cgi_gateway.h>
#include <opkele/exception.h>
#include <opkele/types.h>
#include <opkele/util.h>
#include <opkele/uris.h>
#include <opkele/discovery.h>
#include <opkele/association.h>
#include <opkele/sreg.h>
using namespace opkele;
#include <opkele/prequeue_rp.h>
#include <opkele/debug.h>
#include "sqlite.h"
#include "kingate_openid_message.h"
#undef DUMB_RP
#ifdef DUMB_RP
# define DUMBTHROW throw opkele::dumb_RP(OPKELE_CP_ "This RP is dumb")
#else
# define DUMBTHROW (void)0
#endif
class rpdb_t : public sqlite3_t {
public:
rpdb_t()
: sqlite3_t("/tmp/RP.db") {
assert(_D);
char **resp; int nrow,ncol; char *errm;
if(sqlite3_get_table(
_D,"SELECT a_op FROM assoc LIMIT 0",
&resp,&nrow,&ncol,&errm)!=SQLITE_OK) {
extern const char *__RP_db_bootstrap;
DOUT_("Bootstrapping DB");
if(sqlite3_exec(_D,__RP_db_bootstrap,NULL,NULL,&errm)!=SQLITE_OK)
throw opkele::exception(OPKELE_CP_ string("Failed to bootstrap SQLite database: ")+errm);
}else
sqlite3_free_table(resp);
}
};
class example_rp_t : public opkele::prequeue_RP {
public:
mutable rpdb_t db;
kingate::cookie htc;
long as_id;
int ordinal;
kingate::cgi_gateway& gw;
example_rp_t(kingate::cgi_gateway& g)
: as_id(-1), ordinal(0), gw(g), have_eqtop(false) {
try {
htc = gw.cookies.get_cookie("ht_session");
as_id = opkele::util::string_to_long(gw.get_param("asid"));
}catch(kingate::exception_notfound& kenf) {
uuid_t uuid; uuid_generate(uuid);
htc = kingate::cookie("ht_session",util::encode_base64(uuid,sizeof(uuid)));
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"INSERT INTO ht_sessions (hts_id) VALUES (%Q)",
htc.get_value().c_str());
db.exec(S);
}
}
/* Global persistent store */
opkele::assoc_t store_assoc(
const string& OP,const string& handle,
const string& type,const secret_t& secret,
int expires_in) {
DUMBTHROW;
DOUT_("Storing '" << handle << "' assoc with '" << OP << "'");
time_t exp = time(0)+expires_in;
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"INSERT INTO assoc"
" (a_op,a_handle,a_type,a_ctime,a_etime,a_secret)"
" VALUES ("
" %Q,%Q,%Q,"
" datetime('now'), datetime('now','+%d seconds'),"
" %Q"
" );", OP.c_str(), handle.c_str(), type.c_str(),
expires_in,
util::encode_base64(&(secret.front()),secret.size()).c_str() );
db.exec(S);
return opkele::assoc_t(new opkele::association(
OP, handle, type, secret, exp, false ));
}
opkele::assoc_t find_assoc(
const string& OP) {
DUMBTHROW;
DOUT_("Looking for an assoc with '" << OP << '\'');
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT"
" a_op,a_handle,a_type,a_secret,"
" strftime('%%s',a_etime) AS a_etime"
" FROM assoc"
" WHERE a_op=%Q AND a_itime IS NULL AND NOT a_stateless"
" AND ( a_etime > datetime('now','-30 seconds') )"
" LIMIT 1",
OP.c_str());
sqlite3_table_t T;
int nr,nc;
db.get_table(S,T,&nr,&nc);
if(nr<1)
throw opkele::failed_lookup(OPKELE_CP_ "Couldn't find unexpired handle");
assert(nr==1);
assert(nc==5);
secret_t secret;
util::decode_base64(T.get(1,3,nc),secret);
DOUT_(" found '" << T.get(1,1,nc) << '\'');
return opkele::assoc_t(new opkele::association(
T.get(1,0,nc), T.get(1,1,nc), T.get(1,2,nc),
secret, strtol(T.get(1,4,nc),0,0), false ));
}
opkele::assoc_t retrieve_assoc(
const string& OP,const string& handle) {
DUMBTHROW;
DOUT_("Retrieving assoc '" << handle << "' with '" << OP << '\'');
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT"
" a_op,a_handle,a_type,a_secret,"
" strftime('%%s',a_etime) AS a_etime"
" FROM assoc"
" WHERE a_op=%Q AND a_handle=%Q"
" AND a_itime IS NULL AND NOT a_stateless"
" LIMIT 1",
OP.c_str(),handle.c_str());
sqlite3_table_t T;
int nr,nc;
db.get_table(S,T,&nr,&nc);
if(nr<1)
throw opkele::failed_lookup(OPKELE_CP_ "couldn't retrieve valid association");
assert(nr==1); assert(nc==5);
secret_t secret; util::decode_base64(T.get(1,3,nc),secret);
DOUT_(" found. type=" << T.get(1,2,nc) << '\'');
return opkele::assoc_t(new opkele::association(
T.get(1,0,nc), T.get(1,1,nc), T.get(1,2,nc),
secret, strtol(T.get(1,4,nc),0,0), false ));
}
void invalidate_assoc(
const string& OP,const string& handle) {
DUMBTHROW;
DOUT_("Invalidating assoc '" << handle << "' with '" << OP << '\'');
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"UPDATE assoc SET a_itime=datetime('now')"
" WHERE a_op=%Q AND a_handle=%Q",
OP.c_str(), handle.c_str() );
db.exec(S);
}
void check_nonce(const string& OP,const string& nonce) {
DOUT_("Checking nonce '" << nonce << "' from '" << OP << '\'');
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT 1 FROM nonces WHERE n_op=%Q AND n_once=%Q",
OP.c_str(), nonce.c_str());
sqlite3_table_t T;
int nr,nc;
db.get_table(S,T,&nr,&nc);
if(nr)
throw opkele::id_res_bad_nonce(OPKELE_CP_ "already seen that nonce");
sqlite3_mem_t<char*>
SS = sqlite3_mprintf(
"INSERT INTO nonces (n_op,n_once) VALUES (%Q,%Q)",
OP.c_str(), nonce.c_str());
db.exec(SS);
}
/* Session perisistent store */
void begin_queueing() {
assert(as_id>=0);
DOUT_("Resetting queue for session '" << htc.get_value() << "'/" << as_id);
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"DELETE FROM endpoints_queue"
" WHERE as_id=%ld",
as_id);
db.exec(S);
}
void queue_endpoint(const opkele::openid_endpoint_t& ep) {
assert(as_id>=0);
DOUT_("Queueing endpoint " << ep.claimed_id << " : " << ep.local_id << " @ " << ep.uri);
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"INSERT INTO endpoints_queue"
" (as_id,eq_ctime,eq_ordinal,eq_uri,eq_claimed_id,eq_local_id)"
" VALUES (%ld,strftime('%%s','now'),%d,%Q,%Q,%Q)",
as_id,ordinal++,
ep.uri.c_str(),ep.claimed_id.c_str(),ep.local_id.c_str());
db.exec(S);
}
mutable openid_endpoint_t eqtop;
mutable bool have_eqtop;
const openid_endpoint_t& get_endpoint() const {
assert(as_id>=0);
if(!have_eqtop) {
sqlite3_mem_t<char*>
S = sqlite3_mprintf(
"SELECT"
" eq_uri, eq_claimed_id, eq_local_id"
" FROM endpoints_queue"
" JOIN auth_sessions USING(as_id)"
" WHERE hts_id=%Q AND as_id=%ld"
" ORDER BY eq_ctime,eq_ordinal"
" LIMIT 1",htc.get_value().c_str(),as_id);
sqlite3_table_t T; int nr,nc;
db.get_table(S,T,&nr,&nc);
if(nr<1)
throw opkele::exception(OPKELE_CP_ "No more endpoints queued");
assert(nr==1); assert(nc==3);
eqtop.uri = T.get(1,0,nc);
eqtop.claimed_id = T.get(1,1,nc);
eqtop.local_id = T.get(1,2,nc);
have_eqtop = true;
}
return eqtop;
}
void next_endpoint() {
assert(as_id>=0);
get_endpoint();
have_eqtop = false;
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"DELETE FROM endpoints_queue"
" WHERE as_id=%ld AND eq_uri=%Q AND eq_local_id=%Q",
htc.get_value().c_str(),as_id,
eqtop.uri.c_str());
db.exec(S);
}
mutable string _nid;
void set_normalized_id(const string& nid) {
assert(as_id>=0);
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"UPDATE auth_sessions"
" SET as_normalized_id=%Q"
" WHERE hts_id=%Q and as_id=%ld",
nid.c_str(),
htc.get_value().c_str(),as_id);
db.exec(S);
_nid = nid;
}
const string get_normalized_id() const {
assert(as_id>=0);
if(_nid.empty()) {
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"SELECT as_normalized_id"
" FROM"
" auth_sessions"
" WHERE"
" hts_id=%Q AND as_id=%ld",
htc.get_value().c_str(),as_id);
sqlite3_table_t T; int nr,nc;
db.get_table(S,T,&nr,&nc);
assert(nr==1); assert(nc==1);
_nid = T.get(1,0,nc);
}
return _nid;
}
const string get_this_url() const {
bool s = gw.has_meta("SSL_PROTOCOL_VERSION");
string rv = s?"https://":"http://";
rv += gw.http_request_header("Host");
const string& port = gw.get_meta("SERVER_PORT");
if( port!=(s?"443":"80") ) {
rv += ':'; rv += port;
}
rv += gw.get_meta("REQUEST_URI");
return rv;
}
void initiate(const string& usi) {
allocate_asid();
prequeue_RP::initiate(usi);
}
string get_self_url() const {
string rv = get_this_url();
string::size_type q = rv.find('?');
if(q!=string::npos)
rv.erase(q);
return rv;
}
void allocate_asid() {
sqlite3_mem_t<char*> S = sqlite3_mprintf(
"INSERT INTO auth_sessions (hts_id)"
" VALUES (%Q)",
htc.get_value().c_str());
db.exec(S);
as_id = sqlite3_last_insert_rowid(db);
DOUT_("Allocated authentication session id "<<as_id);
assert(as_id>=0);
}
#ifdef DUMB_RP
virtual assoc_t associate(const string& OP) {
DUMBTHROW;
}
#endif
};
int main(int,char **) {
try {
kingate::plaincgi_interface ci;
kingate::cgi_gateway gw(ci);
string op;
try { op = gw.get_param("op"); }catch(kingate::exception_notfound&) { }
if(op=="initiate") {
example_rp_t rp(gw);
string usi = gw.get_param("openid_identity");
rp.initiate(usi);
opkele::sreg_t sreg(opkele::sreg_t::fields_NONE,opkele::sreg_t::fields_ALL);
opkele::openid_message_t cm;
string loc;
cout <<
"Set-Cookie: " << rp.htc.set_cookie_header() << "\n"
"Status: 302 Going to OP\n"
"Location: " << (
loc = rp.checkid_(cm,opkele::mode_checkid_setup,
rp.get_self_url()+
"?op=confirm&asid="+opkele::util::long_to_string(rp.as_id),
rp.get_self_url(),&sreg).append_query(rp.get_endpoint().uri)
)
<< "\n\n";
DOUT_("Going to " << loc);
}else if(op=="confirm") {
kingate_openid_message_t om(gw);
example_rp_t rp(gw);
opkele::sreg_t sreg(opkele::sreg_t::fields_NONE,opkele::sreg_t::fields_ALL);
rp.id_res(om,&sreg);
cout <<
"Content-Type: text/plain\n\n";
for(opkele::basic_openid_message::fields_iterator i=om.fields_begin();
i!=om.fields_end();++i) {
cout << *i << '=' << om.get_field(*i) << endl;
}
cout << endl
<< "SREG fields: " << sreg.has_fields << endl;
}else{
cout <<
"Content-type: text/html\n\n"
"<html>"
"<head><title>test RP</title></head>"
"<body>"
"<form action='' method='post'>"
"<input type='hidden' name='op' value='initiate' />"
"<input type='text' name='openid_identity'/>"
"<input type='submit' name='submit' value='submit' />"
"</form>"
"<br/><br/>"
"<a href='?op=initiate&openid_identity=www.myopenid.com&dummy=" << time(0) << "'>login with myopenid.com account</a>"
"<br/>"
"</body"
"</html>"
;
}
#ifdef OPKELE_HAVE_KONFORKA
}catch(konforka::exception& e) {
#else
}catch(std::exception& e){
#endif
DOUT_("Oops: " << e.what());
cout << "Content-Type: text/plain\n\n"
"Exception:\n"
" what: " << e.what() << endl;
#ifdef OPKELE_HAVE_KONFORKA
cout << " where: " << e.where() << endl;
if(!e._seen.empty()) {
cout << " seen:" << endl;
for(list<konforka::code_point>::const_iterator
i=e._seen.begin();i!=e._seen.end();++i) {
cout << " " << i->c_str() << endl;
}
}
#endif
}
}
|