00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <boost/algorithm/string.hpp>
00011 #include <pion/algorithm.hpp>
00012 #include <pion/http/cookie_auth.hpp>
00013 #include <pion/http/response_writer.hpp>
00014 #include <pion/http/server.hpp>
00015 #include <ctime>
00016
00017
00018 namespace pion {
00019 namespace http {
00020
00021
00022
00023
00024 const unsigned int cookie_auth::CACHE_EXPIRATION = 3600;
00025 const unsigned int cookie_auth::RANDOM_COOKIE_BYTES = 20;
00026 const std::string cookie_auth::AUTH_COOKIE_NAME = "pion_session_id";
00027
00028
00029
00030
00031 cookie_auth::cookie_auth(user_manager_ptr userManager,
00032 const std::string& login,
00033 const std::string& logout,
00034 const std::string& redirect)
00035 : http::auth(userManager), m_login(login), m_logout(logout), m_redirect(redirect),
00036 m_random_gen(), m_random_range(0, 255), m_random_die(m_random_gen, m_random_range),
00037 m_cache_cleanup_time(boost::posix_time::second_clock::universal_time())
00038 {
00039
00040 set_logger(PION_GET_LOGGER("pion.http.cookie_auth"));
00041
00042
00043
00044
00045
00046 m_random_gen.seed(static_cast<boost::mt19937::result_type>(::time(NULL)));
00047
00048
00049 for (unsigned int n = 0; n < 100; ++n)
00050 m_random_die();
00051 }
00052
00053 bool cookie_auth::handle_request(http::request_ptr& http_request_ptr, tcp::connection_ptr& tcp_conn)
00054 {
00055 if (process_login(http_request_ptr,tcp_conn)) {
00056 return false;
00057 }
00058
00059 if (!need_authentication(http_request_ptr)) {
00060 return true;
00061 }
00062
00063
00064 if (!m_redirect.empty() && m_redirect==http_request_ptr->get_resource()) {
00065 return true;
00066 }
00067
00068
00069 boost::posix_time::ptime time_now(boost::posix_time::second_clock::universal_time());
00070 expire_cache(time_now);
00071
00072
00073 const std::string auth_cookie(http_request_ptr->get_cookie(AUTH_COOKIE_NAME));
00074 if (! auth_cookie.empty()) {
00075
00076 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
00077 user_cache_type::iterator user_cache_itr=m_user_cache.find(auth_cookie);
00078 if (user_cache_itr != m_user_cache.end()) {
00079
00080
00081 http_request_ptr->set_user(user_cache_itr->second.second);
00082
00083 user_cache_itr->second.first = time_now;
00084 return true;
00085 }
00086 }
00087
00088
00089 handle_unauthorized(http_request_ptr,tcp_conn);
00090 return false;
00091 }
00092
00093 void cookie_auth::set_option(const std::string& name, const std::string& value)
00094 {
00095 if (name=="login")
00096 m_login = value;
00097 else if (name=="logout")
00098 m_logout = value;
00099 else if (name=="redirect")
00100 m_redirect = value;
00101 else
00102 BOOST_THROW_EXCEPTION( error::bad_arg() << error::errinfo_arg_name(name) );
00103 }
00104
00105 bool cookie_auth::process_login(http::request_ptr& http_request_ptr, tcp::connection_ptr& tcp_conn)
00106 {
00107
00108 std::string resource(http::server::strip_trailing_slash(http_request_ptr->get_resource()));
00109
00110 if (resource != m_login && resource != m_logout) {
00111 return false;
00112 }
00113
00114 std::string redirect_url = http_request_ptr->get_query("url");
00115 std::string new_cookie;
00116 bool delete_cookie = false;
00117
00118 if (resource == m_login) {
00119
00120
00121 std::string username = http_request_ptr->get_query("user");
00122 std::string password = http_request_ptr->get_query("pass");
00123
00124
00125 user_ptr user=m_user_manager->get_user(username,password);
00126 if (!user) {
00127 handle_unauthorized(http_request_ptr,tcp_conn);
00128 return true;
00129 }
00130
00131
00132
00133 std::string rand_binary;
00134 rand_binary.reserve(RANDOM_COOKIE_BYTES);
00135 for (unsigned int i=0; i<RANDOM_COOKIE_BYTES ; i++) {
00136 rand_binary += static_cast<unsigned char>(m_random_die());
00137 }
00138 algorithm::base64_encode(rand_binary, new_cookie);
00139
00140
00141 boost::posix_time::ptime time_now(boost::posix_time::second_clock::universal_time());
00142 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
00143 m_user_cache.insert(std::make_pair(new_cookie,std::make_pair(time_now,user)));
00144 } else {
00145
00146
00147 const std::string auth_cookie(http_request_ptr->get_cookie(AUTH_COOKIE_NAME));
00148 if (! auth_cookie.empty()) {
00149 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
00150 user_cache_type::iterator user_cache_itr=m_user_cache.find(auth_cookie);
00151 if (user_cache_itr!=m_user_cache.end()) {
00152 m_user_cache.erase(user_cache_itr);
00153 }
00154 }
00155
00156 delete_cookie = true;
00157 }
00158
00159
00160 if (! redirect_url.empty()) {
00161 handle_redirection(http_request_ptr,tcp_conn,redirect_url,new_cookie,delete_cookie);
00162 } else {
00163
00164 handle_ok(http_request_ptr,tcp_conn,new_cookie,delete_cookie);
00165 }
00166
00167
00168 return true;
00169 }
00170
00171 void cookie_auth::handle_unauthorized(http::request_ptr& http_request_ptr,
00172 tcp::connection_ptr& tcp_conn)
00173 {
00174
00175 if (!m_redirect.empty()) {
00176 handle_redirection(http_request_ptr,tcp_conn,m_redirect,"",false);
00177 return;
00178 }
00179
00180
00181 static const std::string CONTENT =
00182 " <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\""
00183 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">"
00184 "<HTML>"
00185 "<HEAD>"
00186 "<TITLE>Error</TITLE>"
00187 "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">"
00188 "</HEAD>"
00189 "<BODY><H1>401 Unauthorized.</H1></BODY>"
00190 "</HTML> ";
00191 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr,
00192 boost::bind(&tcp::connection::finish, tcp_conn)));
00193 writer->get_response().set_status_code(http::types::RESPONSE_CODE_UNAUTHORIZED);
00194 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_UNAUTHORIZED);
00195 writer->write_no_copy(CONTENT);
00196 writer->send();
00197 }
00198
00199 void cookie_auth::handle_redirection(http::request_ptr& http_request_ptr,
00200 tcp::connection_ptr& tcp_conn,
00201 const std::string &redirection_url,
00202 const std::string &new_cookie,
00203 bool delete_cookie
00204 )
00205 {
00206
00207 static const std::string CONTENT =
00208 " <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\""
00209 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">"
00210 "<HTML>"
00211 "<HEAD>"
00212 "<TITLE>Redirect</TITLE>"
00213 "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">"
00214 "</HEAD>"
00215 "<BODY><H1>302 Found.</H1></BODY>"
00216 "</HTML> ";
00217 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr,
00218 boost::bind(&tcp::connection::finish, tcp_conn)));
00219 writer->get_response().set_status_code(http::types::RESPONSE_CODE_FOUND);
00220 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_FOUND);
00221 writer->get_response().add_header(http::types::HEADER_LOCATION, redirection_url);
00222
00223
00224
00225 if (delete_cookie) {
00226
00227 writer->get_response().delete_cookie(AUTH_COOKIE_NAME,"");
00228 } else if (!new_cookie.empty()) {
00229
00230 writer->get_response().set_cookie(AUTH_COOKIE_NAME, new_cookie,"");
00231 }
00232
00233 writer->write_no_copy(CONTENT);
00234 writer->send();
00235 }
00236
00237 void cookie_auth::handle_ok(http::request_ptr& http_request_ptr,
00238 tcp::connection_ptr& tcp_conn,
00239 const std::string &new_cookie,
00240 bool delete_cookie
00241 )
00242 {
00243
00244 http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr,
00245 boost::bind(&tcp::connection::finish, tcp_conn)));
00246 writer->get_response().set_status_code(http::types::RESPONSE_CODE_NO_CONTENT);
00247 writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_NO_CONTENT);
00248
00249
00250
00251 if (delete_cookie) {
00252
00253 writer->get_response().delete_cookie(AUTH_COOKIE_NAME,"");
00254 } else if(!new_cookie.empty()) {
00255
00256 writer->get_response().set_cookie(AUTH_COOKIE_NAME, new_cookie,"");
00257 }
00258 writer->send();
00259 }
00260
00261 void cookie_auth::expire_cache(const boost::posix_time::ptime &time_now)
00262 {
00263 if (time_now > m_cache_cleanup_time + boost::posix_time::seconds(CACHE_EXPIRATION)) {
00264
00265 boost::mutex::scoped_lock cache_lock(m_cache_mutex);
00266 user_cache_type::iterator i;
00267 user_cache_type::iterator next=m_user_cache.begin();
00268 while (next!=m_user_cache.end()) {
00269 i=next;
00270 ++next;
00271 if (time_now > i->second.first + boost::posix_time::seconds(CACHE_EXPIRATION)) {
00272
00273 m_user_cache.erase(i);
00274 }
00275 }
00276 m_cache_cleanup_time = time_now;
00277 }
00278 }
00279
00280 }
00281 }