joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 1 | // Copyright 2015 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/refresh_token_store.h" |
| 6 | |
| 7 | #include "base/files/file_util.h" |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 8 | #include "base/files/important_file_writer.h" |
| 9 | #include "base/json/json_reader.h" |
| 10 | #include "base/json/json_writer.h" |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 11 | #include "base/logging.h" |
avi | 5a080f01 | 2015-12-22 23:15:43 | [diff] [blame] | 12 | #include "base/macros.h" |
dcheng | 0765c49 | 2016-04-06 22:41:53 | [diff] [blame] | 13 | #include "base/memory/ptr_util.h" |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 14 | #include "base/values.h" |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 15 | |
| 16 | namespace { |
| 17 | const base::FilePath::CharType kTokenFileName[] = |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 18 | FILE_PATH_LITERAL("refresh_tokens.json"); |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 19 | const base::FilePath::CharType kRemotingFolder[] = |
| 20 | FILE_PATH_LITERAL("remoting"); |
| 21 | const base::FilePath::CharType kRefreshTokenStoreFolder[] = |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 22 | FILE_PATH_LITERAL("token_store"); |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 23 | } // namespace |
| 24 | |
| 25 | namespace remoting { |
| 26 | namespace test { |
| 27 | |
| 28 | // Provides functionality to write a refresh token to a local folder on disk and |
| 29 | // read it back during subsequent tool runs. |
| 30 | class RefreshTokenStoreOnDisk : public RefreshTokenStore { |
| 31 | public: |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 32 | RefreshTokenStoreOnDisk(const std::string& user_name, |
| 33 | const base::FilePath& refresh_token_file_path); |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 34 | ~RefreshTokenStoreOnDisk() override; |
| 35 | |
| 36 | // RefreshTokenStore interface. |
| 37 | std::string FetchRefreshToken() override; |
| 38 | bool StoreRefreshToken(const std::string& refresh_token) override; |
| 39 | |
| 40 | private: |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 41 | // Returns the path for the file used to read from or store a refresh token |
| 42 | // for the user. |
| 43 | base::FilePath GetPathForRefreshTokenFile(); |
| 44 | |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 45 | // Used to access the user specific token file. |
| 46 | std::string user_name_; |
| 47 | |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 48 | // Path used to retrieve the refresh token file. |
| 49 | base::FilePath refresh_token_file_path_; |
| 50 | |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 51 | DISALLOW_COPY_AND_ASSIGN(RefreshTokenStoreOnDisk); |
| 52 | }; |
| 53 | |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 54 | RefreshTokenStoreOnDisk::RefreshTokenStoreOnDisk( |
| 55 | const std::string& user_name, |
joedow | d6fad194 | 2015-06-20 01:08:21 | [diff] [blame] | 56 | const base::FilePath& refresh_token_path) |
| 57 | : user_name_(user_name), |
| 58 | refresh_token_file_path_(base::MakeAbsoluteFilePath(refresh_token_path)) { |
joedow | 13648c6 | 2015-03-26 03:47:55 | [diff] [blame] | 59 | } |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 60 | |
joedow | 13648c6 | 2015-03-26 03:47:55 | [diff] [blame] | 61 | RefreshTokenStoreOnDisk::~RefreshTokenStoreOnDisk() { |
| 62 | } |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 63 | |
| 64 | std::string RefreshTokenStoreOnDisk::FetchRefreshToken() { |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 65 | base::FilePath refresh_token_file_path(GetPathForRefreshTokenFile()); |
| 66 | DCHECK(!refresh_token_file_path.empty()); |
joedow | 9cfae7c | 2015-07-06 18:44:18 | [diff] [blame] | 67 | VLOG(1) << "Reading token from: " << refresh_token_file_path.value(); |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 68 | |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 69 | std::string file_contents; |
| 70 | if (!base::ReadFileToString(refresh_token_file_path, &file_contents)) { |
joedow | 9cfae7c | 2015-07-06 18:44:18 | [diff] [blame] | 71 | VLOG(1) << "Couldn't read token file: " << refresh_token_file_path.value(); |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 72 | return std::string(); |
| 73 | } |
| 74 | |
dcheng | 0765c49 | 2016-04-06 22:41:53 | [diff] [blame] | 75 | std::unique_ptr<base::Value> token_data( |
| 76 | base::JSONReader::Read(file_contents)); |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 77 | base::DictionaryValue* tokens = nullptr; |
| 78 | if (!token_data || !token_data->GetAsDictionary(&tokens)) { |
| 79 | LOG(ERROR) << "Refresh token file contents were not valid JSON, " |
| 80 | << "could not retrieve token."; |
| 81 | return std::string(); |
| 82 | } |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 83 | |
| 84 | std::string refresh_token; |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 85 | if (!tokens->GetStringWithoutPathExpansion(user_name_, &refresh_token)) { |
| 86 | // This may not be an error as the file could exist but contain refresh |
| 87 | // tokens for other users. |
joedow | 9cfae7c | 2015-07-06 18:44:18 | [diff] [blame] | 88 | VLOG(1) << "Could not find token for: " << user_name_; |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 89 | return std::string(); |
| 90 | } |
| 91 | |
| 92 | return refresh_token; |
| 93 | } |
| 94 | |
| 95 | bool RefreshTokenStoreOnDisk::StoreRefreshToken( |
| 96 | const std::string& refresh_token) { |
| 97 | DCHECK(!refresh_token.empty()); |
| 98 | |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 99 | base::FilePath file_path(GetPathForRefreshTokenFile()); |
| 100 | DCHECK(!file_path.empty()); |
joedow | 9cfae7c | 2015-07-06 18:44:18 | [diff] [blame] | 101 | VLOG(2) << "Storing token to: " << file_path.value(); |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 102 | |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 103 | base::FilePath refresh_token_file_dir(file_path.DirName()); |
| 104 | if (!base::DirectoryExists(refresh_token_file_dir) && |
| 105 | !base::CreateDirectory(refresh_token_file_dir)) { |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 106 | LOG(ERROR) << "Failed to create directory, refresh token not stored."; |
| 107 | return false; |
| 108 | } |
| 109 | |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 110 | std::string file_contents("{}"); |
| 111 | if (base::PathExists(file_path)) { |
| 112 | if (!base::ReadFileToString(file_path, &file_contents)) { |
| 113 | LOG(ERROR) << "Invalid token file: " << file_path.value(); |
| 114 | return false; |
| 115 | } |
| 116 | } |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 117 | |
dcheng | 0765c49 | 2016-04-06 22:41:53 | [diff] [blame] | 118 | std::unique_ptr<base::Value> token_data( |
| 119 | base::JSONReader::Read(file_contents)); |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 120 | base::DictionaryValue* tokens = nullptr; |
| 121 | if (!token_data || !token_data->GetAsDictionary(&tokens)) { |
| 122 | LOG(ERROR) << "Invalid refresh token file format, could not store token."; |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 123 | return false; |
| 124 | } |
| 125 | |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 126 | std::string json_string; |
| 127 | tokens->SetStringWithoutPathExpansion(user_name_, refresh_token); |
| 128 | if (!base::JSONWriter::Write(*token_data, &json_string)) { |
| 129 | LOG(ERROR) << "Couldn't convert JSON data to string"; |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 130 | return false; |
| 131 | } |
| 132 | |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 133 | if (!base::ImportantFileWriter::WriteFileAtomically(file_path, json_string)) { |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 134 | LOG(ERROR) << "Failed to save refresh token to the file on disk."; |
| 135 | return false; |
| 136 | } |
| 137 | |
| 138 | return true; |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | base::FilePath RefreshTokenStoreOnDisk::GetPathForRefreshTokenFile() { |
| 142 | base::FilePath refresh_token_file_path(refresh_token_file_path_); |
| 143 | |
| 144 | // If we weren't given a specific file path, then use the default path. |
| 145 | if (refresh_token_file_path.empty()) { |
| 146 | if (!GetTempDir(&refresh_token_file_path)) { |
| 147 | LOG(WARNING) << "Failed to retrieve temporary directory path."; |
| 148 | return base::FilePath(); |
| 149 | } |
| 150 | |
| 151 | refresh_token_file_path = refresh_token_file_path.Append(kRemotingFolder); |
| 152 | refresh_token_file_path = |
| 153 | refresh_token_file_path.Append(kRefreshTokenStoreFolder); |
| 154 | } |
| 155 | |
| 156 | // If no file has been specified, then we will use a default file name. |
| 157 | if (refresh_token_file_path.Extension().empty()) { |
| 158 | refresh_token_file_path = refresh_token_file_path.Append(kTokenFileName); |
| 159 | } |
| 160 | |
| 161 | return refresh_token_file_path; |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 162 | } |
| 163 | |
dcheng | 0765c49 | 2016-04-06 22:41:53 | [diff] [blame] | 164 | std::unique_ptr<RefreshTokenStore> RefreshTokenStore::OnDisk( |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 165 | const std::string& user_name, |
| 166 | const base::FilePath& refresh_token_file_path) { |
dcheng | 0765c49 | 2016-04-06 22:41:53 | [diff] [blame] | 167 | return base::WrapUnique<RefreshTokenStore>( |
joedow | 9c250277 | 2015-06-11 19:43:19 | [diff] [blame] | 168 | new RefreshTokenStoreOnDisk(user_name, refresh_token_file_path)); |
joedow | ea77cec | 2015-02-25 23:12:51 | [diff] [blame] | 169 | } |
| 170 | |
| 171 | } // namespace test |
| 172 | } // namespace remoting |