1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
|
// Copyright 2019 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/test_token_storage.h"
#include <optional>
#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/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 kScopesKey[] = "scopes";
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(const TestTokenStorageOnDisk&) = delete;
TestTokenStorageOnDisk& operator=(const TestTokenStorageOnDisk&) = delete;
~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 FetchScopes() override;
bool StoreScopes(const std::string& scopes) 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_;
};
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::FetchScopes() {
return FetchTokenFromKey(kScopesKey);
}
bool TestTokenStorageOnDisk::StoreScopes(const std::string& scopes) {
return StoreTokenForKey(kScopesKey, scopes);
}
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();
}
std::optional<base::Value::Dict> token_data =
base::JSONReader::ReadDict(file_contents);
if (!token_data) {
LOG(ERROR) << "File contents were not valid JSON, "
<< "could not retrieve token.";
return std::string();
}
const std::string* token =
token_data->FindStringByDottedPath(user_name_ + '.' + key);
if (!token) {
VLOG(1) << "Could not find token for: " << key;
return std::string();
}
return *token;
}
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;
}
}
std::optional<base::Value::Dict> token_data =
base::JSONReader::ReadDict(file_contents);
if (!token_data) {
LOG(ERROR) << "Invalid token file format, could not store token.";
return false;
}
std::string json_string;
token_data->SetByDottedPath(user_name_ + '.' + key, 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
|