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.
5 #include "chrome/browser/policy/test/local_policy_test_server.h"
12 #include "base/base_paths.h"
13 #include "base/files/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"
27 // Filename in the temporary directory storing the policy data.
28 const base::FilePath::CharType kPolicyFileName
[] = FILE_PATH_LITERAL("policy");
30 // Private signing key file within the temporary directory.
31 const base::FilePath::CharType kSigningKeyFileName
[] =
32 FILE_PATH_LITERAL("signing_key");
34 // Private signing key signature file within the temporary directory.
35 const base::FilePath::CharType kSigningKeySignatureFileName
[] =
36 FILE_PATH_LITERAL("signing_key.sig");
38 // The file containing client definitions to be passed to the server.
39 const base::FilePath::CharType kClientStateFileName
[] =
40 FILE_PATH_LITERAL("clients");
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";
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.
53 bool IsUnsafeCharacter(char c
) {
54 return !(isalnum(c
) || c
== '.' || c
== '@' || c
== '-');
59 LocalPolicyTestServer::LocalPolicyTestServer()
60 : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP
,
61 net::BaseTestServer::kLocalhost
,
63 CHECK(server_data_dir_
.CreateUniqueTempDir());
64 config_file_
= server_data_dir_
.path().Append(kPolicyFileName
);
67 LocalPolicyTestServer::LocalPolicyTestServer(const base::FilePath
& config_file
)
68 : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP
,
69 net::BaseTestServer::kLocalhost
,
71 config_file_(config_file
) {}
73 LocalPolicyTestServer::LocalPolicyTestServer(const std::string
& test_name
)
74 : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP
,
75 net::BaseTestServer::kLocalhost
,
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")
84 .AppendASCII("policy")
85 .AppendASCII(base::StringPrintf("policy_%s.json", test_name
.c_str()));
88 LocalPolicyTestServer::~LocalPolicyTestServer() {}
90 bool LocalPolicyTestServer::SetSigningKeyAndSignature(
91 const crypto::RSAPrivateKey
* key
, const std::string
& signature
) {
92 CHECK(server_data_dir_
.IsValid());
94 std::vector
<uint8
> signing_key_bits
;
95 if (!key
->ExportPrivateKey(&signing_key_bits
))
98 policy_key_
= server_data_dir_
.path().Append(kSigningKeyFileName
);
99 int bytes_written
= base::WriteFile(
101 reinterpret_cast<const char*>(vector_as_array(&signing_key_bits
)),
102 signing_key_bits
.size());
104 if (bytes_written
!= static_cast<int>(signing_key_bits
.size()))
107 // Write the signature data.
108 base::FilePath signature_file
= server_data_dir_
.path().Append(
109 kSigningKeySignatureFileName
);
110 bytes_written
= base::WriteFile(
115 return bytes_written
== static_cast<int>(signature
.size());
118 void LocalPolicyTestServer::RegisterClient(const std::string
& dm_token
,
119 const std::string
& device_id
) {
120 CHECK(server_data_dir_
.IsValid());
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());
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
);
135 client_dict
->Set(kClientStateKeyAllowedPolicyTypes
, types
.release());
136 clients_
.Set(dm_token
, client_dict
.release());
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());
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()));
148 return base::WriteFile(policy_file
, policy
.c_str(), policy
.size()) ==
149 static_cast<int>(policy
.size());
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());
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()));
161 return base::WriteFile(data_file
, data
.c_str(), data
.size()) ==
162 static_cast<int>(data
.size());
165 GURL
LocalPolicyTestServer::GetServiceURL() const {
166 return GetURL("device_management");
169 bool LocalPolicyTestServer::SetPythonPath() const {
170 if (!net::LocalTestServer::SetPythonPath())
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.";
179 AppendToPythonPath(net_testserver_path
.DirName());
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";
187 AppendToPythonPath(third_party_dir
188 .AppendASCII("third_party")
189 .AppendASCII("protobuf")
190 .AppendASCII("python"));
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.";
199 AppendToPythonPath(pyproto_dir
200 .AppendASCII("policy")
201 .AppendASCII("proto"));
202 #if defined(OS_CHROMEOS)
203 AppendToPythonPath(pyproto_dir
204 .AppendASCII("chrome")
205 .AppendASCII("browser")
206 .AppendASCII("chromeos")
207 .AppendASCII("policy")
208 .AppendASCII("proto"));
214 bool LocalPolicyTestServer::GetTestServerPath(
215 base::FilePath
* testserver_path
) const {
216 base::FilePath source_root
;
217 if (!PathService::Get(base::DIR_SOURCE_ROOT
, &source_root
)) {
218 LOG(ERROR
) << "Failed to get DIR_SOURCE_ROOT";
221 *testserver_path
= source_root
222 .AppendASCII("chrome")
223 .AppendASCII("browser")
224 .AppendASCII("policy")
226 .AppendASCII("policy_testserver.py");
230 bool LocalPolicyTestServer::GenerateAdditionalArguments(
231 base::DictionaryValue
* arguments
) const {
232 if (!net::LocalTestServer::GenerateAdditionalArguments(arguments
))
235 arguments
->SetString("config-file", config_file_
.AsUTF8Unsafe());
236 if (!policy_key_
.empty())
237 arguments
->SetString("policy-key", policy_key_
.AsUTF8Unsafe());
238 if (server_data_dir_
.IsValid()) {
239 arguments
->SetString("data-dir", server_data_dir_
.path().AsUTF8Unsafe());
241 if (!clients_
.empty()) {
243 base::JSONWriter::Write(clients_
, &json
);
244 base::FilePath client_state_file
=
245 server_data_dir_
.path().Append(kClientStateFileName
);
246 if (base::WriteFile(client_state_file
, json
.c_str(), json
.size()) !=
247 static_cast<int>(json
.size())) {
250 arguments
->SetString("client-state", client_state_file
.AsUTF8Unsafe());
257 std::string
LocalPolicyTestServer::GetSelector(const std::string
& type
,
258 const std::string
& entity_id
) {
259 std::string selector
= type
;
260 if (!entity_id
.empty())
261 selector
= base::StringPrintf("%s/%s", type
.c_str(), entity_id
.c_str());
262 std::replace_if(selector
.begin(), selector
.end(), IsUnsafeCharacter
, '_');
266 } // namespace policy