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