From 11522a4b90a4056319cdbc204fffc63780cbfa51 Mon Sep 17 00:00:00 2001 From: jsdelfino Date: Mon, 16 Jul 2012 06:47:49 +0000 Subject: Add an HTTPD auth provider allowing the authentication logic to be implemented by a component, and cleanup the SSL and mod-security config a bit. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1361915 13f79535-47bb-0310-9956-ffa450edef68 --- sca-cpp/trunk/modules/oauth/mod-oauth2.cpp | 141 ++++++++++++++++++++++------- 1 file changed, 110 insertions(+), 31 deletions(-) (limited to 'sca-cpp/trunk/modules/oauth/mod-oauth2.cpp') diff --git a/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp b/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp index cbece191aa..e384a0e742 100644 --- a/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp +++ b/sca-cpp/trunk/modules/oauth/mod-oauth2.cpp @@ -64,6 +64,20 @@ public: perthread_ptr cs; }; +/** + * Authentication provider configuration. + */ +class AuthnProviderConf { +public: + AuthnProviderConf() : name(), provider(NULL) { + } + AuthnProviderConf(const string name, const authn_provider* provider) : name(name), provider(provider) { + } + + string name; + const authn_provider* provider; +}; + /** * Directory configuration. */ @@ -77,8 +91,38 @@ public: bool enabled; string login; list > scopeattrs; + list apcs; }; +/** + * Run the authnz hooks to authenticate a request. + */ +const failable checkAuthnzProviders(const string& user, request_rec* r, const list& apcs) { + if (isNil(apcs)) + return mkfailure("Authentication failure for: " + user, HTTP_UNAUTHORIZED); + const AuthnProviderConf apc = car(apcs); + if (apc.provider == NULL || !apc.provider->check_password) + return checkAuthnzProviders(user, r, cdr(apcs)); + + apr_table_setn(r->notes, AUTHN_PROVIDER_NAME_NOTE, c_str(apc.name)); + const authn_status auth_result = apc.provider->check_password(r, c_str(string("/oauth2/") + user), "password"); + apr_table_unset(r->notes, AUTHN_PROVIDER_NAME_NOTE); + if (auth_result != AUTH_GRANTED) + return checkAuthnzProviders(user, r, cdr(apcs)); + return OK; +} + +const failable checkAuthnz(const string& user, request_rec* r, const list& apcs) { + if (substr(user, 0, 1) == "/") + return mkfailure(string("Encountered FakeBasicAuth spoof: ") + user, HTTP_UNAUTHORIZED); + + if (isNil(apcs)) { + const authn_provider* provider = (const authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, AUTHN_DEFAULT_PROVIDER, AUTHN_PROVIDER_VERSION); + return checkAuthnzProviders(user, r, mklist(AuthnProviderConf(AUTHN_DEFAULT_PROVIDER, provider))); + } + return checkAuthnzProviders(user, r, apcs); +} + /** * Return the user info for a session. */ @@ -89,13 +133,13 @@ const failable userInfo(const value& sid, const memcache::MemCached& mc) /** * Handle an authenticated request. */ -const failable authenticated(const list >& attrs, const list >& info, request_rec* r) { - debug(info, "modoauth2::authenticated::info"); +const failable authenticated(const list >& userinfo, const bool check, request_rec* r, const list >& scopeattrs, const list& apcs) { + debug(userinfo, "modoauth2::authenticated::userinfo"); - if (isNil(attrs)) { + if (isNil(scopeattrs)) { // Store user id in an environment variable - const list id = assoc("id", info); + const list id = assoc("id", userinfo); if (isNil(id) || isNil(cdr(id))) return mkfailure("Couldn't retrieve user id"); apr_table_set(r->subprocess_env, "OAUTH2_ID", apr_pstrdup(r->pool, c_str(cadr(id)))); @@ -104,12 +148,16 @@ const failable authenticated(const list >& attrs, const list
  • user == NULL || r->user[0] == '\0') r->user = apr_pstrdup(r->pool, c_str(cadr(id))); + + // Run the authnz hooks to check the authenticated user + if (check) + return checkAuthnz(r->user == NULL? "" : r->user, r, apcs); return OK; } - // Store each configure OAuth scope attribute in an environment variable - const list a = car(attrs); - const list v = assoc(cadr(a), info); + // Store each configured OAuth scope attribute in an environment variable + const list a = car(scopeattrs); + const list v = assoc(cadr(a), userinfo); if (!isNil(v) && !isNil(cdr(v))) { // Map the REMOTE_USER attribute to the request user field @@ -118,7 +166,7 @@ const failable authenticated(const list >& attrs, const list
  • subprocess_env, apr_pstrdup(r->pool, c_str(car(a))), apr_pstrdup(r->pool, c_str(cadr(v)))); } - return authenticated(cdr(attrs), info, r); + return authenticated(userinfo, check, r, cdr(scopeattrs), apcs); } /** @@ -181,7 +229,8 @@ const failable > profileUserInfo(const value& cid, const list /** * Handle an access_token request. */ -const failable accessToken(const list >& args, request_rec* r, const list >& appkeys, const perthread_ptr& cs, const memcache::MemCached& mc) { +const failable accessToken(const list >& args, request_rec* r, const list >& appkeys, const perthread_ptr& cs, const list >& scopeattrs, const list& apcs, const memcache::MemCached& mc) { + // Extract access_token URI, client ID and authorization code parameters const list state = assoc("state", args); if (isNil(state) || isNil(cdr(state))) @@ -245,17 +294,22 @@ const failable accessToken(const list >& args, request_rec* r, debug(content(profres), "modoauth2::access_token::info"); // Retrieve the user info from the profile - const failable > iv = profileUserInfo(cadr(cid), content(profres)); - if (!hasContent(iv)) - return mkfailure(iv); + const failable > userinfo = profileUserInfo(cadr(cid), content(profres)); + if (!hasContent(userinfo)) + return mkfailure(userinfo); - // Store user info in memcached keyed by session ID + // Validate the authenticated user + const failable authrc = authenticated(content(userinfo), true, r, scopeattrs, apcs); + if (!hasContent(authrc)) + return authrc; + + // Store user info in memcached keyed by a session ID const value sid = string("OAuth2_") + mkrand(); - const failable prc = memcache::put(mklist("tuscanyOAuth2", sid), content(iv), mc); + const failable prc = memcache::put(mklist("tuscanyOAuth2", sid), content(userinfo), mc); if (!hasContent(prc)) return mkfailure(prc); - // Send session ID to the client in a cookie + // Send the session ID to the client in a cookie debug(c_str(openauth::cookie("TuscanyOAuth2", sid, httpd::hostName(r))), "modoauth2::access_token::setcookie"); apr_table_set(r->err_headers_out, "Set-Cookie", c_str(openauth::cookie("TuscanyOAuth2", sid, httpd::hostName(r)))); return httpd::externalRedirect(httpd::url(httpd::unescape(cadr(ref)), r), r); @@ -265,20 +319,19 @@ const failable accessToken(const list >& args, request_rec* r, * Check user authentication. */ static int checkAuthn(request_rec *r) { + gc_scoped_pool pool(r->pool); + // Decline if we're not enabled or AuthType is not set to Open const DirConf& dc = httpd::dirConf(r, &mod_tuscany_oauth2); if (!dc.enabled) return DECLINED; const char* atype = ap_auth_type(r); - debug(atype, "modopenauth::checkAuthn::auth_type"); if (atype == NULL || strcasecmp(atype, "Open")) return DECLINED; - - // Create a scoped memory pool - gc_scoped_pool pool(r->pool); + debug_httpdRequest(r, "modoauth2::checkAuthn::input"); + debug(atype, "modopenauth::checkAuthn::auth_type"); // Get the server configuration - debug_httpdRequest(r, "modoauth2::checkAuthn::input"); const ServerConf& sc = httpd::serverConf(r, &mod_tuscany_oauth2); // Get session id from the request @@ -288,24 +341,33 @@ static int checkAuthn(request_rec *r) { if (substr(content(sid), 0, 7) != "OAuth2_") return DECLINED; - // If we're authenticated store the user info in the request - const failable info = userInfo(content(sid), sc.mc); - if (hasContent(info)) { - r->ap_auth_type = const_cast(atype); - return httpd::reportStatus(authenticated(dc.scopeattrs, content(info), r)); - } + // Extract the user info from the auth session + const failable userinfo = userInfo(content(sid), sc.mc); + if (!hasContent(userinfo)) + return httpd::reportStatus(mkfailure(userinfo)); + r->ap_auth_type = const_cast(atype); + return httpd::reportStatus(authenticated(content(userinfo), false, r, dc.scopeattrs, dc.apcs)); } + // Get the request args + const list > args = httpd::queryArgs(r); + // Handle OAuth authorize request step if (string(r->uri) == "/oauth2/authorize/") { r->ap_auth_type = const_cast(atype); - return httpd::reportStatus(authorize(httpd::queryArgs(r), r, sc.appkeys)); + return httpd::reportStatus(authorize(args, r, sc.appkeys)); } // Handle OAuth access_token request step if (string(r->uri) == "/oauth2/access_token/") { r->ap_auth_type = const_cast(atype); - return httpd::reportStatus(accessToken(httpd::queryArgs(r), r, sc.appkeys, sc.cs, sc.mc)); + const failable authrc = accessToken(args, r, sc.appkeys, sc.cs, dc.scopeattrs, dc.apcs, sc.mc); + + // Redirect to the login page if user is not authorized + if (!hasContent(authrc) && rcode(authrc) == HTTP_UNAUTHORIZED) + return httpd::reportStatus(openauth::login(dc.login, string("/"), 1, r)); + + return httpd::reportStatus(authrc); } // Redirect to the login page, unless we have a session id or an authorization @@ -316,10 +378,11 @@ static int checkAuthn(request_rec *r) { hasContent(openauth::sessionID(r, "TuscanyOpenAuth")) || hasContent(openauth::sessionID(r, "TuscanyOAuth1"))) return DECLINED; - if ((substr(string(r->uri), 0, 8) == "/oauth1/") || !isNil(assoc("openid_identifier", httpd::queryArgs(r)))) + if ((substr(string(r->uri), 0, 8) == "/oauth1/") || !isNil(assoc("openid_identifier", args))) return DECLINED; + r->ap_auth_type = const_cast(atype); - return httpd::reportStatus(openauth::login(dc.login, r)); + return httpd::reportStatus(openauth::login(dc.login, value(), value(), r)); } /** @@ -344,6 +407,7 @@ 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(s, &mod_tuscany_oauth2); debug(httpd::serverName(s), "modoauth2::postConfig::serverName"); @@ -360,7 +424,7 @@ public: } const gc_ptr operator()() const { - return new (gc_new()) http::CURLSession(ca, cert, key, ""); + return new (gc_new()) http::CURLSession(ca, cert, key, "", 0); } private: @@ -374,6 +438,7 @@ private: */ void childInit(apr_pool_t* p, server_rec* s) { gc_scoped_pool pool(p); + ServerConf* psc = (ServerConf*)ap_get_module_config(s->module_config, &mod_tuscany_oauth2); if(psc == NULL) { cfailure << "[Tuscany] Due to one or more errors mod_tuscany_oauth2 loading failed. Causing apache to stop loading." << endl; @@ -445,11 +510,25 @@ const char* confScopeAttr(cmd_parms *cmd, void* c, const char* arg1, const char* dc.scopeattrs = cons >(mklist(arg1, arg2), dc.scopeattrs); return NULL; } +const char* confAuthnProvider(cmd_parms *cmd, void *c, const char* arg) { + gc_scoped_pool pool(cmd->pool); + DirConf& dc = httpd::dirConf(c); + + // Lookup and cache the Authn provider + const authn_provider* provider = (authn_provider*)ap_lookup_provider(AUTHN_PROVIDER_GROUP, arg, AUTHN_PROVIDER_VERSION); + if (provider == NULL) + return apr_psprintf(cmd->pool, "Unknown Authn provider: %s", arg); + if (!provider->check_password) + return apr_psprintf(cmd->pool, "The '%s' Authn provider doesn't support password authentication", arg); + dc.apcs = append(dc.apcs, mklist(AuthnProviderConf(arg, provider))); + return NULL; +} /** * HTTP server module declaration. */ const command_rec commands[] = { + AP_INIT_ITERATE("AuthOAuthProvider", (const char*(*)())confAuthnProvider, NULL, OR_AUTHCFG, "Auth providers for a directory or location"), AP_INIT_TAKE3("AddAuthOAuth2AppKey", (const char*(*)())confAppKey, NULL, RSRC_CONF, "OAuth 2.0 name app-id app-key"), AP_INIT_ITERATE("AddAuthOAuthMemcached", (const char*(*)())confMemcached, NULL, RSRC_CONF, "Memcached server host:port"), AP_INIT_FLAG("AuthOAuth", (const char*(*)())confEnabled, NULL, OR_AUTHCFG, "OAuth 2.0 authentication On | Off"), -- cgit v1.2.3