Fix handling of login session expiration and incorrect caching of login redirect responses.

git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1517413 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
jsdelfino 2013-08-26 03:04:28 +00:00
commit 18b3a48e84
25 changed files with 167 additions and 104 deletions

View file

@ -44,6 +44,14 @@ else
sslsuffix="-ssl" sslsuffix="-ssl"
fi fi
# Configure session crypto
cat >>$root/conf/httpd.conf <<EOF
# Configure session crypto
SessionCryptoPassphrase $pw
SessionCryptoDriver openssl
EOF
# Disallow public access to server resources # Disallow public access to server resources
cat >$root/conf/noauth$sslsuffix.conf <<EOF cat >$root/conf/noauth$sslsuffix.conf <<EOF
# Generated by: form-auth-conf $* # Generated by: form-auth-conf $*
@ -64,8 +72,8 @@ AuthnCacheProvideFor $providers
AuthnCacheContext / AuthnCacheContext /
AuthFormLoginRequiredLocation /login/ AuthFormLoginRequiredLocation /login/
Session On Session On
SessionCookieName TuscanyFormAuth domain=.$host; path=/; secure; httponly SessionCookieName TuscanyFormAuth domain=.$host; max-age=604800; path=/; secure; httponly
SessionCryptoPassphrase $pw SessionMaxAge 0
Require valid-user Require valid-user
</Location> </Location>

View file

@ -32,7 +32,7 @@
<script type="text/javascript"> <script type="text/javascript">
function submitFormSignin() { function submitFormSignin() {
clearauthcookie(); clearAuthCookie();
document.formSignin.httpd_location.value = '/'; document.formSignin.httpd_location.value = '/';
document.formSignin.submit(); document.formSignin.submit();
} }

View file

@ -33,7 +33,7 @@
<form name="signout" action="/login" method="GET"> <form name="signout" action="/login" method="GET">
<script type="text/javascript"> <script type="text/javascript">
function submitSignout() { function submitSignout() {
clearauthcookie(); clearAuthCookie();
document.signout.submit(); document.signout.submit();
return true; return true;
} }

View file

@ -423,6 +423,7 @@ template<typename R> const failable<list<R> > apply(const list<list<string> >& h
// Set the request headers // Set the request headers
curl_slist* hl = headers(NULL, car(hdr)); curl_slist* hl = headers(NULL, car(hdr));
hl = curl_slist_append(hl, "X-Accept: text/x-scheme; charset=utf-8"); hl = curl_slist_append(hl, "X-Accept: text/x-scheme; charset=utf-8");
hl = curl_slist_append(hl, "X-Cache-Control: no-cache");
curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl); curl_easy_setopt(ch, CURLOPT_HTTPHEADER, hl);
// Convert request body to a string // Convert request body to a string

View file

@ -74,7 +74,7 @@ ServerSignature Off
ServerTokens Prod ServerTokens Prod
Timeout 45 Timeout 45
RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500 RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
LimitRequestBody 1048576 LimitRequestBody 8388608
HostNameLookups Off HostNameLookups Off
#MaxKeepAliveRequests 25 #MaxKeepAliveRequests 25
#MaxConnectionsPerChild 100 #MaxConnectionsPerChild 100
@ -93,9 +93,9 @@ AddCharset utf-8 .html .js .css
# Configure cache control # Configure cache control
<Directory /> <Directory />
ExpiresActive On SetEnvIf X-Cache-Control no-cache x-no-cache
ExpiresDefault A604800 Header merge Cache-Control max-age=604800 env=!x-no-cache
Header onsuccess merge Cache-Control public env=!private-cache Header merge Cache-Control public env=!x-no-cache
</Directory> </Directory>
# Enable Linux Kernel sendfile # Enable Linux Kernel sendfile
@ -262,8 +262,6 @@ cat >$root/conf/pubauth.conf <<EOF
AuthType None AuthType None
Session Off Session Off
Require all granted Require all granted
# Mark login page with a header
Header set X-Login open-auth
</Location> </Location>
<Location /login/dologin> <Location /login/dologin>
Session Off Session Off

View file

