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