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_path
)
55 : user_name_(user_name
),
56 refresh_token_file_path_(base::MakeAbsoluteFilePath(refresh_token_path
)) {
59 RefreshTokenStoreOnDisk::~RefreshTokenStoreOnDisk() {
62 std::string
RefreshTokenStoreOnDisk::FetchRefreshToken() {
63 base::FilePath
refresh_token_file_path(GetPathForRefreshTokenFile());
64 DCHECK(!refresh_token_file_path
.empty());
65 VLOG(1) << "Reading token from: " << refresh_token_file_path
.value();
67 std::string file_contents
;
68 if (!base::ReadFileToString(refresh_token_file_path
, &file_contents
)) {
69 VLOG(1) << "Couldn't read token file: " << refresh_token_file_path
.value();
73 scoped_ptr
<base::Value
> token_data(base::JSONReader::Read(file_contents
));
74 base::DictionaryValue
* tokens
= nullptr;
75 if (!token_data
|| !token_data
->GetAsDictionary(&tokens
)) {
76 LOG(ERROR
) << "Refresh token file contents were not valid JSON, "
77 << "could not retrieve token.";
81 std::string refresh_token
;
82 if (!tokens
->GetStringWithoutPathExpansion(user_name_
, &refresh_token
)) {
83 // This may not be an error as the file could exist but contain refresh
84 // tokens for other users.
85 VLOG(1) << "Could not find token for: " << user_name_
;
92 bool RefreshTokenStoreOnDisk::StoreRefreshToken(
93 const std::string
& refresh_token
) {
94 DCHECK(!refresh_token
.empty());
96 base::FilePath
file_path(GetPathForRefreshTokenFile());
97 DCHECK(!file_path
.empty());
98 VLOG(2) << "Storing token to: " << file_path
.value();
100 base::FilePath
refresh_token_file_dir(file_path
.DirName());
101 if (!base::DirectoryExists(refresh_token_file_dir
) &&
102 !base::CreateDirectory(refresh_token_file_dir
)) {
103 LOG(ERROR
) << "Failed to create directory, refresh token not stored.";
107 std::string
file_contents("{}");
108 if (base::PathExists(file_path
)) {
109 if (!base::ReadFileToString(file_path
, &file_contents
)) {
110 LOG(ERROR
) << "Invalid token file: " << file_path
.value();
115 scoped_ptr
<base::Value
> token_data(base::JSONReader::Read(file_contents
));
116 base::DictionaryValue
* tokens
= nullptr;
117 if (!token_data
|| !token_data
->GetAsDictionary(&tokens
)) {
118 LOG(ERROR
) << "Invalid refresh token file format, could not store token.";
122 std::string json_string
;
123 tokens
->SetStringWithoutPathExpansion(user_name_
, refresh_token
);
124 if (!base::JSONWriter::Write(*token_data
, &json_string
)) {
125 LOG(ERROR
) << "Couldn't convert JSON data to string";
129 if (!base::ImportantFileWriter::WriteFileAtomically(file_path
, json_string
)) {
130 LOG(ERROR
) << "Failed to save refresh token to the file on disk.";
137 base::FilePath
RefreshTokenStoreOnDisk::GetPathForRefreshTokenFile() {
138 base::FilePath
refresh_token_file_path(refresh_token_file_path_
);
140 // If we weren't given a specific file path, then use the default path.
141 if (refresh_token_file_path
.empty()) {
142 if (!GetTempDir(&refresh_token_file_path
)) {
143 LOG(WARNING
) << "Failed to retrieve temporary directory path.";
144 return base::FilePath();
147 refresh_token_file_path
= refresh_token_file_path
.Append(kRemotingFolder
);
148 refresh_token_file_path
=
149 refresh_token_file_path
.Append(kRefreshTokenStoreFolder
);
152 // If no file has been specified, then we will use a default file name.
153 if (refresh_token_file_path
.Extension().empty()) {
154 refresh_token_file_path
= refresh_token_file_path
.Append(kTokenFileName
);
157 return refresh_token_file_path
;
160 scoped_ptr
<RefreshTokenStore
> RefreshTokenStore::OnDisk(
161 const std::string
& user_name
,
162 const base::FilePath
& refresh_token_file_path
) {
163 return make_scoped_ptr
<RefreshTokenStore
>(
164 new RefreshTokenStoreOnDisk(user_name
, refresh_token_file_path
));
168 } // namespace remoting