blob: b0dc107c1193da74d469d5edb28a6a75e74dbd00 [file] [log] [blame]
Yuwei Huang946cebd42019-03-04 22:14:201// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "remoting/test/test_token_storage.h"
6
7#include "base/files/file_util.h"
8#include "base/files/important_file_writer.h"
9#include "base/json/json_reader.h"
10#include "base/json/json_writer.h"
11#include "base/logging.h"
12#include "base/macros.h"
13#include "base/memory/ptr_util.h"
14#include "base/values.h"
15
16namespace {
17const base::FilePath::CharType kTokenFileName[] =
18 FILE_PATH_LITERAL("tokens.json");
19const base::FilePath::CharType kRemotingFolder[] =
20 FILE_PATH_LITERAL("remoting");
21const base::FilePath::CharType kTokenStoreFolder[] =
22 FILE_PATH_LITERAL("token_store");
23constexpr char kUnspecifiedUsername[] = "unspecified";
24constexpr char kRefreshTokenKey[] = "refresh_token";
Yuwei Huang41ddec52019-04-02 02:08:1925constexpr char kUserEmailKey[] = "user_email";
Yuwei Huang946cebd42019-03-04 22:14:2026constexpr char kAccessTokenKey[] = "access_token";
27constexpr char kDeviceIdKey[] = "device_id";
28} // namespace
29
30namespace remoting {
31namespace test {
32
33// Provides functionality to write a refresh token to a local folder on disk and
34// read it back during subsequent tool runs.
35class TestTokenStorageOnDisk : public TestTokenStorage {
36 public:
37 TestTokenStorageOnDisk(const std::string& user_name,
38 const base::FilePath& tokens_file_path);
39 ~TestTokenStorageOnDisk() override;
40
41 // TestTokenStorage interface.
42 std::string FetchRefreshToken() override;
43 bool StoreRefreshToken(const std::string& refresh_token) override;
Yuwei Huang41ddec52019-04-02 02:08:1944 std::string FetchUserEmail() override;
45 bool StoreUserEmail(const std::string& user_email) override;
Yuwei Huang946cebd42019-03-04 22:14:2046 std::string FetchAccessToken() override;
47 bool StoreAccessToken(const std::string& access_token) override;
48 std::string FetchDeviceId() override;
49 bool StoreDeviceId(const std::string& device_id) override;
50
51 private:
52 std::string FetchTokenFromKey(const std::string& key);
53 bool StoreTokenForKey(const std::string& key, const std::string& value);
54
55 // Returns the path for the file used to read from or store a token for the
56 // user.
57 base::FilePath GetPathForTokens();
58
59 // Used to access the user specific token file.
60 std::string user_name_;
61
62 // Path used to retrieve the tokens file.
63 base::FilePath file_path_;
64
65 DISALLOW_COPY_AND_ASSIGN(TestTokenStorageOnDisk);
66};
67
68TestTokenStorageOnDisk::TestTokenStorageOnDisk(const std::string& user_name,
69 const base::FilePath& file_path)
70 : user_name_(user_name), file_path_(base::MakeAbsoluteFilePath(file_path)) {
71 if (user_name_.empty()) {
72 user_name_ = kUnspecifiedUsername;
73 }
74 VLOG(0) << "User name: " << user_name_;
75 VLOG(0) << "Storage file path: " << GetPathForTokens();
76}
77
78TestTokenStorageOnDisk::~TestTokenStorageOnDisk() = default;
79
80std::string TestTokenStorageOnDisk::FetchRefreshToken() {
81 return FetchTokenFromKey(kRefreshTokenKey);
82}
83
84bool TestTokenStorageOnDisk::StoreRefreshToken(
85 const std::string& refresh_token) {
86 return StoreTokenForKey(kRefreshTokenKey, refresh_token);
87}
88
Yuwei Huang41ddec52019-04-02 02:08:1989std::string TestTokenStorageOnDisk::FetchUserEmail() {
90 return FetchTokenFromKey(kUserEmailKey);
91}
92
93bool TestTokenStorageOnDisk::StoreUserEmail(const std::string& user_email) {
94 return StoreTokenForKey(kUserEmailKey, user_email);
95}
96
Yuwei Huang946cebd42019-03-04 22:14:2097std::string TestTokenStorageOnDisk::FetchAccessToken() {
98 return FetchTokenFromKey(kAccessTokenKey);
99}
100
101bool TestTokenStorageOnDisk::StoreAccessToken(const std::string& access_token) {
102 return StoreTokenForKey(kAccessTokenKey, access_token);
103}
104
105std::string TestTokenStorageOnDisk::FetchDeviceId() {
106 return FetchTokenFromKey(kDeviceIdKey);
107}
108
109bool TestTokenStorageOnDisk::StoreDeviceId(const std::string& device_id) {
110 return StoreTokenForKey(kDeviceIdKey, device_id);
111}
112
113std::string TestTokenStorageOnDisk::FetchTokenFromKey(const std::string& key) {
114 base::FilePath file_path(GetPathForTokens());
115 DCHECK(!file_path.empty());
116 VLOG(1) << "Reading string from: " << file_path.value();
117
118 std::string file_contents;
119 if (!base::ReadFileToString(file_path, &file_contents)) {
120 VLOG(1) << "Couldn't read file: " << file_path.value();
121 return std::string();
122 }
123
124 base::Optional<base::Value> token_data(base::JSONReader::Read(file_contents));
125 base::DictionaryValue* tokens = nullptr;
126 if (!token_data.has_value() || !token_data->GetAsDictionary(&tokens)) {
127 LOG(ERROR) << "File contents were not valid JSON, "
128 << "could not retrieve token.";
129 return std::string();
130 }
131
132 base::Value* token = tokens->FindPath({user_name_, key});
133 if (!token) {
134 VLOG(1) << "Could not find token for: " << key;
135 return std::string();
136 }
137
138 return token->GetString();
139}
140
141bool TestTokenStorageOnDisk::StoreTokenForKey(const std::string& key,
142 const std::string& value) {
143 DCHECK(!value.empty());
144
145 base::FilePath file_path(GetPathForTokens());
146 DCHECK(!file_path.empty());
147 VLOG(2) << "Storing token to: " << file_path.value();
148
149 base::FilePath file_dir(file_path.DirName());
150 if (!base::DirectoryExists(file_dir) && !base::CreateDirectory(file_dir)) {
151 LOG(ERROR) << "Failed to create directory, token not stored.";
152 return false;
153 }
154
155 std::string file_contents("{}");
156 if (base::PathExists(file_path)) {
157 if (!base::ReadFileToString(file_path, &file_contents)) {
158 LOG(ERROR) << "Invalid token file: " << file_path.value();
159 return false;
160 }
161 }
162
163 base::Optional<base::Value> token_data(base::JSONReader::Read(file_contents));
164 base::DictionaryValue* tokens = nullptr;
165 if (!token_data.has_value() || !token_data->GetAsDictionary(&tokens)) {
166 LOG(ERROR) << "Invalid token file format, could not store token.";
167 return false;
168 }
169
170 std::string json_string;
171 tokens->SetPath({user_name_, key}, base::Value(value));
172 if (!base::JSONWriter::Write(*token_data, &json_string)) {
173 LOG(ERROR) << "Couldn't convert JSON data to string";
174 return false;
175 }
176
177 if (!base::ImportantFileWriter::WriteFileAtomically(file_path, json_string)) {
178 LOG(ERROR) << "Failed to save token to the file on disk.";
179 return false;
180 }
181
182 return true;
183}
184
185base::FilePath TestTokenStorageOnDisk::GetPathForTokens() {
186 base::FilePath file_path(file_path_);
187
188 // If we weren't given a specific file path, then use the default path.
189 if (file_path_.empty()) {
190 if (!GetTempDir(&file_path)) {
191 LOG(WARNING) << "Failed to retrieve temporary directory path.";
192 return base::FilePath();
193 }
194
195 file_path = file_path.Append(kRemotingFolder);
196 file_path = file_path.Append(kTokenStoreFolder);
197 }
198
199 // If no file has been specified, then we will use a default file name.
200 if (file_path.Extension().empty()) {
201 file_path = file_path.Append(kTokenFileName);
202 }
203
204 return file_path;
205}
206
207std::unique_ptr<TestTokenStorage> TestTokenStorage::OnDisk(
208 const std::string& user_name,
209 const base::FilePath& refresh_token_file_path) {
210 return std::make_unique<TestTokenStorageOnDisk>(user_name,
211 refresh_token_file_path);
212}
213
214} // namespace test
215} // namespace remoting