local_policy_test_server.cc revision 3551c9c881056c480085172ff9840cab31610854
1// Copyright (c) 2013 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.
4
5#include "chrome/browser/policy/test/local_policy_test_server.h"
6
7#include <ctype.h>
8
9#include <algorithm>
10#include <vector>
11
12#include "base/base_paths.h"
13#include "base/file_util.h"
14#include "base/json/json_writer.h"
15#include "base/path_service.h"
16#include "base/stl_util.h"
17#include "base/strings/stringprintf.h"
18#include "chrome/browser/policy/cloud/cloud_policy_constants.h"
19#include "crypto/rsa_private_key.h"
20#include "net/test/python_utils.h"
21#include "net/test/spawned_test_server/base_test_server.h"
22
23namespace policy {
24
25namespace {
26
27// Filename in the temporary directory storing the policy data.
28const base::FilePath::CharType kPolicyFileName[] = FILE_PATH_LITERAL("policy");
29
30// Private signing key file within the temporary directory.
31const base::FilePath::CharType kSigningKeyFileName[] =
32    FILE_PATH_LITERAL("signing_key");
33
34// The file containing client definitions to be passed to the server.
35const base::FilePath::CharType kClientStateFileName[] =
36    FILE_PATH_LITERAL("clients");
37
38// Dictionary keys for the client state file. Needs to be kept in sync with
39// policy_testserver.py.
40const char kClientStateKeyAllowedPolicyTypes[] = "allowed_policy_types";
41const char kClientStateKeyDeviceId[] = "device_id";
42const char kClientStateKeyDeviceToken[] = "device_token";
43const char kClientStateKeyMachineName[] = "machine_name";
44const char kClientStateKeyMachineId[] = "machine_id";
45
46// Checks whether a given character should be replaced when constructing a file
47// name. To keep things simple, this is a bit over-aggressive. Needs to be kept
48// in sync with policy_testserver.py.
49bool IsUnsafeCharacter(char c) {
50  return !(isalnum(c) || c == '.' || c == '@' || c == '-');
51}
52
53}  // namespace
54
55LocalPolicyTestServer::LocalPolicyTestServer()
56    : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP,
57                           net::BaseTestServer::kLocalhost,
58                           base::FilePath()) {
59  CHECK(server_data_dir_.CreateUniqueTempDir());
60  config_file_ = server_data_dir_.path().Append(kPolicyFileName);
61}
62
63LocalPolicyTestServer::LocalPolicyTestServer(const base::FilePath& config_file)
64    : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP,
65                           net::BaseTestServer::kLocalhost,
66                           base::FilePath()),
67      config_file_(config_file) {}
68
69LocalPolicyTestServer::LocalPolicyTestServer(const std::string& test_name)
70    : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP,
71                           net::BaseTestServer::kLocalhost,
72                           base::FilePath()) {
73  // Read configuration from a file in chrome/test/data/policy.
74  base::FilePath source_root;
75  CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &source_root));
76  config_file_ = source_root
77      .AppendASCII("chrome")
78      .AppendASCII("test")
79      .AppendASCII("data")
80      .AppendASCII("policy")
81      .AppendASCII(base::StringPrintf("policy_%s.json", test_name.c_str()));
82}
83
84LocalPolicyTestServer::~LocalPolicyTestServer() {}
85
86bool LocalPolicyTestServer::SetSigningKey(const crypto::RSAPrivateKey* key) {
87  CHECK(server_data_dir_.IsValid());
88
89  std::vector<uint8> signing_key_bits;
90  if (!key->ExportPrivateKey(&signing_key_bits))
91    return false;
92
93  policy_key_ = server_data_dir_.path().Append(kSigningKeyFileName);
94  int bytes_written = file_util::WriteFile(
95      policy_key_,
96      reinterpret_cast<const char*>(vector_as_array(&signing_key_bits)),
97      signing_key_bits.size());
98  return bytes_written == static_cast<int>(signing_key_bits.size());
99}
100
101void LocalPolicyTestServer::RegisterClient(const std::string& dm_token,
102                                           const std::string& device_id) {
103  CHECK(server_data_dir_.IsValid());
104
105  scoped_ptr<base::DictionaryValue> client_dict(new base::DictionaryValue());
106  client_dict->SetString(kClientStateKeyDeviceId, device_id);
107  client_dict->SetString(kClientStateKeyDeviceToken, dm_token);
108  client_dict->SetString(kClientStateKeyMachineName, std::string());
109  client_dict->SetString(kClientStateKeyMachineId, std::string());
110
111  // Allow all policy types for now.
112  scoped_ptr<base::ListValue> types(new base::ListValue());
113  types->AppendString(dm_protocol::kChromeDevicePolicyType);
114  types->AppendString(dm_protocol::kChromeUserPolicyType);
115  types->AppendString(dm_protocol::kChromePublicAccountPolicyType);
116  types->AppendString(dm_protocol::kChromeExtensionPolicyType);
117
118  client_dict->Set(kClientStateKeyAllowedPolicyTypes, types.release());
119  clients_.Set(dm_token, client_dict.release());
120}
121
122bool LocalPolicyTestServer::UpdatePolicy(const std::string& type,
123                                         const std::string& entity_id,
124                                         const std::string& policy) {
125  CHECK(server_data_dir_.IsValid());
126
127  std::string selector = GetSelector(type, entity_id);
128  base::FilePath policy_file = server_data_dir_.path().AppendASCII(
129      base::StringPrintf("policy_%s.bin", selector.c_str()));
130
131  return file_util::WriteFile(policy_file, policy.c_str(), policy.size()) ==
132      static_cast<int>(policy.size());
133}
134
135bool LocalPolicyTestServer::UpdatePolicyData(const std::string& type,
136                                             const std::string& entity_id,
137                                             const std::string& data) {
138  CHECK(server_data_dir_.IsValid());
139
140  std::string selector = GetSelector(type, entity_id);
141  base::FilePath data_file = server_data_dir_.path().AppendASCII(
142      base::StringPrintf("policy_%s.data", selector.c_str()));
143
144  return file_util::WriteFile(data_file, data.c_str(), data.size()) ==
145      static_cast<int>(data.size());
146}
147
148GURL LocalPolicyTestServer::GetServiceURL() const {
149  return GetURL("device_management");
150}
151
152bool LocalPolicyTestServer::SetPythonPath() const {
153  if (!net::LocalTestServer::SetPythonPath())
154    return false;
155
156  // Add the net/tools/testserver directory to the path.
157  base::FilePath net_testserver_path;
158  if (!LocalTestServer::GetTestServerPath(&net_testserver_path)) {
159    LOG(ERROR) << "Failed to get net testserver path.";
160    return false;
161  }
162  AppendToPythonPath(net_testserver_path.DirName());
163
164  // We need protobuf python bindings.
165  base::FilePath third_party_dir;
166  if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) {
167    LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
168    return false;
169  }
170  AppendToPythonPath(third_party_dir
171                     .AppendASCII("third_party")
172                     .AppendASCII("protobuf")
173                     .AppendASCII("python"));
174
175  // Add the generated python protocol buffer bindings.
176  base::FilePath pyproto_dir;
177  if (!GetPyProtoPath(&pyproto_dir)) {
178    LOG(ERROR) << "Cannot find pyproto dir for generated code.";
179    return false;
180  }
181
182  AppendToPythonPath(pyproto_dir
183                     .AppendASCII("chrome")
184                     .AppendASCII("browser")
185                     .AppendASCII("policy")
186                     .AppendASCII("proto")
187                     .AppendASCII("cloud"));
188  AppendToPythonPath(pyproto_dir
189                     .AppendASCII("policy")
190                     .AppendASCII("proto"));
191#if defined(OS_CHROMEOS)
192  AppendToPythonPath(pyproto_dir
193                     .AppendASCII("chrome")
194                     .AppendASCII("browser")
195                     .AppendASCII("policy")
196                     .AppendASCII("proto")
197                     .AppendASCII("chromeos"));
198#endif
199
200  return true;
201}
202
203bool LocalPolicyTestServer::GetTestServerPath(
204    base::FilePath* testserver_path) const {
205  base::FilePath source_root;
206  if (!PathService::Get(base::DIR_SOURCE_ROOT, &source_root)) {
207    LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
208    return false;
209  }
210  *testserver_path = source_root
211      .AppendASCII("chrome")
212      .AppendASCII("browser")
213      .AppendASCII("policy")
214      .AppendASCII("test")
215      .AppendASCII("policy_testserver.py");
216  return true;
217}
218
219bool LocalPolicyTestServer::GenerateAdditionalArguments(
220    base::DictionaryValue* arguments) const {
221  if (!net::LocalTestServer::GenerateAdditionalArguments(arguments))
222    return false;
223
224  arguments->SetString("config-file", config_file_.AsUTF8Unsafe());
225  if (!policy_key_.empty())
226    arguments->SetString("policy-key", policy_key_.AsUTF8Unsafe());
227  if (server_data_dir_.IsValid()) {
228    arguments->SetString("data-dir", server_data_dir_.path().AsUTF8Unsafe());
229
230    if (!clients_.empty()) {
231      std::string json;
232      base::JSONWriter::Write(&clients_, &json);
233      base::FilePath client_state_file =
234          server_data_dir_.path().Append(kClientStateFileName);
235      if (file_util::WriteFile(client_state_file, json.c_str(), json.size()) !=
236          static_cast<int>(json.size())) {
237        return false;
238      }
239      arguments->SetString("client-state", client_state_file.AsUTF8Unsafe());
240    }
241  }
242
243  return true;
244}
245
246std::string LocalPolicyTestServer::GetSelector(const std::string& type,
247                                               const std::string& entity_id) {
248  std::string selector = type;
249  if (!entity_id.empty())
250    selector = base::StringPrintf("%s/%s", type.c_str(), entity_id.c_str());
251  std::replace_if(selector.begin(), selector.end(), IsUnsafeCharacter, '_');
252  return selector;
253}
254
255}  // namespace policy
256