Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
oauth2.cc
1 #include <iostream>
2 #include <sstream>
3 
4 #include <curl/curl.h>
5 #include <boost/property_tree/json_parser.hpp>
6 #include <boost/property_tree/ptree.hpp>
7 
8 #include "logging/logging.h"
9 #include "oauth2.h"
10 #include "utilities/utils.h"
11 
12 using boost::property_tree::ptree;
13 using boost::property_tree::json_parser::json_parser_error;
14 using std::stringstream;
15 
16 /**
17  * Handle CURL write callbacks by appending to a stringstream
18  */
19 size_t curl_handle_write_sstream(void *buffer, size_t size, size_t nmemb, void *userp) {
20  auto *body = static_cast<stringstream *>(userp);
21  body->write(static_cast<const char *>(buffer), static_cast<std::streamsize>(size * nmemb));
22  return size * nmemb;
23 }
24 
25 AuthenticationResult OAuth2::Authenticate() {
26  CurlEasyWrapper curl_handle;
27  std::string token_suffix = "/token";
28  std::string post_data = "grant_type=client_credentials";
29  auto use_cognito = false;
30  if (server_.length() >= token_suffix.length()) {
31  use_cognito = (0 == server_.compare(server_.length() - token_suffix.length(), token_suffix.length(), token_suffix));
32  }
33 
34  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_VERBOSE, get_curlopt_verbose());
35  // We need this check for backwards-compatibility with previous versions of treehub.json.
36  // Previous versions have the server URL *without* the token path, so it needs to be hardcoded.
37  // The new version has the full URL with the `/oauth2/token` path at the end, so nothing needs
38  // to be appended. Also we can't send the `scope` to Auth+, it confuses it.
39  // This check can be removed after we finish the migration to Cognito. At that point there's
40  // no need to attempt to be backwards-compatible anymore, since old credentials will have a
41  // client_id that will have been removed from user-profile, and a server URL to Auth+, which
42  // will be in computer heaven.
43  // check similar implementation in Scala for garage-sign here:
44  // https://main.gitlab.in.here.com/olp/edge/ota/connect/back-end/ota-tuf/-/blob/master/cli/src/main/scala/com/advancedtelematic/tuf/cli/http/OAuth2Client.scala
45  if (use_cognito) {
46  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_URL, (server_).c_str());
47  } else {
48  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_URL, (server_ + token_suffix).c_str());
49  }
50  if (!ca_certs_.empty()) {
51  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_CAINFO, ca_certs_.c_str());
52  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_CAPATH, NULL);
53  }
54 
55  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
56  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_USERNAME, client_id_.c_str());
57  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_PASSWORD, client_secret_.c_str());
58  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_POST, 1);
59  if (use_cognito) {
60  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_COPYPOSTFIELDS, (post_data + "&scope=" + scope_).c_str());
61  } else {
62  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_COPYPOSTFIELDS, post_data.c_str());
63  }
64 
65  stringstream body;
66  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_WRITEFUNCTION, &curl_handle_write_sstream);
67  curlEasySetoptWrapper(curl_handle.get(), CURLOPT_WRITEDATA, &body);
68 
69  curl_easy_perform(curl_handle.get());
70 
71  long rescode; // NOLINT(google-runtime-int)
72  curl_easy_getinfo(curl_handle.get(), CURLINFO_RESPONSE_CODE, &rescode);
73  if (rescode == 200) {
74  ptree pt;
75  try {
76  read_json(body, pt);
77  token_ = pt.get("access_token", "");
78  LOG_TRACE << "Got OAuth2 access token:" << token_;
79  return AuthenticationResult::kSuccess;
80  } catch (const json_parser_error &e) {
81  token_ = "";
82  return AuthenticationResult::kFailure;
83  }
84  } else {
85  return AuthenticationResult::kFailure;
86  }
87 }
88 
89 // vim: set tabstop=2 shiftwidth=2 expandtab:
CurlEasyWrapper
Definition: utils.h:146
OAuth2::Authenticate
AuthenticationResult Authenticate()
Synchronously attempt to get an access token from Auth+.
Definition: oauth2.cc:25