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
This commit is contained in:
parent
b796454ac3
commit
ddf34aa2e6
21 changed files with 1018 additions and 202 deletions
|
@ -96,13 +96,16 @@ Google AppEngine 1.3.2 (http://code.google.com/appengine/)
|
|||
Java:
|
||||
a Java 5+ JDK (http://openjdk.java.net/, http://harmony.apache.org/)
|
||||
|
||||
OpenID user sign in:
|
||||
OpenID authentication:
|
||||
Mod_auth_openid (http://trac.butterfat.net/public/mod_auth_openid)
|
||||
build it from source at http://github.com/jsdelfino/mod_auth_openid
|
||||
requires Libopkele (http://kin.klever.net/libopkele/ or
|
||||
http://github.com/jsdelfino/libopkele)
|
||||
and HTML Tidy (http://tidy.sourceforge.net/)
|
||||
|
||||
OAuth authorization:
|
||||
Liboauth 0.9.1 (http://liboauth.sourceforge.net/)
|
||||
|
||||
XMPP Chat:
|
||||
Apache Vysper 0.5 (http://mina.apache.org/)
|
||||
|
||||
|
@ -157,7 +160,7 @@ To build the SQL Database utility component (requires PostgreSQL):
|
|||
To build the Web service utility component (requires Apache Axis2/C):
|
||||
--enable-webservice
|
||||
|
||||
To build the support for OAuth authentication:
|
||||
To build the support for OAuth authorization:
|
||||
--enable-oauth
|
||||
|
||||
To build the support for OpenID authentication:
|
||||
|
@ -197,7 +200,7 @@ dependencies installed under $HOME:
|
|||
--enable-log --with-thrift=$HOME/thrift-0.2.0-bin \
|
||||
--with-scribe=$HOME/scribe-2.2-bin \
|
||||
--enable-openid --with-mod-auth-openid=$HOME/mod-auth-openid-bin \
|
||||
--enable-oauth \
|
||||
--enable-oauth --with-liboauth=$HOME/liboauth-0.9.1-bin \
|
||||
--enable-maintainer-mode
|
||||
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ in C++ and integrated with the Apache HTTPD server.
|
|||
|
||||
It supports SCA components written in C++ and Python. Experimental support
|
||||
for other programming languages is under construction. SCA bindings are
|
||||
available for the JSON-RPC, ATOMPub and RSS protocols. User authentication
|
||||
is supported using OpenID and OAuth.
|
||||
available for the JSON-RPC, ATOMPub and RSS protocols. User signin is
|
||||
implemented using OpenID and OAuth.
|
||||
|
||||
Several useful SCA components are provided on top of the SCA runtime, which
|
||||
can be used to help assemble distributed SCA composite applications:
|
||||
|
@ -59,8 +59,8 @@ Here's a rough guide to the Tuscany SCA source tree:
|
|||
| | |-- http HTTP protocol
|
||||
| | |-- java Support for Java components
|
||||
| | |-- json JSON-RPC encoding
|
||||
| | |-- oauth OAuth authentication
|
||||
| | |-- openid OpenID authentication
|
||||
| | |-- oauth User signin using OAuth
|
||||
| | |-- openid User signin using OpenID
|
||||
| | |-- python Support for Python components
|
||||
| | |-- scheme Support for Scheme components
|
||||
| | |-- server Apache HTTPD server integration
|
||||
|
|
|
@ -528,6 +528,23 @@ AC_ARG_ENABLE(oauth, [AS_HELP_STRING([--enable-oauth], [enable OAuth support [de
|
|||
esac ],
|
||||
[ AC_MSG_RESULT(no)])
|
||||
if test "${want_oauth}" = "true"; then
|
||||
|
||||
# Configure path to Liboauth includes and lib.
|
||||
AC_MSG_CHECKING([for liboauth])
|
||||
AC_ARG_WITH([liboauth], [AC_HELP_STRING([--with-liboauth=PATH], [path to liboauth [default=/usr/local]])], [
|
||||
LIBOAUTH_INCLUDE="${withval}/include"
|
||||
LIBOAUTH_LIB="${withval}/lib"
|
||||
AC_MSG_RESULT("${withval}")
|
||||
], [
|
||||
LIBOAUTH_INCLUDE="/usr/local/include"
|
||||
LIBOAUTH_LIB="/usr/local/lib"
|
||||
AC_MSG_RESULT(/usr/local)
|
||||
])
|
||||
AC_SUBST(LIBOAUTH_INCLUDE)
|
||||
AC_SUBST(LIBOAUTH_LIB)
|
||||
LIBS="-L${LIBOAUTH_LIB} ${defaultlibs}"
|
||||
AC_CHECK_LIB([oauth], [oauth_sign_url2], [], [AC_MSG_ERROR([couldn't find a suitable liboauth, use --with-liboauth=PATH])], [-lssl])
|
||||
|
||||
AM_CONDITIONAL([WANT_OAUTH], true)
|
||||
AC_DEFINE([WANT_OAUTH], 1, [enable OAuth support])
|
||||
|
||||
|
@ -739,14 +756,14 @@ if test "${want_chat}" = "true"; then
|
|||
|
||||
# Configure path to Libstrophe includes and lib.
|
||||
AC_MSG_CHECKING([for libstrophe])
|
||||
AC_ARG_WITH([libstrophe], [AC_HELP_STRING([--with-libstrophe=PATH], [path to libstrophe [default=${HOME}/libstrophe-bin]])], [
|
||||
AC_ARG_WITH([libstrophe], [AC_HELP_STRING([--with-libstrophe=PATH], [path to libstrophe [default=/usr/local]])], [
|
||||
LIBSTROPHE_INCLUDE="${withval}/include"
|
||||
LIBSTROPHE_LIB="${withval}/lib"
|
||||
AC_MSG_RESULT("${withval}")
|
||||
], [
|
||||
LIBSTROPHE_INCLUDE="${HOME}/libstrophe-bin/include"
|
||||
LIBSTROPHE_LIB="${HOME}/libstrophe-bin/lib"
|
||||
AC_MSG_RESULT(${HOME}/libstrophe-bin)
|
||||
LIBSTROPHE_INCLUDE="/usr/local/include"
|
||||
LIBSTROPHE_LIB="/usr/local/lib"
|
||||
AC_MSG_RESULT(/usr/local)
|
||||
])
|
||||
AC_SUBST(LIBSTROPHE_INCLUDE)
|
||||
AC_SUBST(LIBSTROPHE_LIB)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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> ());
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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="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="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>
|
||||
|
|
|
@ -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>
|
||||
|
|
565
sca-cpp/trunk/modules/oauth/mod-oauth1.cpp
Normal file
565
sca-cpp/trunk/modules/oauth/mod-oauth1.cpp
Normal file
|
@ -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
|
||||
};
|
||||
|
||||
}
|
|
@ -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)))
|
||||
|
@ -155,117 +127,102 @@ const failable<int> authenticated(const list<list<value> >& info, request_rec* r
|
|||
return DECLINED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
97
sca-cpp/trunk/modules/oauth/oauth.hpp
Normal file
97
sca-cpp/trunk/modules/oauth/oauth.hpp
Normal file
|
@ -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 */
|
|
@ -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
|
||||
|
36
sca-cpp/trunk/modules/oauth/oauth2-appkey-conf
Executable file
36
sca-cpp/trunk/modules/oauth/oauth2-appkey-conf
Executable file
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -246,6 +246,18 @@ if [ "$?" != "0" ]; then
|
|||
fi
|
||||
cd $build
|
||||
|
||||
# Build Liboauth
|
||||
wget http://liboauth.sourceforge.net/pool/liboauth-0.9.1.tar.gz
|
||||
tar xzf liboauth-0.9.1.tar.gz
|
||||
cd liboauth-0.9.1
|
||||
./configure --prefix=$build/liboauth-0.9.1-bin CURL_CFLAGS="-I$build/curl-7.19.5-bin/include" CURL_LIBS="-L$build/curl-7.19.5-bin/lib -R$build/curl-7.19.5-bin/lib -lcurl"
|
||||
make
|
||||
make install
|
||||
if [ "$?" != "0" ]; then
|
||||
exit $?
|
||||
fi
|
||||
cd $build
|
||||
|
||||
# Build PostgreSQL
|
||||
sudo apt-get -y install libreadline-dev
|
||||
if [ "$?" != "0" ]; then
|
||||
|
@ -324,7 +336,7 @@ git clone git://git.apache.org/tuscany-sca-cpp.git
|
|||
cd tuscany-sca-cpp
|
||||
cp etc/git-exclude .git/info/exclude
|
||||
./bootstrap
|
||||
./configure --prefix=$build/tuscany-sca-cpp-bin --with-curl=$build/curl-7.19.5-bin --with-apr=$build/httpd-2.2.16-bin --with-httpd=$build/httpd-2.2.16-bin --with-memcached=$build/memcached-1.4.4-bin --with-tinycdb=$build/tinycdb-0.77-bin --with-js-include=$build/tracemonkey-bin/include/js --with-js-lib=$build/tracemonkey-bin/lib --with-libcloud=$build/libcloud-0.3.1-bin --enable-threads --enable-python --enable-gae --with-gae=$build/google_appengine --enable-java --with-java=/usr/lib/jvm/java-6-openjdk --enable-webservice --with-libxml2=$build/libxml2-2.7.7-bin --with-axis2c=$build/axis2c-1.6.0-bin --enable-queue --with-qpidc=$build/qpidc-0.6-bin --enable-chat --with-libstrophe=$build/libstrophe-bin --with-vysper=$build/vysper-0.5 --enable-sqldb --with-pgsql=$build/postgresql-9.0-bin --enable-log --with-thrift=$build/thrift-0.2.0-bin --with-scribe=$build/scribe-2.2-bin --enable-openid --with-mod-auth-openid=$build/mod-auth-openid-bin --enable-oauth
|
||||
./configure --prefix=$build/tuscany-sca-cpp-bin --with-curl=$build/curl-7.19.5-bin --with-apr=$build/httpd-2.2.16-bin --with-httpd=$build/httpd-2.2.16-bin --with-memcached=$build/memcached-1.4.4-bin --with-tinycdb=$build/tinycdb-0.77-bin --with-js-include=$build/tracemonkey-bin/include/js --with-js-lib=$build/tracemonkey-bin/lib --with-libcloud=$build/libcloud-0.3.1-bin --enable-threads --enable-python --enable-gae --with-gae=$build/google_appengine --enable-java --with-java=/usr/lib/jvm/java-6-openjdk --enable-webservice --with-libxml2=$build/libxml2-2.7.7-bin --with-axis2c=$build/axis2c-1.6.0-bin --enable-queue --with-qpidc=$build/qpidc-0.6-bin --enable-chat --with-libstrophe=$build/libstrophe-bin --with-vysper=$build/vysper-0.5 --enable-sqldb --with-pgsql=$build/postgresql-9.0-bin --enable-log --with-thrift=$build/thrift-0.2.0-bin --with-scribe=$build/scribe-2.2-bin --enable-openid --with-mod-auth-openid=$build/mod-auth-openid-bin --enable-oauth --with-liboauth=$build/liboauth-0.9.1-bin
|
||||
make
|
||||
make install
|
||||
if [ "$?" != "0" ]; then
|
||||
|
@ -333,5 +345,5 @@ fi
|
|||
cd $build
|
||||
|
||||
# Create bin archive
|
||||
tar czf tuscany-sca-cpp-all-1.0.tar.gz tuscany-sca-cpp tuscany-sca-cpp-bin axis2c-1.6.0-bin libxml2-2.7.7-bin curl-7.19.5-bin httpd-2.2.16-bin tracemonkey-bin google_appengine libstrophe-bin memcached-1.4.4-bin tinycdb-0.77-bin qpidc-0.6-bin vysper-0.5 postgresql-9.0-bin thrift-0.2.0-bin scribe-2.2-bin libcloud-0.3.1-bin htmltidy-bin libopkele-bin mod-auth-openid-bin
|
||||
tar czf tuscany-sca-cpp-all-1.0.tar.gz tuscany-sca-cpp tuscany-sca-cpp-bin axis2c-1.6.0-bin libxml2-2.7.7-bin curl-7.19.5-bin httpd-2.2.16-bin tracemonkey-bin google_appengine libstrophe-bin memcached-1.4.4-bin tinycdb-0.77-bin qpidc-0.6-bin vysper-0.5 postgresql-9.0-bin thrift-0.2.0-bin scribe-2.2-bin libcloud-0.3.1-bin htmltidy-bin libopkele-bin mod-auth-openid-bin liboauth-0.9.1-bin
|
||||
|
||||
|
|
Loading…
Reference in a new issue