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/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