pion
5.0.6
|
00001 // --------------------------------------------------------------------- 00002 // pion: a Boost C++ framework for building lightweight HTTP interfaces 00003 // --------------------------------------------------------------------- 00004 // Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion) 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/algorithm.hpp> 00012 #include <pion/http/basic_auth.hpp> 00013 #include <pion/http/response_writer.hpp> 00014 #include <pion/http/server.hpp> 00015 00016 00017 namespace pion { // begin namespace pion 00018 namespace http { // begin namespace http 00019 00020 00021 // static members of basic_auth 00022 00023 const unsigned int basic_auth::CACHE_EXPIRATION = 300; // 5 minutes 00024 00025 00026 // basic_auth member functions 00027 00028 basic_auth::basic_auth(user_manager_ptr userManager, const std::string& realm) 00029 : http::auth(userManager), m_realm(realm), 00030 m_cache_cleanup_time(boost::posix_time::second_clock::universal_time()) 00031 { 00032 set_logger(PION_GET_LOGGER("pion.http.basic_auth")); 00033 } 00034 00035 bool basic_auth::handle_request(http::request_ptr& http_request_ptr, tcp::connection_ptr& tcp_conn) 00036 { 00037 if (!need_authentication(http_request_ptr)) { 00038 return true; // this request does not require authentication 00039 } 00040 00041 boost::posix_time::ptime time_now(boost::posix_time::second_clock::universal_time()); 00042 if (time_now > m_cache_cleanup_time + boost::posix_time::seconds(CACHE_EXPIRATION)) { 00043 // expire cache 00044 boost::mutex::scoped_lock cache_lock(m_cache_mutex); 00045 user_cache_type::iterator i; 00046 user_cache_type::iterator next=m_user_cache.begin(); 00047 while (next!=m_user_cache.end()) { 00048 i=next; 00049 ++next; 00050 if (time_now > i->second.first + boost::posix_time::seconds(CACHE_EXPIRATION)) { 00051 // ok - this is an old record.. expire it now 00052 m_user_cache.erase(i); 00053 } 00054 } 00055 m_cache_cleanup_time = time_now; 00056 } 00057 00058 // if we are here, we need to check if access authorized... 00059 std::string authorization = http_request_ptr->get_header(http::types::HEADER_AUTHORIZATION); 00060 if (!authorization.empty()) { 00061 std::string credentials; 00062 if (parse_authorization(authorization, credentials)) { 00063 // to do - use fast cache to match with active credentials 00064 boost::mutex::scoped_lock cache_lock(m_cache_mutex); 00065 user_cache_type::iterator user_cache_ptr=m_user_cache.find(credentials); 00066 if (user_cache_ptr!=m_user_cache.end()) { 00067 // we found the credentials in our cache... 00068 // we can approve authorization now! 00069 http_request_ptr->set_user(user_cache_ptr->second.second); 00070 user_cache_ptr->second.first = time_now; 00071 return true; 00072 } 00073 00074 std::string username; 00075 std::string password; 00076 00077 if (parse_credentials(credentials, username, password)) { 00078 // match username/password 00079 user_ptr user=m_user_manager->get_user(username, password); 00080 if (user) { 00081 // add user to the cache 00082 m_user_cache.insert(std::make_pair(credentials, std::make_pair(time_now, user))); 00083 // add user credentials to the request object 00084 http_request_ptr->set_user(user); 00085 return true; 00086 } 00087 } 00088 } 00089 } 00090 00091 // user not found 00092 handle_unauthorized(http_request_ptr, tcp_conn); 00093 return false; 00094 } 00095 00096 void basic_auth::set_option(const std::string& name, const std::string& value) 00097 { 00098 if (name=="realm") 00099 m_realm = value; 00100 else 00101 BOOST_THROW_EXCEPTION( error::bad_arg() << error::errinfo_arg_name(name) ); 00102 } 00103 00104 bool basic_auth::parse_authorization(const std::string& authorization, std::string &credentials) 00105 { 00106 if (!boost::algorithm::starts_with(authorization, "Basic ")) 00107 return false; 00108 credentials = authorization.substr(6); 00109 if (credentials.empty()) 00110 return false; 00111 return true; 00112 } 00113 00114 bool basic_auth::parse_credentials(const std::string &credentials, 00115 std::string &username, std::string &password) 00116 { 00117 std::string user_password; 00118 00119 if (! algorithm::base64_decode(credentials, user_password)) 00120 return false; 00121 00122 // find ':' symbol 00123 std::string::size_type i = user_password.find(':'); 00124 if (i==0 || i==std::string::npos) 00125 return false; 00126 00127 username = user_password.substr(0, i); 00128 password = user_password.substr(i+1); 00129 00130 return true; 00131 } 00132 00133 void basic_auth::handle_unauthorized(http::request_ptr& http_request_ptr, 00134 tcp::connection_ptr& tcp_conn) 00135 { 00136 // authentication failed, send 401..... 00137 static const std::string CONTENT = 00138 " <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"" 00139 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">" 00140 "<HTML>" 00141 "<HEAD>" 00142 "<TITLE>Error</TITLE>" 00143 "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">" 00144 "</HEAD>" 00145 "<BODY><H1>401 Unauthorized.</H1></BODY>" 00146 "</HTML> "; 00147 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr, 00148 boost::bind(&tcp::connection::finish, tcp_conn))); 00149 writer->get_response().set_status_code(http::types::RESPONSE_CODE_UNAUTHORIZED); 00150 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_UNAUTHORIZED); 00151 writer->get_response().add_header("WWW-Authenticate", "Basic realm=\"" + m_realm + "\""); 00152 writer->write_no_copy(CONTENT); 00153 writer->send(); 00154 } 00155 00156 } // end namespace http 00157 } // end namespace pion