|  | // Copyright 2024 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "remoting/test/session_authz_playground.h" | 
|  |  | 
|  | #include <cstdio> | 
|  | #include <cstdlib> | 
|  | #include <memory> | 
|  | #include <string_view> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/command_line.h" | 
|  | #include "base/compiler_specific.h" | 
|  | #include "base/files/file_path.h" | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/memory/scoped_refptr.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/task/bind_post_task.h" | 
|  | #include "base/task/single_thread_task_runner.h" | 
|  | #include "base/time/time.h" | 
|  | #include "remoting/base/certificate_helpers.h" | 
|  | #include "remoting/base/oauth_token_getter.h" | 
|  | #include "remoting/base/oauth_token_getter_impl.h" | 
|  | #include "remoting/base/url_request_context_getter.h" | 
|  | #include "remoting/host/host_config.h" | 
|  | #include "remoting/proto/session_authz_service.h" | 
|  | #include "remoting/test/cli_util.h" | 
|  |  | 
|  | namespace remoting { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | constexpr char kOAuthScope[] = | 
|  | "https://www.googleapis.com/auth/chromoting.me2me.host"; | 
|  |  | 
|  | void PrintErrorAndExit(const std::string& api_name, const HttpStatus& status) { | 
|  | UNSAFE_TODO(fprintf(stderr, "Failed to call API: %s, error code: %d\n", | 
|  | api_name.data(), static_cast<int>(status.error_code()))); | 
|  | UNSAFE_TODO(fprintf(stderr, "%s\n", status.error_message().data())); | 
|  | UNSAFE_TODO(fprintf(stderr, "%s\n", status.response_body().data())); | 
|  | std::exit(1); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | SessionAuthzPlayground::SessionAuthzPlayground() { | 
|  | auto url_request_context_getter = | 
|  | base::MakeRefCounted<URLRequestContextGetter>( | 
|  | base::SingleThreadTaskRunner::GetCurrentDefault()); | 
|  | url_loader_factory_owner_ = | 
|  | std::make_unique<network::TransitionalURLLoaderFactoryOwner>( | 
|  | url_request_context_getter, /* is_trusted= */ true); | 
|  | } | 
|  |  | 
|  | SessionAuthzPlayground::~SessionAuthzPlayground() = default; | 
|  |  | 
|  | void SessionAuthzPlayground::Start() { | 
|  | auto* cmd_line = base::CommandLine::ForCurrentProcess(); | 
|  | base::CommandLine::StringVector args = cmd_line->GetArgs(); | 
|  | base::FilePath host_config_file_path; | 
|  | if (args.size() == 1) { | 
|  | host_config_file_path = base::FilePath(args[0]); | 
|  | } | 
|  | if (host_config_file_path.empty()) { | 
|  | printf("Usage: %s <path-to-host-config-json>\n", | 
|  | cmd_line->GetProgram().MaybeAsASCII().c_str()); | 
|  | std::exit(1); | 
|  | } | 
|  |  | 
|  | service_client_ = std::make_unique<CorpSessionAuthzServiceClient>( | 
|  | url_loader_factory_owner_->GetURLLoaderFactory(), | 
|  | CreateClientCertStoreInstance(), | 
|  | CreateOAuthTokenGetter(host_config_file_path), | 
|  | /* support_id= */ std::string_view()); | 
|  |  | 
|  | run_loop_ = std::make_unique<base::RunLoop>(); | 
|  | GenerateHostToken(); | 
|  | run_loop_->Run(); | 
|  | } | 
|  |  | 
|  | void SessionAuthzPlayground::GenerateHostToken() { | 
|  | printf("Fetching host token...\n"); | 
|  | service_client_->GenerateHostToken( | 
|  | base::BindOnce( | 
|  | [](const HttpStatus& status, | 
|  | std::unique_ptr<internal::GenerateHostTokenResponseStruct> | 
|  | response) { | 
|  | if (!status.ok()) { | 
|  | PrintErrorAndExit("GenerateHostToken", status); | 
|  | } | 
|  | if (!response) { | 
|  | fprintf(stderr, "No response was received.\n"); | 
|  | } | 
|  | UNSAFE_TODO( | 
|  | printf("Host token: %s\n", response->host_token.data())); | 
|  | UNSAFE_TODO( | 
|  | printf("Session ID: %s\n", response->session_id.data())); | 
|  | return response->session_id; | 
|  | }) | 
|  | .Then(base::BindPostTaskToCurrentDefault( | 
|  | base::BindOnce(&SessionAuthzPlayground::VerifySessionToken, | 
|  | base::Unretained(this))))); | 
|  | } | 
|  |  | 
|  | void SessionAuthzPlayground::VerifySessionToken(const std::string& session_id) { | 
|  | printf("Enter session token generated by the client: "); | 
|  | std::string session_token = test::ReadString(); | 
|  | service_client_->VerifySessionToken( | 
|  | session_token, | 
|  | base::BindOnce( | 
|  | [](const std::string& session_id, const HttpStatus& status, | 
|  | std::unique_ptr<internal::VerifySessionTokenResponseStruct> | 
|  | response) { | 
|  | if (!status.ok()) { | 
|  | PrintErrorAndExit("VerifySessionToken", status); | 
|  | } | 
|  | if (!response) { | 
|  | fprintf(stderr, "No response was received.\n"); | 
|  | } | 
|  | UNSAFE_TODO(printf("Reauth token: %s\n", | 
|  | response->session_reauth_token.data())); | 
|  | printf("Reauth token expires in: %fs\n", | 
|  | response->session_reauth_token_lifetime.InSecondsF()); | 
|  | UNSAFE_TODO( | 
|  | printf("Session ID: %s\n", response->session_id.data())); | 
|  | UNSAFE_TODO( | 
|  | printf("Shared secret: %s\n", response->shared_secret.data())); | 
|  | if (session_id != response->session_id) { | 
|  | UNSAFE_TODO(fprintf(stderr, | 
|  | "Unexpected session ID. Expected: %s\n", | 
|  | session_id.data())); | 
|  | std::exit(1); | 
|  | } | 
|  | return response->session_reauth_token; | 
|  | }, | 
|  | session_id) | 
|  | .Then(base::BindPostTaskToCurrentDefault( | 
|  | base::BindPostTaskToCurrentDefault( | 
|  | base::BindOnce(&SessionAuthzPlayground::ReauthorizeHost, | 
|  | base::Unretained(this), session_id))))); | 
|  | } | 
|  |  | 
|  | void SessionAuthzPlayground::ReauthorizeHost(const std::string& session_id, | 
|  | const std::string& reauth_token) { | 
|  | printf("Reauthorize host? [y/N]: "); | 
|  | bool shouldReauthorize = test::ReadYNBool(); | 
|  | if (!shouldReauthorize) { | 
|  | run_loop_->Quit(); | 
|  | } | 
|  | service_client_->ReauthorizeHost( | 
|  | reauth_token, session_id, base::TimeTicks::Max(), | 
|  | base::BindOnce([](const HttpStatus& status, | 
|  | std::unique_ptr<internal::ReauthorizeHostResponseStruct> | 
|  | response) { | 
|  | if (!status.ok()) { | 
|  | PrintErrorAndExit("ReauthorizeHost", status); | 
|  | } | 
|  | if (!response) { | 
|  | fprintf(stderr, "No response was received.\n"); | 
|  | } | 
|  | UNSAFE_TODO(printf("Reauth token: %s\n", | 
|  | response->session_reauth_token.data())); | 
|  | printf("Reauth token expires in: %fs\n", | 
|  | response->session_reauth_token_lifetime.InSecondsF()); | 
|  | return response->session_reauth_token; | 
|  | }) | 
|  | .Then(base::BindPostTaskToCurrentDefault( | 
|  | base::BindOnce(&SessionAuthzPlayground::ReauthorizeHost, | 
|  | base::Unretained(this), session_id)))); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<OAuthTokenGetter> | 
|  | SessionAuthzPlayground::CreateOAuthTokenGetter( | 
|  | const base::FilePath& host_config_file_path) { | 
|  | auto host_config_dict = HostConfigFromJsonFile(host_config_file_path); | 
|  | if (!host_config_dict.has_value()) { | 
|  | UNSAFE_TODO(fprintf(stderr, "Cannot read host config file: %s\n", | 
|  | host_config_file_path.MaybeAsASCII().data())); | 
|  | std::exit(1); | 
|  | } | 
|  | const std::string* refresh_token = | 
|  | host_config_dict->FindString(kOAuthRefreshTokenConfigPath); | 
|  | if (!refresh_token) { | 
|  | fprintf(stderr, "Cannot find OAuth refresh token from host config file.\n"); | 
|  | std::exit(1); | 
|  | } | 
|  | const std::string* service_account_email = | 
|  | host_config_dict->FindString(kServiceAccountConfigPath); | 
|  | if (!service_account_email) { | 
|  | fprintf(stderr, "Cannot find service account email.\n"); | 
|  | std::exit(1); | 
|  | } | 
|  | auto oauth_credentials = | 
|  | std::make_unique<OAuthTokenGetter::OAuthAuthorizationCredentials>( | 
|  | *service_account_email, *refresh_token, | 
|  | /* is_service_account= */ true, | 
|  | std::vector<std::string>{kOAuthScope}); | 
|  | return std::make_unique<OAuthTokenGetterImpl>( | 
|  | std::move(oauth_credentials), | 
|  | url_loader_factory_owner_->GetURLLoaderFactory(), false); | 
|  | } | 
|  |  | 
|  | }  // namespace remoting |