SSPCPP-616 - clean up concatenated string literals
[shibboleth/cpp-sp.git] / shibsp / handler / impl / ExternalAuthHandler.cpp
index 5c12068..c17ca1f 100644 (file)
@@ -61,6 +61,9 @@ using namespace opensaml;
 using saml2::NameID;
 using saml2::AuthnStatement;
 using saml2::AuthnContext;
+# ifndef min
+#  define min(a,b)            (((a) < (b)) ? (a) : (b))
+# endif
 #endif
 
 using namespace shibspconstants;
@@ -91,7 +94,11 @@ namespace shibsp {
 
     private:
         pair<bool,long> processMessage(
-            const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse, const DDF* respDDF=nullptr
+            const Application& application,
+            HTTPRequest& httpRequest,
+            HTTPResponse& httpResponse,
+            DDF& reqDDF,
+            const DDF* respDDF=nullptr
             ) const;
 #ifndef SHIBSP_LITE
         LoginEvent* newLoginEvent(const Application& application, const HTTPRequest& request) const;
@@ -121,41 +128,43 @@ namespace shibsp {
 
 };
 
-static ostream& json_safe(ostream& os, const char* buf)
-{
-    os << '"';
-    for (; *buf; ++buf) {
-        switch (*buf) {
-            case '\\':
-            case '"':
-                os << '\\';
-                os << *buf;
-                break;
-            case '\b':
-                os << "\\b";
-                break;
-            case '\t':
-                os << "\\t";
-                break;
-            case '\n':
-                os << "\\n";
-                break;
-            case '\f':
-                os << "\\f";
-                break;
-            case '\r':
-                os << "\\r";
-                break;
-            default:
-                os << *buf;
+namespace {
+    static ostream& json_safe(ostream& os, const char* buf)
+    {
+        os << '"';
+        for (; *buf; ++buf) {
+            switch (*buf) {
+                case '\\':
+                case '"':
+                    os << '\\';
+                    os << *buf;
+                    break;
+                case '\b':
+                    os << "\\b";
+                    break;
+                case '\t':
+                    os << "\\t";
+                    break;
+                case '\n':
+                    os << "\\n";
+                    break;
+                case '\f':
+                    os << "\\f";
+                    break;
+                case '\r':
+                    os << "\\r";
+                    break;
+                default:
+                    os << *buf;
+            }
         }
+        os << '"';
+        return os;
     }
-    os << '"';
-    return os;
-}
+};
 
 ExternalAuth::ExternalAuth(const DOMElement* e, const char* appId)
-    : SecuredHandler(e, Category::getInstance(SHIBSP_LOGCAT".ExternalAuth"), "acl", "127.0.0.1 ::1")
+    : SecuredHandler(e, Category::getInstance(SHIBSP_LOGCAT ".ExternalAuth"), "acl", "127.0.0.1 ::1")
 {
     setAddress("run::ExternalAuth");
 }
@@ -169,8 +178,18 @@ pair<bool,long> ExternalAuth::run(SPRequest& request, bool isHandler) const
 
     try {
         if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
-            // When out of process, we run natively and directly process the message.
-            return processMessage(request.getApplication(), request, request);
+            // When out of process, we run natively and directly process the message, except that we
+            // have to indirect the request anyway in order to override the client address. This is
+            // the simplest way to get a delegated HTTPRequest object, and since this code path is
+            // not really one we expect to use, it's good enough.
+            vector<string> headers(1, "User-Agent");
+            headers.push_back("Accept");
+            headers.push_back("Accept-Language");
+            headers.push_back("Cookie");
+            DDF in = wrap(request, &headers);
+            DDFJanitor jin(in);
+            scoped_ptr<HTTPRequest> fakedreq(getRequest(in));
+            return processMessage(request.getApplication(), *fakedreq, request, in);
         }
         else {
             // When not out of process, we remote all the message processing.
@@ -214,7 +233,7 @@ void ExternalAuth::receive(DDF& in, ostream& out)
     // which we just return as an empty structure, or a response/redirect,
     // which we capture in the facade and send back.
     try {
-        processMessage(*app, *req, *resp, &ret);
+        processMessage(*app, *req, *resp, in, &ret);
     }
     catch (std::exception& ex) {
         m_log.error("raising exception: %s", ex.what());
@@ -224,7 +243,7 @@ void ExternalAuth::receive(DDF& in, ostream& out)
 }
 
 pair<bool,long> ExternalAuth::processMessage(
-    const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse, const DDF* respDDF
+    const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse, DDF& reqDDF, const DDF* respDDF
     ) const
 {
 #ifndef SHIBSP_LITE
@@ -233,9 +252,19 @@ pair<bool,long> ExternalAuth::processMessage(
     MetadataProvider* m = application.getMetadataProvider(false);
     Locker mocker(m);
 
+    scoped_ptr<TransactionLog::Event> event;
+    LoginEvent* login_event = nullptr;
+    if (SPConfig::getConfig().isEnabled(SPConfig::Logging)) {
+        event.reset(SPConfig::getConfig().EventManager.newPlugin(LOGIN_EVENT, nullptr));
+        login_event = dynamic_cast<LoginEvent*>(event.get());
+        if (login_event)
+            login_event->m_app = &application;
+        else
+            m_log.warn("unable to audit event, log event object was of an incorrect type");
+    }
+
     string ctype(httpRequest.getContentType());
     if (ctype == "text/xml" || ctype == "application/samlassertion+xml") {
-        // Body should contain an assertion.
         const char* body = httpRequest.getRequestBody();
         if (!body)
             throw FatalProfileException("Request body was empty.");
@@ -319,6 +348,14 @@ pair<bool,long> ExternalAuth::processMessage(
             authncontext_decl = authnContext->getAuthnContextDeclRef() ? authnContext->getAuthnContextDeclRef()->getReference() : nullptr;
         }
 
+        // Extract client address.
+        reqDDF.addmember("client_addr").string((const char*)nullptr);
+        if (ssoStatement->getSubjectLocality() && ssoStatement->getSubjectLocality()->getAddress()) {
+            auto_ptr_char addr(ssoStatement->getSubjectLocality()->getAddress());
+            if (addr.get())
+                reqDDF.getmember("client_addr").string(addr.get());
+        }
+
         // The context will handle deleting attributes and tokens.
         vector<const Assertion*> tokens(1, token);
         scoped_ptr<ResolutionContext> ctx(
@@ -357,6 +394,24 @@ pair<bool,long> ExternalAuth::processMessage(
             &tokens,
             &ctx->getResolvedAttributes()
             );
+
+        if (login_event) {
+            login_event->m_binding = "ExternalAuth/XML";
+            login_event->m_sessionID = session_id.c_str();
+            login_event->m_peer = issuer.first;
+            auto_ptr_char prot(protocol);
+            login_event->m_protocol = prot.get();
+            login_event->m_nameID = nameid;
+            login_event->m_saml2AuthnStatement = ssoStatement;
+            if (ctx)
+                login_event->m_attributes = &ctx->getResolvedAttributes();
+            try {
+                application.getServiceProvider().getTransactionLog()->write(*login_event);
+            }
+            catch (std::exception& ex) {
+                m_log.warn("exception auditing event: %s", ex.what());
+            }
+        }
     }
     else if (ctype == "application/x-www-form-urlencoded") {
         auto_ptr_XMLCh protocol(httpRequest.getParameter("protocol"));
@@ -441,6 +496,9 @@ pair<bool,long> ExternalAuth::processMessage(
             }
         }
 
+        // Get actual client address.
+        reqDDF.addmember("client_addr").string(httpRequest.getParameter("address"));
+
         scoped_ptr<ResolutionContext> ctx(
             resolveAttributes(
                 application,
@@ -478,6 +536,22 @@ pair<bool,long> ExternalAuth::processMessage(
             &tokens,
             &ctx->getResolvedAttributes()
             );
+
+        if (login_event) {
+            login_event->m_binding = "ExternalAuth/POST";
+            login_event->m_sessionID = session_id.c_str();
+            login_event->m_peer = issuer.first;
+            login_event->m_protocol = httpRequest.getParameter("protocol");
+            login_event->m_nameID = nameid.get();
+            if (ctx)
+                login_event->m_attributes = &ctx->getResolvedAttributes();
+            try {
+                application.getServiceProvider().getTransactionLog()->write(*login_event);
+            }
+            catch (std::exception& ex) {
+                m_log.warn("exception auditing event: %s", ex.what());
+            }
+        }
     }
     else {
         throw FatalProfileException("Submission was not in a recognized SAML assertion or form-encoded format.");
@@ -528,7 +602,6 @@ pair<bool,long> ExternalAuth::processMessage(
         static const XMLCh _SessionID[] = UNICODE_LITERAL_9(S,e,s,s,i,o,n,I,D);
         static const XMLCh _RelayState[] = UNICODE_LITERAL_10(R,e,l,a,y,S,t,a,t,e);
         static const XMLCh _Cookie[] = UNICODE_LITERAL_6(C,o,o,k,i,e);
-        static const XMLCh _name[] = UNICODE_LITERAL_4(n,a,m,e);
         DOMDocument* retdoc = XMLToolingConfig::getConfig().getParser().newDocument();
         XercesJanitor<DOMDocument> retjanitor(retdoc);
         retdoc->appendChild(retdoc->createElement(_ExternalAuth));