net/src/HTTPBasicAuth.cpp

00001 // ------------------------------------------------------------------
00002 // pion-net: a C++ framework for building lightweight HTTP interfaces
00003 // ------------------------------------------------------------------
00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
00005 //
00006 // Distributed under the Boost Software License, Version 1.0.
00007 // See http://www.boost.org/LICENSE_1_0.txt
00008 //
00009 
00010 #include <boost/algorithm/string.hpp>
00011 #include <pion/net/HTTPBasicAuth.hpp>
00012 #include <pion/net/HTTPResponseWriter.hpp>
00013 #include <pion/net/HTTPServer.hpp>
00014 
00015 
00016 namespace pion {    // begin namespace pion
00017 namespace net {     // begin namespace net (Pion Network Library)
00018     
00019     
00020 // static members of HTTPBasicAuth
00021 
00022 const unsigned int  HTTPBasicAuth::CACHE_EXPIRATION = 300;  // 5 minutes
00023 
00024 
00025 // HTTPBasicAuth member functions
00026 
00027 HTTPBasicAuth::HTTPBasicAuth(PionUserManagerPtr userManager, const std::string& realm)
00028     : HTTPAuth(userManager), m_realm(realm),
00029     m_cache_cleanup_time(boost::posix_time::second_clock::universal_time())
00030 {
00031     setLogger(PION_GET_LOGGER("pion.net.HTTPBasicAuth"));
00032 }
00033     
00034 bool HTTPBasicAuth::handleRequest(HTTPRequestPtr& request, TCPConnectionPtr& tcp_conn)
00035 {
00036     if (!needAuthentication(request)) {
00037         return true; // this request does not require authentication
00038     }
00039     
00040     PionDateTime time_now(boost::posix_time::second_clock::universal_time());
00041     if (time_now > m_cache_cleanup_time + boost::posix_time::seconds(CACHE_EXPIRATION)) {
00042         // expire cache
00043         boost::mutex::scoped_lock cache_lock(m_cache_mutex);
00044         PionUserCache::iterator i;
00045         PionUserCache::iterator next=m_user_cache.begin();
00046         while (next!=m_user_cache.end()) {
00047             i=next;
00048             ++next;
00049             if (time_now > i->second.first + boost::posix_time::seconds(CACHE_EXPIRATION)) {
00050                 // ok - this is an old record.. expire it now
00051                 m_user_cache.erase(i);
00052             }
00053         }
00054         m_cache_cleanup_time = time_now;
00055     }
00056     
00057     // if we are here, we need to check if access authorized...
00058     std::string authorization = request->getHeader(HTTPTypes::HEADER_AUTHORIZATION);
00059     if (!authorization.empty()) {
00060         std::string credentials;
00061         if (parseAuthorization(authorization, credentials)) {
00062             // to do - use fast cache to match with active credentials
00063             boost::mutex::scoped_lock cache_lock(m_cache_mutex);
00064             PionUserCache::iterator user_cache_ptr=m_user_cache.find(credentials);
00065             if (user_cache_ptr!=m_user_cache.end()) {
00066                 // we found the credentials in our cache...
00067                 // we can approve authorization now!
00068                 request->setUser(user_cache_ptr->second.second);
00069                 user_cache_ptr->second.first = time_now;
00070                 return true;
00071             }
00072     
00073             std::string username;
00074             std::string password;
00075     
00076             if (parseCredentials(credentials, username, password)) {
00077                 // match username/password
00078                 PionUserPtr user=m_user_manager->getUser(username, password);
00079                 if (user) {
00080                     // add user to the cache
00081                     m_user_cache.insert(std::make_pair(credentials, std::make_pair(time_now, user)));
00082                     // add user credentials to the request object
00083                     request->setUser(user);
00084                     return true;
00085                 }
00086             }
00087         }
00088     }
00089 
00090     // user not found
00091     handleUnauthorized(request, tcp_conn);
00092     return false;
00093 }
00094     
00095 void HTTPBasicAuth::setOption(const std::string& name, const std::string& value) 
00096 {
00097     if (name=="realm")
00098         m_realm = value;
00099     else
00100         throw UnknownOptionException(name);
00101 }
00102     
00103 bool HTTPBasicAuth::parseAuthorization(const std::string& authorization, std::string &credentials)
00104 {
00105     if (!boost::algorithm::starts_with(authorization, "Basic "))
00106         return false;
00107     credentials = authorization.substr(6);
00108     if (credentials.empty())
00109         return false;
00110     return true;
00111 }
00112     
00113 bool HTTPBasicAuth::parseCredentials(const std::string &credentials,
00114     std::string &username, std::string &password)
00115 {
00116     std::string user_password;
00117     
00118     if (! HTTPTypes::base64_decode(credentials, user_password))
00119         return false;
00120 
00121     // find ':' symbol
00122     std::string::size_type i = user_password.find(':');
00123     if (i==0 || i==std::string::npos)
00124         return false;
00125     
00126     username = user_password.substr(0, i);
00127     password = user_password.substr(i+1);
00128     
00129     return true;
00130 }
00131     
00132 void HTTPBasicAuth::handleUnauthorized(HTTPRequestPtr& http_request,
00133     TCPConnectionPtr& tcp_conn)
00134 {
00135     // authentication failed, send 401.....
00136     static const std::string CONTENT =
00137         " <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\""
00138         "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">"
00139         "<HTML>"
00140         "<HEAD>"
00141         "<TITLE>Error</TITLE>"
00142         "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">"
00143         "</HEAD>"
00144         "<BODY><H1>401 Unauthorized.</H1></BODY>"
00145         "</HTML> ";
00146     HTTPResponseWriterPtr writer(HTTPResponseWriter::create(tcp_conn, *http_request,
00147     boost::bind(&TCPConnection::finish, tcp_conn)));
00148     writer->getResponse().setStatusCode(HTTPTypes::RESPONSE_CODE_UNAUTHORIZED);
00149     writer->getResponse().setStatusMessage(HTTPTypes::RESPONSE_MESSAGE_UNAUTHORIZED);
00150     writer->getResponse().addHeader("WWW-Authenticate", "Basic realm=\"" + m_realm + "\"");
00151     writer->writeNoCopy(CONTENT);
00152     writer->send();
00153 }
00154     
00155 }   // end namespace net
00156 }   // end namespace pion

Generated on Fri Apr 30 14:48:53 2010 for pion-net by  doxygen 1.4.7