blob: 44847be014367f7c84823e5412338f7b9a6bb79d [file] [log] [blame]
// 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/memory/ptr_util.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() = default;
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();
}
std::unique_ptr<base::Value> 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;
}
}
std::unique_ptr<base::Value> 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->SetKey(user_name_, base::Value(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;
}
std::unique_ptr<RefreshTokenStore> RefreshTokenStore::OnDisk(
const std::string& user_name,
const base::FilePath& refresh_token_file_path) {
return base::WrapUnique<RefreshTokenStore>(
new RefreshTokenStoreOnDisk(user_name, refresh_token_file_path));
}
} // namespace test
} // namespace remoting