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 // The file containing client definitions to be passed to the server. 35 const 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. 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"; 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. 49 bool IsUnsafeCharacter(char c) { 50 return !(isalnum(c) || c == '.' || c == '@' || c == '-'); 51 } 52 53 } // namespace 54 55 LocalPolicyTestServer::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 63 LocalPolicyTestServer::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 69 LocalPolicyTestServer::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(base::DIR_SOURCE_ROOT, &source_root)); 76 config_file_ = source_root 77 .AppendASCII("chrome") 78 .AppendASCII("test") 79 .AppendASCII("data") 80 .AppendASCII("policy") 81 .AppendASCII(base::StringPrintf("policy_%s.json", test_name.c_str())); 82 } 83 84 LocalPolicyTestServer::~LocalPolicyTestServer() {} 85 86 bool LocalPolicyTestServer::SetSigningKey(const crypto::RSAPrivateKey* key) { 87 CHECK(server_data_dir_.IsValid()); 88 89 std::vector<uint8> signing_key_bits; 90 if (!key->ExportPrivateKey(&signing_key_bits)) 91 return false; 92 93 policy_key_ = server_data_dir_.path().Append(kSigningKeyFileName); 94 int bytes_written = file_util::WriteFile( 95 policy_key_, 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()); 99 } 100 101 void LocalPolicyTestServer::RegisterClient(const std::string& dm_token, 102 const std::string& device_id) { 103 CHECK(server_data_dir_.IsValid()); 104 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()); 110 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); 117 118 client_dict->Set(kClientStateKeyAllowedPolicyTypes, types.release()); 119 clients_.Set(dm_token, client_dict.release()); 120 } 121 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()); 126 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())); 130 131 return file_util::WriteFile(policy_file, policy.c_str(), policy.size()) == 132 static_cast<int>(policy.size()); 133 } 134 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()); 139 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())); 143 144 return file_util::WriteFile(data_file, data.c_str(), data.size()) == 145 static_cast<int>(data.size()); 146 } 147 148 GURL LocalPolicyTestServer::GetServiceURL() const { 149 return GetURL("device_management"); 150 } 151 152 bool LocalPolicyTestServer::SetPythonPath() const { 153 if (!net::LocalTestServer::SetPythonPath()) 154 return false; 155 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."; 160 return false; 161 } 162 AppendToPythonPath(net_testserver_path.DirName()); 163 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"; 168 return false; 169 } 170 AppendToPythonPath(third_party_dir 171 .AppendASCII("third_party") 172 .AppendASCII("protobuf") 173 .AppendASCII("python")); 174 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."; 179 return false; 180 } 181 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")); 198 #endif 199 200 return true; 201 } 202 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"; 208 return false; 209 } 210 *testserver_path = source_root 211 .AppendASCII("chrome") 212 .AppendASCII("browser") 213 .AppendASCII("policy") 214 .AppendASCII("test") 215 .AppendASCII("policy_testserver.py"); 216 return true; 217 } 218 219 bool LocalPolicyTestServer::GenerateAdditionalArguments( 220 base::DictionaryValue* arguments) const { 221 if (!net::LocalTestServer::GenerateAdditionalArguments(arguments)) 222 return false; 223 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()); 229 230 if (!clients_.empty()) { 231 std::string json; 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())) { 237 return false; 238 } 239 arguments->SetString("client-state", client_state_file.AsUTF8Unsafe()); 240 } 241 } 242 243 return true; 244 } 245 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, '_'); 252 return selector; 253 } 254 255 } // namespace policy 256