Commit bdc4a979 authored by Yuwei Huang's avatar Yuwei Huang Committed by Commit Bot

[Remoting] Integrate with OAuthTokenGetter for FtlClient

This CL makes FtlClient use OAuthTokenGetter to retrieve access token
and attach it to the gRPC request. It also has some other changes in the
FTL playground:
* Move FtlSignalingPlayground into a separate class to make it cleaner.
* Make FtlSignalingPlayground prompt and read access code from the user
  and use it to start up an OAuthTokenGetter.

Bug: 927962
Change-Id: I43d2f19b53b41e2fa3652e6c6e4c09eb54ae889e
Reviewed-on: https://chromium-review.googlesource.com/c/1496317Reviewed-by: default avatarJoe Downing <joedow@chromium.org>
Commit-Queue: Joe Downing <joedow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636861}
parent 0daaef05
......@@ -23,7 +23,9 @@ constexpr char kFtlServerEndpoint[] = "instantmessaging-pa.googleapis.com";
namespace remoting {
FtlClient::FtlClient() {
FtlClient::FtlClient(OAuthTokenGetter* token_getter) : weak_factory_(this) {
DCHECK(token_getter);
token_getter_ = token_getter;
auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
auto channel = grpc::CreateChannel(kFtlServerEndpoint, channel_creds);
peer_to_peer_stub_ = PeerToPeer::NewStub(channel);
......@@ -35,10 +37,42 @@ void FtlClient::GetIceServer(RpcCallback<ftl::GetICEServerResponse> callback) {
ftl::GetICEServerRequest request;
request.set_allocated_header(BuildRequestHeader().release());
dispatcher_.ExecuteAsyncRpc(
GetOAuthTokenAndExecuteRpc(
base::BindOnce(&PeerToPeer::Stub::AsyncGetICEServer,
base::Unretained(peer_to_peer_stub_.get())),
CreateClientContext(), request, std::move(callback));
request, std::move(callback));
}
template <typename RequestType, typename ResponseType>
void FtlClient::GetOAuthTokenAndExecuteRpc(
GrpcAsyncDispatcher::AsyncRpcFunction<RequestType, ResponseType> rpc,
const RequestType& request,
RpcCallback<ResponseType> callback) {
token_getter_->CallWithToken(base::BindOnce(
&FtlClient::ExecuteRpcWithFetchedOAuthToken<RequestType, ResponseType>,
weak_factory_.GetWeakPtr(), std::move(rpc), request,
std::move(callback)));
}
template <typename RequestType, typename ResponseType>
void FtlClient::ExecuteRpcWithFetchedOAuthToken(
GrpcAsyncDispatcher::AsyncRpcFunction<RequestType, ResponseType> rpc,
const RequestType& request,
RpcCallback<ResponseType> callback,
OAuthTokenGetter::Status status,
const std::string& user_email,
const std::string& access_token) {
std::unique_ptr<grpc::ClientContext> context = CreateClientContext();
if (status != OAuthTokenGetter::Status::SUCCESS) {
LOG(ERROR) << "Failed to fetch access token. Status: " << status;
}
if (status == OAuthTokenGetter::Status::SUCCESS && !access_token.empty()) {
context->set_credentials(grpc::AccessTokenCredentials(access_token));
} else {
LOG(WARNING) << "Attempting to execute RPC without access token.";
}
dispatcher_.ExecuteAsyncRpc(std::move(rpc), std::move(context), request,
std::move(callback));
}
// static
......
......@@ -6,8 +6,11 @@
#define REMOTING_SIGNALING_FTL_CLIENT_H_
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "remoting/base/oauth_token_getter.h"
#include "remoting/signaling/ftl_services.grpc.pb.h"
#include "remoting/signaling/grpc_async_dispatcher.h"
#include "third_party/grpc/src/include/grpcpp/support/status.h"
......@@ -21,7 +24,7 @@ class FtlClient {
using RpcCallback =
base::OnceCallback<void(grpc::Status, const ResponseType&)>;
FtlClient();
explicit FtlClient(OAuthTokenGetter* token_getter);
~FtlClient();
// Retrieves the ice server configs.
......@@ -31,12 +34,29 @@ class FtlClient {
using PeerToPeer =
google::internal::communications::instantmessaging::v1::PeerToPeer;
template <typename RequestType, typename ResponseType>
void GetOAuthTokenAndExecuteRpc(
GrpcAsyncDispatcher::AsyncRpcFunction<RequestType, ResponseType> rpc,
const RequestType& request,
RpcCallback<ResponseType> callback);
template <typename RequestType, typename ResponseType>
void ExecuteRpcWithFetchedOAuthToken(
GrpcAsyncDispatcher::AsyncRpcFunction<RequestType, ResponseType> rpc,
const RequestType& request,
RpcCallback<ResponseType> callback,
OAuthTokenGetter::Status status,
const std::string& user_email,
const std::string& access_token);
static std::unique_ptr<grpc::ClientContext> CreateClientContext();
static std::unique_ptr<ftl::RequestHeader> BuildRequestHeader();
OAuthTokenGetter* token_getter_;
std::unique_ptr<PeerToPeer::Stub> peer_to_peer_stub_;
GrpcAsyncDispatcher dispatcher_;
base::WeakPtrFactory<FtlClient> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FtlClient);
};
......
......@@ -180,9 +180,19 @@ if (enable_remoting_host && !is_android && !is_chromeos) {
executable("ftl_signaling_playground") {
sources = [
"ftl_signaling_playground.cc",
"ftl_signaling_playground.h",
"ftl_signaling_playground_main.cc",
"test_oauth_token_factory.cc",
"test_oauth_token_factory.h",
]
deps = [
"//google_apis",
"//mojo/core/embedder",
"//remoting/base",
"//remoting/base:authorization",
"//remoting/signaling",
"//services/network:network_service",
"//services/network/public/mojom",
]
}
......
......@@ -2,39 +2,100 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/at_exit.h"
#include "remoting/test/ftl_signaling_playground.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "remoting/base/oauth_token_getter_impl.h"
#include "remoting/signaling/ftl_client.h"
#include "remoting/test/test_oauth_token_factory.h"
namespace remoting {
namespace {
class FtlSignalingPlayground {
public:
FtlSignalingPlayground();
~FtlSignalingPlayground();
constexpr char kSwitchNameHelp[] = "help";
constexpr char kSwitchNameAuthCode[] = "code";
void GetIceServer(base::OnceClosure on_done);
// Reads a newline-terminated string from stdin.
std::string ReadString() {
const int kMaxLen = 1024;
std::string str(kMaxLen, 0);
char* result = fgets(&str[0], kMaxLen, stdin);
if (!result)
return std::string();
size_t newline_index = str.find('\n');
if (newline_index != std::string::npos)
str[newline_index] = '\0';
str.resize(strlen(&str[0]));
return str;
}
private:
static void OnGetIceServerResponse(base::OnceClosure on_done,
grpc::Status status,
const ftl::GetICEServerResponse& response);
// Read the value of |switch_name| from command line if it exists, otherwise
// read from stdin.
std::string ReadStringFromCommandLineOrStdin(const std::string& switch_name,
const std::string& read_prompt) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switch_name)) {
return command_line->GetSwitchValueASCII(switch_name);
}
printf("%s", read_prompt.c_str());
return ReadString();
}
FtlClient client_;
DISALLOW_COPY_AND_ASSIGN(FtlSignalingPlayground);
};
} // namespace
namespace remoting {
FtlSignalingPlayground::FtlSignalingPlayground() = default;
FtlSignalingPlayground::~FtlSignalingPlayground() = default;
bool FtlSignalingPlayground::ShouldPrintHelp() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchNameHelp);
}
void FtlSignalingPlayground::PrintHelp() {
printf("Usage: %s [--code=<auth-code>]\n",
base::CommandLine::ForCurrentProcess()
->GetProgram()
.MaybeAsASCII()
.c_str());
}
void FtlSignalingPlayground::StartAndAuthenticate() {
DCHECK(!token_getter_factory_);
DCHECK(!token_getter_);
DCHECK(!client_);
static const std::string read_auth_code_prompt = base::StringPrintf(
"Please authenticate at:\n\n"
" %s\n\n"
"Enter the auth code: ",
TestOAuthTokenGetterFactory::GetAuthorizationCodeUri().c_str());
std::string auth_code = ReadStringFromCommandLineOrStdin(
kSwitchNameAuthCode, read_auth_code_prompt);
token_getter_factory_ = std::make_unique<TestOAuthTokenGetterFactory>();
// We can't get back the refresh token since we have first-party scope, so
// we are not trying to store it.
// TODO(yuweih): Consider storing the access token and reuse it until it is
// expired.
token_getter_ = token_getter_factory_->CreateFromIntermediateCredentials(
auth_code,
base::DoNothing::Repeatedly<const std::string&, const std::string&>());
client_ = std::make_unique<FtlClient>(token_getter_.get());
}
void FtlSignalingPlayground::GetIceServer(base::OnceClosure on_done) {
client_.GetIceServer(base::BindOnce(
DCHECK(client_);
client_->GetIceServer(base::BindOnce(
&FtlSignalingPlayground::OnGetIceServerResponse, std::move(on_done)));
VLOG(0) << "Running GetIceServer...";
}
......@@ -72,17 +133,3 @@ void FtlSignalingPlayground::OnGetIceServerResponse(
}
} // namespace remoting
int main(int argc, char const* argv[]) {
base::AtExitManager exitManager;
base::CommandLine::Init(argc, argv);
base::MessageLoopForIO message_loop;
remoting::FtlSignalingPlayground playground;
base::RunLoop run_loop;
playground.GetIceServer(run_loop.QuitClosure());
run_loop.Run();
return 0;
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_TEST_FTL_SIGNALING_PLAYGROUND_H_
#define REMOTING_TEST_FTL_SIGNALING_PLAYGROUND_H_
#include <memory>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "remoting/base/oauth_token_getter.h"
#include "remoting/signaling/ftl_client.h"
namespace remoting {
class TestOAuthTokenGetterFactory;
class FtlSignalingPlayground {
public:
FtlSignalingPlayground();
~FtlSignalingPlayground();
bool ShouldPrintHelp();
void PrintHelp();
void StartAndAuthenticate();
void GetIceServer(base::OnceClosure on_done);
private:
static void OnGetIceServerResponse(base::OnceClosure on_done,
grpc::Status status,
const ftl::GetICEServerResponse& response);
std::unique_ptr<TestOAuthTokenGetterFactory> token_getter_factory_;
std::unique_ptr<OAuthTokenGetter> token_getter_;
std::unique_ptr<FtlClient> client_;
DISALLOW_COPY_AND_ASSIGN(FtlSignalingPlayground);
};
} // namespace remoting
#endif // REMOTING_TEST_FTL_SIGNALING_PLAYGROUND_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/task/task_scheduler/task_scheduler.h"
#include "mojo/core/embedder/embedder.h"
#include "remoting/test/ftl_signaling_playground.h"
int main(int argc, char const* argv[]) {
base::AtExitManager exitManager;
base::CommandLine::Init(argc, argv);
base::MessageLoopForIO message_loop;
remoting::FtlSignalingPlayground playground;
if (playground.ShouldPrintHelp()) {
playground.PrintHelp();
return 0;
}
base::TaskScheduler::CreateAndStartWithDefaultParams(
"FtlSignalingPlayground");
mojo::core::Init();
playground.StartAndAuthenticate();
base::RunLoop run_loop;
playground.GetIceServer(run_loop.QuitClosure());
run_loop.Run();
return 0;
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/test/test_oauth_token_factory.h"
#include <utility>
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "google_apis/google_api_keys.h"
#include "net/base/escape.h"
#include "remoting/base/oauth_token_getter_impl.h"
#include "remoting/base/url_request_context_getter.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/transitional_url_loader_factory_owner.h"
namespace remoting {
namespace {
constexpr char kChromotingAuthScopeValues[] =
"https://www.googleapis.com/auth/chromoting "
"https://www.googleapis.com/auth/googletalk "
"https://www.googleapis.com/auth/userinfo.email "
"https://www.googleapis.com/auth/tachyon";
constexpr char kOauthRedirectUrl[] =
"https://chromoting-oauth.talkgadget."
"google.com/talkgadget/oauth/chrome-remote-desktop/dev";
} // namespace
TestOAuthTokenGetterFactory::TestOAuthTokenGetterFactory() {
auto url_request_context_getter =
base::MakeRefCounted<URLRequestContextGetter>(
base::ThreadTaskRunnerHandle::Get());
url_loader_factory_owner_ =
std::make_unique<network::TransitionalURLLoaderFactoryOwner>(
url_request_context_getter);
}
TestOAuthTokenGetterFactory::~TestOAuthTokenGetterFactory() = default;
// static
std::string TestOAuthTokenGetterFactory::GetAuthorizationCodeUri() {
// Replace space characters with a '+' sign when formatting.
bool use_plus = true;
return base::StringPrintf(
"https://accounts.google.com/o/oauth2/auth"
"?scope=%s"
"&redirect_uri=https://chromoting-oauth.talkgadget.google.com/"
"talkgadget/oauth/chrome-remote-desktop/dev"
"&response_type=code"
"&client_id=%s"
"&access_type=offline",
net::EscapeUrlEncodedData(kChromotingAuthScopeValues, use_plus).c_str(),
net::EscapeUrlEncodedData(
google_apis::GetOAuth2ClientID(google_apis::CLIENT_REMOTING),
use_plus)
.c_str());
}
std::unique_ptr<OAuthTokenGetter>
TestOAuthTokenGetterFactory::CreateFromIntermediateCredentials(
const std::string& auth_code,
const OAuthTokenGetter::CredentialsUpdatedCallback& on_credentials_update) {
auto oauth_credentials =
std::make_unique<OAuthTokenGetter::OAuthIntermediateCredentials>(
auth_code, /* is_service_account */ false);
oauth_credentials->oauth_redirect_uri = kOauthRedirectUrl;
return std::make_unique<OAuthTokenGetterImpl>(
std::move(oauth_credentials), on_credentials_update,
url_loader_factory_owner_->GetURLLoaderFactory(),
/* auto_refresh */ true);
}
} // namespace remoting
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_TEST_TEST_OAUTH_TOKEN_FACTORY_H_
#define REMOTING_TEST_TEST_OAUTH_TOKEN_FACTORY_H_
#include <memory>
#include <string>
#include "base/macros.h"
#include "remoting/base/oauth_token_getter.h"
namespace network {
class TransitionalURLLoaderFactoryOwner;
} // namespace network
namespace remoting {
// The factory object must outlive all OAuthTokenGetters it creates.
class TestOAuthTokenGetterFactory {
public:
TestOAuthTokenGetterFactory();
~TestOAuthTokenGetterFactory();
static std::string GetAuthorizationCodeUri();
std::unique_ptr<OAuthTokenGetter> CreateFromIntermediateCredentials(
const std::string& auth_code,
const OAuthTokenGetter::CredentialsUpdatedCallback&
on_credentials_update);
private:
std::unique_ptr<network::TransitionalURLLoaderFactoryOwner>
url_loader_factory_owner_;
DISALLOW_COPY_AND_ASSIGN(TestOAuthTokenGetterFactory);
};
} // namespace remoting
#endif // REMOTING_TEST_TEST_OAUTH_TOKEN_FACTORY_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment