blob: 0b3213297c24cda9dbd092ecd26e9ce8e53b214e [file] [log] [blame]
// 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_token_storage.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/values.h"
namespace {
const base::FilePath::CharType kTokenFileName[] =
FILE_PATH_LITERAL("tokens.json");
const base::FilePath::CharType kRemotingFolder[] =
FILE_PATH_LITERAL("remoting");
const base::FilePath::CharType kTokenStoreFolder[] =
FILE_PATH_LITERAL("token_store");
constexpr char kUnspecifiedUsername[] = "unspecified";
constexpr char kRefreshTokenKey[] = "refresh_token";
constexpr char kUserEmailKey[] = "user_email";
constexpr char kAccessTokenKey[] = "access_token";
constexpr char kDeviceIdKey[] = "device_id";
} // namespace
namespace remoting {
namespace test {
// Provides functionality to write a refresh token to a local folder on disk and
// read it back during subsequent tool runs.
class TestTokenStorageOnDisk : public TestTokenStorage {
public:
TestTokenStorageOnDisk(const std::string& user_name,
const base::FilePath& tokens_file_path);
~TestTokenStorageOnDisk() override;
// TestTokenStorage interface.
std::string FetchRefreshToken() override;
bool StoreRefreshToken(const std::string& refresh_token) override;
std::string FetchUserEmail() override;
bool StoreUserEmail(const std::string& user_email) override;
std::string FetchAccessToken() override;
bool StoreAccessToken(const std::string& access_token) override;
std::string FetchDeviceId() override;
bool StoreDeviceId(const std::string& device_id) override;
private:
std::string FetchTokenFromKey(const std::string& key);
bool StoreTokenForKey(const std::string& key, const std::string& value);
// Returns the path for the file used to read from or store a token for the
// user.
base::FilePath GetPathForTokens();
// Used to access the user specific token file.
std::string user_name_;
// Path used to retrieve the tokens file.
base::FilePath file_path_;
DISALLOW_COPY_AND_ASSIGN(TestTokenStorageOnDisk);
};
TestTokenStorageOnDisk::TestTokenStorageOnDisk(const std::string& user_name,
const base::FilePath& file_path)
: user_name_(user_name), file_path_(base::MakeAbsoluteFilePath(file_path)) {
if (user_name_.empty()) {
user_name_ = kUnspecifiedUsername;
}
VLOG(0) << "User name: " << user_name_;
VLOG(0) << "Storage file path: " << GetPathForTokens();
}
TestTokenStorageOnDisk::~TestTokenStorageOnDisk() = default;
std::string TestTokenStorageOnDisk::FetchRefreshToken() {
return FetchTokenFromKey(kRefreshTokenKey);
}
bool TestTokenStorageOnDisk::StoreRefreshToken(
const std::string& refresh_token) {
return StoreTokenForKey(kRefreshTokenKey, refresh_token);
}
std::string TestTokenStorageOnDisk::FetchUserEmail() {
return FetchTokenFromKey(kUserEmailKey);
}
bool TestTokenStorageOnDisk::StoreUserEmail(const std::string& user_email) {
return StoreTokenForKey(kUserEmailKey, user_email);
}
std::string TestTokenStorageOnDisk::FetchAccessToken() {
return FetchTokenFromKey(kAccessTokenKey);
}
bool TestTokenStorageOnDisk::StoreAccessToken(const std::string& access_token) {
return StoreTokenForKey(kAccessTokenKey, access_token);
}
std::string TestTokenStorageOnDisk::FetchDeviceId() {
return FetchTokenFromKey(kDeviceIdKey);
}
bool TestTokenStorageOnDisk::StoreDeviceId(const std::string& device_id) {
return StoreTokenForKey(kDeviceIdKey, device_id);
}
std::string TestTokenStorageOnDisk::FetchTokenFromKey(const std::string& key) {
base::FilePath file_path(GetPathForTokens());
DCHECK(!file_path.empty());
VLOG(1) << "Reading string from: " << file_path.value();
std::string file_contents;
if (!base::ReadFileToString(file_path, &file_contents)) {
VLOG(1) << "Couldn't read file: " << file_path.value();
return std::string();
}
absl::optional<base::Value> token_data(base::JSONReader::Read(file_contents));
base::DictionaryValue* tokens = nullptr;
if (!token_data.has_value() || !token_data->GetAsDictionary(&tokens)) {
LOG(ERROR) << "File contents were not valid JSON, "
<< "could not retrieve token.";
return std::string();
}
base::Value* token = tokens->FindPath({user_name_, key});
if (!token) {
VLOG(1) << "Could not find token for: " << key;
return std::string();
}
return token->GetString();
}
bool TestTokenStorageOnDisk::StoreTokenForKey(const std::string& key,
const std::string& value) {
DCHECK(!value.empty());
base::FilePath file_path(GetPathForTokens());
DCHECK(!file_path.empty());
VLOG(2) << "Storing token to: " << file_path.value();
base::FilePath file_dir(file_path.DirName());
if (!base::DirectoryExists(file_dir) && !base::CreateDirectory(file_dir)) {
LOG(ERROR) << "Failed to create directory, token not stored.";
return false;
}
std::string file_contents("{}");
if (base::PathExists(file_path)) {
if (!base::ReadFileToString(file_path, &file_contents)) {
LOG(ERROR) << "Invalid token file: " << file_path.value();
return false;
}
}
absl::optional<base::Value> token_data(base::JSONReader::Read(file_contents));
base::DictionaryValue* tokens = nullptr;
if (!token_data.has_value() || !token_data->GetAsDictionary(&tokens)) {
LOG(ERROR) << "Invalid token file format, could not store token.";
return false;
}
std::string json_string;
tokens->SetPath({user_name_, key}, base::Value(value));
if (!base::JSONWriter::Write(*token_data, &json_string)) {
LOG(ERROR) << "Couldn't convert JSON data to string";
return false;
}
if (!base::ImportantFileWriter::WriteFileAtomically(file_path, json_string)) {
LOG(ERROR) << "Failed to save token to the file on disk.";
return false;
}
return true;
}
base::FilePath TestTokenStorageOnDisk::GetPathForTokens() {
base::FilePath file_path(file_path_);
// If we weren't given a specific file path, then use the default path.
if (file_path_.empty()) {
if (!GetTempDir(&file_path)) {
LOG(WARNING) << "Failed to retrieve temporary directory path.";
return base::FilePath();
}
file_path = file_path.Append(kRemotingFolder);
file_path = file_path.Append(kTokenStoreFolder);
}
// If no file has been specified, then we will use a default file name.
if (file_path.Extension().empty()) {
file_path = file_path.Append(kTokenFileName);
}
return file_path;
}
std::unique_ptr<TestTokenStorage> TestTokenStorage::OnDisk(
const std::string& user_name,
const base::FilePath& refresh_token_file_path) {
return std::make_unique<TestTokenStorageOnDisk>(user_name,
refresh_token_file_path);
}
} // namespace test
} // namespace remoting