1// Copyright 2014 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
6#include "components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h"
7
8#include "base/md5.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/run_loop.h"
11#include "base/strings/string16.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/time/time.h"
14#include "components/data_reduction_proxy/browser/data_reduction_proxy_params_test_utils.h"
15#include "components/data_reduction_proxy/browser/data_reduction_proxy_settings_test_utils.h"
16#include "net/base/auth.h"
17#include "net/base/host_port_pair.h"
18#include "testing/gmock/include/gmock/gmock.h"
19#include "testing/gtest/include/gtest/gtest.h"
20#include "url/gurl.h"
21
22namespace {
23const char kChromeProxyHeader[] = "chrome-proxy";
24const char kOtherProxy[] = "testproxy:17";
25
26
27#if defined(OS_ANDROID)
28  const char kClient[] = "android";
29#elif defined(OS_IOS)
30  const char kClient[] = "ios";
31#else
32  const char kClient[] = "";
33#endif
34const char kVersion[] = "0.1.2.3";
35const char kExpectedBuild[] = "2";
36const char kExpectedPatch[] = "3";
37const char kBogusVersion[] = "0.0";
38const char kTestKey[] = "test-key";
39const char kExpectedCredentials[] = "96bd72ec4a050ba60981743d41787768";
40const char kExpectedSession[] = "0-1633771873-1633771873-1633771873";
41
42const char kTestKey2[] = "test-key2";
43const char kExpectedCredentials2[] = "c911fdb402f578787562cf7f00eda972";
44const char kExpectedSession2[] = "0-1633771873-1633771873-1633771873";
45#if defined(OS_ANDROID)
46const char kExpectedHeader2[] =
47    "ps=0-1633771873-1633771873-1633771873, "
48    "sid=c911fdb402f578787562cf7f00eda972, b=2, p=3, c=android";
49const char kExpectedHeader3[] =
50    "ps=86401-1633771873-1633771873-1633771873, "
51    "sid=d7c1c34ef6b90303b01c48a6c1db6419, b=2, p=3, c=android";
52const char kExpectedHeader4[] =
53    "ps=0-1633771873-1633771873-1633771873, "
54    "sid=c911fdb402f578787562cf7f00eda972, c=android";
55#elif defined(OS_IOS)
56const char kExpectedHeader2[] =
57    "ps=0-1633771873-1633771873-1633771873, "
58    "sid=c911fdb402f578787562cf7f00eda972, b=2, p=3, c=ios";
59const char kExpectedHeader3[] =
60    "ps=86401-1633771873-1633771873-1633771873, "
61    "sid=d7c1c34ef6b90303b01c48a6c1db6419, b=2, p=3, c=ios";
62const char kExpectedHeader4[] =
63    "ps=0-1633771873-1633771873-1633771873, "
64    "sid=c911fdb402f578787562cf7f00eda972, c=ios";
65#else
66const char kExpectedHeader2[] =
67    "ps=0-1633771873-1633771873-1633771873, "
68    "sid=c911fdb402f578787562cf7f00eda972, b=2, p=3";
69const char kExpectedHeader3[] =
70    "ps=86401-1633771873-1633771873-1633771873, "
71    "sid=d7c1c34ef6b90303b01c48a6c1db6419, b=2, p=3";
72const char kExpectedHeader4[] =
73    "ps=0-1633771873-1633771873-1633771873, "
74    "sid=c911fdb402f578787562cf7f00eda972";
75#endif
76
77const char kDataReductionProxyKey[] = "12345";
78}  // namespace
79
80
81namespace data_reduction_proxy {
82namespace {
83class TestDataReductionProxyAuthRequestHandler
84    : public DataReductionProxyAuthRequestHandler {
85 public:
86  TestDataReductionProxyAuthRequestHandler(
87      const std::string& client,
88      const std::string& version,
89      DataReductionProxyParams* params,
90      base::MessageLoopProxy* loop_proxy)
91      : DataReductionProxyAuthRequestHandler(
92            client, version, params, loop_proxy) {}
93
94  virtual std::string GetDefaultKey() const OVERRIDE {
95    return kTestKey;
96  }
97
98  virtual base::Time Now() const OVERRIDE {
99    return base::Time::UnixEpoch() + now_offset_;
100  }
101
102  virtual void RandBytes(void* output, size_t length) OVERRIDE {
103    char* c =  static_cast<char*>(output);
104    for (size_t i = 0; i < length; ++i) {
105      c[i] = 'a';
106    }
107  }
108
109  // Time after the unix epoch that Now() reports.
110  void set_offset(const base::TimeDelta& now_offset) {
111    now_offset_ = now_offset;
112  }
113
114 private:
115  base::TimeDelta now_offset_;
116};
117
118}  // namespace
119
120class DataReductionProxyAuthRequestHandlerTest : public testing::Test {
121 public:
122  DataReductionProxyAuthRequestHandlerTest()
123      : loop_proxy_(base::MessageLoopProxy::current().get()) {
124  }
125  // Required for MessageLoopProxy::current().
126  base::MessageLoopForUI loop_;
127  base::MessageLoopProxy* loop_proxy_;
128};
129
130TEST_F(DataReductionProxyAuthRequestHandlerTest, AuthorizationOnIO) {
131  scoped_ptr<TestDataReductionProxyParams> params;
132  params.reset(
133      new TestDataReductionProxyParams(
134          DataReductionProxyParams::kAllowed |
135          DataReductionProxyParams::kFallbackAllowed |
136          DataReductionProxyParams::kPromoAllowed,
137          TestDataReductionProxyParams::HAS_EVERYTHING &
138          ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
139          ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
140  // loop_proxy_ is just the current message loop. This means loop_proxy_
141  // is the network thread used by DataReductionProxyAuthRequestHandler.
142  TestDataReductionProxyAuthRequestHandler auth_handler(kClient,
143                                                        kVersion,
144                                                        params.get(),
145                                                        loop_proxy_);
146  auth_handler.Init();
147  base::RunLoop().RunUntilIdle();
148  EXPECT_EQ(auth_handler.client_, kClient);
149  EXPECT_EQ(kExpectedBuild, auth_handler.build_number_);
150  EXPECT_EQ(kExpectedPatch, auth_handler.patch_number_);
151  EXPECT_EQ(auth_handler.key_, kTestKey);
152  EXPECT_EQ(kExpectedCredentials, auth_handler.credentials_);
153  EXPECT_EQ(kExpectedSession, auth_handler.session_);
154
155  // Now set a key.
156  auth_handler.InitAuthentication(kTestKey2);
157  base::RunLoop().RunUntilIdle();
158  EXPECT_EQ(kTestKey2, auth_handler.key_);
159  EXPECT_EQ(kExpectedCredentials2, auth_handler.credentials_);
160  EXPECT_EQ(kExpectedSession2, auth_handler.session_);
161
162  // Don't write headers if the proxy is invalid.
163  net::HttpRequestHeaders headers;
164  auth_handler.MaybeAddRequestHeader(NULL, net::ProxyServer(), &headers);
165  EXPECT_FALSE(headers.HasHeader(kChromeProxyHeader));
166
167  // Don't write headers with a valid proxy, that's not a data reduction proxy.
168  auth_handler.MaybeAddRequestHeader(
169      NULL,
170      net::ProxyServer::FromURI(kOtherProxy, net::ProxyServer::SCHEME_HTTP),
171      &headers);
172  EXPECT_FALSE(headers.HasHeader(kChromeProxyHeader));
173
174  // Don't write headers with a valid data reduction ssl proxy.
175  auth_handler.MaybeAddRequestHeader(
176      NULL,
177      net::ProxyServer::FromURI(
178          net::HostPortPair::FromURL(
179              GURL(params->DefaultSSLOrigin())).ToString(),
180          net::ProxyServer::SCHEME_HTTP),
181      &headers);
182  EXPECT_FALSE(headers.HasHeader(kChromeProxyHeader));
183
184  // Write headers with a valid data reduction proxy.
185  auth_handler.MaybeAddRequestHeader(
186      NULL,
187      net::ProxyServer::FromURI(
188          net::HostPortPair::FromURL(GURL(params->DefaultOrigin())).ToString(),
189          net::ProxyServer::SCHEME_HTTP),
190      &headers);
191  EXPECT_TRUE(headers.HasHeader(kChromeProxyHeader));
192  std::string header_value;
193  headers.GetHeader(kChromeProxyHeader, &header_value);
194  EXPECT_EQ(kExpectedHeader2, header_value);
195
196  // Write headers with a valid data reduction ssl proxy when one is expected.
197  net::HttpRequestHeaders ssl_headers;
198  auth_handler.MaybeAddProxyTunnelRequestHandler(
199      net::HostPortPair::FromURL(GURL(params->DefaultSSLOrigin())),
200      &ssl_headers);
201  EXPECT_TRUE(ssl_headers.HasHeader(kChromeProxyHeader));
202  std::string ssl_header_value;
203  ssl_headers.GetHeader(kChromeProxyHeader, &ssl_header_value);
204  EXPECT_EQ(kExpectedHeader2, ssl_header_value);
205
206  // Fast forward 24 hours. The header should be the same.
207  auth_handler.set_offset(base::TimeDelta::FromSeconds(24 * 60 * 60));
208  net::HttpRequestHeaders headers2;
209  // Write headers with a valid data reduction proxy.
210  auth_handler.MaybeAddRequestHeader(
211      NULL,
212      net::ProxyServer::FromURI(
213          net::HostPortPair::FromURL(GURL(params->DefaultOrigin())).ToString(),
214          net::ProxyServer::SCHEME_HTTP),
215      &headers2);
216  EXPECT_TRUE(headers2.HasHeader(kChromeProxyHeader));
217  std::string header_value2;
218  headers2.GetHeader(kChromeProxyHeader, &header_value2);
219  EXPECT_EQ(kExpectedHeader2, header_value2);
220
221  // Fast forward one more second. The header should be new.
222  auth_handler.set_offset(base::TimeDelta::FromSeconds(24 * 60 * 60 + 1));
223  net::HttpRequestHeaders headers3;
224  // Write headers with a valid data reduction proxy.
225  auth_handler.MaybeAddRequestHeader(
226      NULL,
227      net::ProxyServer::FromURI(
228          net::HostPortPair::FromURL(GURL(params->DefaultOrigin())).ToString(),
229          net::ProxyServer::SCHEME_HTTP),
230      &headers3);
231  EXPECT_TRUE(headers3.HasHeader(kChromeProxyHeader));
232  std::string header_value3;
233  headers3.GetHeader(kChromeProxyHeader, &header_value3);
234  EXPECT_EQ(kExpectedHeader3, header_value3);
235}
236
237TEST_F(DataReductionProxyAuthRequestHandlerTest, AuthorizationIgnoresEmptyKey) {
238scoped_ptr<TestDataReductionProxyParams> params;
239  params.reset(
240      new TestDataReductionProxyParams(
241          DataReductionProxyParams::kAllowed |
242          DataReductionProxyParams::kFallbackAllowed |
243          DataReductionProxyParams::kPromoAllowed,
244          TestDataReductionProxyParams::HAS_EVERYTHING &
245          ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
246          ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
247  // loop_proxy_ is just the current message loop. This means loop_proxy_
248  // is the network thread used by DataReductionProxyAuthRequestHandler.
249  TestDataReductionProxyAuthRequestHandler auth_handler(kClient,
250                                                        kVersion,
251                                                        params.get(),
252                                                        loop_proxy_);
253  auth_handler.Init();
254  base::RunLoop().RunUntilIdle();
255  EXPECT_EQ(auth_handler.client_, kClient);
256  EXPECT_EQ(kExpectedBuild, auth_handler.build_number_);
257  EXPECT_EQ(kExpectedPatch, auth_handler.patch_number_);
258  EXPECT_EQ(auth_handler.key_, kTestKey);
259  EXPECT_EQ(kExpectedCredentials, auth_handler.credentials_);
260  EXPECT_EQ(kExpectedSession, auth_handler.session_);
261
262  // Now set an empty key. The auth handler should ignore that, and the key
263  // remains |kTestKey|.
264  auth_handler.InitAuthentication("");
265  base::RunLoop().RunUntilIdle();
266  EXPECT_EQ(auth_handler.key_, kTestKey);
267  EXPECT_EQ(kExpectedCredentials, auth_handler.credentials_);
268  EXPECT_EQ(kExpectedSession, auth_handler.session_);
269}
270
271TEST_F(DataReductionProxyAuthRequestHandlerTest, AuthorizationBogusVersion) {
272  scoped_ptr<TestDataReductionProxyParams> params;
273  params.reset(
274      new TestDataReductionProxyParams(
275          DataReductionProxyParams::kAllowed |
276          DataReductionProxyParams::kFallbackAllowed |
277          DataReductionProxyParams::kPromoAllowed,
278          TestDataReductionProxyParams::HAS_EVERYTHING &
279          ~TestDataReductionProxyParams::HAS_DEV_ORIGIN &
280          ~TestDataReductionProxyParams::HAS_DEV_FALLBACK_ORIGIN));
281  TestDataReductionProxyAuthRequestHandler auth_handler(kClient,
282                                                        kBogusVersion,
283                                                        params.get(),
284                                                        loop_proxy_);
285  EXPECT_TRUE(auth_handler.build_number_.empty());
286  EXPECT_TRUE(auth_handler.patch_number_.empty());
287
288  // Now set a key.
289  auth_handler.InitAuthentication(kTestKey2);
290  base::RunLoop().RunUntilIdle();
291  EXPECT_EQ(kTestKey2, auth_handler.key_);
292  EXPECT_EQ(kExpectedCredentials2, auth_handler.credentials_);
293  EXPECT_EQ(kExpectedSession2, auth_handler.session_);
294
295  net::HttpRequestHeaders headers;
296  // Write headers with a valid data reduction proxy;
297  auth_handler.MaybeAddRequestHeader(
298      NULL,
299      net::ProxyServer::FromURI(
300          net::HostPortPair::FromURL(GURL(params->DefaultOrigin())).ToString(),
301          net::ProxyServer::SCHEME_HTTP),
302      &headers);
303  EXPECT_TRUE(headers.HasHeader(kChromeProxyHeader));
304  std::string header_value;
305  headers.GetHeader(kChromeProxyHeader, &header_value);
306  EXPECT_EQ(kExpectedHeader4, header_value);
307}
308
309TEST_F(DataReductionProxyAuthRequestHandlerTest, AuthHashForSalt) {
310  std::string salt = "8675309"; // Jenny's number to test the hash generator.
311  std::string salted_key = salt + kDataReductionProxyKey + salt;
312  base::string16 expected_hash = base::UTF8ToUTF16(base::MD5String(salted_key));
313  EXPECT_EQ(expected_hash,
314            DataReductionProxyAuthRequestHandler::AuthHashForSalt(
315                8675309, kDataReductionProxyKey));
316}
317
318}  // namespace data_reduction_proxy
319