diff options
author | jsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68> | 2010-09-19 00:09:52 +0000 |
---|---|---|
committer | jsdelfino <jsdelfino@13f79535-47bb-0310-9956-ffa450edef68> | 2010-09-19 00:09:52 +0000 |
commit | ddf34aa2e69d6c90b4fe95516168254a80eb32f3 (patch) | |
tree | 7e5273c1e222b2cb8d523f2bc4087fe15bc9bcb2 /sca-cpp/trunk/modules | |
parent | b796454ac3a94879f122cc19ddec5a7ff87fe5ee (diff) |
Improvements to the oauth module and support for oauth 1.0a using liboauth.
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@998571 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'sca-cpp/trunk/modules')
17 files changed, 973 insertions, 189 deletions
diff --git a/sca-cpp/trunk/modules/http/httpd.hpp b/sca-cpp/trunk/modules/http/httpd.hpp index 870aed71b7..528404b8fc 100644 --- a/sca-cpp/trunk/modules/http/httpd.hpp +++ b/sca-cpp/trunk/modules/http/httpd.hpp @@ -245,6 +245,8 @@ const string unescape(const string& uri) { const list<value> queryArg(const string& s) { debug(s, "httpd::queryArg::string"); const list<string> t = tokenize("=", s); + if (isNil(cdr(t))) + return mklist<value>(c_str(car(t)), ""); return mklist<value>(c_str(car(t)), cadr(t)); } diff --git a/sca-cpp/trunk/modules/json/json.hpp b/sca-cpp/trunk/modules/json/json.hpp index bade096b86..fdf752a362 100644 --- a/sca-cpp/trunk/modules/json/json.hpp +++ b/sca-cpp/trunk/modules/json/json.hpp @@ -166,6 +166,8 @@ const list<value> jsPropertiesToValues(const list<value>& propertiesSoFar, JSObj jsval idv; JS_IdToValue(cx, id, &idv); if(JSVAL_IS_STRING(idv)) { + if (isNil(val) && !isList(val)) + return jsPropertiesToValues(propertiesSoFar, o, i, cx); const string name = JS_GetStringBytes(JSVAL_TO_STRING(idv)); if (substr(name, 0, 1) == atsign) return jsPropertiesToValues(cons<value>(mklist<value>(attribute, c_str(substr(name, 1)), val), propertiesSoFar), o, i, cx); @@ -194,6 +196,8 @@ const value jsValToValue(const jsval& jsv, const JSONContext& cx) { } case JSTYPE_OBJECT: { JSObject* o = JSVAL_TO_OBJECT(jsv); + if (o == NULL) + return value(); JSObject* i = JS_NewPropertyIterator(cx, o); if(i == NULL) return value(list<value> ()); diff --git a/sca-cpp/trunk/modules/oauth/Makefile.am b/sca-cpp/trunk/modules/oauth/Makefile.am index 077b50bddf..84bbe32eec 100644 --- a/sca-cpp/trunk/modules/oauth/Makefile.am +++ b/sca-cpp/trunk/modules/oauth/Makefile.am @@ -17,18 +17,23 @@ if WANT_OAUTH -INCLUDES = -I${HTTPD_INCLUDE} +INCLUDES = -I${HTTPD_INCLUDE} -I${LIBOAUTH_INCLUDE} -dist_mod_SCRIPTS = oauth-conf oauth-memcached-conf oauth-app-conf +dist_mod_SCRIPTS = oauth-conf oauth-memcached-conf oauth1-appkey-conf oauth2-appkey-conf moddir=$(prefix)/modules/oauth -mod_LTLIBRARIES = libmod_tuscany_oauth.la -noinst_DATA = libmod_tuscany_oauth.so +mod_LTLIBRARIES = libmod_tuscany_oauth1.la libmod_tuscany_oauth2.la +noinst_DATA = libmod_tuscany_oauth1.so libmod_tuscany_oauth2.so -libmod_tuscany_oauth_la_SOURCES = mod-oauth.cpp -libmod_tuscany_oauth_la_LDFLAGS = -lxml2 -lcurl -lmozjs -libmod_tuscany_oauth.so: - ln -s .libs/libmod_tuscany_oauth.so +libmod_tuscany_oauth1_la_SOURCES = mod-oauth1.cpp +libmod_tuscany_oauth1_la_LDFLAGS = -L${LIBOAUTH_LIB} -R${LIBOAUTH_LIB} -loauth -lxml2 -lcurl -lmozjs +libmod_tuscany_oauth1.so: + ln -s .libs/libmod_tuscany_oauth1.so + +libmod_tuscany_oauth2_la_SOURCES = mod-oauth2.cpp +libmod_tuscany_oauth2_la_LDFLAGS = -lxml2 -lcurl -lmozjs +libmod_tuscany_oauth2.so: + ln -s .libs/libmod_tuscany_oauth2.so EXTRA_DIST = oauth.composite user-info.scm htdocs/index.html htdocs/login/index.html htdocs/logout/index.html htdocs/unprotected/index.html diff --git a/sca-cpp/trunk/modules/oauth/htdocs/index.html b/sca-cpp/trunk/modules/oauth/htdocs/index.html index e6295a93b5..dd75b736db 100644 --- a/sca-cpp/trunk/modules/oauth/htdocs/index.html +++ b/sca-cpp/trunk/modules/oauth/htdocs/index.html @@ -25,16 +25,28 @@ var component = new tuscany.sca.Component("Protected"); var userInfo = new tuscany.sca.Reference("userInfo"); var user = userInfo.apply("getuser"); var email = userInfo.apply("getemail"); +var nickname = userInfo.apply("getnickname"); +var fullname = userInfo.apply("getfullname"); +var firstname = userInfo.apply("getfirstname"); +var lastname = userInfo.apply("getlastname"); </script> </head> <body> <h1>Protected area - It works!</h1> <p>The following info is returned by a JSONRPC service:</p> -<div id="user"></div> -<div id="email"></div> +<div>User: <span id="user"></span></div> +<div>Email: <span id="email"></span></div> +<div>Nickname: <span id="nickname"></span></div> +<div>Fullname: <span id="fullname"></span></div> +<div>Firstname: <span id="firstname"></span></div> +<div>Lastname: <span id="lastname"></span></div> <script type="text/javascript"> -document.getElementById('user').innerHTML="User: " + user; -document.getElementById('email').innerHTML="Email: " + email; +document.getElementById('user').innerHTML=user; +document.getElementById('email').innerHTML=email; +document.getElementById('nickname').innerHTML=nickname; +document.getElementById('fullname').innerHTML=fullname; +document.getElementById('firstname').innerHTML=firstname; +document.getElementById('lastname').innerHTML=lastname; </script> <p><a href="info">User info</a></p> <p><a href="login">Sign in</a></p> diff --git a/sca-cpp/trunk/modules/oauth/htdocs/login/index.html b/sca-cpp/trunk/modules/oauth/htdocs/login/index.html index a8ed099fae..607c55ca18 100644 --- a/sca-cpp/trunk/modules/oauth/htdocs/login/index.html +++ b/sca-cpp/trunk/modules/oauth/htdocs/login/index.html @@ -17,7 +17,7 @@ under the License. --> -<html><body><h1>Sign in with an OAuth 2.0 provider</h1> +<html><body><h1>Sign in with an OAuth provider</h1> <script type="text/javascript"> function queryParams() { @@ -45,38 +45,70 @@ if (typeof(oauthReferrer()) == 'undefined') { document.location = '/'; } -function submitSignin(w) { +function submitSignin2(w) { parms = w(); - document.signin.mod_oauth_authorize.value = parms[0]; - document.signin.mod_oauth_access_token.value = parms[1]; - document.signin.mod_oauth_client_id.value = parms[2]; - document.signin.mod_oauth_info.value = parms[3]; - document.signin.action = oauthReferrer(); - document.signin.submit(); + document.signin2.mod_oauth2_authorize.value = parms[0]; + document.signin2.mod_oauth2_access_token.value = parms[1]; + document.signin2.mod_oauth2_client_id.value = parms[2]; + document.signin2.mod_oauth2_info.value = parms[3]; + document.signin2.action = oauthReferrer(); + document.signin2.submit(); } function withFacebook() { - var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'app1234', 'https://graph.facebook.com/me']; + var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'testfacebookapp', 'https://graph.facebook.com/me']; return parms; } function withGithub() { - var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'app2345', 'https://github.com/api/v2/json/user/show']; + var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'testgithubapp', 'https://github.com/api/v2/json/user/show']; + return parms; +} + +function submitSignin1(w) { + parms = w(); + document.signin1.mod_oauth1_request_token.value = parms[0]; + document.signin1.mod_oauth1_authorize.value = parms[1]; + document.signin1.mod_oauth1_access_token.value = parms[2]; + document.signin1.mod_oauth1_client_id.value = parms[3]; + document.signin1.mod_oauth1_info.value = parms[4]; + document.signin1.action = oauthReferrer(); + document.signin1.submit(); +} + +function withLinkedin() { + var parms = ['https://api.linkedin.com/uas/oauth/requestToken', 'https://www.linkedin.com/uas/oauth/authorize', 'https://api.linkedin.com/uas/oauth/accessToken', 'testlinkedinapp', 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,public-profile-url)']; + return parms; +} + +function withTwitter() { + var parms = ['https://api.twitter.com/oauth/request_token', 'https://api.twitter.com/oauth/authorize', 'https://api.twitter.com/oauth/access_token', 'testtwitterapp', 'https://api.twitter.com/1/statuses/user_timeline.json']; return parms; } </script> -<form name="signin" action="/" method="GET"> -<input type="hidden" name="mod_oauth_authorize" value=""/> -<input type="hidden" name="mod_oauth_access_token" value=""/> -<input type="hidden" name="mod_oauth_client_id" value=""/> -<input type="hidden" name="mod_oauth_info" value=""/> -<input type="hidden" name="mod_oauth_step" value="authorize"/> +<form name="fields"> +<p>Sign in with your Facebook account<br/><input type="button" onclick="submitSignin2(withFacebook)" value="Sign in"/></p> +<p>Sign in with your Github account<br/><input type="button" onclick="submitSignin2(withGithub)" value="Sign in"/></p> +<p>Sign in with your Linkedin account<br/><input type="button" onclick="submitSignin1(withLinkedin)" value="Sign in"/></p> +<p>Sign in with your Twitter account<br/><input type="button" onclick="submitSignin1(withTwitter)" value="Sign in"/></p> +</form> + +<form name="signin2" action="/" method="GET"> +<input type="hidden" name="mod_oauth2_authorize" value=""/> +<input type="hidden" name="mod_oauth2_access_token" value=""/> +<input type="hidden" name="mod_oauth2_client_id" value=""/> +<input type="hidden" name="mod_oauth2_info" value=""/> +<input type="hidden" name="mod_oauth2_step" value="authorize"/> </form> -<form name="fields"> -<p>Sign in with your Facebook account<br/><input type="button" onclick="submitSignin(withFacebook)" value="Sign in"/></p> -<p>Sign in with your Github account<br/><input type="button" onclick="submitSignin(withGithub)" value="Sign in"/></p> +<form name="signin1" action="/" method="GET"> +<input type="hidden" name="mod_oauth1_request_token" value=""/> +<input type="hidden" name="mod_oauth1_authorize" value=""/> +<input type="hidden" name="mod_oauth1_access_token" value=""/> +<input type="hidden" name="mod_oauth1_client_id" value=""/> +<input type="hidden" name="mod_oauth1_info" value=""/> +<input type="hidden" name="mod_oauth1_step" value="authorize"/> </form> </body></html> diff --git a/sca-cpp/trunk/modules/oauth/htdocs/login/mixed.html b/sca-cpp/trunk/modules/oauth/htdocs/login/mixed.html index 16246cc621..c187aa2a84 100644 --- a/sca-cpp/trunk/modules/oauth/htdocs/login/mixed.html +++ b/sca-cpp/trunk/modules/oauth/htdocs/login/mixed.html @@ -17,7 +17,7 @@ under the License. --> -<html><body><h1>Sign in with an OpenID or OAuth 2.0 provider</h1> +<html><body><h1>Sign in with an OpenID or OAuth provider</h1> <script type="text/javascript"> function queryParams() { @@ -91,23 +91,44 @@ function withXRDSEndpoint() { return document.fields.endpoint.value; } -function submitOAuthSignin(w) { +function submitOAuth2Signin(w) { parms = w(); - document.oauthSignin.mod_oauth_authorize.value = parms[0]; - document.oauthSignin.mod_oauth_access_token.value = parms[1]; - document.oauthSignin.mod_oauth_client_id.value = parms[2]; - document.oauthSignin.mod_oauth_info.value = parms[3]; - document.oauthSignin.action = openauthReferrer(); - document.oauthSignin.submit(); + document.oauth2Signin.mod_oauth2_authorize.value = parms[0]; + document.oauth2Signin.mod_oauth2_access_token.value = parms[1]; + document.oauth2Signin.mod_oauth2_client_id.value = parms[2]; + document.oauth2Signin.mod_oauth2_info.value = parms[3]; + document.oauth2Signin.action = openauthReferrer(); + document.oauth2Signin.submit(); } function withFacebook() { - var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'app1234', 'https://graph.facebook.com/me']; + var parms = ['https://graph.facebook.com/oauth/authorize', 'https://graph.facebook.com/oauth/access_token', 'testfacebookapp', 'https://graph.facebook.com/me']; return parms; } function withGithub() { - var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'app2345', 'https://github.com/api/v2/json/user/show']; + var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'testgithubapp', 'https://github.com/api/v2/json/user/show']; + return parms; +} + +function submitOAuth1Signin(w) { + parms = w(); + document.oauth1Signin.mod_oauth1_request_token.value = parms[0]; + document.oauth1Signin.mod_oauth1_authorize.value = parms[1]; + document.oauth1Signin.mod_oauth1_access_token.value = parms[2]; + document.oauth1Signin.mod_oauth1_client_id.value = parms[3]; + document.oauth1Signin.mod_oauth1_info.value = parms[4]; + document.oauth1Signin.action = openauthReferrer(); + document.oauth1Signin.submit(); +} + +function withLinkedin() { + var parms = ['https://api.linkedin.com/uas/oauth/requestToken', 'https://www.linkedin.com/uas/oauth/authorize', 'https://api.linkedin.com/uas/oauth/accessToken', 'testlinkedinapp', 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,public-profile-url)']; + return parms; +} + +function withTwitter() { + var parms = ['https://api.twitter.com/oauth/request_token', 'https://api.twitter.com/oauth/authorize', 'https://api.twitter.com/oauth/access_token', 'testtwitterapp', 'https://api.twitter.com/1/statuses/user_timeline.json']; return parms; } </script> @@ -139,20 +160,32 @@ function withGithub() { <input type="text" size="50" name="endpoint" value="https://www.google.com/accounts/o8/id"/><br/> <input type="button" onclick="submitOpenIDSignin(withXRDSEndpoint)" value="Sign in"/></p> -<p>Sign in with your Facebook account<br/><input type="button" onclick="submitOAuthSignin(withFacebook)" value="Sign in"/></p> -<p>Sign in with your Github account<br/><input type="button" onclick="submitOAuthSignin(withGithub)" value="Sign in"/></p> +<p>Sign in with your Facebook account<br/><input type="button" onclick="submitOAuth2Signin(withFacebook)" value="Sign in"/></p> +<p>Sign in with your Github account<br/><input type="button" onclick="submitOAuth2Signin(withGithub)" value="Sign in"/></p> + +<p>Sign in with your Linkedin account<br/><input type="button" onclick="submitOAuth1Signin(withLinkedin)" value="Sign in"/></p> +<p>Sign in with your Twitter account<br/><input type="button" onclick="submitOAuth1Signin(withTwitter)" value="Sign in"/></p> </form> <form name="openIDSignin" action="/" method="GET"> <input type="hidden" name="openid_identifier" value=""/> </form> -<form name="oauthSignin" action="/" method="GET"> -<input type="hidden" name="mod_oauth_authorize" value=""/> -<input type="hidden" name="mod_oauth_access_token" value=""/> -<input type="hidden" name="mod_oauth_client_id" value=""/> -<input type="hidden" name="mod_oauth_info" value=""/> -<input type="hidden" name="mod_oauth_step" value="authorize"/> +<form name="oauth2Signin" action="/" method="GET"> +<input type="hidden" name="mod_oauth2_authorize" value=""/> +<input type="hidden" name="mod_oauth2_access_token" value=""/> +<input type="hidden" name="mod_oauth2_client_id" value=""/> +<input type="hidden" name="mod_oauth2_info" value=""/> +<input type="hidden" name="mod_oauth2_step" value="authorize"/> +</form> + +<form name="oauth1Signin" action="/" method="GET"> +<input type="hidden" name="mod_oauth1_request_token" value=""/> +<input type="hidden" name="mod_oauth1_authorize" value=""/> +<input type="hidden" name="mod_oauth1_access_token" value=""/> +<input type="hidden" name="mod_oauth1_client_id" value=""/> +<input type="hidden" name="mod_oauth1_info" value=""/> +<input type="hidden" name="mod_oauth1_step" value="authorize"/> </form> </body></html> diff --git a/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp b/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp new file mode 100644 index 0000000000..23ef90b0dd --- /dev/null +++ b/sca-cpp/trunk/modules/oauth/mod-oauth1.cpp @@ -0,0 +1,565 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +/** + * HTTPD module for OAuth 1.0 authentication. + */ + +#include <stdlib.h> +#include <sys/stat.h> + +extern "C" { +#include <oauth.h> +} + +#include "string.hpp" +#include "stream.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../json/json.hpp" +#include "../http/httpd.hpp" +#include "../http/http.hpp" +#include "../../components/cache/memcache.hpp" +#include "oauth.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_oauth1; +} + +namespace tuscany { +namespace oauth1 { + +/** + * Server configuration. + */ +class ServerConf { +public: + ServerConf(apr_pool_t* p, server_rec* s) : p(p), server(s) { + } + + const gc_pool p; + server_rec* server; + string ca; + string cert; + string key; + list<list<value> > appkeys; + list<string> mcaddrs; + memcache::MemCached mc; + http::CURLSession cs; +}; + +/** + * Directory configuration. + */ +class DirConf { +public: + DirConf(apr_pool_t* p, char* d) : p(p), dir(d), enabled(false), login("") { + } + + const gc_pool p; + const char* dir; + bool enabled; + string login; +}; + +/** + * Check user authentication. + */ +static int checkUserID(request_rec *r) { + // Decline if we're not enabled or AuthType is not set to Open + const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_oauth1); + if (!dc.enabled) + return DECLINED; + const char* atype = ap_auth_type(r); + if (atype == NULL || strcasecmp(atype, "Open")) + return DECLINED; + + gc_scoped_pool pool(r->pool); + httpdDebugRequest(r, "modoauth1::checkUserID::input"); + return OK; +} + +/** + * Handle an authenticated request. + */ +const failable<int> authenticated(const list<list<value> >& info, request_rec* r) { + debug(info, "modoauth1::authenticated::info"); + + const list<value> id = assoc<value>("id", info); + if (isNil(id) || isNil(cdr(id))) + return mkfailure<int>("Couldn't retrieve user id"); + r->user = apr_pstrdup(r->pool, c_str(cadr(id))); + + const list<value> email = assoc<value>("email", info); + if (!isNil(email) && !isNil(cdr(email))) + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "EMAIL"), apr_pstrdup(r->pool, c_str(cadr(email)))); + + const list<value> screenname = assoc<value>("screen_name", info); + if (!isNil(screenname) && !isNil(cdr(screenname))) + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "NICKNAME"), apr_pstrdup(r->pool, c_str(cadr(screenname)))); + + const list<value> name = assoc<value>("name", info); + if (!isNil(name) && !isNil(cdr(name))) + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "FULLNAME"), apr_pstrdup(r->pool, c_str(cadr(name)))); + + const list<value> firstname = assoc<value>("first-name", info); + if (!isNil(firstname) && !isNil(cdr(firstname))) + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "FIRSTNAME"), apr_pstrdup(r->pool, c_str(cadr(firstname)))); + + const list<value> lastname = assoc<value>("last-name", info); + if (!isNil(lastname) && !isNil(cdr(lastname))) + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "LASTNAME"), apr_pstrdup(r->pool, c_str(cadr(lastname)))); + + if(r->ap_auth_type == NULL) + r->ap_auth_type = const_cast<char*>("OAuth"); + return DECLINED; +} + +/** + * Convert a query string containing oauth args to an authorization header. + */ +const string header(const string& qs, const string& redir, const string& verif) { + const list<list<value> > args = httpd::queryArgs(qs); + ostringstream hdr; + hdr << "Authorization: OAuth " + << "oauth_nonce=\"" << string(cadr(assoc<value>("oauth_nonce", args))) << "\", "; + + if (length(redir) != 0) + hdr << "oauth_callback=\"" << httpd::escape(redir) << "\", "; + + hdr << "oauth_signature_method=\"" << string(cadr(assoc<value>("oauth_signature_method", args))) << "\", " + << "oauth_timestamp=\"" << string(cadr(assoc<value>("oauth_timestamp", args))) << "\", " + << "oauth_consumer_key=\"" << string(cadr(assoc<value>("oauth_consumer_key", args))) << "\", "; + + const list<value> atok = assoc<value>("oauth_token", args); + if (!isNil(atok) && !isNil(cdr(atok))) + hdr << "oauth_token=\"" << string(cadr(atok)) << "\", "; + + if (length(verif) != 0) + hdr << "oauth_verifier=\"" << verif << "\", "; + + hdr << "oauth_signature=\"" << string(cadr(assoc<value>("oauth_signature", args))) << "\", " + << "oauth_version=\"" << string(cadr(assoc<value>("oauth_version", args))) << "\""; + debug(str(hdr), "modoauth1::authheader"); + return str(hdr); +} + + +/** + * Sign a request. + */ +const list<string> sign(const string& verb, const string& uri, const list<value> appkey, const string& tok, const string& sec) { + char* qs = NULL; + char* suri = oauth_sign_url2(c_str(uri), &qs, OA_HMAC, c_str(verb), c_str(car(appkey)), c_str(cadr(appkey)), length(tok) != 0? c_str(tok) : NULL, length(sec) != 0? c_str(sec) : NULL); + const list<string> res = mklist<string>(suri, qs); + free(suri); + free(qs); + return res; +} + +/** + * Handle an authorize request. + */ +const failable<int> authorize(const list<list<value> >& args, request_rec* r, const ServerConf& sc) { + // Extract authorize, access_token, client ID and info URIs + const list<value> req = assoc<value>("mod_oauth1_request_token", args); + if (isNil(req) || isNil(cdr(req))) + return mkfailure<int>("Missing mod_oauth1_request_token parameter"); + const list<value> auth = assoc<value>("mod_oauth1_authorize", args); + if (isNil(auth) || isNil(cdr(auth))) + return mkfailure<int>("Missing mod_oauth1_authorize parameter"); + const list<value> tok = assoc<value>("mod_oauth1_access_token", args); + if (isNil(tok) || isNil(cdr(tok))) + return mkfailure<int>("Missing mod_oauth1_access_token parameter"); + const list<value> cid = assoc<value>("mod_oauth1_client_id", args); + if (isNil(cid) || isNil(cdr(cid))) + return mkfailure<int>("Missing mod_oauth1_client_id parameter"); + const list<value> info = assoc<value>("mod_oauth1_info", args); + if (isNil(info) || isNil(cdr(info))) + return mkfailure<int>("Missing mod_oauth1_info parameter"); + + // Build the redirect URI + const list<list<value> > redirargs = mklist<list<value> >(mklist<value>("mod_oauth1_step", "access_token"), tok, cid, info); + const string redir = httpd::url(r->uri, r) + string("?") + httpd::queryString(redirargs); + debug(redir, "modoauth1::authorize::redir"); + + // Lookup client app configuration + const list<value> app = assoc<value>(cadr(cid), sc.appkeys); + if (isNil(app) || isNil(cdr(app))) + return mkfailure<int>(string("client id not found: ") + cadr(cid)); + list<value> appkey = cadr(app); + + // Build and sign the request token URI + const string requri = httpd::unescape(cadr(req)) + string("&") + httpd::queryString(mklist<list<value> >(mklist<value>("oauth_callback", httpd::escape(redir)))); + const list<string> srequri = sign("POST", requri, appkey, "", ""); + debug(srequri, "modoauth1::authorize::srequri"); + + // Put the args into an oauth header + const string reqhdr = header(cadr(srequri), redir, ""); + + // Send the request token request + char* pres = oauth_http_post2(c_str(car(srequri)), "", c_str(reqhdr)); + if (pres == NULL) + return mkfailure<int>("Couldn't send request token request"); + const string res(pres); + free(pres); + debug(res, "modoauth1::authorize::res"); + const list<list<value> > resargs = httpd::queryArgs(res); + + // Retrieve the request token + const list<value> conf = assoc<value>("oauth_callback_confirmed", resargs); + if (isNil(conf) || isNil(cdr(conf)) || cadr(conf) != "true") + return mkfailure<int>("Couldn't confirm oauth_callback"); + const list<value> tv = assoc<value>("oauth_token", resargs); + if (isNil(tv) || isNil(cdr(tv))) + return mkfailure<int>("Couldn't retrieve oauth_token"); + const list<value> sv = assoc<value>("oauth_token_secret", resargs); + if (isNil(sv) || isNil(cdr(sv))) + return mkfailure<int>("Couldn't retrieve oauth_token_secret"); + + // Store the request token in memcached + const failable<bool> prc = memcache::put(mklist<value>("tuscanyOAuth1Token", cadr(tv)), cadr(sv), sc.mc); + if (!hasContent(prc)) + return mkfailure<int>(reason(prc)); + + // Redirect to the authorize URI + const string authuri = httpd::unescape(cadr(auth)) + string("?") + httpd::queryString(mklist<list<value> >(tv)); + debug(authuri, "modoauth1::authorize::authuri"); + return httpd::externalRedirect(authuri, r); +} + +/** + * Extract user info from a profile/info response. + * TODO This currently only works for twitter and linkedin profiles and + * needs to be made configurable. + */ +const failable<list<value> > profileUserInfo(const string& info) { + if (substr(info, 0, 1) == "[") { + // JSON profile + json::JSONContext cx; + const list<value> infov(json::jsonValues(content(json::readJSON(mklist<string>(info), cx)))); + if (isNil(infov)) + return mkfailure<list<value> >("Couldn't retrieve user info"); + debug(infov, "modoauth1::access_token::info"); + const list<value> uv = assoc<value>("user", car(infov)); + if (isNil(uv) || isNil(cdr(uv))) + return mkfailure<list<value> >("Couldn't retrieve user info"); + const list<value> iv = cdr(uv); + return iv; + + } else { + // XML profile + const list<value> infov = elementsToValues(readXML(mklist<string>(info))); + if (isNil(infov)) + return mkfailure<list<value> >("Couldn't retrieve user info"); + debug(infov, "modoauth1::access_token::info"); + const list<value> pv = car(infov); + if (isNil(pv) || isNil(cdr(pv))) + return mkfailure<list<value> >("Couldn't retrieve user info"); + const list<value> iv = cdr(pv); + return iv; + } +} + +/** + * Handle an access_token request. + */ +const failable<int> access_token(const list<list<value> >& args, request_rec* r, const ServerConf& sc) { + // Extract access_token URI, client ID and verification code + const list<value> tok = assoc<value>("mod_oauth1_access_token", args); + if (isNil(tok) || isNil(cdr(tok))) + return mkfailure<int>("Missing mod_oauth1_access_token parameter"); + const list<value> cid = assoc<value>("mod_oauth1_client_id", args); + if (isNil(cid) || isNil(cdr(cid))) + return mkfailure<int>("Missing mod_oauth1_client_id parameter"); + const list<value> info = assoc<value>("mod_oauth1_info", args); + if (isNil(info) || isNil(cdr(info))) + return mkfailure<int>("Missing mod_oauth1_info parameter"); + const list<value> tv = assoc<value>("oauth_token", args); + if (isNil(tv) || isNil(cdr(tv))) + return mkfailure<int>("Missing oauth_token parameter"); + const list<value> vv = assoc<value>("oauth_verifier", args); + if (isNil(vv) || isNil(cdr(vv))) + return mkfailure<int>("Missing oauth_verifier parameter"); + + // Lookup client app configuration + const list<value> app = assoc<value>(cadr(cid), sc.appkeys); + if (isNil(app) || isNil(cdr(app))) + return mkfailure<int>(string("client id not found: ") + cadr(cid)); + list<value> appkey = cadr(app); + + // Retrieve the request token from memcached + const failable<value> sv = memcache::get(mklist<value>("tuscanyOAuth1Token", cadr(tv)), sc.mc); + if (!hasContent(sv)) + return mkfailure<int>(reason(sv)); + + // Build and sign access token request URI + const string tokuri = httpd::unescape(cadr(tok)) + string("?") + httpd::queryString(mklist<list<value> >(vv)); + const list<string> stokuri = sign("POST", tokuri, appkey, cadr(tv), content(sv)); + debug(stokuri, "modoauth1::access_token::stokuri"); + + // Put the args into an oauth header + string tokhdr = header(cadr(stokuri), "", cadr(vv)); + + // Send the access token request + char* ptokres = oauth_http_post2(c_str(car(stokuri)), "", c_str(tokhdr)); + if (ptokres == NULL) + return mkfailure<int>("Couldn't post access_token request"); + const string tokres(ptokres); + free(ptokres); + debug(tokres, "modoauth1::access_token::res"); + const list<list<value> > tokresargs = httpd::queryArgs(tokres); + + // Retrieve the access token + const list<value> atv = assoc<value>("oauth_token", tokresargs); + if (isNil(atv) || isNil(cdr(atv))) + return mkfailure<int>("Couldn't retrieve oauth_token"); + const list<value> asv = assoc<value>("oauth_token_secret", tokresargs); + if (isNil(asv) || isNil(cdr(asv))) + return mkfailure<int>("Couldn't retrieve oauth_token_secret"); + debug(atv, "modoauth1::access_token::token"); + + // Build and sign user profile request URI + const string profuri = httpd::unescape(cadr(info)); + const list<string> sprofuri = sign("GET", profuri, appkey, cadr(atv), cadr(asv)); + debug(sprofuri, "modoauth1::access_token::sprofuri"); + + // Put the args into an oauth header + string profhdr = header(cadr(sprofuri), "", ""); + + // Send the user profile request + char* pprofres = oauth_http_get2(c_str(car(sprofuri)), NULL, c_str(profhdr)); + if (pprofres == NULL) + return mkfailure<int>("Couldn't get user info"); + const string profres(pprofres); + free(pprofres); + debug(profres, "modoauth1::access_token::profres"); + + // Retrieve the user info from the profile + const failable<list<value> > iv = profileUserInfo(profres); + if (!hasContent(iv)) + return mkfailure<int>(reason(iv)); + + // Store user info in memcached keyed by session ID + const value sid = string("OAuth1_") + mkrand(); + const failable<bool> prc = memcache::put(mklist<value>("tuscanyOpenAuth", sid), content(iv), sc.mc); + if (!hasContent(prc)) + return mkfailure<int>(reason(prc)); + + // Send session ID to the client in a cookie + apr_table_set(r->err_headers_out, "Set-Cookie", c_str(oauth::cookie(sid))); + return httpd::externalRedirect(httpd::url(r->uri, r), r); +} + +/** + * Handle a request. + */ +int handler(request_rec* r) { + // Decline if we're not enabled or if the user is already + // authenticated by another module + const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_oauth1); + if(!dc.enabled) + return DECLINED; + if (r->user != NULL || apr_table_get(r->subprocess_env, "SSL_REMOTE_USER") != NULL) + return DECLINED; + + gc_scoped_pool pool(r->pool); + httpdDebugRequest(r, "modoauth1::handler::input"); + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_oauth1); + + // Get session id from the request + const maybe<string> sid = oauth::sessionID(r); + if (hasContent(sid)) { + // Decline if the session id was not created by this module + if (substr(content(sid), 0, 7) != "OAuth1_") + return DECLINED; + + // If we're authenticated store the user info in the request + const failable<value> info = oauth::userInfo(content(sid), sc.mc); + if (hasContent(info)) + return httpd::reportStatus(authenticated(content(info), r)); + } + + // Get the request args + const list<list<value> > args = httpd::queryArgs(r); + + // Decline if the request is for OpenID authentication + if (!isNil(assoc<value>("openid_identifier", args))) + return DECLINED; + + // Decline if the request is for OAuth2 authentication + if (!isNil(assoc<value>("mod_oauth2_step", args))) + return DECLINED; + + // Determine the OAuth protocol flow step, conveniently passed + // around in a request arg + const list<value> sl = assoc<value>("mod_oauth1_step", args); + const value step = !isNil(sl) && !isNil(cdr(sl))? cadr(sl) : ""; + + // Handle OAuth authorize request step + if (step == "authorize") + return httpd::reportStatus(authorize(args, r, sc)); + + // Handle OAuth access_token request step + if (step == "access_token") + return httpd::reportStatus(access_token(args, r, sc)); + + // Redirect to the login page + return httpd::reportStatus(oauth::login(dc.login, r)); +} + +/** + * Process the module configuration. + */ +int postConfigMerge(ServerConf& mainsc, server_rec* s) { + if (s == NULL) + return OK; + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_oauth1); + debug(httpd::serverName(s), "modoauth1::postConfigMerge::serverName"); + + // Merge configuration from main server + if (isNil(sc.appkeys)) + sc.appkeys = mainsc.appkeys; + sc.mc = mainsc.mc; + sc.cs = mainsc.cs; + + return postConfigMerge(mainsc, s->next); +} + +int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, server_rec* s) { + gc_scoped_pool pool(p); + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_oauth1); + debug(httpd::serverName(s), "modoauth1::postConfig::serverName"); + + // Merge server configurations + return postConfigMerge(sc, s); +} + +/** + * Child process initialization. + */ +void childInit(apr_pool_t* p, server_rec* s) { + gc_scoped_pool pool(p); + ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_oauth1); + if(psc == NULL) { + cfailure << "[Tuscany] Due to one or more errors mod_tuscany_oauth1 loading failed. Causing apache to stop loading." << endl; + exit(APEXIT_CHILDFATAL); + } + ServerConf& sc = *psc; + + // Connect to Memcached + if (isNil(sc.mcaddrs)) + sc.mc = *(new (gc_new<memcache::MemCached>()) memcache::MemCached("localhost", 11211)); + else + sc.mc = *(new (gc_new<memcache::MemCached>()) memcache::MemCached(sc.mcaddrs)); + + // Setup a CURL session + sc.cs = *(new (gc_new<http::CURLSession>()) http::CURLSession(sc.ca, sc.cert, sc.key)); + + // Merge the updated configuration into the virtual hosts + postConfigMerge(sc, s->next); +} + +/** + * Configuration commands. + */ +const char* confAppKey(cmd_parms *cmd, unused void *c, const char *arg1, const char* arg2, const char* arg3) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth1); + sc.appkeys = cons<list<value> >(mklist<value>(arg1, mklist<value>(arg2, arg3)), sc.appkeys); + return NULL; +} +const char* confMemcached(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth1); + sc.mcaddrs = cons<string>(arg, sc.mcaddrs); + return NULL; +} +const char* confEnabled(cmd_parms *cmd, void *c, const int arg) { + gc_scoped_pool pool(cmd->pool); + DirConf& dc = httpd::dirConf<DirConf>(c); + dc.enabled = (bool)arg; + return NULL; +} +const char* confLogin(cmd_parms *cmd, void *c, const char* arg) { + gc_scoped_pool pool(cmd->pool); + DirConf& dc = httpd::dirConf<DirConf>(c); + dc.login = arg; + return NULL; +} +const char* confCAFile(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth1); + sc.ca = arg; + return NULL; +} +const char* confCertFile(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth1); + sc.cert = arg; + return NULL; +} +const char* confCertKeyFile(cmd_parms *cmd, unused void *c, const char *arg) { + gc_scoped_pool pool(cmd->pool); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth1); + sc.key = arg; + return NULL; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + AP_INIT_TAKE3("AddAuthOAuth1AppKey", (const char*(*)())confAppKey, NULL, RSRC_CONF, "OAuth 2.0 name app-id app-key"), + AP_INIT_ITERATE("AddAuthOAuthMemcached", (const char*(*)())confMemcached, NULL, RSRC_CONF, "Memcached server host:port"), + AP_INIT_FLAG("AuthOAuth", (const char*(*)())confEnabled, NULL, OR_AUTHCFG, "OAuth 2.0 authentication On | Off"), + AP_INIT_TAKE1("AuthOAuthLoginPage", (const char*(*)())confLogin, NULL, OR_AUTHCFG, "OAuth 2.0 login page"), + AP_INIT_TAKE1("AuthOAuthSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "OAUth 2.0 SSL CA certificate file"), + AP_INIT_TAKE1("AuthOAuthSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "OAuth 2.0 SSL certificate file"), + AP_INIT_TAKE1("AuthOAuthSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "OAuth 2.0 SSL certificate key file"), + {NULL, NULL, NULL, 0, NO_ARGS, NULL} +}; + +void registerHooks(unused apr_pool_t *p) { + ap_hook_post_config(postConfig, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_child_init(childInit, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_check_user_id(checkUserID, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(handler, NULL, NULL, APR_HOOK_FIRST); +} + +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_oauth1 = { + STANDARD20_MODULE_STUFF, + // dir config and merger + tuscany::httpd::makeDirConf<tuscany::oauth1::DirConf>, NULL, + // server config and merger + tuscany::httpd::makeServerConf<tuscany::oauth1::ServerConf>, NULL, + // commands and hooks + tuscany::oauth1::commands, tuscany::oauth1::registerHooks +}; + +} diff --git a/sca-cpp/trunk/modules/oauth/mod-oauth.cpp b/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp index e7158c14b0..60eadc282c 100644 --- a/sca-cpp/trunk/modules/oauth/mod-oauth.cpp +++ b/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp @@ -34,13 +34,14 @@ #include "../http/httpd.hpp" #include "../http/http.hpp" #include "../../components/cache/memcache.hpp" +#include "oauth.hpp" extern "C" { -extern module AP_MODULE_DECLARE_DATA mod_tuscany_oauth; +extern module AP_MODULE_DECLARE_DATA mod_tuscany_oauth2; } namespace tuscany { -namespace oauth { +namespace oauth2 { /** * Server configuration. @@ -55,7 +56,7 @@ public: string ca; string cert; string key; - list<list<value> > apps; + list<list<value> > appkeys; list<string> mcaddrs; memcache::MemCached mc; http::CURLSession cs; @@ -80,7 +81,7 @@ public: */ static int checkUserID(request_rec *r) { // Decline if we're not enabled or AuthType is not set to Open - const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_oauth); + const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_oauth2); if (!dc.enabled) return DECLINED; const char* atype = ap_auth_type(r); @@ -88,44 +89,15 @@ static int checkUserID(request_rec *r) { return DECLINED; gc_scoped_pool pool(r->pool); - httpdDebugRequest(r, "modoauth::checkUserID::input"); + httpdDebugRequest(r, "modoauth2::checkUserID::input"); return OK; } /** - * Return the session id from a request. - */ -const maybe<string> sessionID(const list<string> c) { - if (isNil(c)) - return maybe<string>(); - const list<string> kv = tokenize("=", car(c)); - if (!isNil(kv) && !isNil(cdr(kv))) { - if (car(kv) == "TuscanyOpenAuth") - return cadr(kv); - } - return sessionID(cdr(c)); -} - -const maybe<string> sessionID(const request_rec* r) { - const char* c = apr_table_get(r->headers_in, "Cookie"); - debug(c, "modoauth::sessionid::cookies"); - if (c == NULL) - return maybe<string>(); - return sessionID(tokenize(";", c)); -} - -/** - * Return the user info for a session. - */ -const failable<value> userInfo(const value& sid, const ServerConf& sc) { - return memcache::get(mklist<value>("tuscanyOpenAuth", sid), sc.mc); -} - -/** * Handle an authenticated request. */ const failable<int> authenticated(const list<list<value> >& info, request_rec* r) { - debug(info, "modoauth::authenticated::info"); + debug(info, "modoauth2::authenticated::info"); const list<value> id = assoc<value>("id", info); if (isNil(id) || isNil(cdr(id))) @@ -156,116 +128,101 @@ const failable<int> authenticated(const list<list<value> >& info, request_rec* r } /** - * Redirect to the configured login page. - */ -const failable<int> login(const string& page, request_rec* r) { - const list<list<value> > largs = mklist<list<value> >(mklist<value>("openauth_referrer", httpd::escape(httpd::url(r->uri, r)))); - const string loc = httpd::url(page, r) + string("?") + httpd::queryString(largs); - debug(loc, "modoauth::login::uri"); - return httpd::externalRedirect(loc, r); -} - -/** * Handle an authorize request. */ -const failable<int> authorize(const list<list<value> >& args, request_rec* r) { +const failable<int> authorize(const list<list<value> >& args, request_rec* r, const ServerConf& sc) { // Extract authorize, access_token, client ID and info URIs - const list<value> auth = assoc<value>("mod_oauth_authorize", args); + const list<value> auth = assoc<value>("mod_oauth2_authorize", args); if (isNil(auth) || isNil(cdr(auth))) - return mkfailure<int>("Missing mod_oauth_authorize parameter"); - const list<value> tok = assoc<value>("mod_oauth_access_token", args); + return mkfailure<int>("Missing mod_oauth2_authorize parameter"); + const list<value> tok = assoc<value>("mod_oauth2_access_token", args); if (isNil(tok) || isNil(cdr(tok))) - return mkfailure<int>("Missing mod_oauth_access_token parameter"); - const list<value> cid = assoc<value>("mod_oauth_client_id", args); + return mkfailure<int>("Missing mod_oauth2_access_token parameter"); + const list<value> cid = assoc<value>("mod_oauth2_client_id", args); if (isNil(cid) || isNil(cdr(cid))) - return mkfailure<int>("Missing mod_oauth_client_id parameter"); - const list<value> info = assoc<value>("mod_oauth_info", args); + return mkfailure<int>("Missing mod_oauth2_client_id parameter"); + const list<value> info = assoc<value>("mod_oauth2_info", args); if (isNil(info) || isNil(cdr(info))) - return mkfailure<int>("Missing mod_oauth_info parameter"); + return mkfailure<int>("Missing mod_oauth2_info parameter"); // Build the redirect URI - const list<list<value> > rargs = mklist<list<value> >(mklist<value>("mod_oauth_step", "access_token"), tok, cid, info); + const list<list<value> > rargs = mklist<list<value> >(mklist<value>("mod_oauth2_step", "access_token"), tok, cid, info); const string redir = httpd::url(r->uri, r) + string("?") + httpd::queryString(rargs); - debug(redir, "modoauth::authorize::redir"); + debug(redir, "modoauth2::authorize::redir"); + + // Lookup client app configuration + const list<value> app = assoc<value>(cadr(cid), sc.appkeys); + if (isNil(app) || isNil(cdr(app))) + return mkfailure<int>(string("client id not found: ") + cadr(cid)); + list<value> appkey = cadr(app); // Redirect to the authorize URI - const list<list<value> > aargs = mklist<list<value> >(mklist<value>("client_id", cadr(cid)), mklist<value>("scope", "email"), mklist<value>("redirect_uri", httpd::escape(redir))); + const list<list<value> > aargs = mklist<list<value> >(mklist<value>("client_id", car(appkey)), mklist<value>("scope", "email"), mklist<value>("redirect_uri", httpd::escape(redir))); const string uri = httpd::unescape(cadr(auth)) + string("?") + httpd::queryString(aargs); - debug(uri, "modoauth::authorize::uri"); + debug(uri, "modoauth2::authorize::uri"); return httpd::externalRedirect(uri, r); } /** - * Convert a session id to a cookie string. - */ -const string cookie(const string& sid) { - const time_t t = time(NULL) + 86400; - char exp[32]; - strftime(exp, 32, "%a, %d-%b-%Y %H:%M:%S GMT", gmtime(&t)); - const string c = string("TuscanyOpenAuth=") + sid + string(";path=/;expires=" + string(exp)) + ";secure=TRUE"; - debug(c, "modoauth::cookie"); - return c; -} - -/** * Handle an access_token request. */ const failable<int> access_token(const list<list<value> >& args, request_rec* r, const ServerConf& sc) { // Extract access_token URI, client ID and authorization code - const list<value> tok = assoc<value>("mod_oauth_access_token", args); + const list<value> tok = assoc<value>("mod_oauth2_access_token", args); if (isNil(tok) || isNil(cdr(tok))) - return mkfailure<int>("Missing mod_oauth_access_token parameter"); - const list<value> cid = assoc<value>("mod_oauth_client_id", args); + return mkfailure<int>("Missing mod_oauth2_access_token parameter"); + const list<value> cid = assoc<value>("mod_oauth2_client_id", args); if (isNil(cid) || isNil(cdr(cid))) - return mkfailure<int>("Missing mod_oauth_client_id parameter"); - const list<value> info = assoc<value>("mod_oauth_info", args); + return mkfailure<int>("Missing mod_oauth2_client_id parameter"); + const list<value> info = assoc<value>("mod_oauth2_info", args); if (isNil(info) || isNil(cdr(info))) - return mkfailure<int>("Missing mod_oauth_info parameter"); + return mkfailure<int>("Missing mod_oauth2_info parameter"); const list<value> code = assoc<value>("code", args); if (isNil(code) || isNil(cdr(code))) return mkfailure<int>("Missing code parameter"); // Lookup client app configuration - const list<value> app = assoc<value>(cadr(cid), sc.apps); + const list<value> app = assoc<value>(cadr(cid), sc.appkeys); if (isNil(app) || isNil(cdr(app))) return mkfailure<int>(string("client id not found: ") + cadr(cid)); + list<value> appkey = cadr(app); // Build the redirect URI - const list<list<value> > rargs = mklist<list<value> >(mklist<value>("mod_oauth_step", "access_token"), tok, cid, info); + const list<list<value> > rargs = mklist<list<value> >(mklist<value>("mod_oauth2_step", "access_token"), tok, cid, info); const string redir = httpd::url(r->uri, r) + string("?") + httpd::queryString(rargs); - debug(redir, "modoauth::access_token::redir"); + debug(redir, "modoauth2::access_token::redir"); // Request access token - const list<list<value> > targs = mklist<list<value> >(mklist<value>("client_id", cadr(cid)), mklist<value>("redirect_uri", httpd::escape(redir)), mklist<value>("client_secret", cadr(app)), code); + const list<list<value> > targs = mklist<list<value> >(mklist<value>("client_id", car(appkey)), mklist<value>("redirect_uri", httpd::escape(redir)), mklist<value>("client_secret", cadr(appkey)), code); const string turi = httpd::unescape(cadr(tok)) + string("?") + httpd::queryString(targs); - debug(turi, "modoauth::access_token::tokenuri"); + debug(turi, "modoauth2::access_token::tokenuri"); const failable<value> tr = http::get(turi, sc.cs); if (!hasContent(tr)) return mkfailure<int>(reason(tr)); - debug(tr, "modoauth::access_token::response"); + debug(tr, "modoauth2::access_token::response"); const list<value> tv = assoc<value>("access_token", httpd::queryArgs(join("", convertValues<string>(content(tr))))); - if (isNil(app) || isNil(cdr(app))) + if (isNil(tv) || isNil(cdr(tv))) return mkfailure<int>("Couldn't retrieve access_token"); - debug(tv, "modoauth::access_token::token"); + debug(tv, "modoauth2::access_token::token"); // Request user info // TODO Make this step configurable const list<list<value> > iargs = mklist<list<value> >(tv); const string iuri = httpd::unescape(cadr(info)) + string("?") + httpd::queryString(iargs); - debug(iuri, "modoauth::access_token::infouri"); + debug(iuri, "modoauth2::access_token::infouri"); const failable<value> iv = http::get(iuri, sc.cs); - if (isNil(app) || isNil(cdr(app))) + if (!hasContent(iv)) return mkfailure<int>("Couldn't retrieve user info"); - debug(iv, "modoauth::access_token::info"); + debug(content(iv), "modoauth2::access_token::info"); // Store user info in memcached keyed by session ID - const value sid = string("OAuth_") + mkrand(); + const value sid = string("OAuth2_") + mkrand(); const failable<bool> prc = memcache::put(mklist<value>("tuscanyOpenAuth", sid), content(iv), sc.mc); if (!hasContent(prc)) return mkfailure<int>(reason(prc)); // Send session ID to the client in a cookie - apr_table_set(r->err_headers_out, "Set-Cookie", c_str(cookie(sid))); + apr_table_set(r->err_headers_out, "Set-Cookie", c_str(oauth::cookie(sid))); return httpd::externalRedirect(httpd::url(r->uri, r), r); } @@ -275,25 +232,25 @@ const failable<int> access_token(const list<list<value> >& args, request_rec* r, int handler(request_rec* r) { // Decline if we're not enabled or if the user is already // authenticated by another module - const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_oauth); + const DirConf& dc = httpd::dirConf<DirConf>(r, &mod_tuscany_oauth2); if(!dc.enabled) return DECLINED; if (r->user != NULL || apr_table_get(r->subprocess_env, "SSL_REMOTE_USER") != NULL) return DECLINED; gc_scoped_pool pool(r->pool); - httpdDebugRequest(r, "modoauth::handler::input"); - const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_oauth); + httpdDebugRequest(r, "modoauth2::handler::input"); + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_oauth2); // Get session id from the request - const maybe<string> sid = sessionID(r); + const maybe<string> sid = oauth::sessionID(r); if (hasContent(sid)) { // Decline if the session id was not created by this module - if (substr(content(sid), 0, 6) != "OAuth_") + if (substr(content(sid), 0, 7) != "OAuth2_") return DECLINED; // If we're authenticated store the user info in the request - const failable<value> info = userInfo(content(sid), sc); + const failable<value> info = oauth::userInfo(content(sid), sc.mc); if (hasContent(info)) return httpd::reportStatus(authenticated(content(info), r)); } @@ -305,21 +262,25 @@ int handler(request_rec* r) { if (!isNil(assoc<value>("openid_identifier", args))) return DECLINED; + // Decline if the request is for OAuth1 authentication + if (!isNil(assoc<value>("mod_oauth1_step", args))) + return DECLINED; + // Determine the OAuth protocol flow step, conveniently passed // around in a request arg - const list<value> sl = assoc<value>("mod_oauth_step", args); + const list<value> sl = assoc<value>("mod_oauth2_step", args); const value step = !isNil(sl) && !isNil(cdr(sl))? cadr(sl) : ""; // Handle OAuth authorize request step if (step == "authorize") - return httpd::reportStatus(authorize(args, r)); + return httpd::reportStatus(authorize(args, r, sc)); // Handle OAuth access_token request step if (step == "access_token") return httpd::reportStatus(access_token(args, r, sc)); // Redirect to the login page - return httpd::reportStatus(login(dc.login, r)); + return httpd::reportStatus(oauth::login(dc.login, r)); } /** @@ -328,12 +289,12 @@ int handler(request_rec* r) { int postConfigMerge(ServerConf& mainsc, server_rec* s) { if (s == NULL) return OK; - ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_oauth); - debug(httpd::serverName(s), "modoauth::postConfigMerge::serverName"); + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_oauth2); + debug(httpd::serverName(s), "modoauth2::postConfigMerge::serverName"); // Merge configuration from main server - if (isNil(sc.apps)) - sc.apps = mainsc.apps; + if (isNil(sc.appkeys)) + sc.appkeys = mainsc.appkeys; sc.mc = mainsc.mc; sc.cs = mainsc.cs; @@ -342,8 +303,8 @@ int postConfigMerge(ServerConf& mainsc, server_rec* s) { int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, server_rec* s) { gc_scoped_pool pool(p); - ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_oauth); - debug(httpd::serverName(s), "modoauth::postConfig::serverName"); + ServerConf& sc = httpd::serverConf<ServerConf>(s, &mod_tuscany_oauth2); + debug(httpd::serverName(s), "modoauth2::postConfig::serverName"); // Merge server configurations return postConfigMerge(sc, s); @@ -354,9 +315,9 @@ int postConfig(apr_pool_t* p, unused apr_pool_t* plog, unused apr_pool_t* ptemp, */ void childInit(apr_pool_t* p, server_rec* s) { gc_scoped_pool pool(p); - ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_oauth); + ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_oauth2); if(psc == NULL) { - cfailure << "[Tuscany] Due to one or more errors mod_tuscany_oauth loading failed. Causing apache to stop loading." << endl; + cfailure << "[Tuscany] Due to one or more errors mod_tuscany_oauth2 loading failed. Causing apache to stop loading." << endl; exit(APEXIT_CHILDFATAL); } ServerConf& sc = *psc; @@ -377,15 +338,15 @@ void childInit(apr_pool_t* p, server_rec* s) { /** * Configuration commands. */ -const char* confApp(cmd_parms *cmd, unused void *c, const char *arg1, const char* arg2) { +const char* confAppKey(cmd_parms *cmd, unused void *c, const char *arg1, const char* arg2, const char* arg3) { gc_scoped_pool pool(cmd->pool); - ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth); - sc.apps = cons<list<value> >(mklist<value>(arg1, arg2), sc.apps); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth2); + sc.appkeys = cons<list<value> >(mklist<value>(arg1, mklist<value>(arg2, arg3)), sc.appkeys); return NULL; } const char* confMemcached(cmd_parms *cmd, unused void *c, const char *arg) { gc_scoped_pool pool(cmd->pool); - ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth2); sc.mcaddrs = cons<string>(arg, sc.mcaddrs); return NULL; } @@ -403,19 +364,19 @@ const char* confLogin(cmd_parms *cmd, void *c, const char* arg) { } const char* confCAFile(cmd_parms *cmd, unused void *c, const char *arg) { gc_scoped_pool pool(cmd->pool); - ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth2); sc.ca = arg; return NULL; } const char* confCertFile(cmd_parms *cmd, unused void *c, const char *arg) { gc_scoped_pool pool(cmd->pool); - ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth2); sc.cert = arg; return NULL; } const char* confCertKeyFile(cmd_parms *cmd, unused void *c, const char *arg) { gc_scoped_pool pool(cmd->pool); - ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth); + ServerConf& sc = httpd::serverConf<ServerConf>(cmd, &mod_tuscany_oauth2); sc.key = arg; return NULL; } @@ -424,13 +385,13 @@ const char* confCertKeyFile(cmd_parms *cmd, unused void *c, const char *arg) { * HTTP server module declaration. */ const command_rec commands[] = { - AP_INIT_ITERATE2("AddAuthOAuthApp", (const char*(*)())confApp, NULL, RSRC_CONF, "OAuth app-id app-secret"), + AP_INIT_TAKE3("AddAuthOAuth2AppKey", (const char*(*)())confAppKey, NULL, RSRC_CONF, "OAuth 2.0 name app-id app-key"), AP_INIT_ITERATE("AddAuthOAuthMemcached", (const char*(*)())confMemcached, NULL, RSRC_CONF, "Memcached server host:port"), - AP_INIT_FLAG("AuthOAuth", (const char*(*)())confEnabled, NULL, OR_AUTHCFG, "OAuth authentication On | Off"), - AP_INIT_TAKE1("AuthOAuthLoginPage", (const char*(*)())confLogin, NULL, OR_AUTHCFG, "OAuth login page"), - AP_INIT_TAKE1("AuthOAuthSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "OAUth SSL CA certificate file"), - AP_INIT_TAKE1("AuthOAuthSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "OAuth SSL certificate file"), - AP_INIT_TAKE1("AuthOAuthSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "OAuth SSL certificate key file"), + AP_INIT_FLAG("AuthOAuth", (const char*(*)())confEnabled, NULL, OR_AUTHCFG, "OAuth 2.0 authentication On | Off"), + AP_INIT_TAKE1("AuthOAuthLoginPage", (const char*(*)())confLogin, NULL, OR_AUTHCFG, "OAuth 2.0 login page"), + AP_INIT_TAKE1("AuthOAuthSSLCACertificateFile", (const char*(*)())confCAFile, NULL, RSRC_CONF, "OAUth 2.0 SSL CA certificate file"), + AP_INIT_TAKE1("AuthOAuthSSLCertificateFile", (const char*(*)())confCertFile, NULL, RSRC_CONF, "OAuth 2.0 SSL certificate file"), + AP_INIT_TAKE1("AuthOAuthSSLCertificateKeyFile", (const char*(*)())confCertKeyFile, NULL, RSRC_CONF, "OAuth 2.0 SSL certificate key file"), {NULL, NULL, NULL, 0, NO_ARGS, NULL} }; @@ -446,14 +407,14 @@ void registerHooks(unused apr_pool_t *p) { extern "C" { -module AP_MODULE_DECLARE_DATA mod_tuscany_oauth = { +module AP_MODULE_DECLARE_DATA mod_tuscany_oauth2 = { STANDARD20_MODULE_STUFF, // dir config and merger - tuscany::httpd::makeDirConf<tuscany::oauth::DirConf>, NULL, + tuscany::httpd::makeDirConf<tuscany::oauth2::DirConf>, NULL, // server config and merger - tuscany::httpd::makeServerConf<tuscany::oauth::ServerConf>, NULL, + tuscany::httpd::makeServerConf<tuscany::oauth2::ServerConf>, NULL, // commands and hooks - tuscany::oauth::commands, tuscany::oauth::registerHooks + tuscany::oauth2::commands, tuscany::oauth2::registerHooks }; } diff --git a/sca-cpp/trunk/modules/oauth/oauth-conf b/sca-cpp/trunk/modules/oauth/oauth-conf index 7123d8710e..5c3fde2ccc 100755 --- a/sca-cpp/trunk/modules/oauth/oauth-conf +++ b/sca-cpp/trunk/modules/oauth/oauth-conf @@ -29,7 +29,8 @@ host=`echo $conf | awk '{ print $6 }'` cat >>$root/conf/httpd.conf <<EOF # Generated by: oauth-conf $* # Load support for OAuth authentication -LoadModule mod_tuscany_oauth $here/libmod_tuscany_oauth.so +LoadModule mod_tuscany_oauth1 $here/libmod_tuscany_oauth1.so +LoadModule mod_tuscany_oauth2 $here/libmod_tuscany_oauth2.so # Enable OAuth authentication <Location /> @@ -49,10 +50,20 @@ AuthOAuth Off AuthOAuth Off </Location> +# Configure OAuth App keys +Include $root/cert/oauth-keys.conf +Include $HOME/.oauth/*-key.conf + +EOF + +cat >$root/cert/oauth-keys.conf <<EOF +# Generated by: oauth-conf $* +# OAuth App keys + EOF cat >>$root/conf/vhost-ssl.conf <<EOF -# Generated by: openid-conf $* +# Generated by: oauth-conf $* # Require OAuth authentication <Location /> AuthType Open diff --git a/sca-cpp/trunk/modules/oauth/oauth-memcached-conf b/sca-cpp/trunk/modules/oauth/oauth-memcached-conf index 8c133688d7..6634511d61 100755 --- a/sca-cpp/trunk/modules/oauth/oauth-memcached-conf +++ b/sca-cpp/trunk/modules/oauth/oauth-memcached-conf @@ -25,7 +25,7 @@ port=$3 # Configure HTTPD mod_tuscany_oauth module cache cat >>$root/conf/httpd.conf <<EOF -# Generated by: oauth-cache-conf $* +# Generated by: oauth-memcached-conf $* AddAuthOAuthMemcached $host:$port EOF diff --git a/sca-cpp/trunk/modules/oauth/oauth.composite b/sca-cpp/trunk/modules/oauth/oauth.composite index b1fc095ce5..0119db22c9 100644 --- a/sca-cpp/trunk/modules/oauth/oauth.composite +++ b/sca-cpp/trunk/modules/oauth/oauth.composite @@ -32,8 +32,12 @@ <service name="info"> <t:binding.jsonrpc uri="info"/> </service> - <property name="user">anonymous</property> - <property name="email">anonymous@example.com</property> + <property name="user">?</property> + <property name="email">?</property> + <property name="nickname">?</property> + <property name="fullname">?</property> + <property name="firstname">?</property> + <property name="lastname">?</property> </component> </composite> diff --git a/sca-cpp/trunk/modules/oauth/oauth.hpp b/sca-cpp/trunk/modules/oauth/oauth.hpp new file mode 100644 index 0000000000..ab92cbd381 --- /dev/null +++ b/sca-cpp/trunk/modules/oauth/oauth.hpp @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* $Rev$ $Date$ */ + +#ifndef tuscany_oauth_hpp +#define tuscany_oauth_hpp + +/** + * OAuth support utility functions. + */ + +#include "string.hpp" +#include "stream.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../json/json.hpp" +#include "../http/httpd.hpp" +#include "../http/http.hpp" +#include "../../components/cache/memcache.hpp" + +namespace tuscany { +namespace oauth { + +/** + * Return the session id from a request. + */ +const maybe<string> sessionID(const list<string> c) { + if (isNil(c)) + return maybe<string>(); + const list<string> kv = tokenize("=", car(c)); + if (!isNil(kv) && !isNil(cdr(kv))) { + if (car(kv) == "TuscanyOpenAuth") + return cadr(kv); + } + return sessionID(cdr(c)); +} + +const maybe<string> sessionID(const request_rec* r) { + const char* c = apr_table_get(r->headers_in, "Cookie"); + debug(c, "oauth::sessionid::cookies"); + if (c == NULL) + return maybe<string>(); + return sessionID(tokenize(";", c)); +} + +/** + * Return the user info for a session. + */ +const failable<value> userInfo(const value& sid, const memcache::MemCached& mc) { + return memcache::get(mklist<value>("tuscanyOpenAuth", sid), mc); +} + +/** + * Convert a session id to a cookie string. + */ +const string cookie(const string& sid) { + const time_t t = time(NULL) + 86400; + char exp[32]; + strftime(exp, 32, "%a, %d-%b-%Y %H:%M:%S GMT", gmtime(&t)); + const string c = string("TuscanyOpenAuth=") + sid + string(";path=/;expires=" + string(exp)) + ";secure=TRUE"; + debug(c, "oauth::cookie"); + return c; +} + +/** + * Redirect to the configured login page. + */ +const failable<int> login(const string& page, request_rec* r) { + const list<list<value> > largs = mklist<list<value> >(mklist<value>("openauth_referrer", httpd::escape(httpd::url(r->uri, r)))); + const string loc = httpd::url(page, r) + string("?") + httpd::queryString(largs); + debug(loc, "oauth::login::uri"); + return httpd::externalRedirect(loc, r); +} + +} +} + +#endif /* tuscany_oauth_hpp */ diff --git a/sca-cpp/trunk/modules/oauth/oauth-app-conf b/sca-cpp/trunk/modules/oauth/oauth1-appkey-conf index 56beb0af95..fca7d4e8f3 100755 --- a/sca-cpp/trunk/modules/oauth/oauth-app-conf +++ b/sca-cpp/trunk/modules/oauth/oauth1-appkey-conf @@ -20,13 +20,17 @@ here=`readlink -f $0`; here=`dirname $here` mkdir -p $1 root=`readlink -f $1` -id=$2 -secret=$3 +name=$2 +id=$3 +secret=$4 -# Configure an OAuth App -cat >>$root/conf/httpd.conf <<EOF -# Generated by: oauth-app-conf $* -AddAuthOAuthApp $id $secret +# Configure an OAuth 1.0 app key +mkdir -p $root/cert +umask 0007 + +cat >>$root/cert/oauth-keys.conf <<EOF +# Generated by: oauth1-appkey-conf $* +AddAuthOAuth1AppKey $name $id $secret EOF diff --git a/sca-cpp/trunk/modules/oauth/oauth2-appkey-conf b/sca-cpp/trunk/modules/oauth/oauth2-appkey-conf new file mode 100755 index 0000000000..0a7986f07e --- /dev/null +++ b/sca-cpp/trunk/modules/oauth/oauth2-appkey-conf @@ -0,0 +1,36 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +here=`readlink -f $0`; here=`dirname $here` +mkdir -p $1 +root=`readlink -f $1` +name=$2 +id=$3 +secret=$4 + +# Configure an OAuth 2.0 app key +mkdir -p $root/cert +umask 0007 + +cat >>$root/cert/oauth-keys.conf <<EOF +# Generated by: oauth2-appkey-conf $* +AddAuthOAuth2AppKey $name $id $secret + +EOF + diff --git a/sca-cpp/trunk/modules/oauth/start-mixed-test b/sca-cpp/trunk/modules/oauth/start-mixed-test index 7a96773dd3..468e6861d8 100755 --- a/sca-cpp/trunk/modules/oauth/start-mixed-test +++ b/sca-cpp/trunk/modules/oauth/start-mixed-test @@ -32,7 +32,12 @@ here=`readlink -f $0`; here=`dirname $here` ./oauth-conf tmp ./oauth-memcached-conf tmp localhost 11212 ./oauth-memcached-conf tmp localhost 11213 -./oauth-app-conf tmp app1234 secret6789 + +# Configure your app keys here +./oauth1-appkey-conf tmp testtwitterapp app2345 secret7890 +./oauth1-appkey-conf tmp testlinkedinapp app3456 secret4567 +./oauth2-appkey-conf tmp testfacebookapp app1234 secret6789 +./oauth2-appkey-conf tmp testgithubapp app5678 secret8901 ../openid/openid-conf tmp ../openid/openid-step2-conf tmp diff --git a/sca-cpp/trunk/modules/oauth/start-test b/sca-cpp/trunk/modules/oauth/start-test index 0946397f1a..47171a91ae 100755 --- a/sca-cpp/trunk/modules/oauth/start-test +++ b/sca-cpp/trunk/modules/oauth/start-test @@ -29,7 +29,12 @@ ./oauth-conf tmp ./oauth-memcached-conf tmp localhost 11212 ./oauth-memcached-conf tmp localhost 11213 -./oauth-app-conf tmp app1234 secret6789 + +# Configure your app keys here +./oauth1-appkey-conf tmp testtwitterapp app2345 secret7890 +./oauth1-appkey-conf tmp testlinkedinapp app3456 secret4567 +./oauth2-appkey-conf tmp testfacebookapp app1234 secret6789 +./oauth2-appkey-conf tmp testgithubapp app5678 secret8901 ../../modules/server/server-conf tmp ../../modules/server/scheme-conf tmp diff --git a/sca-cpp/trunk/modules/oauth/user-info.scm b/sca-cpp/trunk/modules/oauth/user-info.scm index 0c4ebcfaf7..58a84f86b0 100644 --- a/sca-cpp/trunk/modules/oauth/user-info.scm +++ b/sca-cpp/trunk/modules/oauth/user-info.scm @@ -17,10 +17,18 @@ ; OAuth support test case -(define (get id user email) (list "text/html" (list - "<html><body><p>The following info is generated on the server:</p><div>User: " (user) "</div><div>Email: " (email) "</div></body></html>"))) +(define (get id user email nickname fullname firstname lastname) (list "text/html" (list + "<html><body><p>The following info is generated on the server:</p><div>User: " (user) "</div><div>Email: " (email) "</div><div>Nickname: " (nickname) "</div><div>Fullname: " (fullname) "</div><div>Firstname: " (firstname) "</div><div>Lastname: " (lastname) "</div></body></html>"))) -(define (getuser user email) (user)) +(define (getuser user email nickname fullname firstname lastname) (user)) -(define (getemail user email) (email)) +(define (getemail user email nickname fullname firstname lastname) (email)) + +(define (getnickname user email nickname fullname firstname lastname) (nickname)) + +(define (getfullname user email nickname fullname firstname lastname) (fullname)) + +(define (getfirstname user email nickname fullname firstname lastname) (firstname)) + +(define (getlastname user email nickname fullname firstname lastname) (lastname)) |