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/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 
     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(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 
     81 LocalPolicyTestServer::~LocalPolicyTestServer() {}
     82 
     83 bool 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 
     98 void 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 
    119 bool 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 
    132 bool 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 
    145 GURL LocalPolicyTestServer::GetServiceURL() const {
    146   return GetURL("device_management");
    147 }
    148 
    149 bool 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 
    200 bool 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 
    216 bool 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 
    243 std::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