local_policy_test_server.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/file_util.h"
13#include "base/json/json_writer.h"
14#include "base/path_service.h"
15#include "base/stl_util.h"
16#include "base/strings/stringprintf.h"
17#include "chrome/browser/policy/cloud/cloud_policy_constants.h"
18#include "chrome/common/chrome_paths.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(chrome::DIR_TEST_DATA, &source_root));
76  config_file_ = source_root
77      .AppendASCII("policy")
78      .AppendASCII(base::StringPrintf("policy_%s.json", test_name.c_str()));
79}
80
81LocalPolicyTestServer::~LocalPolicyTestServer() {}
82
83bool LocalPolicyTestServer::SetSigningKey(const crypto::RSAPrivateKey* key) {
84  CHECK(server_data_dir_.IsValid());
85
86  std::vector<uint8> signing_key_bits;
87  if (!key->ExportPrivateKey(&signing_key_bits))
88    return false;
89
90  policy_key_ = server_data_dir_.path().Append(kSigningKeyFileName);
91  int bytes_written = file_util::WriteFile(
92      policy_key_,
93      reinterpret_cast<const char*>(vector_as_array(&signing_key_bits)),
94      signing_key_bits.size());
95  return bytes_written == static_cast<int>(signing_key_bits.size());
96}
97
98void LocalPolicyTestServer::RegisterClient(const std::string& dm_token,
99                                           const std::string& device_id) {
100  CHECK(server_data_dir_.IsValid());
101
102  scoped_ptr<base::DictionaryValue> client_dict(new base::DictionaryValue());
103  client_dict->SetString(kClientStateKeyDeviceId, device_id);
104  client_dict->SetString(kClientStateKeyDeviceToken, dm_token);
105  client_dict->SetString(kClientStateKeyMachineName, std::string());
106  client_dict->SetString(kClientStateKeyMachineId, std::string());
107
108  // Allow all policy types for now.
109  scoped_ptr<base::ListValue> types(new base::ListValue());
110  types->AppendString(dm_protocol::kChromeDevicePolicyType);
111  types->AppendString(dm_protocol::kChromeUserPolicyType);
112  types->AppendString(dm_protocol::kChromePublicAccountPolicyType);
113  types->AppendString(dm_protocol::kChromeExtensionPolicyType);
114
115  client_dict->Set(kClientStateKeyAllowedPolicyTypes, types.release());
116  clients_.Set(dm_token, client_dict.release());
117}
118
119bool LocalPolicyTestServer::UpdatePolicy(const std::string& type,
120                                         const std::string& entity_id,
121                                         const std::string& policy) {
122  CHECK(server_data_dir_.IsValid());
123
124  std::string selector = GetSelector(type, entity_id);
125  base::FilePath policy_file = server_data_dir_.path().AppendASCII(
126      base::StringPrintf("policy_%s.bin", selector.c_str()));
127
128  return file_util::WriteFile(policy_file, policy.c_str(), policy.size()) ==
129      static_cast<int>(policy.size());
130}
131
132bool LocalPolicyTestServer::UpdatePolicyData(const std::string& type,
133                                             const std::string& entity_id,
134                                             const std::string& data) {
135  CHECK(server_data_dir_.IsValid());
136
137  std::string selector = GetSelector(type, entity_id);
138  base::FilePath data_file = server_data_dir_.path().AppendASCII(
139      base::StringPrintf("policy_%s.data", selector.c_str()));
140
141  return file_util::WriteFile(data_file, data.c_str(), data.size()) ==
142      static_cast<int>(data.size());
143}
144
145GURL LocalPolicyTestServer::GetServiceURL() const {
146  return GetURL("device_management");
147}
148
149bool LocalPolicyTestServer::SetPythonPath() const {
150  if (!net::LocalTestServer::SetPythonPath())
151    return false;
152
153  // Add the net/tools/testserver directory to the path.
154  base::FilePath net_testserver_path;
155  if (!LocalTestServer::GetTestServerPath(&net_testserver_path)) {
156    LOG(ERROR) << "Failed to get net testserver path.";
157    return false;
158  }
159  AppendToPythonPath(net_testserver_path.DirName());
160
161  // We need protobuf python bindings.
162  base::FilePath third_party_dir;
163  if (!PathService::Get(base::DIR_SOURCE_ROOT, &third_party_dir)) {
164    LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
165    return false;
166  }
167  AppendToPythonPath(third_party_dir
168                     .AppendASCII("third_party")
169                     .AppendASCII("protobuf")
170                     .AppendASCII("python"));
171
172  // Add the generated python protocol buffer bindings.
173  base::FilePath pyproto_dir;
174  if (!GetPyProtoPath(&pyproto_dir)) {
175    LOG(ERROR) << "Cannot find pyproto dir for generated code.";
176    return false;
177  }
178
179  AppendToPythonPath(pyproto_dir
180                     .AppendASCII("chrome")
181                     .AppendASCII("browser")
182                     .AppendASCII("policy")
183                     .AppendASCII("proto")
184                     .AppendASCII("cloud"));
185  AppendToPythonPath(pyproto_dir
186                     .AppendASCII("policy")
187                     .AppendASCII("proto"));
188#if defined(OS_CHROMEOS)
189  AppendToPythonPath(pyproto_dir
190                     .AppendASCII("chrome")
191                     .AppendASCII("browser")
192                     .AppendASCII("policy")
193                     .AppendASCII("proto")
194                     .AppendASCII("chromeos"));
195#endif
196
197  return true;
198}
199
200bool LocalPolicyTestServer::GetTestServerPath(
201    base::FilePath* testserver_path) const {
202  base::FilePath source_root;
203  if (!PathService::Get(base::DIR_SOURCE_ROOT, &source_root)) {
204    LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
205    return false;
206  }
207  *testserver_path = source_root
208      .AppendASCII("chrome")
209      .AppendASCII("browser")
210      .AppendASCII("policy")
211      .AppendASCII("test")
212      .AppendASCII("policy_testserver.py");
213  return true;
214}
215
216bool LocalPolicyTestServer::GenerateAdditionalArguments(
217    base::DictionaryValue* arguments) const {
218  if (!net::LocalTestServer::GenerateAdditionalArguments(arguments))
219    return false;
220
221  arguments->SetString("config-file", config_file_.AsUTF8Unsafe());
222  if (!policy_key_.empty())
223    arguments->SetString("policy-key", policy_key_.AsUTF8Unsafe());
224  if (server_data_dir_.IsValid()) {
225    arguments->SetString("data-dir", server_data_dir_.path().AsUTF8Unsafe());
226
227    if (!clients_.empty()) {
228      std::string json;
229      base::JSONWriter::Write(&clients_, &json);
230      base::FilePath client_state_file =
231          server_data_dir_.path().Append(kClientStateFileName);
232      if (file_util::WriteFile(client_state_file, json.c_str(), json.size()) !=
233          static_cast<int>(json.size())) {
234        return false;
235      }
236      arguments->SetString("client-state", client_state_file.AsUTF8Unsafe());
237    }
238  }
239
240  return true;
241}
242
243std::string LocalPolicyTestServer::GetSelector(const std::string& type,
244                                               const std::string& entity_id) {
245  std::string selector = type;
246  if (!entity_id.empty())
247    selector = base::StringPrintf("%s/%s", type.c_str(), entity_id.c_str());
248  std::replace_if(selector.begin(), selector.end(), IsUnsafeCharacter, '_');
249  return selector;
250}
251
252}  // namespace policy
253