summaryrefslogtreecommitdiffstats
path: root/remoting/test/refresh_token_store.cc
blob: dfe3964b966a4c0e62fe9a0e4df6050fa0544ab7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// 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<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;
    }
  }

  scoped_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->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> RefreshTokenStore::OnDisk(
    const std::string& user_name,
    const base::FilePath& refresh_token_file_path) {
  return make_scoped_ptr<RefreshTokenStore>(
      new RefreshTokenStoreOnDisk(user_name, refresh_token_file_path));
}

}  // namespace test
}  // namespace remoting