// Copyright 2015 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/refresh_token_store.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/values.h" namespace { const base::FilePath::CharType kTokenFileName[] = FILE_PATH_LITERAL("refresh_tokens.json"); const base::FilePath::CharType kRemotingFolder[] = FILE_PATH_LITERAL("remoting"); const base::FilePath::CharType kRefreshTokenStoreFolder[] = FILE_PATH_LITERAL("token_store"); } // 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 RefreshTokenStoreOnDisk : public RefreshTokenStore { public: RefreshTokenStoreOnDisk(const std::string& user_name, const base::FilePath& refresh_token_file_path); ~RefreshTokenStoreOnDisk() override; // RefreshTokenStore interface. std::string FetchRefreshToken() override; bool StoreRefreshToken(const std::string& refresh_token) override; private: // Returns the path for the file used to read from or store a refresh token // for the user. base::FilePath GetPathForRefreshTokenFile(); // Used to access the user specific token file. std::string user_name_; // Path used to retrieve the refresh token file. base::FilePath refresh_token_file_path_; DISALLOW_COPY_AND_ASSIGN(RefreshTokenStoreOnDisk); }; RefreshTokenStoreOnDisk::RefreshTokenStoreOnDisk( const std::string& user_name, const base::FilePath& refresh_token_path) : user_name_(user_name), refresh_token_file_path_(base::MakeAbsoluteFilePath(refresh_token_path)) { } RefreshTokenStoreOnDisk::~RefreshTokenStoreOnDisk() { } std::string RefreshTokenStoreOnDisk::FetchRefreshToken() { base::FilePath refresh_token_file_path(GetPathForRefreshTokenFile()); DCHECK(!refresh_token_file_path.empty()); VLOG(1) << "Reading token from: " << refresh_token_file_path.value(); std::string file_contents; if (!base::ReadFileToString(refresh_token_file_path, &file_contents)) { VLOG(1) << "Couldn't read token file: " << refresh_token_file_path.value(); return std::string(); } scoped_ptr token_data(base::JSONReader::Read(file_contents)); base::DictionaryValue* tokens = nullptr; if (!token_data || !token_data->GetAsDictionary(&tokens)) { LOG(ERROR) << "Refresh token file contents were not valid JSON, " << "could not retrieve token."; return std::string(); } std::string refresh_token; if (!tokens->GetStringWithoutPathExpansion(user_name_, &refresh_token)) { // This may not be an error as the file could exist but contain refresh // tokens for other users. VLOG(1) << "Could not find token for: " << user_name_; return std::string(); } return refresh_token; } bool RefreshTokenStoreOnDisk::StoreRefreshToken( const std::string& refresh_token) { DCHECK(!refresh_token.empty()); base::FilePath file_path(GetPathForRefreshTokenFile()); DCHECK(!file_path.empty()); VLOG(2) << "Storing token to: " << file_path.value(); base::FilePath refresh_token_file_dir(file_path.DirName()); if (!base::DirectoryExists(refresh_token_file_dir) && !base::CreateDirectory(refresh_token_file_dir)) { LOG(ERROR) << "Failed to create directory, refresh 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; } } scoped_ptr token_data(base::JSONReader::Read(file_contents)); base::DictionaryValue* tokens = nullptr; if (!token_data || !token_data->GetAsDictionary(&tokens)) { LOG(ERROR) << "Invalid refresh token file format, could not store token."; return false; } std::string json_string; tokens->SetStringWithoutPathExpansion(user_name_, refresh_token); 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 refresh token to the file on disk."; return false; } return true; } base::FilePath RefreshTokenStoreOnDisk::GetPathForRefreshTokenFile() { base::FilePath refresh_token_file_path(refresh_token_file_path_); // If we weren't given a specific file path, then use the default path. if (refresh_token_file_path.empty()) { if (!GetTempDir(&refresh_token_file_path)) { LOG(WARNING) << "Failed to retrieve temporary directory path."; return base::FilePath(); } refresh_token_file_path = refresh_token_file_path.Append(kRemotingFolder); refresh_token_file_path = refresh_token_file_path.Append(kRefreshTokenStoreFolder); } // If no file has been specified, then we will use a default file name. if (refresh_token_file_path.Extension().empty()) { refresh_token_file_path = refresh_token_file_path.Append(kTokenFileName); } return refresh_token_file_path; } scoped_ptr RefreshTokenStore::OnDisk( const std::string& user_name, const base::FilePath& refresh_token_file_path) { return make_scoped_ptr( new RefreshTokenStoreOnDisk(user_name, refresh_token_file_path)); } } // namespace test } // namespace remoting