Kea  1.9.9-git
basic_auth_config.cc
Go to the documentation of this file.
1 // Copyright (C) 2020-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <http/auth_log.h>
10 #include <http/basic_auth_config.h>
11 #include <util/strutil.h>
12 
13 using namespace isc;
14 using namespace isc::data;
15 using namespace isc::dhcp;
16 using namespace isc::util;
17 using namespace std;
18 
19 namespace isc {
20 namespace http {
21 
22 BasicHttpAuthClient::BasicHttpAuthClient(const std::string& user,
23  const std::string& password,
24  const isc::data::ConstElementPtr& user_context)
25  : user_(user), password_(password) {
26  if (user_context) {
27  setContext(user_context);
28  }
29 }
30 
33  ElementPtr result = Element::createMap();
34 
35  // Set user-context
36  contextToElement(result);
37 
38  // Set user
39  result->set("user", Element::create(user_));
40 
41  // Set password
42  result->set("password", Element::create(password_));
43 
44  return (result);
45 }
46 
47 void
48 BasicHttpAuthConfig::add(const std::string& user,
49  const std::string& password,
50  const ConstElementPtr& user_context) {
51  BasicHttpAuth basic_auth(user, password);
52  list_.push_back(BasicHttpAuthClient(user, password, user_context));
53  map_[basic_auth.getCredential()] = user;
54 }
55 
56 void
58  list_.clear();
59  map_.clear();
60 }
61 
62 bool
64  return (map_.empty());
65 }
66 
69  ElementPtr result = Element::createMap();
70 
71  // Set user-context
72  contextToElement(result);
73 
74  // Set type
75  result->set("type", Element::create(string("basic")));
76 
77  // Set realm
78  result->set("realm", Element::create(getRealm()));
79 
80  // Set clients
81  ElementPtr clients = Element::createList();
82  for (auto client : list_) {
83  clients->add(client.toElement());
84  }
85  result->set("clients", clients);
86 
87  return (result);
88 }
89 
90 void
92  if (!config) {
93  return;
94  }
95  if (config->getType() != Element::map) {
96  isc_throw(DhcpConfigError, "authentication must be a map ("
97  << config->getPosition() << ")");
98  }
99 
100  // Get and verify the type.
101  ConstElementPtr type = config->get("type");
102  if (!type) {
103  isc_throw(DhcpConfigError, "type is required in authentication ("
104  << config->getPosition() << ")");
105  }
106  if (type->getType() != Element::string) {
107  isc_throw(DhcpConfigError, "type is must be a string ("
108  << type->getPosition() << ")");
109  }
110  if (type->stringValue() != "basic") {
111  isc_throw(DhcpConfigError, "only basic HTTP authentication is "
112  << "supported: type is '" << type->stringValue()
113  << "' not 'basic' (" << type->getPosition() << ")");
114  }
115 
116  // Get the realm.
117  ConstElementPtr realm = config->get("realm");
118  if (realm) {
119  if (realm->getType() != Element::string) {
120  isc_throw(DhcpConfigError, "realm is must be a string ("
121  << realm->getPosition() << ")");
122  }
123  setRealm(realm->stringValue());
124  }
125 
126  // Get user context
127  ConstElementPtr user_context_cfg = config->get("user-context");
128  if (user_context_cfg) {
129  if (user_context_cfg->getType() != Element::map) {
130  isc_throw(DhcpConfigError, "user-context must be a map ("
131  << user_context_cfg->getPosition() << ")");
132  }
133  setContext(user_context_cfg);
134  }
135 
136  // Get clients.
137  ConstElementPtr clients = config->get("clients");
138  if (!clients) {
139  return;
140  }
141  if (clients->getType() != Element::list) {
142  isc_throw(DhcpConfigError, "clients must be a list ("
143  << clients->getPosition() << ")");
144  }
145 
146  // Iterate on clients.
147  for (auto client : clients->listValue()) {
148  if (client->getType() != Element::map) {
149  isc_throw(DhcpConfigError, "clients items must be maps ("
150  << client->getPosition() << ")");
151  }
152 
153  // user
154  ConstElementPtr user_cfg = client->get("user");
155  if (!user_cfg) {
156  isc_throw(DhcpConfigError, "user is required in clients items ("
157  << client->getPosition() << ")");
158  }
159  if (user_cfg->getType() != Element::string) {
160  isc_throw(DhcpConfigError, "user must be a string ("
161  << user_cfg->getPosition() << ")");
162  }
163  string user = user_cfg->stringValue();
164  if (user.empty()) {
165  isc_throw(DhcpConfigError, "user must be not be empty ("
166  << user_cfg->getPosition() << ")");
167  }
168  if (user.find(':') != string::npos) {
169  isc_throw(DhcpConfigError, "user must not contain a ':': '"
170  << user << "' (" << user_cfg->getPosition() << ")");
171  }
172 
173  // password
174  string password;
175  ConstElementPtr password_cfg = client->get("password");
176  if (password_cfg) {
177  if (password_cfg->getType() != Element::string) {
178  isc_throw(DhcpConfigError, "password must be a string ("
179  << password_cfg->getPosition() << ")");
180  }
181  password = password_cfg->stringValue();
182  }
183 
184  // user context
185  ConstElementPtr user_context = client->get("user-context");
186  if (user_context) {
187  if (user_context->getType() != Element::map) {
188  isc_throw(DhcpConfigError, "user-context must be a map ("
189  << user_context->getPosition() << ")");
190  }
191  }
192 
193  // add it.
194  try {
195  add(user, password, user_context);
196  } catch (const std::exception& ex) {
197  isc_throw(DhcpConfigError, ex.what() << " ("
198  << client->getPosition() << ")");
199  }
200  }
201 }
202 
205  const HttpRequestPtr& request) const {
206  const BasicHttpAuthMap& credentials = getCredentialMap();
207  bool authentic = false;
208  if (credentials.empty()) {
209  authentic = true;
210  } else try {
211  string value = request->getHeaderValue("Authorization");
212  // Trim space characters.
213  value = str::trim(value);
214  if (value.size() < 8) {
215  isc_throw(BadValue, "header content is too short");
216  }
217  // Get the authentication scheme which must be "basic".
218  string scheme = value.substr(0, 5);
219  str::lowercase(scheme);
220  if (scheme != "basic") {
221  isc_throw(BadValue, "not basic authentication");
222  }
223  // Skip the authentication scheme name and space characters.
224  value = value.substr(5);
225  value = str::trim(value);
226  // Verify the credential is in the list.
227  const auto it = credentials.find(value);
228  if (it != credentials.end()) {
230  .arg(it->second);
231  authentic = true;
232  } else {
234  authentic = false;
235  }
236  } catch (const HttpMessageNonExistingHeader&) {
238  } catch (const BadValue& ex) {
240  .arg(ex.what());
241  }
242  if (authentic) {
243  return (HttpResponseJsonPtr());
244  }
245  const string& realm = getRealm();
246  const string& scheme = "Basic";
247  HttpResponsePtr response =
249  response->reset();
250  response->context()->headers_.push_back(
251  HttpHeaderContext("WWW-Authenticate",
252  scheme + " realm=\"" + realm + "\""));
253  response->finalize();
254  return (boost::dynamic_pointer_cast<HttpResponseJson>(response));
255 }
256 
257 } // end of namespace isc::http
258 } // end of namespace isc
const isc::log::MessageID HTTP_CLIENT_REQUEST_NO_AUTH_HEADER
Definition: auth_messages.h:14
void parse(const isc::data::ConstElementPtr &config)
Parses basic HTTP authentication configuration.
void setContext(const data::ConstElementPtr &ctx)
Sets user context.
Definition: user_context.h:30
void lowercase(std::string &text)
Lowercase String.
Definition: strutil.h:151
Exception thrown when attempt is made to retrieve a non-existing header.
Definition: http_message.h:30
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
virtual HttpResponsePtr createStockHttpResponse(const HttpRequestPtr &request, const HttpStatusCode &status_code) const =0
Creates implementation specific HTTP response.
boost::shared_ptr< HttpResponseJson > HttpResponseJsonPtr
Pointer to the HttpResponseJson object.
Definition: response_json.h:24
void setRealm(const std::string &realm)
Set the realm.
Definition: auth_config.h:32
const isc::log::MessageID HTTP_CLIENT_REQUEST_NOT_AUTHORIZED
Definition: auth_messages.h:13
const isc::log::MessageID HTTP_CLIENT_REQUEST_AUTHORIZED
Definition: auth_messages.h:11
Represents a basic HTTP authentication.
Definition: basic_auth.h:21
Specifies an interface for classes creating HTTP responses from HTTP requests.
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
STL namespace.
const std::string & getRealm() const
Returns the realm.
Definition: auth_config.h:39
void contextToElement(data::ElementPtr map) const
Merge unparse a user_context object.
Definition: user_context.cc:15
virtual bool empty() const
Empty predicate.
std::unordered_map< std::string, std::string > BasicHttpAuthMap
Type of basic HTTP authentication credential and user id map, e.g.
virtual isc::http::HttpResponseJsonPtr checkAuth(const isc::http::HttpResponseCreator &creator, const isc::http::HttpRequestPtr &request) const
Validate HTTP request.
isc::log::Logger auth_logger("auth")
Defines the logger used by the HTTP authentication.
Definition: auth_log.h:18
const BasicHttpAuthMap & getCredentialMap() const
Returns the credential and user id map.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition: edns.h:19
To be removed. Please use ConfigError instead.
const std::string & getCredential() const
Returns the credential (base64 of the UTF-8 secret).
Definition: basic_auth.h:43
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
Definition: response.h:78
virtual isc::data::ElementPtr toElement() const
Unparses basic HTTP authentication configuration.
virtual void clear()
Clear configuration.
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
virtual isc::data::ElementPtr toElement() const
Unparses basic HTTP authentication client configuration.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
const isc::log::MessageID HTTP_CLIENT_REQUEST_BAD_AUTH_HEADER
Definition: auth_messages.h:12
Defines the logger used by the top-level component of kea-dhcp-ddns.
void add(const std::string &user, const std::string &password, const isc::data::ConstElementPtr &user_context=isc::data::ConstElementPtr())
Add a client configuration.
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
boost::shared_ptr< HttpRequest > HttpRequestPtr
Pointer to the HttpRequest object.
Definition: request.h:27
HTTP header context.
Basic HTTP authentication client configuration.