@ -418,9 +418,7 @@ const failable<int> writeResult(const failable<list<string> >& ls, const string&
const string ob(str(os)); const string ob(str(os));
// Make sure browsers come back and check for updated dynamic content // Make sure browsers come back and check for updated dynamic content
apr_table_set(r->headers_out, "Cache-Control", "must-revalidate, max-age=0"); apr_table_setn(r->headers_out, "Cache-Control", "must-revalidate, max-age=0");
apr_table_set(r->headers_out, "Expires", "Tue, 01 Jan 1980 00:00:00 GMT");
apr_table_set(r->subprocess_env, "private-cache", "1");
// Compute and return an Etag for the returned content // Compute and return an Etag for the returned content
const string etag(ap_md5_binary(r->pool, (const unsigned char*)c_str(ob), (int)length(ob))); const string etag(ap_md5_binary(r->pool, (const unsigned char*)c_str(ob), (int)length(ob)));
@ -698,8 +696,8 @@ const int externalRedirect(const string& uri, request_rec* const r) {
debug(uri, "httpd::externalRedirect"); debug(uri, "httpd::externalRedirect");
r->status = HTTP_MOVED_TEMPORARILY; r->status = HTTP_MOVED_TEMPORARILY;
apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(uri))); apr_table_setn(r->headers_out, "Location", apr_pstrdup(r->pool, c_str(uri)));
apr_table_setn(r->headers_out, "Cache-Control", "no-store"); apr_table_setn(r->headers_out, "Cache-Control", "no-cache, no-store, must-revalidate, max-age=0");
apr_table_addn(r->err_headers_out, "Cache-Control", "no-store"); apr_table_setn(r->err_headers_out, "Cache-Control", "no-cache, no-store, must-revalidate, max-age=0");
r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:/") + uri)); r->filename = apr_pstrdup(r->pool, c_str(string("/redirect:/") + uri));
return HTTP_MOVED_TEMPORARILY; return HTTP_MOVED_TEMPORARILY;
} }

View file

@ -28,6 +28,7 @@
* - OAuth2 using Tuscany's mod-tuscany-oauth2 * - OAuth2 using Tuscany's mod-tuscany-oauth2
* - OpenID using mod_auth_openid * - OpenID using mod_auth_openid
* - Form-based using HTTPD's mod_auth_form * - Form-based using HTTPD's mod_auth_form
* - HTTP basic auth using mod_auth_basic
* - SSL certificate using SSLFakeBasicAuth and mod_auth_basic * - SSL certificate using SSLFakeBasicAuth and mod_auth_basic
*/ */
@ -142,7 +143,7 @@ const failable<int> checkAuthnzProviders(const string& user, const string& pw, r
} }
const failable<int> checkAuthnz(const string& user, const string& pw, request_rec* const r, const DirConf& dc) { const failable<int> checkAuthnz(const string& user, const string& pw, request_rec* const r, const DirConf& dc) {
if(substr(user, 0, 1) == "/" && pw == "password") if(substr(user, 0, 1) == "/")
return mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED); return mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED);
if(isNull((const list<AuthnProviderConf>)dc.apcs)) { if(isNull((const list<AuthnProviderConf>)dc.apcs)) {
@ -224,9 +225,17 @@ const failable<int> authenticated(const list<value>& info, request_rec* const r)
const list<value> id = assoc<value>("id", info); const list<value> id = assoc<value>("id", info);
if(isNull(id) || isNull(cdr(id))) if(isNull(id) || isNull(cdr(id)))
return mkfailure<int>("Couldn't retrieve user id", HTTP_UNAUTHORIZED); return mkfailure<int>("Couldn't retrieve user id", HTTP_UNAUTHORIZED);
r->user = apr_pstrdup(r->pool, c_str(cadr(id))); const string sid = cadr(id);
if (find(sid, '@') != length(sid))
apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "EMAIL"), apr_pstrdup(r->pool, c_str(sid)));
r->user = apr_pstrdup(r->pool, c_str(sid));
apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "NICKNAME"), apr_pstrdup(r->pool, c_str(cadr(id)))); // Update the request user field with the authorized user id returned by the authnz hooks
const char* auser = apr_table_get(r->subprocess_env, "AUTHZ_USER");
if (auser != NULL)
r->user = apr_pstrdup(r->pool, auser);
apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, "NICKNAME"), apr_pstrdup(r->pool, r->user));
return OK; return OK;
} }

View file

@ -59,6 +59,14 @@ LoadModule mod_tuscany_openauth $here/libmod_tuscany_openauth$libsuffix
EOF EOF
# Configure session crypto
cat >>$root/conf/httpd.conf <<EOF
# Configure session crypto
SessionCryptoPassphrase $pw
SessionCryptoDriver openssl
EOF
# Disallow public access to server resources # Disallow public access to server resources
cat >$root/conf/noauth$sslsuffix.conf <<EOF cat >$root/conf/noauth$sslsuffix.conf <<EOF
# Generated by: open-auth-conf $* # Generated by: open-auth-conf $*
@ -78,7 +86,7 @@ AuthnCacheProvideFor $providers
AuthnCacheContext / AuthnCacheContext /
AuthOpenAuthLoginPage /login/ AuthOpenAuthLoginPage /login/
Session On Session On
SessionCryptoPassphrase $pw SessionMaxAge 0
AuthOpenAuth On AuthOpenAuth On
Require valid-user Require valid-user
</Location> </Location>

View file

