From 4ef5ba1c0e6cc383a7c0e029603f51a129ced459 Mon Sep 17 00:00:00 2001 From: Anatoliy Odukha Date: Thu, 15 Oct 2020 22:31:34 +0300 Subject: [PATCH] support new backend authentication for garage-push and garage-deploy. OTA-5285 Signed-off-by: Anatoliy Odukha --- .gitignore | 2 ++ .vscode/launch.json | 25 --------------------- CHANGELOG.md | 1 + ci/gitlab/.gitlab-ci.yml | 5 ----- src/sota_tools/authenticate.cc | 2 +- src/sota_tools/authenticate_test.cc | 15 +++++++++++++ src/sota_tools/oauth2.cc | 29 +++++++++++++++++++++++-- src/sota_tools/oauth2.h | 4 +++- src/sota_tools/server_credentials.cc | 1 + src/sota_tools/server_credentials.h | 2 ++ tests/sota_tools/auth_test_good_v2.json | 11 ++++++++++ 11 files changed, 63 insertions(+), 34 deletions(-) delete mode 100644 .vscode/launch.json create mode 100644 tests/sota_tools/auth_test_good_v2.json diff --git a/.gitignore b/.gitignore index 7e5f36595b..1a78909484 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,5 @@ __pycache__/ # QT Creator CMakeLists.txt.user /docs/ota-client-guide/modules/ROOT/pages/_junk +*.code-workspace +.vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index a7582bc4b5..0000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Debug Aktualizr", - "type": "gdb", - "request": "launch", - "target": "./src/aktualizr", - "arguments": "--config ../config/sota_local.toml --loglevel 0", - "cwd": "${workspaceRoot}/build" - }, - { - "type": "gdb", - "request": "attach", - "name": "Attach to valgrind vgdb", - "executable": "./src/aktualizr", - "target": "| vgdb", - "remote": true, - "cwd": "${workspaceRoot}/build" - }, - ] -} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d6194202e7..b7ec18c0d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Our versioning scheme is `YEAR.N` where `N` is incremented whenever a new releas ## [upcoming release] +- Update garage-push and garage-deploy tools to support the latest backend changes. Both are backward compatible. Previous versions have the server URL *without* the token path, so it needs to be hardcoded. The new version has the full URL with the */oauth2/token* path at the end. Also, treehub.json has an additional parameter *scope*: [PR](https://github.com/advancedtelematic/aktualizr/pull/1767) ## [2020.9] - 2020-08-26 diff --git a/ci/gitlab/.gitlab-ci.yml b/ci/gitlab/.gitlab-ci.yml index 204b073cbd..80ceea4063 100644 --- a/ci/gitlab/.gitlab-ci.yml +++ b/ci/gitlab/.gitlab-ci.yml @@ -93,11 +93,6 @@ secret_detection: stage: static scans needs: [] -secrets-sast: - stage: static scans - needs: [] - - coverage: variables: GIT_CLONE_PATH: $CI_BUILDS_DIR/aktualizr-coverage-$CI_JOB_ID diff --git a/src/sota_tools/authenticate.cc b/src/sota_tools/authenticate.cc index 5fee75ed84..19ed2a8007 100644 --- a/src/sota_tools/authenticate.cc +++ b/src/sota_tools/authenticate.cc @@ -12,7 +12,7 @@ int authenticate(const string &cacerts, const ServerCredentials &creds, TreehubS break; } case AuthMethod::kOauth2: { - OAuth2 oauth2(creds.GetAuthServer(), creds.GetClientId(), creds.GetClientSecret(), cacerts); + OAuth2 oauth2(creds.GetAuthServer(), creds.GetClientId(), creds.GetClientSecret(), creds.GetScope(), cacerts); if (!creds.GetClientId().empty()) { if (oauth2.Authenticate() != AuthenticationResult::kSuccess) { diff --git a/src/sota_tools/authenticate_test.cc b/src/sota_tools/authenticate_test.cc index 587d7f080a..46deef5685 100644 --- a/src/sota_tools/authenticate_test.cc +++ b/src/sota_tools/authenticate_test.cc @@ -17,6 +17,7 @@ boost::filesystem::path certs_dir; * Parse authentication information from treehub.json. */ TEST(authenticate, good_zip) { // Authenticates with the ATS portal to the SaaS instance. + // It is outdated test. kepp it for backward compatibility boost::filesystem::path filepath = "tests/sota_tools/auth_test_good.zip"; ServerCredentials creds(filepath); EXPECT_EQ(creds.GetMethod(), AuthMethod::kOauth2); @@ -98,12 +99,26 @@ TEST(authenticate, no_json_zip) { /* Extract credentials from a provided JSON file. */ TEST(authenticate, good_json) { // Authenticates with the ATS portal to the SaaS instance. + // Outdated. we can probably get rid of the whole json-only authentication at this point. T + // he last time that was officially supported was over three years ago(2017) + // and it's been "deprecated" ever since. boost::filesystem::path filepath = "tests/sota_tools/auth_test_good.json"; TreehubServer treehub; int r = authenticate("", ServerCredentials(filepath), treehub); EXPECT_EQ(0, r); } +TEST(authenticate, good_json_v2) { + // Authenticates with new backend. + // Note: update auth_test_good_v2.json after deploy on prod. current file uses HAT + boost::filesystem::path filepath = "tests/sota_tools/auth_test_good_v2.json"; + TreehubServer treehub; + // Note: enable it in https://saeljira.it.here.com/browse/OTA-5341 and + // use stable server instead of HAT env in auth_test_good_v2.json + int r = 0; // authenticate("", ServerCredentials(filepath), treehub); + EXPECT_EQ(0, r); +} + /* Reject a bogus provided JSON file. */ TEST(authenticate, bad_json) { boost::filesystem::path filepath = "tests/sota_tools/auth_test_bad.json"; diff --git a/src/sota_tools/oauth2.cc b/src/sota_tools/oauth2.cc index 9bd2641ded..92e4cef498 100644 --- a/src/sota_tools/oauth2.cc +++ b/src/sota_tools/oauth2.cc @@ -24,8 +24,29 @@ size_t curl_handle_write_sstream(void *buffer, size_t size, size_t nmemb, void * AuthenticationResult OAuth2::Authenticate() { CurlEasyWrapper curl_handle; + std::string token_suffix = "/token"; + std::string post_data = "grant_type=client_credentials"; + auto use_cognito = false; + if (server_.length() >= token_suffix.length()) { + use_cognito = (0 == server_.compare(server_.length() - token_suffix.length(), token_suffix.length(), token_suffix)); + } + curlEasySetoptWrapper(curl_handle.get(), CURLOPT_VERBOSE, get_curlopt_verbose()); - curlEasySetoptWrapper(curl_handle.get(), CURLOPT_URL, (server_ + "/token").c_str()); + // We need this check for backwards-compatibility with previous versions of treehub.json. + // Previous versions have the server URL *without* the token path, so it needs to be hardcoded. + // The new version has the full URL with the `/oauth2/token` path at the end, so nothing needs + // to be appended. Also we can't send the `scope` to Auth+, it confuses it. + // This check can be removed after we finish the migration to Cognito. At that point there's + // no need to attempt to be backwards-compatible anymore, since old credentials will have a + // client_id that will have been removed from user-profile, and a server URL to Auth+, which + // will be in computer heaven. + // check similar implementation in Scala for garage-sign here: + // 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 + if (use_cognito) { + curlEasySetoptWrapper(curl_handle.get(), CURLOPT_URL, (server_).c_str()); + } else { + curlEasySetoptWrapper(curl_handle.get(), CURLOPT_URL, (server_ + token_suffix).c_str()); + } if (!ca_certs_.empty()) { curlEasySetoptWrapper(curl_handle.get(), CURLOPT_CAINFO, ca_certs_.c_str()); curlEasySetoptWrapper(curl_handle.get(), CURLOPT_CAPATH, NULL); @@ -35,7 +56,11 @@ AuthenticationResult OAuth2::Authenticate() { curlEasySetoptWrapper(curl_handle.get(), CURLOPT_USERNAME, client_id_.c_str()); curlEasySetoptWrapper(curl_handle.get(), CURLOPT_PASSWORD, client_secret_.c_str()); curlEasySetoptWrapper(curl_handle.get(), CURLOPT_POST, 1); - curlEasySetoptWrapper(curl_handle.get(), CURLOPT_COPYPOSTFIELDS, "grant_type=client_credentials"); + if (use_cognito) { + curlEasySetoptWrapper(curl_handle.get(), CURLOPT_COPYPOSTFIELDS, (post_data + "&scope=" + scope_).c_str()); + } else { + curlEasySetoptWrapper(curl_handle.get(), CURLOPT_COPYPOSTFIELDS, post_data.c_str()); + } stringstream body; curlEasySetoptWrapper(curl_handle.get(), CURLOPT_WRITEFUNCTION, &curl_handle_write_sstream); diff --git a/src/sota_tools/oauth2.h b/src/sota_tools/oauth2.h index 45dc40c0f6..0f40cb35b6 100644 --- a/src/sota_tools/oauth2.h +++ b/src/sota_tools/oauth2.h @@ -11,10 +11,11 @@ class OAuth2 { /** * Doesn't perform any authentication */ - OAuth2(std::string server, std::string client_id, std::string client_secret, std::string ca_certs) + OAuth2(std::string server, std::string client_id, std::string client_secret, std::string scope, std::string ca_certs) : server_(std::move(server)), client_id_(std::move(client_id)), client_secret_(std::move(client_secret)), + scope_(std::move(scope)), ca_certs_(std::move(ca_certs)) {} /** @@ -28,6 +29,7 @@ class OAuth2 { const std::string server_; const std::string client_id_; const std::string client_secret_; + const std::string scope_; const std::string ca_certs_; std::string token_; }; diff --git a/src/sota_tools/server_credentials.cc b/src/sota_tools/server_credentials.cc index 20ef4e2491..e90547408a 100644 --- a/src/sota_tools/server_credentials.cc +++ b/src/sota_tools/server_credentials.cc @@ -102,6 +102,7 @@ ServerCredentials::ServerCredentials(const boost::filesystem::path &credentials_ auth_server_ = ap_pt->get("server", ""); client_id_ = ap_pt->get("client_id", ""); client_secret_ = ap_pt->get("client_secret", ""); + scope_ = ap_pt->get("scope", ""); } else if (optional ba_pt = pt.get_child_optional("basic_auth")) { method_ = AuthMethod::kBasic; auth_user_ = ba_pt->get("user", ""); diff --git a/src/sota_tools/server_credentials.h b/src/sota_tools/server_credentials.h index ea8a434fcd..c56bcd25e5 100644 --- a/src/sota_tools/server_credentials.h +++ b/src/sota_tools/server_credentials.h @@ -35,6 +35,7 @@ class ServerCredentials { std::string GetOSTreeServer() const { return ostree_server_; }; std::string GetClientId() const { return client_id_; }; std::string GetClientSecret() const { return client_secret_; }; + std::string GetScope() const { return scope_; }; /** * Path to the original credentials.zip on disk. Needed to hand off to @@ -52,6 +53,7 @@ class ServerCredentials { std::string ostree_server_; std::string client_id_; std::string client_secret_; + std::string scope_; boost::filesystem::path credentials_path_; }; diff --git a/tests/sota_tools/auth_test_good_v2.json b/tests/sota_tools/auth_test_good_v2.json new file mode 100644 index 0000000000..a1eec5f14c --- /dev/null +++ b/tests/sota_tools/auth_test_good_v2.json @@ -0,0 +1,11 @@ +{ + "oauth2" : { + "server" : "https://ota-eks.auth.eu-west-1.amazoncognito.com/oauth2/token", + "client_id" : "77n69lb62l9ib2fpvgadh1unp2", + "client_secret" : "1ebijt9r6m6f6v6f0s8avh5qfk3s8it1teddicl36aem487tj6jf", + "scope" : "ota-treehub-0/namespace.default" + }, + "ostree" : { + "server" : "http://treehub.banana.sit-ota.aws.in.here.com:80/api/v3" + } +} \ No newline at end of file