pion  5.0.6
http_basic_auth.cpp
1 // ---------------------------------------------------------------------
2 // pion: a Boost C++ framework for building lightweight HTTP interfaces
3 // ---------------------------------------------------------------------
4 // Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion)
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See http://www.boost.org/LICENSE_1_0.txt
8 //
9 
10 #include <boost/algorithm/string.hpp>
11 #include <pion/algorithm.hpp>
12 #include <pion/http/basic_auth.hpp>
13 #include <pion/http/response_writer.hpp>
14 #include <pion/http/server.hpp>
15 
16 
17 namespace pion { // begin namespace pion
18 namespace http { // begin namespace http
19 
20 
21 // static members of basic_auth
22 
23 const unsigned int basic_auth::CACHE_EXPIRATION = 300; // 5 minutes
24 
25 
26 // basic_auth member functions
27 
28 basic_auth::basic_auth(user_manager_ptr userManager, const std::string& realm)
29  : http::auth(userManager), m_realm(realm),
30  m_cache_cleanup_time(boost::posix_time::second_clock::universal_time())
31 {
32  set_logger(PION_GET_LOGGER("pion.http.basic_auth"));
33 }
34 
35 bool basic_auth::handle_request(http::request_ptr& http_request_ptr, tcp::connection_ptr& tcp_conn)
36 {
37  if (!need_authentication(http_request_ptr)) {
38  return true; // this request does not require authentication
39  }
40 
41  boost::posix_time::ptime time_now(boost::posix_time::second_clock::universal_time());
42  if (time_now > m_cache_cleanup_time + boost::posix_time::seconds(CACHE_EXPIRATION)) {
43  // expire cache
44  boost::mutex::scoped_lock cache_lock(m_cache_mutex);
45  user_cache_type::iterator i;
46  user_cache_type::iterator next=m_user_cache.begin();
47  while (next!=m_user_cache.end()) {
48  i=next;
49  ++next;
50  if (time_now > i->second.first + boost::posix_time::seconds(CACHE_EXPIRATION)) {
51  // ok - this is an old record.. expire it now
52  m_user_cache.erase(i);
53  }
54  }
55  m_cache_cleanup_time = time_now;
56  }
57 
58  // if we are here, we need to check if access authorized...
59  std::string authorization = http_request_ptr->get_header(http::types::HEADER_AUTHORIZATION);
60  if (!authorization.empty()) {
61  std::string credentials;
62  if (parse_authorization(authorization, credentials)) {
63  // to do - use fast cache to match with active credentials
64  boost::mutex::scoped_lock cache_lock(m_cache_mutex);
65  user_cache_type::iterator user_cache_ptr=m_user_cache.find(credentials);
66  if (user_cache_ptr!=m_user_cache.end()) {
67  // we found the credentials in our cache...
68  // we can approve authorization now!
69  http_request_ptr->set_user(user_cache_ptr->second.second);
70  user_cache_ptr->second.first = time_now;
71  return true;
72  }
73 
74  std::string username;
75  std::string password;
76 
77  if (parse_credentials(credentials, username, password)) {
78  // match username/password
79  user_ptr user=m_user_manager->get_user(username, password);
80  if (user) {
81  // add user to the cache
82  m_user_cache.insert(std::make_pair(credentials, std::make_pair(time_now, user)));
83  // add user credentials to the request object
84  http_request_ptr->set_user(user);
85  return true;
86  }
87  }
88  }
89  }
90 
91  // user not found
92  handle_unauthorized(http_request_ptr, tcp_conn);
93  return false;
94 }
95 
96 void basic_auth::set_option(const std::string& name, const std::string& value)
97 {
98  if (name=="realm")
99  m_realm = value;
100  else
101  BOOST_THROW_EXCEPTION( error::bad_arg() << error::errinfo_arg_name(name) );
102 }
103 
104 bool basic_auth::parse_authorization(const std::string& authorization, std::string &credentials)
105 {
106  if (!boost::algorithm::starts_with(authorization, "Basic "))
107  return false;
108  credentials = authorization.substr(6);
109  if (credentials.empty())
110  return false;
111  return true;
112 }
113 
114 bool basic_auth::parse_credentials(const std::string &credentials,
115  std::string &username, std::string &password)
116 {
117  std::string user_password;
118 
119  if (! algorithm::base64_decode(credentials, user_password))
120  return false;
121 
122  // find ':' symbol
123  std::string::size_type i = user_password.find(':');
124  if (i==0 || i==std::string::npos)
125  return false;
126 
127  username = user_password.substr(0, i);
128  password = user_password.substr(i+1);
129 
130  return true;
131 }
132 
133 void basic_auth::handle_unauthorized(http::request_ptr& http_request_ptr,
134  tcp::connection_ptr& tcp_conn)
135 {
136  // authentication failed, send 401.....
137  static const std::string CONTENT =
138  " <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\""
139  "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">"
140  "<HTML>"
141  "<HEAD>"
142  "<TITLE>Error</TITLE>"
143  "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=ISO-8859-1\">"
144  "</HEAD>"
145  "<BODY><H1>401 Unauthorized.</H1></BODY>"
146  "</HTML> ";
147  http::response_writer_ptr writer(http::response_writer::create(tcp_conn, *http_request_ptr,
148  boost::bind(&tcp::connection::finish, tcp_conn)));
149  writer->get_response().set_status_code(http::types::RESPONSE_CODE_UNAUTHORIZED);
150  writer->get_response().set_status_message(http::types::RESPONSE_MESSAGE_UNAUTHORIZED);
151  writer->get_response().add_header("WWW-Authenticate", "Basic realm=\"" + m_realm + "\"");
152  writer->write_no_copy(CONTENT);
153  writer->send();
154 }
155 
156 } // end namespace http
157 } // end namespace pion
user_manager_ptr m_user_manager
container used to manager user objects
Definition: auth.hpp:156
static bool parse_authorization(std::string const &authorization, std::string &credentials)
virtual bool handle_request(http::request_ptr &http_request_ptr, tcp::connection_ptr &tcp_conn)
void handle_unauthorized(http::request_ptr &http_request_ptr, tcp::connection_ptr &tcp_conn)
static bool base64_decode(std::string const &input, std::string &output)
Definition: algorithm.cpp:24
void set_logger(logger log_ptr)
sets the logger to be used
Definition: auth.hpp:149
static bool parse_credentials(std::string const &credentials, std::string &username, std::string &password)
exception thrown for an invalid configuration argument or option
Definition: error.hpp:132
basic_auth(user_manager_ptr userManager, const std::string &realm="PION")
default constructor
static boost::shared_ptr< response_writer > create(tcp::connection_ptr &tcp_conn, http::response_ptr &http_response_ptr, finished_handler_t handler=finished_handler_t())
virtual void set_option(const std::string &name, const std::string &value)
bool need_authentication(http::request_ptr const &http_request_ptr) const
Definition: http_auth.cpp:37