@ -71,6 +71,16 @@ const maybe<string> sessionID(const request_rec* const r, const string& key) {
return sessionID(tokenize(";", c), key); return sessionID(tokenize(";", c), key);
} }
/**
* Convert a number of seconds to an expiration date.
*/
const string expires(const int s) {
const time_t t = time(NULL) + s;
char exp[32];
strftime(exp, 32, "%a, %d-%b-%Y %H:%M:%S GMT", gmtime(&t));
return string(exp);
}
/** /**
* Convert a session id to a cookie string. * Convert a session id to a cookie string.
*/ */
@ -80,10 +90,8 @@ const string cookie(const string& key, const string& sid, const string& domain)
debug(c, "openauth::cookie"); debug(c, "openauth::cookie");
return c; return c;
} }
const time_t t = time(NULL) + 86400; const string exp = "604800";
char exp[32]; const string c = key + string("=") + sid + (length(exp) != 0? string("; max-age=") + exp : emptyString) + "; domain=." + httpd::realm(domain) + "; path=/; secure; httponly";
strftime(exp, 32, "%a, %d-%b-%Y %H:%M:%S GMT", gmtime(&t));
const string c = key + string("=") + sid + "; expires=" + string(exp) + "; domain=." + httpd::realm(domain) + "; path=/; secure; httponly";
debug(c, "openauth::cookie"); debug(c, "openauth::cookie");
return c; return c;
} }
@ -92,6 +100,16 @@ const string cookie(const string& key, const string& sid, const string& domain)
* Redirect to the configured login page. * Redirect to the configured login page.
*/ */
const failable<int> login(const string& page, const value& ref, const value& attempt, request_rec* const r) { const failable<int> login(const string& page, const value& ref, const value& attempt, request_rec* const r) {
// Don't redirect non-cacheable requests, just respond with an uncacheable 403 response
const char* cc = apr_table_get(r->headers_in, "X-Cache-Control");
if(cc != NULL && !strcmp(cc, "no-cache")) {
apr_table_setn(r->headers_out, "Cache-Control", "no-cache, no-store, must-revalidate, max-age=0");
apr_table_setn(r->err_headers_out, "Cache-Control", "no-cache, no-store, must-revalidate, max-age=0");
return HTTP_FORBIDDEN;
}
// Redirect to the login page
const list<value> rarg = ref == string("/")? nilListValue : mklist<value>(mklist<value>("openauth_referrer", httpd::escape(httpd::url(isNull(ref)? r->uri : ref, r)))); const list<value> rarg = ref == string("/")? nilListValue : mklist<value>(mklist<value>("openauth_referrer", httpd::escape(httpd::url(isNull(ref)? r->uri : ref, r))));
const list<value> aarg = isNull(attempt)? nilListValue : mklist<value>(mklist<value>("openauth_attempt", attempt)); const list<value> aarg = isNull(attempt)? nilListValue : mklist<value>(mklist<value>("openauth_attempt", attempt));
const list<value> largs = append(rarg, aarg); const list<value> largs = append(rarg, aarg);

View file

@ -55,17 +55,17 @@ if (typeof(oauthReferrer()) == 'undefined') {
document.location = '/'; document.location = '/';
} }
function clearauthcookie() { function clearAuthCookie() {
document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
return true; return true;
} }
function submitSignin2(w) { function submitSignin2(w) {
parms = w(); parms = w();
clearauthcookie(); clearAuthCookie();
document.signin2.oauth2_authorize.value = parms[0]; document.signin2.oauth2_authorize.value = parms[0];
document.signin2.oauth2_access_token.value = parms[1]; document.signin2.oauth2_access_token.value = parms[1];
document.signin2.oauth2_client_id.value = parms[2]; document.signin2.oauth2_client_id.value = parms[2];
@ -89,7 +89,7 @@ function withGithub() {
function submitSignin1(w) { function submitSignin1(w) {
parms = w(); parms = w();
clearauthcookie(); clearAuthCookie();
document.signin1.oauth1_request_token.value = parms[0]; document.signin1.oauth1_request_token.value = parms[0];
document.signin1.oauth1_authorize.value = parms[1]; document.signin1.oauth1_authorize.value = parms[1];
document.signin1.oauth1_access_token.value = parms[2]; document.signin1.oauth1_access_token.value = parms[2];

View file

@ -29,16 +29,16 @@
<h1>Sign in with a Form, an OpenID provider or an OAuth provider</h1> <h1>Sign in with a Form, an OpenID provider or an OAuth provider</h1>
<script type="text/javascript"> <script type="text/javascript">
function clearauthcookie() { function clearAuthCookie() {
document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
return true; return true;
} }
function submitFormSignin() { function submitFormSignin() {
clearauthcookie(); clearAuthCookie();
document.formSignin.httpd_location.value = '/'; document.formSignin.httpd_location.value = '/';
document.formSignin.submit(); document.formSignin.submit();
} }
@ -69,7 +69,7 @@ if (typeof(openauthReferrer()) == 'undefined') {
} }
function submitOpenIDSignin(w) { function submitOpenIDSignin(w) {
clearauthcookie(); clearAuthCookie();
document.openIDSignin.openid_identifier.value = w(); document.openIDSignin.openid_identifier.value = w();
document.openIDSignin.action = openauthReferrer(); document.openIDSignin.action = openauthReferrer();
document.openIDSignin.submit(); document.openIDSignin.submit();
@ -117,7 +117,7 @@ function withXRDSEndpoint() {
function submitOAuth2Signin(w) { function submitOAuth2Signin(w) {
parms = w(); parms = w();
clearauthcookie(); clearAuthCookie();
document.oauth2Signin.oauth2_authorize.value = parms[0]; document.oauth2Signin.oauth2_authorize.value = parms[0];
document.oauth2Signin.oauth2_access_token.value = parms[1]; document.oauth2Signin.oauth2_access_token.value = parms[1];
document.oauth2Signin.oauth2_client_id.value = parms[2]; document.oauth2Signin.oauth2_client_id.value = parms[2];
@ -141,7 +141,7 @@ function withGithub() {
function submitOAuth1Signin(w) { function submitOAuth1Signin(w) {
parms = w(); parms = w();
clearauthcookie(); clearAuthCookie();
document.oauth1Signin.oauth1_request_token.value = parms[0]; document.oauth1Signin.oauth1_request_token.value = parms[0];
document.oauth1Signin.oauth1_authorize.value = parms[1]; document.oauth1Signin.oauth1_authorize.value = parms[1];
document.oauth1Signin.oauth1_access_token.value = parms[2]; document.oauth1Signin.oauth1_access_token.value = parms[2];

View file

@ -31,16 +31,16 @@
<form name="signout" action="/login" method="GET"> <form name="signout" action="/login" method="GET">
<script type="text/javascript"> <script type="text/javascript">
function clearauthcookie() { function clearAuthCookie() {
document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
return true; return true;
} }
function submitSignout() { function submitSignout() {
clearauthcookie(); clearAuthCookie();
document.signout.submit(); document.signout.submit();
return true; return true;
} }

View file

@ -139,7 +139,7 @@ const failable<value> userInfo(const value& sid, const memcache::MemCached& mc)
/** /**
* Handle an authenticated request. * Handle an authenticated request.
*/ */
const failable<int> authenticated(const list<value>& userinfo, const bool check, request_rec* const r, const list<value>& scopeattrs, const list<AuthnProviderConf>& apcs) { const failable<int> authenticated(const list<value>& userinfo, request_rec* const r, const list<value>& scopeattrs, const list<AuthnProviderConf>& apcs) {
debug(userinfo, "modoauth2::authenticated::userinfo"); debug(userinfo, "modoauth2::authenticated::userinfo");
if (isNull(scopeattrs)) { if (isNull(scopeattrs)) {
@ -156,8 +156,15 @@ const failable<int> authenticated(const list<value>& userinfo, const bool check,
r->user = apr_pstrdup(r->pool, c_str(cadr(id))); r->user = apr_pstrdup(r->pool, c_str(cadr(id)));
// Run the authnz hooks to check the authenticated user // Run the authnz hooks to check the authenticated user
if (check) const failable<int> arc = checkAuthnz(r->user == NULL? emptyString : r->user, r, apcs);
return checkAuthnz(r->user == NULL? emptyString : r->user, r, apcs); if (!hasContent(arc))
return arc;
// Update the request user field with the authorized user id returned by the authnz hooks
const char* auser = apr_table_get(r->subprocess_env, "AUTHZ_USER");
if (auser != NULL)
r->user = apr_pstrdup(r->pool, auser);
return OK; return OK;
} }
@ -172,7 +179,7 @@ const failable<int> authenticated(const list<value>& userinfo, const bool check,
else else
apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, c_str(car(a))), apr_pstrdup(r->pool, c_str(cadr(v)))); apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, c_str(car(a))), apr_pstrdup(r->pool, c_str(cadr(v))));
} }
return authenticated(userinfo, check, r, cdr(scopeattrs), apcs); return authenticated(userinfo, r, cdr(scopeattrs), apcs);
} }
/** /**
@ -293,8 +300,7 @@ const failable<int> authorize(const list<value>& args, request_rec* const r, con
/** /**
* Extract user info from a profile/info response. * Extract user info from a profile/info response.
* TODO This currently only works for Twitter, Foursquare and LinkedIn. * TODO Make this configurable
* User profile parsing needs to be made configurable.
*/ */
const failable<list<value> > profileUserInfo(const value& cid, const string& info) { const failable<list<value> > profileUserInfo(const value& cid, const string& info) {
const string b = substr(info, 0, 1); const string b = substr(info, 0, 1);
@ -424,7 +430,7 @@ const failable<int> accessToken(const list<value>& args, request_rec* r, const l
return mkfailure<int>(userinfo); return mkfailure<int>(userinfo);
// Validate the authenticated user // Validate the authenticated user
const failable<int> authrc = authenticated(content(userinfo), true, r, scopeattrs, apcs); const failable<int> authrc = authenticated(content(userinfo), r, scopeattrs, apcs);
if (!hasContent(authrc)) if (!hasContent(authrc))
return authrc; return authrc;
@ -471,7 +477,7 @@ static int checkAuthn(request_rec *r) {
if (!hasContent(userinfo)) if (!hasContent(userinfo))
return openauth::reportStatus(mkfailure<int>(reason(userinfo), HTTP_UNAUTHORIZED), dc.login, nilValue, r); return openauth::reportStatus(mkfailure<int>(reason(userinfo), HTTP_UNAUTHORIZED), dc.login, nilValue, r);
r->ap_auth_type = const_cast<char*>(atype); r->ap_auth_type = const_cast<char*>(atype);
return openauth::reportStatus(authenticated(content(userinfo), false, r, dc.scopeattrs, dc.apcs), dc.login, nilValue, r); return openauth::reportStatus(authenticated(content(userinfo), r, dc.scopeattrs, dc.apcs), dc.login, nilValue, r);
} }
// Get the request args // Get the request args

View file

@ -133,7 +133,7 @@ const failable<value> userInfo(const value& sid, const memcache::MemCached& mc)
/** /**
* Handle an authenticated request. * Handle an authenticated request.
*/ */
const failable<int> authenticated(const list<value>& userinfo, const bool check, request_rec* const r, const list<value>& scopeattrs, const list<AuthnProviderConf>& apcs) { const failable<int> authenticated(const list<value>& userinfo, request_rec* const r, const list<value>& scopeattrs, const list<AuthnProviderConf>& apcs) {
debug(userinfo, "modoauth2::authenticated::userinfo"); debug(userinfo, "modoauth2::authenticated::userinfo");
if (isNull(scopeattrs)) { if (isNull(scopeattrs)) {
@ -150,8 +150,15 @@ const failable<int> authenticated(const list<value>& userinfo, const bool check,
r->user = apr_pstrdup(r->pool, c_str(cadr(id))); r->user = apr_pstrdup(r->pool, c_str(cadr(id)));
// Run the authnz hooks to check the authenticated user // Run the authnz hooks to check the authenticated user
if (check) const failable<int> arc = checkAuthnz(r->user == NULL? emptyString : r->user, r, apcs);
return checkAuthnz(r->user == NULL? emptyString : r->user, r, apcs); if (!hasContent(arc))
return arc;
// Update the request user field with the authorized user id returned by the authnz hooks
const char* auser = apr_table_get(r->subprocess_env, "AUTHZ_USER");
if (auser != NULL)
r->user = apr_pstrdup(r->pool, auser);
return OK; return OK;
} }
@ -166,7 +173,7 @@ const failable<int> authenticated(const list<value>& userinfo, const bool check,
else else
apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, c_str(car(a))), apr_pstrdup(r->pool, c_str(cadr(v)))); apr_table_set(r->subprocess_env, apr_pstrdup(r->pool, c_str(car(a))), apr_pstrdup(r->pool, c_str(cadr(v))));
} }
return authenticated(userinfo, check, r, cdr(scopeattrs), apcs); return authenticated(userinfo, r, cdr(scopeattrs), apcs);
} }
/** /**
@ -219,8 +226,7 @@ const failable<int> authorize(const list<value>& args, request_rec* const r, con
/** /**
* Extract user info from a profile/info response. * Extract user info from a profile/info response.
* TODO This currently only works for Facebook and Gowalla. * TODO Make this configurable.
* User profile parsing needs to be made configurable.
*/ */
const failable<list<value> > profileUserInfo(const value& cid, const list<value>& info) { const failable<list<value> > profileUserInfo(const value& cid, const list<value>& info) {
return cons<value>(mklist<value>("realm", cid), info); return cons<value>(mklist<value>("realm", cid), info);
@ -299,7 +305,7 @@ const failable<int> accessToken(const list<value>& args, request_rec* r, const l
return mkfailure<int>(userinfo); return mkfailure<int>(userinfo);
// Validate the authenticated user // Validate the authenticated user
const failable<int> authrc = authenticated(content(userinfo), true, r, scopeattrs, apcs); const failable<int> authrc = authenticated(content(userinfo), r, scopeattrs, apcs);
if (!hasContent(authrc)) if (!hasContent(authrc))
return authrc; return authrc;
@ -346,7 +352,7 @@ static int checkAuthn(request_rec *r) {
if (!hasContent(userinfo)) if (!hasContent(userinfo))
return openauth::reportStatus(mkfailure<int>(reason(userinfo), HTTP_UNAUTHORIZED), dc.login, nilValue, r); return openauth::reportStatus(mkfailure<int>(reason(userinfo), HTTP_UNAUTHORIZED), dc.login, nilValue, r);
r->ap_auth_type = const_cast<char*>(atype); r->ap_auth_type = const_cast<char*>(atype);
return openauth::reportStatus(authenticated(content(userinfo), false, r, dc.scopeattrs, dc.apcs), dc.login, nilValue, r); return openauth::reportStatus(authenticated(content(userinfo), r, dc.scopeattrs, dc.apcs), dc.login, nilValue, r);
} }
// Get the request args // Get the request args

View file

@ -55,16 +55,16 @@ if (typeof(openidReferrer()) == 'undefined') {
document.location = '/'; document.location = '/';
} }
function clearauthcookie() { function clearAuthCookie() {
document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
return true; return true;
} }
function submitSignin(w) { function submitSignin(w) {
clearauthcookie(); clearAuthCookie();
document.signin.openid_identifier.value = w(); document.signin.openid_identifier.value = w();
document.signin.action = openidReferrer(); document.signin.action = openidReferrer();
document.signin.submit(); document.signin.submit();

View file

@ -31,16 +31,16 @@
<form name="signout" action="/login" method="GET"> <form name="signout" action="/login" method="GET">
<script type="text/javascript"> <script type="text/javascript">
function clearauthcookie() { function clearAuthCookie() {
document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOpenAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOAuth1=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOAuth2=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainname(window.location.hostname) + '; path=/'; document.cookie = 'TuscanyOpenIDAuth=; expires=' + new Date(1970,01,01).toGMTString() + '; domain=.' + domainName(window.location.hostname) + '; path=/';
return true; return true;
} }
function submitSignout() { function submitSignout() {
clearauthcookie(); clearAuthCookie();
document.signout.submit(); document.signout.submit();
return true; return true;
} }

View file

@ -1266,14 +1266,14 @@ int handler(request_rec* r) {
/** /**
* Call an authenticator component to check a user's password. * Call an authenticator component to check a user's password.
*/ */
authn_status checkPassword(request_rec* r, const char* u, const char* p) { authn_status checkAuthnz(request_rec* r, const char* u, const char* p) {
const gc_scoped_pool sp(r->pool); const gc_scoped_pool sp(r->pool);
// Prevent FakeBasicAuth spoofing // Prevent FakeBasicAuth spoofing
const string user = u; const string user = u;
const string password = p; debug(user, "modeval::checkAuthnz::user");
debug(user, "modeval::checkPassword::user"); const bool extauth = find(user, "/") != length(user);
if (substr(user, 0, 1) != "/" && find(user, "/") != length(user) && password == "password") { if (extauth && substr(user, 0, 1) != "/") {
mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED); mkfailure<int>(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED);
return AUTH_DENIED; return AUTH_DENIED;
} }
@ -1286,35 +1286,46 @@ authn_status checkPassword(request_rec* r, const char* u, const char* p) {
} }
// Retrieve the user's password hash // Retrieve the user's password hash
const list<value> uid = pathValues(user); const list<value> uid = extauth? cdr(pathValues(user)) : pathValues(user);
const failable<value> val = failableResult(((value)sc.vhostc.authenticator)(cons<value>("get", mklist<value>(uid)))); const failable<value> val = failableResult(((value)sc.vhostc.authenticator)(cons<value>("get", mklist<value>(uid))));
if (!hasContent(val)) { if (!hasContent(val) || isNull(content(val))) {
mkfailure<int>(string("SCA authentication check user failed, user not found: ") + user, rcode(val), user != "admin"); mkfailure<int>(string("SCA authentication check user failed, user not found: ") + user, rcode(val), user != "admin");
return AUTH_USER_NOT_FOUND; return AUTH_USER_NOT_FOUND;
} }
const value hval = content(val); debug(content(val), "modeval::checkAuthnz::val");
const list<value> hcontent = isList(hval) && !isNull(hval) && isList(car<value>(hval)) && !isNull(car<value>(hval))? assoc<value>(value("content"), cdr<value>(car<value>(hval))) : nilListValue;
const list<value> hassoc = isNull(hcontent)? nilListValue : assoc<value>(value("hash"), cdr<value>(hcontent)); const value authn = cdr<value>(car<value>(content(val)));
if (isNull(hassoc)) { const list<value> acontent = assoc<value>(value("content"), authn);
const list<value> aauthn = isNull(acontent)? nilListValue : assoc<value>(value("authn"), cdr<value>(acontent));
const list<value> ahash = isNull(aauthn)? nilListValue : assoc<value>(value("hash"), cdr<value>(aauthn));
if (isNull(ahash)) {
mkfailure<int>(string("SCA authentication check user failed, hash not found: ") + user, -1, user != "admin"); mkfailure<int>(string("SCA authentication check user failed, hash not found: ") + user, -1, user != "admin");
return AUTH_USER_NOT_FOUND; return AUTH_USER_NOT_FOUND;
} }
const string hash = cadr<value>(hassoc); const string uhash = cadr<value>(ahash);
if (length(hash) == 0) { if (length(uhash) == 0) {
mkfailure<int>(string("SCA authentication check user failed: ") + user); mkfailure<int>(string("SCA authentication check user failed: ") + user);
return AUTH_USER_NOT_FOUND; return AUTH_USER_NOT_FOUND;
} }
// Cache the hash in the auth cache provider, if available // Use a fixed hash of the string 'password' for externally authenticated users as they
if (authnCacheStore != NULL) // don't present an actual password
authnCacheStore(r, "component", u, NULL, c_str(hash)); const string hash = extauth? "$apr1$OPUrN0Kr$/tc96p1r6LdmvB0mly6gg0" : uhash;
// Validate the presented password against the hash // Validate the password against the hash
const apr_status_t rv = apr_password_validate(p, c_str(hash)); const apr_status_t rv = apr_password_validate(p, c_str(hash));
if (rv != APR_SUCCESS) { if (rv != APR_SUCCESS) {
mkfailure<int>(string("SCA authentication user password check failed: ") + user); mkfailure<int>(string("SCA authentication user password check failed: ") + user);
return AUTH_DENIED; return AUTH_DENIED;
} }
// Update the user field of the request with the authenticated user
const list<value> auser = assoc<value>(value("user"), cdr<value>(aauthn));
if (!isNull(auser)) {
debug(c_str(cadr(auser)), "modeval::checkAuthnz::auth_user");
apr_table_set(r->subprocess_env, "AUTHZ_USER", apr_pstrdup(r->pool, c_str(cadr(auser))));
}
return AUTH_GRANTED; return AUTH_GRANTED;
} }
@ -1569,7 +1580,7 @@ const command_rec commands[] = {
const authn_provider AuthnProvider = { const authn_provider AuthnProvider = {
&checkPassword, &checkAuthnz,
NULL NULL
}; };

View file

@ -57,7 +57,7 @@ if (typeof(openauthReferrer()) == 'undefined') {
} }
function submitOpenIDSignin(w) { function submitOpenIDSignin(w) {
clearauthcookie(); clearAuthCookie();
document.openIDSignin.openid_identifier.value = w(); document.openIDSignin.openid_identifier.value = w();
document.openIDSignin.action = openauthReferrer(); document.openIDSignin.action = openauthReferrer();
document.openIDSignin.submit(); document.openIDSignin.submit();
@ -105,7 +105,7 @@ function withXRDSEndpoint() {
function submitOAuth2Signin(w) { function submitOAuth2Signin(w) {
parms = w(); parms = w();
clearauthcookie(); clearAuthCookie();
document.oauth2Signin.oauth2_authorize.value = parms[0]; document.oauth2Signin.oauth2_authorize.value = parms[0];
document.oauth2Signin.oauth2_access_token.value = parms[1]; document.oauth2Signin.oauth2_access_token.value = parms[1];
document.oauth2Signin.oauth2_client_id.value = parms[2]; document.oauth2Signin.oauth2_client_id.value = parms[2];
@ -129,7 +129,7 @@ function withGithub() {
function submitOAuth1Signin(w) { function submitOAuth1Signin(w) {
parms = w(); parms = w();
clearauthcookie(); clearAuthCookie();
document.oauth1Signin.oauth1_request_token.value = parms[0]; document.oauth1Signin.oauth1_request_token.value = parms[0];
document.oauth1Signin.oauth1_authorize.value = parms[1]; document.oauth1Signin.oauth1_authorize.value = parms[1];
document.oauth1Signin.oauth1_access_token.value = parms[2]; document.oauth1Signin.oauth1_access_token.value = parms[2];

View file

@ -33,7 +33,7 @@
<form name="signout" action="/login" method="GET"> <form name="signout" action="/login" method="GET">
<script type="text/javascript"> <script type="text/javascript">
function submitSignout() { function submitSignout() {
clearauthcookie(); clearAuthCookie();
document.signout.submit(); document.signout.submit();
return true; return true;
} }

View file

@ -57,7 +57,7 @@ if (typeof(openauthReferrer()) == 'undefined') {
} }
function submitOpenIDSignin(w) { function submitOpenIDSignin(w) {
clearauthcookie(); clearAuthCookie();
document.openIDSignin.openid_identifier.value = w(); document.openIDSignin.openid_identifier.value = w();
document.openIDSignin.action = openauthReferrer(); document.openIDSignin.action = openauthReferrer();
document.openIDSignin.submit(); document.openIDSignin.submit();
@ -105,7 +105,7 @@ function withXRDSEndpoint() {
function submitOAuth2Signin(w) { function submitOAuth2Signin(w) {
parms = w(); parms = w();
clearauthcookie(); clearAuthCookie();
document.oauth2Signin.oauth2_authorize.value = parms[0]; document.oauth2Signin.oauth2_authorize.value = parms[0];
document.oauth2Signin.oauth2_access_token.value = parms[1]; document.oauth2Signin.oauth2_access_token.value = parms[1];
document.oauth2Signin.oauth2_client_id.value = parms[2]; document.oauth2Signin.oauth2_client_id.value = parms[2];
@ -129,7 +129,7 @@ function withGithub() {
function submitOAuth1Signin(w) { function submitOAuth1Signin(w) {
parms = w(); parms = w();
clearauthcookie(); clearAuthCookie();
document.oauth1Signin.oauth1_request_token.value = parms[0]; document.oauth1Signin.oauth1_request_token.value = parms[0];
document.oauth1Signin.oauth1_authorize.value = parms[1]; document.oauth1Signin.oauth1_authorize.value = parms[1];
document.oauth1Signin.oauth1_access_token.value = parms[2]; document.oauth1Signin.oauth1_access_token.value = parms[2];

View file

@ -33,7 +33,7 @@
<form name="signout" action="/login" method="GET"> <form name="signout" action="/login" method="GET">
<script type="text/javascript"> <script type="text/javascript">
function submitSignout() { function submitSignout() {
clearauthcookie(); clearAuthCookie();
document.signout.submit(); document.signout.submit();
return true; return true;
} }

View file

@ -57,7 +57,7 @@ if (typeof(openauthReferrer()) == 'undefined') {
} }
function submitOpenIDSignin(w) { function submitOpenIDSignin(w) {
clearauthcookie(); clearAuthCookie();
document.openIDSignin.openid_identifier.value = w(); document.openIDSignin.openid_identifier.value = w();
document.openIDSignin.action = openauthReferrer(); document.openIDSignin.action = openauthReferrer();
document.openIDSignin.submit(); document.openIDSignin.submit();
@ -105,7 +105,7 @@ function withXRDSEndpoint() {
function submitOAuth2Signin(w) { function submitOAuth2Signin(w) {
parms = w(); parms = w();
clearauthcookie(); clearAuthCookie();
document.oauth2Signin.oauth2_authorize.value = parms[0]; document.oauth2Signin.oauth2_authorize.value = parms[0];
document.oauth2Signin.oauth2_access_token.value = parms[1]; document.oauth2Signin.oauth2_access_token.value = parms[1];
document.oauth2Signin.oauth2_client_id.value = parms[2]; document.oauth2Signin.oauth2_client_id.value = parms[2];
@ -129,7 +129,7 @@ function withGithub() {
function submitOAuth1Signin(w) { function submitOAuth1Signin(w) {
parms = w(); parms = w();
clearauthcookie(); clearAuthCookie();
document.oauth1Signin.oauth1_request_token.value = parms[0]; document.oauth1Signin.oauth1_request_token.value = parms[0];
document.oauth1Signin.oauth1_authorize.value = parms[1]; document.oauth1Signin.oauth1_authorize.value = parms[1];
document.oauth1Signin.oauth1_access_token.value = parms[2]; document.oauth1Signin.oauth1_access_token.value = parms[2];

View file

@ -33,7 +33,7 @@
<form name="signout" action="/login" method="GET"> <form name="signout" action="/login" method="GET">
<script type="text/javascript"> <script type="text/javascript">
function submitSignout() { function submitSignout() {
clearauthcookie(); clearAuthCookie();
document.signout.submit(); document.signout.submit();
return true; return true;
} }

View file

@ -32,7 +32,7 @@
<script type="text/javascript"> <script type="text/javascript">
function submitFormSignin() { function submitFormSignin() {
clearauthcookie(); clearAuthCookie();
document.formSignin.httpd_location.value = '/'; document.formSignin.httpd_location.value = '/';
document.formSignin.submit(); document.formSignin.submit();
} }

View file

@ -33,7 +33,7 @@
<form name="signout" action="/login" method="GET"> <form name="signout" action="/login" method="GET">
<script type="text/javascript"> <script type="text/javascript">
function submitSignout() { function submitSignout() {
clearauthcookie(); clearAuthCookie();
document.signout.submit(); document.signout.submit();
return true; return true;
} }