Home | History | Annotate | Download | only in test
      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/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"
     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 // Private signing key signature file within the temporary directory.
     35 const base::FilePath::CharType kSigningKeySignatureFileName[] =
     36     FILE_PATH_LITERAL("signing_key.sig");
     37 
     38 // The file containing client definitions to be passed to the server.
     39 const base::FilePath::CharType kClientStateFileName[] =
     40     FILE_PATH_LITERAL("clients");
     41 
     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";
     49 
     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 == '-');
     55 }
     56 
     57 }  // namespace
     58 
     59 LocalPolicyTestServer::LocalPolicyTestServer()
     60     : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP,
     61                            net::BaseTestServer::kLocalhost,
     62                            base::FilePath()) {
     63   CHECK(server_data_dir_.CreateUniqueTempDir());
     64   config_file_ = server_data_dir_.path().Append(kPolicyFileName);
     65 }
     66 
     67 LocalPolicyTestServer::LocalPolicyTestServer(const base::FilePath& config_file)
     68     : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP,
     69                            net::BaseTestServer::kLocalhost,
     70                            base::FilePath()),
     71       config_file_(config_file) {}
     72 
     73 LocalPolicyTestServer::LocalPolicyTestServer(const std::string& test_name)
     74     : net::LocalTestServer(net::BaseTestServer::TYPE_HTTP,
     75                            net::BaseTestServer::kLocalhost,
     76                            base::FilePath()) {
     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")
     82       .AppendASCII("test")
     83       .AppendASCII("data")
     84       .AppendASCII("policy")
     85       .AppendASCII(base::StringPrintf("policy_%s.json", test_name.c_str()));
     86 }
     87 
     88 LocalPolicyTestServer::~LocalPolicyTestServer() {}
     89 
     90 bool LocalPolicyTestServer::SetSigningKeyAndSignature(
     91     const crypto::RSAPrivateKey* key, const std::string& signature) {
     92   CHECK(server_data_dir_.IsValid());
     93 
     94   std::vector<uint8> signing_key_bits;
     95   if (!key->ExportPrivateKey(&signing_key_bits))
     96     return false;
     97 
     98   policy_key_ = server_data_dir_.path().Append(kSigningKeyFileName);
     99   int bytes_written = base::WriteFile(
    100       policy_key_,
    101       reinterpret_cast<const char*>(vector_as_array(&signing_key_bits)),
    102       signing_key_bits.size());
    103 
    104   if (bytes_written != static_cast<int>(signing_key_bits.size()))
    105     return false;
    106 
    107   // Write the signature data.
    108   base::FilePath signature_file = server_data_dir_.path().Append(
    109       kSigningKeySignatureFileName);
    110   bytes_written = base::WriteFile(
    111       signature_file,
    112       signature.c_str(),
    113       signature.size());
    114 
    115   return bytes_written == static_cast<int>(signature.size());
    116 }
    117 
    118 void LocalPolicyTestServer::RegisterClient(const std::string& dm_token,
    119                                            const std::string& device_id) {
    120   CHECK(server_data_dir_.IsValid());
    121 
    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());
    127 
    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);
    134 
    135   client_dict->Set(kClientStateKeyAllowedPolicyTypes, types.release());
    136   clients_.Set(dm_token, client_dict.release());
    137 }
    138 
    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());
    143 
    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()));
    147 
    148   return base::WriteFile(policy_file, policy.c_str(), policy.size()) ==
    149       static_cast<int>(policy.size());
    150 }
    151 
    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());
    156 
    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()));
    160 
    161   return base::WriteFile(data_file, data.c_str(), data.size()) ==
    162       static_cast<int>(data.size());
    163 }
    164 
    165 GURL LocalPolicyTestServer::GetServiceURL() const {
    166   return GetURL("device_management");
    167 }
    168 
    169 bool LocalPolicyTestServer::SetPythonPath() const {
    170   if (!net::LocalTestServer::SetPythonPath())
    171     return false;
    172 
    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.";
    177     return false;
    178   }
    179   AppendToPythonPath(net_testserver_path.DirName());
    180 
    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";
    185     return false;
    186   }
    187   AppendToPythonPath(third_party_dir
    188                      .AppendASCII("third_party")
    189                      .AppendASCII("protobuf")
    190                      .AppendASCII("python"));
    191 
    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.";
    196     return false;
    197   }
    198 
    199   AppendToPythonPath(pyproto_dir
    200                      .AppendASCII("chrome")
    201                      .AppendASCII("browser")
    202                      .AppendASCII("policy")
    203                      .AppendASCII("proto")
    204                      .AppendASCII("cloud"));
    205   AppendToPythonPath(pyproto_dir
    206                      .AppendASCII("policy")
    207                      .AppendASCII("proto"));
    208 #if defined(OS_CHROMEOS)
    209   AppendToPythonPath(pyproto_dir
    210                      .AppendASCII("chrome")
    211                      .AppendASCII("browser")
    212                      .AppendASCII("policy")
    213                      .AppendASCII("proto")
    214                      .AppendASCII("chromeos"));
    215 #endif
    216 
    217   return true;
    218 }
    219 
    220 bool LocalPolicyTestServer::GetTestServerPath(
    221     base::FilePath* testserver_path) const {
    222   base::FilePath source_root;
    223   if (!PathService::Get(base::DIR_SOURCE_ROOT, &source_root)) {
    224     LOG(ERROR) << "Failed to get DIR_SOURCE_ROOT";
    225     return false;
    226   }
    227   *testserver_path = source_root
    228       .AppendASCII("chrome")
    229       .AppendASCII("browser")
    230       .AppendASCII("policy")
    231       .AppendASCII("test")
    232       .AppendASCII("policy_testserver.py");
    233   return true;
    234 }
    235 
    236 bool LocalPolicyTestServer::GenerateAdditionalArguments(
    237     base::DictionaryValue* arguments) const {
    238   if (!net::LocalTestServer::GenerateAdditionalArguments(arguments))
    239     return false;
    240 
    241   arguments->SetString("config-file", config_file_.AsUTF8Unsafe());
    242   if (!policy_key_.empty())
    243     arguments->SetString("policy-key", policy_key_.AsUTF8Unsafe());
    244   if (server_data_dir_.IsValid()) {
    245     arguments->SetString("data-dir", server_data_dir_.path().AsUTF8Unsafe());
    246 
    247     if (!clients_.empty()) {
    248       std::string json;
    249       base::JSONWriter::Write(&clients_, &json);
    250       base::FilePath client_state_file =
    251           server_data_dir_.path().Append(kClientStateFileName);
    252       if (base::WriteFile(client_state_file, json.c_str(), json.size()) !=
    253           static_cast<int>(json.size())) {
    254         return false;
    255       }
    256       arguments->SetString("client-state", client_state_file.AsUTF8Unsafe());
    257     }
    258   }
    259 
    260   return true;
    261 }
    262 
    263 std::string LocalPolicyTestServer::GetSelector(const std::string& type,
    264                                                const std::string& entity_id) {
    265   std::string selector = type;
    266   if (!entity_id.empty())
    267     selector = base::StringPrintf("%s/%s", type.c_str(), entity_id.c_str());
    268   std::replace_if(selector.begin(), selector.end(), IsUnsafeCharacter, '_');
    269   return selector;
    270 }
    271 
    272 }  // namespace policy
    273