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/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 // The file containing client definitions to be passed to the server.
35 const base::FilePath::CharType kClientStateFileName
[] =
36 FILE_PATH_LITERAL("clients");
38 // Dictionary keys for the client state file. Needs to be kept in sync with
39 // policy_testserver.py.
40 const char kClientStateKeyAllowedPolicyTypes
[] = "allowed_policy_types";
41 const char kClientStateKeyDeviceId
[] = "device_id";
42 const char kClientStateKeyDeviceToken
[] = "device_token";
43 const char kClientStateKeyMachineName
[] = "machine_name";
44 const char kClientStateKeyMachineId
[] = "machine_id";
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.
49 bool IsUnsafeCharacter(char c
) {
50 return !(isalnum(c
) || c
== '.' || c
== '@' || c
== '-');
55 LocalPolicyTestServer::LocalPolicyTestServer()
56 : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP
,
57 net::BaseTestServer::kLocalhost
,
59 CHECK(server_data_dir_
.CreateUniqueTempDir());
60 config_file_
= server_data_dir_
.path().Append(kPolicyFileName
);
63 LocalPolicyTestServer::LocalPolicyTestServer(const base::FilePath
& config_file
)
64 : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP
,
65 net::BaseTestServer::kLocalhost
,
67 config_file_(config_file
) {}
69 LocalPolicyTestServer::LocalPolicyTestServer(const std::string
& test_name
)
70 : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP
,
71 net::BaseTestServer::kLocalhost
,
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")
80 .AppendASCII("policy")
81 .AppendASCII(base::StringPrintf("policy_%s.json", test_name
.c_str()));
84 LocalPolicyTestServer::~LocalPolicyTestServer() {}
86 bool LocalPolicyTestServer::SetSigningKey(const crypto::RSAPrivateKey
* key
) {
87 CHECK(server_data_dir_
.IsValid());
89 std::vector
<uint8
> signing_key_bits
;
90 if (!key
->ExportPrivateKey(&signing_key_bits
))
93 policy_key_
= server_data_dir_
.path().Append(kSigningKeyFileName
);
94 int bytes_written
= file_util::WriteFile(
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());
101 void LocalPolicyTestServer::RegisterClient(const std::string
& dm_token
,
102 const std::string
& device_id
) {
103 CHECK(server_data_dir_
.IsValid());
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());
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
);
118 client_dict
->Set(kClientStateKeyAllowedPolicyTypes
, types
.release());
119 clients_
.Set(dm_token
, client_dict
.release());
122 bool LocalPolicyTestServer::UpdatePolicy(const std::string
& type
,
123 const std::string
& entity_id
,
124 const std::string
& policy
) {
125 CHECK(server_data_dir_
.IsValid());
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()));
131 return file_util::WriteFile(policy_file
, policy
.c_str(), policy
.size()) ==
132 static_cast<int>(policy
.size());
135 bool LocalPolicyTestServer::UpdatePolicyData(const std::string
& type
,
136 const std::string
& entity_id
,
137 const std::string
& data
) {
138 CHECK(server_data_dir_
.IsValid());
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()));
144 return file_util::WriteFile(data_file
, data
.c_str(), data
.size()) ==
145 static_cast<int>(data
.size());
148 GURL
LocalPolicyTestServer::GetServiceURL() const {
149 return GetURL("device_management");
152 bool LocalPolicyTestServer::SetPythonPath() const {
153 if (!net::LocalTestServer::SetPythonPath())
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.";
162 AppendToPythonPath(net_testserver_path
.DirName());
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";
170 AppendToPythonPath(third_party_dir
171 .AppendASCII("third_party")
172 .AppendASCII("protobuf")
173 .AppendASCII("python"));
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.";
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"));
203 bool 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";
210 *testserver_path
= source_root
211 .AppendASCII("chrome")
212 .AppendASCII("browser")
213 .AppendASCII("policy")
215 .AppendASCII("policy_testserver.py");
219 bool LocalPolicyTestServer::GenerateAdditionalArguments(
220 base::DictionaryValue
* arguments
) const {
221 if (!net::LocalTestServer::GenerateAdditionalArguments(arguments
))
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());
230 if (!clients_
.empty()) {
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())) {
239 arguments
->SetString("client-state", client_state_file
.AsUTF8Unsafe());
246 std::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
, '_');
255 } // namespace policy