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#include "base/bind.h"
6#include "base/files/file_util.h"
7#include "base/files/scoped_temp_dir.h"
8#include "base/memory/ref_counted.h"
9#include "base/memory/scoped_vector.h"
10#include "base/message_loop/message_loop.h"
11#include "base/run_loop.h"
12#include "base/stl_util.h"
13#include "base/thread_task_runner_handle.h"
14#include "base/time/time.h"
15#include "chrome/browser/net/quota_policy_channel_id_store.h"
16#include "content/public/test/mock_special_storage_policy.h"
17#include "content/public/test/test_browser_thread_bundle.h"
18#include "net/base/test_data_directory.h"
19#include "net/cookies/cookie_util.h"
20#include "net/ssl/ssl_client_cert_type.h"
21#include "net/test/cert_test_util.h"
22#include "sql/statement.h"
23#include "testing/gtest/include/gtest/gtest.h"
24
25const base::FilePath::CharType kTestChannelIDFilename[] =
26    FILE_PATH_LITERAL("ChannelID");
27
28class QuotaPolicyChannelIDStoreTest : public testing::Test {
29 public:
30  void Load(ScopedVector<net::DefaultChannelIDStore::ChannelID>* channel_ids) {
31    base::RunLoop run_loop;
32    store_->Load(base::Bind(&QuotaPolicyChannelIDStoreTest::OnLoaded,
33                            base::Unretained(this),
34                            &run_loop));
35    run_loop.Run();
36    channel_ids->swap(channel_ids_);
37    channel_ids_.clear();
38  }
39
40  void OnLoaded(base::RunLoop* run_loop,
41                scoped_ptr<ScopedVector<net::DefaultChannelIDStore::ChannelID> >
42                    channel_ids) {
43    channel_ids_.swap(*channel_ids);
44    run_loop->Quit();
45  }
46
47 protected:
48  static base::Time GetTestCertExpirationTime() {
49    // Cert expiration time from 'dumpasn1 unittest.originbound.der':
50    // GeneralizedTime 19/11/2111 02:23:45 GMT
51    // base::Time::FromUTCExploded can't generate values past 2038 on 32-bit
52    // linux, so we use the raw value here.
53    return base::Time::FromInternalValue(GG_INT64_C(16121816625000000));
54  }
55
56  static base::Time GetTestCertCreationTime() {
57    // UTCTime 13/12/2011 02:23:45 GMT
58    base::Time::Exploded exploded_time;
59    exploded_time.year = 2011;
60    exploded_time.month = 12;
61    exploded_time.day_of_week = 0;  // Unused.
62    exploded_time.day_of_month = 13;
63    exploded_time.hour = 2;
64    exploded_time.minute = 23;
65    exploded_time.second = 45;
66    exploded_time.millisecond = 0;
67    return base::Time::FromUTCExploded(exploded_time);
68  }
69
70  virtual void SetUp() {
71    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
72    store_ = new QuotaPolicyChannelIDStore(
73        temp_dir_.path().Append(kTestChannelIDFilename),
74        base::ThreadTaskRunnerHandle::Get(),
75        NULL);
76    ScopedVector<net::DefaultChannelIDStore::ChannelID> channel_ids;
77    Load(&channel_ids);
78    ASSERT_EQ(0u, channel_ids.size());
79    // Make sure the store gets written at least once.
80    store_->AddChannelID(
81        net::DefaultChannelIDStore::ChannelID("google.com",
82                                              base::Time::FromInternalValue(1),
83                                              base::Time::FromInternalValue(2),
84                                              "a",
85                                              "b"));
86  }
87
88  virtual void TearDown() {
89    store_ = NULL;
90    loop_.RunUntilIdle();
91  }
92
93  base::ScopedTempDir temp_dir_;
94  scoped_refptr<QuotaPolicyChannelIDStore> store_;
95  ScopedVector<net::DefaultChannelIDStore::ChannelID> channel_ids_;
96  base::MessageLoop loop_;
97};
98
99// Test if data is stored as expected in the QuotaPolicy database.
100TEST_F(QuotaPolicyChannelIDStoreTest, TestPersistence) {
101  store_->AddChannelID(
102      net::DefaultChannelIDStore::ChannelID("foo.com",
103                                            base::Time::FromInternalValue(3),
104                                            base::Time::FromInternalValue(4),
105                                            "c",
106                                            "d"));
107
108  ScopedVector<net::DefaultChannelIDStore::ChannelID> channel_ids;
109  // Replace the store effectively destroying the current one and forcing it
110  // to write its data to disk. Then we can see if after loading it again it
111  // is still there.
112  store_ = NULL;
113  // Make sure we wait until the destructor has run.
114  base::RunLoop().RunUntilIdle();
115  store_ = new QuotaPolicyChannelIDStore(
116      temp_dir_.path().Append(kTestChannelIDFilename),
117      base::MessageLoopProxy::current(),
118      NULL);
119
120  // Reload and test for persistence
121  Load(&channel_ids);
122  ASSERT_EQ(2U, channel_ids.size());
123  net::DefaultChannelIDStore::ChannelID* goog_channel_id;
124  net::DefaultChannelIDStore::ChannelID* foo_channel_id;
125  if (channel_ids[0]->server_identifier() == "google.com") {
126    goog_channel_id = channel_ids[0];
127    foo_channel_id = channel_ids[1];
128  } else {
129    goog_channel_id = channel_ids[1];
130    foo_channel_id = channel_ids[0];
131  }
132  ASSERT_EQ("google.com", goog_channel_id->server_identifier());
133  ASSERT_STREQ("a", goog_channel_id->private_key().c_str());
134  ASSERT_STREQ("b", goog_channel_id->cert().c_str());
135  ASSERT_EQ(1, goog_channel_id->creation_time().ToInternalValue());
136  ASSERT_EQ(2, goog_channel_id->expiration_time().ToInternalValue());
137  ASSERT_EQ("foo.com", foo_channel_id->server_identifier());
138  ASSERT_STREQ("c", foo_channel_id->private_key().c_str());
139  ASSERT_STREQ("d", foo_channel_id->cert().c_str());
140  ASSERT_EQ(3, foo_channel_id->creation_time().ToInternalValue());
141  ASSERT_EQ(4, foo_channel_id->expiration_time().ToInternalValue());
142
143  // Now delete the cert and check persistence again.
144  store_->DeleteChannelID(*channel_ids[0]);
145  store_->DeleteChannelID(*channel_ids[1]);
146  store_ = NULL;
147  // Make sure we wait until the destructor has run.
148  base::RunLoop().RunUntilIdle();
149  channel_ids.clear();
150  store_ = new QuotaPolicyChannelIDStore(
151      temp_dir_.path().Append(kTestChannelIDFilename),
152      base::MessageLoopProxy::current(),
153      NULL);
154
155  // Reload and check if the cert has been removed.
156  Load(&channel_ids);
157  ASSERT_EQ(0U, channel_ids.size());
158}
159
160// Test if data is stored as expected in the QuotaPolicy database.
161TEST_F(QuotaPolicyChannelIDStoreTest, TestPolicy) {
162  store_->AddChannelID(
163      net::DefaultChannelIDStore::ChannelID("foo.com",
164                                            base::Time::FromInternalValue(3),
165                                            base::Time::FromInternalValue(4),
166                                            "c",
167                                            "d"));
168
169  ScopedVector<net::DefaultChannelIDStore::ChannelID> channel_ids;
170  // Replace the store effectively destroying the current one and forcing it
171  // to write its data to disk. Then we can see if after loading it again it
172  // is still there.
173  store_ = NULL;
174  // Make sure we wait until the destructor has run.
175  base::RunLoop().RunUntilIdle();
176  // Specify storage policy that makes "foo.com" session only.
177  scoped_refptr<content::MockSpecialStoragePolicy> storage_policy =
178      new content::MockSpecialStoragePolicy();
179  storage_policy->AddSessionOnly(
180      net::cookie_util::CookieOriginToURL("foo.com", true));
181  // Reload store, it should still have both channel ids.
182  store_ = new QuotaPolicyChannelIDStore(
183      temp_dir_.path().Append(kTestChannelIDFilename),
184      base::MessageLoopProxy::current(),
185      storage_policy);
186  Load(&channel_ids);
187  ASSERT_EQ(2U, channel_ids.size());
188
189  // Now close the store, and "foo.com" should be deleted according to policy.
190  store_ = NULL;
191  // Make sure we wait until the destructor has run.
192  base::RunLoop().RunUntilIdle();
193  channel_ids.clear();
194  store_ = new QuotaPolicyChannelIDStore(
195      temp_dir_.path().Append(kTestChannelIDFilename),
196      base::MessageLoopProxy::current(),
197      NULL);
198
199  // Reload and check that the "foo.com" cert has been removed.
200  Load(&channel_ids);
201  ASSERT_EQ(1U, channel_ids.size());
202  ASSERT_EQ("google.com", channel_ids[0]->server_identifier());
203}
204