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