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.
5 #include "remoting/test/refresh_token_store.h"
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/values.h"
15 const base::FilePath::CharType kTokenFileName
[] =
16 FILE_PATH_LITERAL("refresh_tokens.json");
17 const base::FilePath::CharType kRemotingFolder
[] =
18 FILE_PATH_LITERAL("remoting");
19 const base::FilePath::CharType kRefreshTokenStoreFolder
[] =
20 FILE_PATH_LITERAL("token_store");
26 // Provides functionality to write a refresh token to a local folder on disk and
27 // read it back during subsequent tool runs.
28 class RefreshTokenStoreOnDisk
: public RefreshTokenStore
{
30 RefreshTokenStoreOnDisk(const std::string
& user_name
,
31 const base::FilePath
& refresh_token_file_path
);
32 ~RefreshTokenStoreOnDisk() override
;
34 // RefreshTokenStore interface.
35 std::string
FetchRefreshToken() override
;
36 bool StoreRefreshToken(const std::string
& refresh_token
) override
;
39 // Returns the path for the file used to read from or store a refresh token
41 base::FilePath
GetPathForRefreshTokenFile();
43 // Used to access the user specific token file.
44 std::string user_name_
;
46 // Path used to retrieve the refresh token file.
47 base::FilePath refresh_token_file_path_
;
49 DISALLOW_COPY_AND_ASSIGN(RefreshTokenStoreOnDisk
);
52 RefreshTokenStoreOnDisk::RefreshTokenStoreOnDisk(
53 const std::string
& user_name
,
54 const base::FilePath
& refresh_token_file_path
)
55 : user_name_(user_name
), refresh_token_file_path_(refresh_token_file_path
) {
58 RefreshTokenStoreOnDisk::~RefreshTokenStoreOnDisk() {
61 std::string
RefreshTokenStoreOnDisk::FetchRefreshToken() {
62 base::FilePath
refresh_token_file_path(GetPathForRefreshTokenFile());
63 DCHECK(!refresh_token_file_path
.empty());
64 DVLOG(2) << "Reading token from: " << refresh_token_file_path
.value();
66 std::string file_contents
;
67 if (!base::ReadFileToString(refresh_token_file_path
, &file_contents
)) {
68 DVLOG(1) << "Couldn't read token file: " << refresh_token_file_path
.value();
72 scoped_ptr
<base::Value
> token_data(base::JSONReader::Read(file_contents
));
73 base::DictionaryValue
* tokens
= nullptr;
74 if (!token_data
|| !token_data
->GetAsDictionary(&tokens
)) {
75 LOG(ERROR
) << "Refresh token file contents were not valid JSON, "
76 << "could not retrieve token.";
80 std::string refresh_token
;
81 if (!tokens
->GetStringWithoutPathExpansion(user_name_
, &refresh_token
)) {
82 // This may not be an error as the file could exist but contain refresh
83 // tokens for other users.
84 DVLOG(1) << "Could not find token for: " << user_name_
;
91 bool RefreshTokenStoreOnDisk::StoreRefreshToken(
92 const std::string
& refresh_token
) {
93 DCHECK(!refresh_token
.empty());
95 base::FilePath
file_path(GetPathForRefreshTokenFile());
96 DCHECK(!file_path
.empty());
97 DVLOG(2) << "Storing token to: " << file_path
.value();
99 base::FilePath
refresh_token_file_dir(file_path
.DirName());
100 if (!base::DirectoryExists(refresh_token_file_dir
) &&
101 !base::CreateDirectory(refresh_token_file_dir
)) {
102 LOG(ERROR
) << "Failed to create directory, refresh token not stored.";
106 std::string
file_contents("{}");
107 if (base::PathExists(file_path
)) {
108 if (!base::ReadFileToString(file_path
, &file_contents
)) {
109 LOG(ERROR
) << "Invalid token file: " << file_path
.value();
114 scoped_ptr
<base::Value
> token_data(base::JSONReader::Read(file_contents
));
115 base::DictionaryValue
* tokens
= nullptr;
116 if (!token_data
|| !token_data
->GetAsDictionary(&tokens
)) {
117 LOG(ERROR
) << "Invalid refresh token file format, could not store token.";
121 std::string json_string
;
122 tokens
->SetStringWithoutPathExpansion(user_name_
, refresh_token
);
123 if (!base::JSONWriter::Write(*token_data
, &json_string
)) {
124 LOG(ERROR
) << "Couldn't convert JSON data to string";
128 if (!base::ImportantFileWriter::WriteFileAtomically(file_path
, json_string
)) {
129 LOG(ERROR
) << "Failed to save refresh token to the file on disk.";
136 base::FilePath
RefreshTokenStoreOnDisk::GetPathForRefreshTokenFile() {
137 base::FilePath
refresh_token_file_path(refresh_token_file_path_
);
139 // If we weren't given a specific file path, then use the default path.
140 if (refresh_token_file_path
.empty()) {
141 if (!GetTempDir(&refresh_token_file_path
)) {
142 LOG(WARNING
) << "Failed to retrieve temporary directory path.";
143 return base::FilePath();
146 refresh_token_file_path
= refresh_token_file_path
.Append(kRemotingFolder
);
147 refresh_token_file_path
=
148 refresh_token_file_path
.Append(kRefreshTokenStoreFolder
);
151 // If no file has been specified, then we will use a default file name.
152 if (refresh_token_file_path
.Extension().empty()) {
153 refresh_token_file_path
= refresh_token_file_path
.Append(kTokenFileName
);
156 return refresh_token_file_path
;
159 scoped_ptr
<RefreshTokenStore
> RefreshTokenStore::OnDisk(
160 const std::string
& user_name
,
161 const base::FilePath
& refresh_token_file_path
) {
162 return make_scoped_ptr
<RefreshTokenStore
>(
163 new RefreshTokenStoreOnDisk(user_name
, refresh_token_file_path
));
167 } // namespace remoting