diff options
Diffstat (limited to 'sandbox/sebastien/cpp/apr-2/modules/oauth')
17 files changed, 1887 insertions, 0 deletions
diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/Makefile.am b/sandbox/sebastien/cpp/apr-2/modules/oauth/Makefile.am new file mode 100644 index 0000000000..6c0cd2bc57 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/Makefile.am @@ -0,0 +1,42 @@ +# 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. + +if WANT_OAUTH + +INCLUDES = -I${HTTPD_INCLUDE} -I${LIBOAUTH_INCLUDE} + +dist_mod_SCRIPTS = oauth-conf oauth-memcached-conf oauth1-appkey-conf oauth2-appkey-conf +moddir=$(prefix)/modules/oauth + +mod_LTLIBRARIES = libmod_tuscany_oauth1.la libmod_tuscany_oauth2.la +noinst_DATA = libmod_tuscany_oauth1.so libmod_tuscany_oauth2.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/public/index.html + +dist_noinst_SCRIPTS = start-test stop-test + +endif diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/index.html b/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/index.html new file mode 100644 index 0000000000..7d26567214 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/index.html @@ -0,0 +1,58 @@ +<!-- + 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. +--> + +<html> +<head> +<script type="text/javascript" src="/component.js"></script> +<script type="text/javascript"> +var protected = sca.component("Protected"); +var userInfo = sca.defun(sca.reference(protected, "userInfo"), "getuser", "getemail", "getnickname", "getfullname", "getfirstname", "getlastname", "getrealm"); +var user = userInfo.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"); +var realm = userInfo.apply("getrealm"); +</script> +</head> +<body> +<h1>Protected area - It works!</h1> +<p>The following info is returned by a JSONRPC service:</p> +<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> +<div>Realm: <span id="realm"></span></div> +<script type="text/javascript"> +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; +document.getElementById('realm').innerHTML=realm; +</script> +<p><a href="info">User info</a></p> +<p><a href="login">Sign in</a></p> +<p><a href="logout">Sign out</a></p> +<p><a href="public">Public area</a></p> +</body></html> diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/login/index.html b/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/login/index.html new file mode 100644 index 0000000000..c41c334b76 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/login/index.html @@ -0,0 +1,116 @@ +<!-- + 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. +--> + +<html><body><h1>Sign in with an OAuth provider</h1> + +<script type="text/javascript"> +function queryParams() { + qp = new Array(); + qs = window.location.search.substring(1).split('&'); + for (i = 0; i < qs.length; i++) { + e = qs[i].indexOf('='); + if (e > 0) + qp[qs[i].substring(0, e)] = unescape(qs[i].substring(e + 1)); + } + return qp; +} + +function oauthReferrer() { + r = queryParams()['openauth_referrer']; + if (typeof(r) == 'undefined') + return r; + q = r.indexOf('?'); + if (q > 0) + return r.substring(0, q); + return r; +} + +if (typeof(oauthReferrer()) == 'undefined') { + document.location = '/'; +} + +function submitSignin2(w) { + parms = w(); + document.cookie = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';path=/;secure=TRUE'; + 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', 'facebook.com', 'https://graph.facebook.com/me']; + return parms; +} + +function withGithub() { + var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'github.com', 'https://github.com/api/v2/json/user/show']; + return parms; +} + +function submitSignin1(w) { + parms = w(); + document.cookie = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';path=/;secure=TRUE'; + 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', 'linkedin.com', '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', 'twitter.com', 'https://api.twitter.com/1/statuses/user_timeline.json']; + return parms; +} +</script> + +<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="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/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/login/mixed.html b/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/login/mixed.html new file mode 100644 index 0000000000..54673e01ee --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/login/mixed.html @@ -0,0 +1,211 @@ +<!-- + 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. +--> + +<html><body><h1>Sign in with a Form, an OpenID provider or an OAuth provider</h1> + +<script type="text/javascript"> +function submitFormSignin() { + document.cookie = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';path=/;secure=TRUE'; + document.formSignin.httpd_location.value = '/'; + document.formSignin.submit(); +} + +function queryParams() { + qp = new Array(); + qs = window.location.search.substring(1).split('&'); + for (i = 0; i < qs.length; i++) { + e = qs[i].indexOf('='); + if (e > 0) + qp[qs[i].substring(0, e)] = unescape(qs[i].substring(e + 1)); + } + return qp; +} + +function openauthReferrer() { + r = queryParams()['openauth_referrer']; + if (typeof(r) == 'undefined') + return r; + q = r.indexOf('?'); + if (q > 0) + return r.substring(0, q); + return r; +} + +if (typeof(openauthReferrer()) == 'undefined') { + document.location = '/'; +} + +function submitOpenIDSignin(w) { + document.cookie = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';path=/;secure=TRUE'; + document.openIDSignin.openid_identifier.value = w(); + document.openIDSignin.action = openauthReferrer(); + document.openIDSignin.submit(); +} + +function withGoogle() { + return 'https://www.google.com/accounts/o8/id'; +} + +function withYahoo() { + return 'https://me.yahoo.com/'; +} + +function withMyOpenID() { + return 'http://www.myopenid.com/xrds'; +} + +function withVerisign() { + return 'https://pip.verisignlabs.com/'; +} + +function withMySpace() { + return 'https://api.myspace.com/openid'; +} + +function withGoogleApps() { + return 'https://www.google.com/accounts/o8/site-xrds?ns=2&hd=' + document.fields.domain.value; +} + +function withLivejournal() { + return 'http://' + document.fields.ljuser.value + '.livejournal.com'; +} + +function withBlogspot() { + return 'http://' + document.fields.bsuser.value + '.blogspot.com'; +} + +function withBlogger() { + return 'http://' + document.fields.bguser.value + '.blogger.com'; +} + +function withXRDSEndpoint() { + return document.fields.endpoint.value; +} + +function submitOAuth2Signin(w) { + parms = w(); + document.cookie = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';path=/;secure=TRUE'; + 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', 'facebook.com', 'https://graph.facebook.com/me']; + return parms; +} + +function withGithub() { + var parms = ['https://github.com/login/oauth/authorize', 'https://github.com/login/oauth/access_token', 'github.com', 'https://github.com/api/v2/json/user/show']; + return parms; +} + +function submitOAuth1Signin(w) { + parms = w(); + document.cookie = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';path=/;secure=TRUE'; + 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', 'linkedin.com', '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', 'twitter.com', 'https://api.twitter.com/1/statuses/user_timeline.json']; + return parms; +} +</script> + +<form name="formSignin" method="POST" action="/login/dologin"> +<p>Sign in with your user id and password<br/> +<table border="0"> +<tr><td>Username:</td><td><input type="text" name="httpd_username" value=""/></td></tr> +<tr><td>Password:</td><td><input type="password" name="httpd_password" value=""/></td></tr> +<tr><td><input type="button" onclick="submitFormSignin()" value="Sign in"/></td><td></td></tr> +</table> +</p> +<input type="hidden" name="httpd_location" value="/"/> +</form> + +<form name="fields"> +<p>Sign in with your Google account<br/><input type="button" onclick="submitOpenIDSignin(withGoogle)" value="Sign in"/></p> +<p>Sign in with your Yahoo account<br/><input type="button" onclick="submitOpenIDSignin(withYahoo)" value="Sign in"/></p> +<p>Sign in with your MyOpenID account<br/><input type="button" onclick="submitOpenIDSignin(withMyOpenID)" value="Sign in"/></p> +<p>Sign in with your Verisign account<br/><input type="button" onclick="submitOpenIDSignin(withVerisign)" value="Sign in"/></p> +<p>Sign in with your MySpace account<br/><input type="button" onclick="submitOpenIDSignin(withMySpace)" value="Sign in"/></p> + +<p>Sign in with a Google apps domain<br/> +<input type="text" size="20" name="domain" value="example.com"/><br/> +<input type="button" onclick="submitOpenIDSignin(withGoogleApps)" value="Sign in"/></p> + +<p>Sign in with your Livejournal account<br/> +<input type="text" size="10" name="ljuser" value=""/><br/> +<input type="button" onclick="submitOpenIDSignin(withLivejournal)" value="Sign in"/></p> + +<p>Sign in with your Blogspot account<br/> +<input type="text" size="10" name="bsuser" value=""/><br/> +<input type="button" onclick="submitOpenIDSignin(withBlogspot)" value="Sign in"/></p> + +<p>Sign in with your Blogger account<br/> +<input type="text" size="10" name="bguser" value=""/><br/> +<input type="button" onclick="submitOpenIDSignin(withBlogger)" value="Sign in"/></p> + +<p>Sign in with an OpenID endpoint<br/> +<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="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="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/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/logout/index.html b/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/logout/index.html new file mode 100644 index 0000000000..02a92d1b31 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/logout/index.html @@ -0,0 +1,33 @@ +<!-- + 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. +--> + +<html><body> +<h1>Sign out</h1> + +<form name="signout" action="/login" method="GET"> +<script type="text/javascript"> +function submitSignout() { + document.cookie = 'TuscanyOpenAuth=;expires=' + new Date(1970,01,01).toGMTString() + ';path=/;secure=TRUE'; + document.signout.submit(); + return true; +} +</script> +<input type="button" onclick="submitSignout()" value="Sign out"/> +</form> +</body></html> diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/public/index.html b/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/public/index.html new file mode 100644 index 0000000000..af2cd7ca19 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/htdocs/public/index.html @@ -0,0 +1,27 @@ +<!-- + 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. +--> + +<html> +<body> +<h1>Unprotected area - It works!</h1> +<p><a href="/info">User info</a></p> +<p><a href="/login">Sign in</a></p> +<p><a href="/logout">Sign out</a></p> +<p><a href="/">Protected area</a></p> +</body></html> diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/mod-oauth1.cpp b/sandbox/sebastien/cpp/apr-2/modules/oauth/mod-oauth1.cpp new file mode 100644 index 0000000000..0f190127db --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/mod-oauth1.cpp @@ -0,0 +1,580 @@ +/* + * 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 "../http/openauth.hpp" +#include "../../components/cache/memcache.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; +}; + +/** + * 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); +} + +/** + * Handle an authenticated request. + */ +const failable<int> authenticated(const list<list<value> >& info, request_rec* r) { + debug(info, "modoauth1::authenticated::info"); + + // Store user info in the request + const list<value> realm = assoc<value>("realm", info); + if (isNil(realm) || isNil(cdr(realm))) + return mkfailure<int>("Couldn't retrieve realm"); + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "REALM"), apr_pstrdup(r->pool, c_str(cadr(realm)))); + + 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)))); + return OK; +} + +/** + * 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, Foursquare and LinkedIn. + * User profile parsing needs to be made configurable. + */ +const failable<list<value> > profileUserInfo(const value& cid, const string& info) { + string b = substr(info, 0, 1); + if (b == "[") { + // Twitter JSON profile + js::JSContext 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)); + debug(uv, "modoauth1::access_token::userInfo"); + if (isNil(uv) || isNil(cdr(uv))) + return mkfailure<list<value> >("Couldn't retrieve user info"); + const list<value> iv = cdr(uv); + return cons<value>(mklist<value>("realm", cid), iv); + } + if (b == "{") { + // Foursquare JSON profile + js::JSContext 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", infov); + debug(uv, "modoauth1::access_token::userInfo"); + if (isNil(uv) || isNil(cdr(uv))) + return mkfailure<list<value> >("Couldn't retrieve user info"); + const list<value> iv = cdr(uv); + return cons<value>(mklist<value>("realm", cid), iv); + } + if (b == "<") { + // 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); + debug(pv, "modoauth1::access_token::userInfo"); + if (isNil(pv) || isNil(cdr(pv))) + return mkfailure<list<value> >("Couldn't retrieve user info"); + const list<value> iv = cdr(pv); + return cons<value>(mklist<value>("realm", cid), iv); + } + return mkfailure<list<value> >("Couldn't retrieve user info"); +} + +/** + * 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(cadr(cid), 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(openauth::cookie(sid))); + return httpd::externalRedirect(httpd::url(r->uri, r), r); +} + +/** + * Check user authentication. + */ +static int checkAuthn(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::checkAuthn::input"); + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_oauth1); + + // Get session id from the request + const maybe<string> sid = openauth::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 = userInfo(content(sid), sc.mc); + if (hasContent(info)) { + r->ap_auth_type = const_cast<char*>(atype); + 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 another authentication provider + if (!isNil(assoc<value>("openid_identifier", args))) + return DECLINED; + 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") { + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(authorize(args, r, sc)); + } + + // Handle OAuth access_token request step + if (step == "access_token") { + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(access_token(args, r, sc)); + } + + // Redirect to the login page + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(openauth::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_authn(checkAuthn, NULL, NULL, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF); +} + +} +} + +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/sandbox/sebastien/cpp/apr-2/modules/oauth/mod-oauth2.cpp b/sandbox/sebastien/cpp/apr-2/modules/oauth/mod-oauth2.cpp new file mode 100644 index 0000000000..b52967977e --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/mod-oauth2.cpp @@ -0,0 +1,432 @@ +/* + * 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 2.0 authentication. + */ + +#include <sys/stat.h> + +#include "string.hpp" +#include "stream.hpp" +#include "list.hpp" +#include "tree.hpp" +#include "value.hpp" +#include "monad.hpp" +#include "../http/httpd.hpp" +#include "../http/http.hpp" +#include "../http/openauth.hpp" +#include "../../components/cache/memcache.hpp" + +extern "C" { +extern module AP_MODULE_DECLARE_DATA mod_tuscany_oauth2; +} + +namespace tuscany { +namespace oauth2 { + +/** + * 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; +}; + +/** + * 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); +} + +/** + * Handle an authenticated request. + */ +const failable<int> authenticated(const list<list<value> >& info, request_rec* r) { + debug(info, "modoauth2::authenticated::info"); + + // Store user info in the request + const list<value> realm = assoc<value>("realm", info); + if (isNil(realm) || isNil(cdr(realm))) + return mkfailure<int>("Couldn't retrieve realm"); + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "REALM"), apr_pstrdup(r->pool, c_str(cadr(realm)))); + + 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> fullname = assoc<value>("name", info); + if (!isNil(fullname) && !isNil(cdr(fullname))) { + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "NICKNAME"), apr_pstrdup(r->pool, c_str(cadr(fullname)))); + apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "FULLNAME"), apr_pstrdup(r->pool, c_str(cadr(fullname)))); + } + + 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)))); + + return OK; +} + +/** + * 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> auth = assoc<value>("mod_oauth2_authorize", args); + if (isNil(auth) || isNil(cdr(auth))) + 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_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_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_oauth2_info parameter"); + + // Build the redirect URI + 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, "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", 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, "modoauth2::authorize::uri"); + return httpd::externalRedirect(uri, r); +} + +/** + * Extract user info from a profile/info response. + * TODO This currently only works for Facebook and Gowalla. + * User profile parsing needs to be made configurable. + */ +const failable<list<value> > profileUserInfo(const value& cid, const list<value>& info) { + return cons<value>(mklist<value>("realm", cid), info); +} + +/** + * 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_oauth2_access_token", args); + if (isNil(tok) || isNil(cdr(tok))) + 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_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_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.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_oauth2_step", "access_token"), tok, cid, info); + const string redir = httpd::url(r->uri, r) + string("?") + httpd::queryString(rargs); + debug(redir, "modoauth2::access_token::redir"); + + // Request access token + 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, "modoauth2::access_token::tokenuri"); + const failable<value> tr = http::get(turi, sc.cs); + if (!hasContent(tr)) + return mkfailure<int>(reason(tr)); + debug(tr, "modoauth2::access_token::response"); + const list<value> tv = assoc<value>("access_token", httpd::queryArgs(join("", convertValues<string>(content(tr))))); + if (isNil(tv) || isNil(cdr(tv))) + return mkfailure<int>("Couldn't retrieve access_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, "modoauth2::access_token::infouri"); + const failable<value> profres = http::get(iuri, sc.cs); + if (!hasContent(profres)) + return mkfailure<int>("Couldn't retrieve user info"); + debug(content(profres), "modoauth2::access_token::info"); + + // Retrieve the user info from the profile + const failable<list<value> > iv = profileUserInfo(cadr(cid), content(profres)); + if (!hasContent(iv)) + return mkfailure<int>(reason(iv)); + + // Store user info in memcached keyed by session ID + 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(openauth::cookie(sid))); + return httpd::externalRedirect(httpd::url(r->uri, r), r); +} + +/** + * Check user authentication. + */ +static int checkAuthn(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_oauth2); + 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, "modoauth2::checkAuthn::input"); + const ServerConf& sc = httpd::serverConf<ServerConf>(r, &mod_tuscany_oauth2); + + // Get session id from the request + const maybe<string> sid = openauth::sessionID(r); + if (hasContent(sid)) { + // Decline if the session id was not created by this module + 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.mc); + if (hasContent(info)) { + r->ap_auth_type = const_cast<char*>(atype); + 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 another authentication provider + if (!isNil(assoc<value>("openid_identifier", args))) + return DECLINED; + 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_oauth2_step", args); + const value step = !isNil(sl) && !isNil(cdr(sl))? cadr(sl) : ""; + + // Handle OAuth authorize request step + if (step == "authorize") { + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(authorize(args, r, sc)); + } + + // Handle OAuth access_token request step + if (step == "access_token") { + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(access_token(args, r, sc)); + } + + // Redirect to the login page + r->ap_auth_type = const_cast<char*>(atype); + return httpd::reportStatus(openauth::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_oauth2); + debug(httpd::serverName(s), "modoauth2::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_oauth2); + debug(httpd::serverName(s), "modoauth2::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_oauth2); + if(psc == NULL) { + 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; + + // 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_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_oauth2); + 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_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_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_oauth2); + sc.key = arg; + return NULL; +} + +/** + * HTTP server module declaration. + */ +const command_rec commands[] = { + 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 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_authn(checkAuthn, NULL, NULL, APR_HOOK_MIDDLE, AP_AUTH_INTERNAL_PER_CONF); +} + +} +} + +extern "C" { + +module AP_MODULE_DECLARE_DATA mod_tuscany_oauth2 = { + STANDARD20_MODULE_STUFF, + // dir config and merger + tuscany::httpd::makeDirConf<tuscany::oauth2::DirConf>, NULL, + // server config and merger + tuscany::httpd::makeServerConf<tuscany::oauth2::ServerConf>, NULL, + // commands and hooks + tuscany::oauth2::commands, tuscany::oauth2::registerHooks +}; + +} diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth-conf b/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth-conf new file mode 100755 index 0000000000..dc3a6ebc9d --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth-conf @@ -0,0 +1,59 @@ +#!/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. + +# Generate an OAuth server conf +here=`readlink -f $0`; here=`dirname $here` +mkdir -p $1 +root=`readlink -f $1` + +conf=`cat $root/conf/httpd.conf | grep "# Generated by: httpd-conf"` +host=`echo $conf | awk '{ print $6 }'` + +# Configure HTTPD mod_tuscany_oauth module +cat >>$root/conf/modules.conf <<EOF +# Generated by: oauth-conf $* +# Load support for OAuth authentication +LoadModule mod_tuscany_oauth1 $here/libmod_tuscany_oauth1.so +LoadModule mod_tuscany_oauth2 $here/libmod_tuscany_oauth2.so + +EOF + +cat >>$root/conf/auth.conf <<EOF +# Generated by: oauth-conf $* +# Enable OAuth authentication +<Location /> +AuthType Open +AuthName "$host" +AuthOAuth On +AuthOAuthLoginPage /login +Require valid-user +</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 + diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth-memcached-conf b/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth-memcached-conf new file mode 100755 index 0000000000..23a82a0486 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth-memcached-conf @@ -0,0 +1,32 @@ +#!/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` +host=$2 +port=$3 + +# Configure HTTPD mod_tuscany_oauth module cache +cat >>$root/conf/auth.conf <<EOF +# Generated by: oauth-memcached-conf $* +AddAuthOAuthMemcached $host:$port + +EOF + diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth.composite b/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth.composite new file mode 100644 index 0000000000..c2025493c8 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth.composite @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://tuscany.apache.org/xmlns/sca/components" + name="oauth"> + + <component name="Protected"> + <t:implementation.widget location="protected/index.html"/> + <reference name="userInfo" target="UserInfo"/> + </component> + + <component name="UserInfo"> + <t:implementation.scheme script="user-info.scm"/> + <service name="info"> + <t:binding.jsonrpc uri="info"/> + </service> + <property name="user">?</property> + <property name="email">?</property> + <property name="nickname">?</property> + <property name="fullname">?</property> + <property name="firstname">?</property> + <property name="lastname">?</property> + <property name="realm">?</property> + </component> + +</composite> diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth1-appkey-conf b/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth1-appkey-conf new file mode 100755 index 0000000000..fca7d4e8f3 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth1-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 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/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth2-appkey-conf b/sandbox/sebastien/cpp/apr-2/modules/oauth/oauth2-appkey-conf new file mode 100755 index 0000000000..0a7986f07e --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/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/sandbox/sebastien/cpp/apr-2/modules/oauth/start-mixed-test b/sandbox/sebastien/cpp/apr-2/modules/oauth/start-mixed-test new file mode 100755 index 0000000000..bfd7667ce4 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/start-mixed-test @@ -0,0 +1,67 @@ +#!/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. + +# Test supporting both OpenID and OAuth in the same app +here=`readlink -f $0`; here=`dirname $here` + +# Setup +../../components/cache/memcached-start 11212 +../../components/cache/memcached-start 11213 + +../../modules/http/ssl-ca-conf tmp localhost +../../modules/http/ssl-cert-conf tmp localhost +../../modules/http/httpd-conf tmp localhost 8090 htdocs +../../modules/http/httpd-ssl-conf tmp 8453 + +./oauth-conf tmp +./oauth-memcached-conf tmp localhost 11212 +./oauth-memcached-conf tmp localhost 11213 + +# Configure your app keys here +./oauth1-appkey-conf tmp twitter.com app2345 secret7890 +./oauth1-appkey-conf tmp linkedin.com app3456 secret4567 +./oauth2-appkey-conf tmp facebook.com app1234 secret6789 +./oauth2-appkey-conf tmp github.com app5678 secret8901 + +../openid/openid-conf tmp +../openid/openid-step2-conf tmp +../openid/openid-memcached-conf tmp localhost 11212 +../openid/openid-memcached-conf tmp localhost 11213 + +../http/open-auth-conf tmp +../http/passwd-auth-conf tmp foo foo + +# For this test to work you need to add your form, oauth and open id ids +# to the authorized user group +../../modules/http/group-auth-conf tmp foo +../../modules/http/group-auth-conf tmp 123456 +../../modules/http/group-auth-conf tmp https://www.google.com/accounts/o8/id?id=12345678 + +../../modules/server/server-conf tmp +../../modules/server/scheme-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite oauth.composite + +Alias /login/index.html $here/htdocs/login/mixed.html + +EOF + +../../modules/http/httpd-start tmp + diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/start-test b/sandbox/sebastien/cpp/apr-2/modules/oauth/start-test new file mode 100755 index 0000000000..0e859ce6e6 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/start-test @@ -0,0 +1,54 @@ +#!/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. + +# Setup +../../ubuntu/ip-redirect-all 80 8090 +../../ubuntu/ip-redirect-all 443 8453 + +../../components/cache/memcached-start 11212 +../../components/cache/memcached-start 11213 + +../../modules/http/ssl-ca-conf tmp localhost +../../modules/http/ssl-cert-conf tmp localhost +../../modules/http/httpd-conf tmp localhost 8090/80 htdocs +../../modules/http/httpd-ssl-conf tmp 8453/443 + +./oauth-conf tmp +./oauth-memcached-conf tmp localhost 11212 +./oauth-memcached-conf tmp localhost 11213 + +# Configure your app keys here +./oauth1-appkey-conf tmp twitter.com app2345 secret7890 +./oauth1-appkey-conf tmp linkedin.com app3456 secret4567 +./oauth2-appkey-conf tmp facebook.com app1234 secret6789 +./oauth2-appkey-conf tmp github.com app5678 secret8901 + +# For this test to work you need to add your oauth user id to the +# authorized user group +../../modules/http/group-auth-conf tmp 123456 + +../../modules/server/server-conf tmp +../../modules/server/scheme-conf tmp +cat >>tmp/conf/httpd.conf <<EOF +SCAContribution `pwd`/ +SCAComposite oauth.composite +EOF + +../../modules/http/httpd-start tmp + diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/stop-test b/sandbox/sebastien/cpp/apr-2/modules/oauth/stop-test new file mode 100755 index 0000000000..a0587f8cb7 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/stop-test @@ -0,0 +1,24 @@ +#!/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. + +# Cleanup +../../modules/http/httpd-stop tmp + +../../components/cache/memcached-stop 11212 +../../components/cache/memcached-stop 11213 diff --git a/sandbox/sebastien/cpp/apr-2/modules/oauth/user-info.scm b/sandbox/sebastien/cpp/apr-2/modules/oauth/user-info.scm new file mode 100644 index 0000000000..4960a0a455 --- /dev/null +++ b/sandbox/sebastien/cpp/apr-2/modules/oauth/user-info.scm @@ -0,0 +1,36 @@ +; 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. + +; OAuth support test case + +(define (get id user email nickname fullname firstname lastname realm) (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><div>Realm: " (realm) "</div></body></html>"))) + +(define (getuser user email nickname fullname firstname lastname realm) (user)) + +(define (getemail user email nickname fullname firstname lastname realm) (email)) + +(define (getnickname user email nickname fullname firstname lastname realm) (nickname)) + +(define (getfullname user email nickname fullname firstname lastname realm) (fullname)) + +(define (getfirstname user email nickname fullname firstname lastname realm) (firstname)) + +(define (getlastname user email nickname fullname firstname lastname realm) (lastname)) + +(define (getrealm user email nickname fullname firstname lastname realm) (realm)) + |