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