diff options
author | cmasone@google.com <cmasone@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-21 01:47:41 +0000 |
---|---|---|
committer | cmasone@google.com <cmasone@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-21 01:47:41 +0000 |
commit | 2125f7df5af31857e7698264ed42e616d8271b63 (patch) | |
tree | 93fedc0c6d069653152d55acb8d30deaac3fe52d /chrome | |
parent | e4dffe1f0817173ac6ef403b0350d8b5e1743f6d (diff) | |
download | chromium_src-2125f7df5af31857e7698264ed42e616d8271b63.zip chromium_src-2125f7df5af31857e7698264ed42e616d8271b63.tar.gz chromium_src-2125f7df5af31857e7698264ed42e616d8271b63.tar.bz2 |
[Chrome OS] Adds support for injecting Corp cookies at startup
To support single-sign-on for Chrome OS, we need a way to inject cookies into Chrome. In the case of session cookies, putting them into Chrome's cookie jar DB doesn't work. This CL adds a command line flag that tells chrome the name of a Unix pipe to open, from which it can read said cookies.
Eventually, I want to replace this pipe-reading with an appropriate usage of DBus, but Chrome OS isn't there yet. This CL adds the appropriate infrastructure, though, and the PipeReader class can later be replaced with something that pulls the cookies off DBus instead.
Review URL: http://codereview.chromium.org/174062
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@23936 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/browser_main.cc | 8 | ||||
-rw-r--r-- | chrome/browser/chromeos/external_cookie_handler.cc | 71 | ||||
-rw-r--r-- | chrome/browser/chromeos/external_cookie_handler.h | 49 | ||||
-rw-r--r-- | chrome/browser/chromeos/external_cookie_handler_unittest.cc | 152 | ||||
-rw-r--r-- | chrome/browser/chromeos/pipe_reader.cc | 17 | ||||
-rw-r--r-- | chrome/browser/chromeos/pipe_reader.h | 50 | ||||
-rw-r--r-- | chrome/browser/chromeos/pipe_reader_unittest.cc | 97 | ||||
-rw-r--r-- | chrome/chrome.gyp | 22 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 7 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 5 |
10 files changed, 477 insertions, 1 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index bb3d1c3..9b71bde 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -117,6 +117,10 @@ #include "views/focus/accelerator_handler.h" #endif +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/external_cookie_handler.h" +#endif + namespace Platform { void WillInitializeMainMessageLoop(const MainFunctionParams& parameters); @@ -793,6 +797,10 @@ int BrowserMain(const MainFunctionParams& parameters) { if (parsed_command_line.HasSwitch(switches::kEnableWebResources)) profile->InitWebResources(); +#if defined(OS_CHROMEOS) + ExternalCookieHandler::GetCookies(parsed_command_line, profile); +#endif + // Stat the directory with the inspector's files so that we can know if we // should display the entry in the context menu or not. browser_process->CheckForInspectorFiles(); diff --git a/chrome/browser/chromeos/external_cookie_handler.cc b/chrome/browser/chromeos/external_cookie_handler.cc new file mode 100644 index 0000000..e578395 --- /dev/null +++ b/chrome/browser/chromeos/external_cookie_handler.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/external_cookie_handler.h" + +#include "base/command_line.h" +#include "chrome/browser/chromeos/pipe_reader.h" +#include "chrome/browser/profile.h" +#include "chrome/common/chrome_switches.h" +#include "googleurl/src/gurl.h" +#include "net/base/cookie_store.h" +#include "net/url_request/url_request_context.h" + +void ExternalCookieHandler::GetCookies(const CommandLine& parsed_command_line, + Profile* profile) { + // If there are Google External SSO cookies, add them to the cookie store. + if (parsed_command_line.HasSwitch(switches::kCookiePipe)) { + std::string pipe_name = + WideToASCII(parsed_command_line.GetSwitchValue(switches::kCookiePipe)); + ExternalCookieHandler cookie_handler(new PipeReader(pipe_name)); + cookie_handler.HandleCookies( + profile->GetRequestContext()->cookie_store()); + } +} + +// static +const char ExternalCookieHandler::kGoogleAccountsUrl[] = + "https://www.google.com/a/google.com/acs"; + +const int kChunkSize = 256; + +// Reads up to a newline, or the end of the data, in increments of |chunk| +std::string ExternalCookieHandler::ReadLine(int chunk) { + std::string cookie_line = reader_->Read(chunk); + + // As long as it's not an empty line... + if (!cookie_line.empty()) { + // and there's no newline at the end... + while ('\n' != cookie_line[cookie_line.length() - 1]) { + // try to pull more data... + std::string piece = reader_->Read(chunk); + if (piece.empty()) // only stop if there's none left. + break; + else + cookie_line.append(piece); // otherwise, append and keep going. + } + } + + return cookie_line; +} + +bool ExternalCookieHandler::HandleCookies(net::CookieStore *cookie_store) { + DCHECK(cookie_store); + if (NULL != reader_.get()) { + GURL url(ExternalCookieHandler::kGoogleAccountsUrl); + net::CookieOptions options; + options.set_include_httponly(); + + // Each line we get is a cookie. Grab up to a newline, then put + // it in to the cookie jar. + std::string cookie_line = ReadLine(kChunkSize); + while (!cookie_line.empty()) { + if (!cookie_store->SetCookieWithOptions(url, cookie_line, options)) + return false; + cookie_line = ReadLine(kChunkSize); + } + return true; + } + return false; +} diff --git a/chrome/browser/chromeos/external_cookie_handler.h b/chrome/browser/chromeos/external_cookie_handler.h new file mode 100644 index 0000000..2886756 --- /dev/null +++ b/chrome/browser/chromeos/external_cookie_handler.h @@ -0,0 +1,49 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_EXTERNAL_COOKIE_HANDLER_H_ +#define CHROME_BROWSER_CHROMEOS_EXTERNAL_COOKIE_HANDLER_H_ + +#include <string> + +#include "base/scoped_ptr.h" +#include "chrome/browser/chromeos/pipe_reader.h" + +// Single sign on cookies for Google can be passed in over a +// pipe. If they've been sent, this reads them and adds them to the +// cookie store as session cookies. + +class CommandLine; +class Profile; +namespace net { +class CookieStore; +} + +class ExternalCookieHandler { + public: + // Takes ownsership of |reader|. + explicit ExternalCookieHandler(PipeReader *reader) : reader_(reader) {} + virtual ~ExternalCookieHandler() {} + + // Given a pipe to read cookies from, reads and adds them to |cookie_store|. + virtual bool HandleCookies(net::CookieStore *cookie_store); + + // Checks |parsed_command_line| for the --cookie-pipe; if found, reads + // cookies from the pipe and adds them to the cookie store found in |profile|. + static void GetCookies(const CommandLine& parsed_command_line, + Profile* profile); + + // The url with which we associate the read-in cookies. + static const char kGoogleAccountsUrl[]; + + private: + // Reads up to a newline, or the end of the data, in increments of |chunk|. + std::string ReadLine(int chunk); + + scoped_ptr<PipeReader> reader_; + + DISALLOW_COPY_AND_ASSIGN(ExternalCookieHandler); +}; + +#endif // CHROME_BROWSER_CHROMEOS_EXTERNAL_COOKIE_HANDLER_H_ diff --git a/chrome/browser/chromeos/external_cookie_handler_unittest.cc b/chrome/browser/chromeos/external_cookie_handler_unittest.cc new file mode 100644 index 0000000..014f52c --- /dev/null +++ b/chrome/browser/chromeos/external_cookie_handler_unittest.cc @@ -0,0 +1,152 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/external_cookie_handler.h" + +#include <set> +#include <vector> + +#include "base/basictypes.h" +#include "base/time.h" +#include "googleurl/src/gurl.h" +#include "net/base/cookie_options.h" +#include "net/base/cookie_store.h" +#include "testing/gtest/include/gtest/gtest.h" + +typedef testing::Test ExternalCookieHandlerTest; + +static const std::string cookie1 = "coookie1\n"; +static const std::string cookie2 = "coookie2\n"; +static const std::string cookie3 = "coookie3"; + +class MockCookieStore : public net::CookieStore { + public: + MockCookieStore() : expected_url_(ExternalCookieHandler::kGoogleAccountsUrl) { + cookies_.insert(cookie1); + cookies_.insert(cookie2); + cookies_.insert(cookie3); + } + virtual ~MockCookieStore() {} + + virtual bool SetCookie(const GURL& url, const std::string& cookie_line) { + EXPECT_TRUE(false); + return true; + } + virtual bool SetCookieWithOptions(const GURL& url, + const std::string& cookie_line, + const net::CookieOptions& options) { + EXPECT_FALSE(options.exclude_httponly()); + EXPECT_EQ(expected_url_, url); + std::set<std::string>::iterator it; + it = cookies_.find(cookie_line); + bool has_cookie = cookies_.end() != it; + if (has_cookie) + cookies_.erase(it); + return has_cookie; + } + + virtual bool SetCookieWithCreationTime(const GURL& url, + const std::string& cookie_line, + const base::Time& creation_time) { + EXPECT_TRUE(false); + return true; + } + virtual bool SetCookieWithCreationTimeWithOptions( + const GURL& url, + const std::string& cookie_line, + const base::Time& creation_time, + const net::CookieOptions& options) { + EXPECT_TRUE(false); + return true; + } + + virtual void SetCookies(const GURL& url, + const std::vector<std::string>& cookies) { + EXPECT_TRUE(false); + } + virtual void SetCookiesWithOptions(const GURL& url, + const std::vector<std::string>& cookies, + const net::CookieOptions& options) { + EXPECT_TRUE(false); + } + + virtual std::string GetCookies(const GURL& url) { + EXPECT_TRUE(false); + return std::string(); + } + virtual std::string GetCookiesWithOptions(const GURL& url, + const net::CookieOptions& options) { + EXPECT_TRUE(false); + return std::string(); + } + + private: + std::set<std::string> cookies_; + const GURL expected_url_; + + DISALLOW_EVIL_CONSTRUCTORS(MockCookieStore); +}; + + +TEST_F(ExternalCookieHandlerTest, MockCookieStoreSanityTest) { + GURL url(ExternalCookieHandler::kGoogleAccountsUrl); + MockCookieStore cookie_store; + net::CookieOptions options; + options.set_include_httponly(); + EXPECT_TRUE(cookie_store.SetCookieWithOptions(url, cookie1, options)); + EXPECT_TRUE(cookie_store.SetCookieWithOptions(url, cookie2, options)); + EXPECT_TRUE(cookie_store.SetCookieWithOptions(url, cookie3, options)); + EXPECT_FALSE(cookie_store.SetCookieWithOptions(url, cookie1, options)); + EXPECT_FALSE(cookie_store.SetCookieWithOptions(url, cookie2, options)); + EXPECT_FALSE(cookie_store.SetCookieWithOptions(url, cookie3, options)); +} + +class MockReader : public PipeReader { + public: + explicit MockReader(const std::vector<std::string>& cookies) + : data_(cookies) { + } + + std::string Read(const uint32 bytes_to_read) { + std::string to_return; + if (!data_.empty()) { + to_return = data_.back(); + data_.pop_back(); + } + return to_return; + } + private: + std::vector<std::string> data_; +}; + +TEST_F(ExternalCookieHandlerTest, SuccessfulReadTest) { + GURL url(ExternalCookieHandler::kGoogleAccountsUrl); + + MockCookieStore cookie_store; + + std::vector<std::string> cookies; + cookies.push_back(cookie3); + cookies.push_back(cookie2); + cookies.push_back(cookie1); + MockReader *reader = new MockReader(cookies); + + ExternalCookieHandler handler(reader); // takes ownership. + EXPECT_TRUE(handler.HandleCookies(&cookie_store)); +} + +TEST_F(ExternalCookieHandlerTest, SuccessfulSlowReadTest) { + GURL url(ExternalCookieHandler::kGoogleAccountsUrl); + + MockCookieStore cookie_store; + + std::vector<std::string> cookies; + cookies.push_back(cookie3); + cookies.push_back(cookie2.substr(2)); + cookies.push_back(cookie2.substr(0, 2)); + cookies.push_back(cookie1); + MockReader *reader = new MockReader(cookies); + + ExternalCookieHandler handler(reader); // takes ownership. + EXPECT_TRUE(handler.HandleCookies(&cookie_store)); +} diff --git a/chrome/browser/chromeos/pipe_reader.cc b/chrome/browser/chromeos/pipe_reader.cc new file mode 100644 index 0000000..16b9f06 --- /dev/null +++ b/chrome/browser/chromeos/pipe_reader.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/pipe_reader.h" + +#include "base/scoped_ptr.h" + +std::string PipeReader::Read(const uint32 bytes_to_read) { + scoped_array<char> buffer(new char[bytes_to_read]); + if (pipe_ || (pipe_ = fopen(pipe_name_.c_str(), "r"))) { + const char* to_return = fgets(buffer.get(), bytes_to_read, pipe_); + if (to_return) + return to_return; // auto-coerced to a std::string. + } + return std::string(); +} diff --git a/chrome/browser/chromeos/pipe_reader.h b/chrome/browser/chromeos/pipe_reader.h new file mode 100644 index 0000000..d2a3bf2 --- /dev/null +++ b/chrome/browser/chromeos/pipe_reader.h @@ -0,0 +1,50 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_PIPE_READER_H_ +#define CHROME_BROWSER_CHROMEOS_PIPE_READER_H_ + +#include <fcntl.h> +#include <string> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "base/basictypes.h" + +// Given a named pipe, this class reads data from it and returns it as a string. +// Currently, we are sending login cookies from the Chrome OS login manager to +// Chrome over a named Unix pipe. We want to replace this with DBus, but +// would like to create a DBus wrapper library to use throughout Chrome OS +// first. This stopgap lets us get the infrastructure for passing credentials +// to Chrome in place, which will help clean up login jankiness, and also +// refactor our code as we await the DBus stuff. +// TODO(cmasone): get rid of this code and replace with DBus. + +class PipeReader { + public: + explicit PipeReader(const std::string& pipe_name) + : pipe_(NULL), + pipe_name_(pipe_name) { + } + virtual ~PipeReader() { + if (pipe_) + fclose(pipe_); + } + + // Reads data from the pipe up until either a '\n' or |bytes_to_read| bytes. + virtual std::string Read(const uint32 bytes_to_read); + + protected: + // For testing. + PipeReader() : pipe_(NULL) {} + + private: + FILE *pipe_; + std::string pipe_name_; + + DISALLOW_COPY_AND_ASSIGN(PipeReader); +}; + +#endif // CHROME_BROWSER_CHROMEOS_PIPE_READER_H_ diff --git a/chrome/browser/chromeos/pipe_reader_unittest.cc b/chrome/browser/chromeos/pipe_reader_unittest.cc new file mode 100644 index 0000000..6d674dd --- /dev/null +++ b/chrome/browser/chromeos/pipe_reader_unittest.cc @@ -0,0 +1,97 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/pipe_reader.h" + +#include <errno.h> + +#include "testing/gtest/include/gtest/gtest.h" + +typedef testing::Test PipeReaderTest; + +TEST_F(PipeReaderTest, SuccessfulReadTest) { + std::string pipe_name("/tmp/MYFIFO"); + /* Create the FIFO if it does not exist */ + umask(0); + mknod(pipe_name.c_str(), S_IFIFO|0666, 0); + const char line[] = "foo"; + + pid_t pID = fork(); + if (pID == 0) { + int pipe = open(pipe_name.c_str(), O_WRONLY); + EXPECT_NE(pipe, -1) << strerror(errno); + write(pipe, line, strlen(line)); + close(pipe); + exit(1); + } else { + PipeReader reader(pipe_name); + // asking for more should still just return the amount that was written. + EXPECT_EQ(line, reader.Read(5 * strlen(line))); + } +} + +TEST_F(PipeReaderTest, SuccessfulMultiLineReadTest) { + std::string pipe_name("/tmp/TESTFIFO"); + /* Create the FIFO if it does not exist */ + umask(0); + mknod(pipe_name.c_str(), S_IFIFO|0666, 0); + const char foo[] = "foo"; + const char boo[] = "boo"; + std::string line(foo); + line.append("\n"); + line.append(boo); + line.append("\n"); + + pid_t pID = fork(); + if (pID == 0) { + int pipe = open(pipe_name.c_str(), O_WRONLY); + EXPECT_NE(pipe, -1) << strerror(errno); + write(pipe, line.c_str(), line.length()); + close(pipe); + exit(1); + } else { + PipeReader reader(pipe_name); + // asking for more should still just return the amount that was written. + std::string my_foo = reader.Read(5 * line.length()); + EXPECT_EQ(my_foo[my_foo.length() - 1], '\n'); + my_foo.resize(my_foo.length() - 1); + EXPECT_EQ(my_foo, foo); + + std::string my_boo = reader.Read(5 * line.length()); + EXPECT_EQ(my_boo[my_boo.length() - 1], '\n'); + my_boo.resize(my_boo.length() - 1); + EXPECT_EQ(my_boo, boo); + } +} + +TEST_F(PipeReaderTest, SuccessfulMultiLineReadNoEndingNewlineTest) { + std::string pipe_name("/tmp/TESTFIFO"); + /* Create the FIFO if it does not exist */ + umask(0); + mknod(pipe_name.c_str(), S_IFIFO|0666, 0); + const char foo[] = "foo"; + const char boo[] = "boo"; + std::string line(foo); + line.append("\n"); + line.append(boo); + + pid_t pID = fork(); + if (pID == 0) { + int pipe = open(pipe_name.c_str(), O_WRONLY); + EXPECT_NE(pipe, -1) << strerror(errno); + write(pipe, line.c_str(), line.length()); + close(pipe); + exit(1); + } else { + PipeReader reader(pipe_name); + // asking for more should still just return the amount that was written. + std::string my_foo = reader.Read(5 * line.length()); + EXPECT_EQ(my_foo[my_foo.length() - 1], '\n'); + my_foo.resize(my_foo.length() - 1); + EXPECT_EQ(my_foo, foo); + + std::string my_boo = reader.Read(5 * line.length()); + EXPECT_EQ(my_boo, boo); + } +} diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 49f8fa5..02ef7ef 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -63,13 +63,13 @@ # TODO(jcampan): move these vars to views.gyp. 'views_unit_tests_sources': [ '../views/view_unittest.cc', - '../views/focus/focus_manager_unittest.cc', ], 'views_unit_tests_sources_win_specific': [ # TODO(jcampan): make the following tests work on Linux. '../views/controls/label_unittest.cc', '../views/controls/table/table_view_unittest.cc', '../views/grid_layout_unittest.cc', + '../views/focus/focus_manager_unittest.cc', ], 'conditions': [ ['OS=="mac"', { @@ -791,6 +791,10 @@ 'browser/chrome_plugin_host.h', 'browser/chrome_thread.cc', 'browser/chrome_thread.h', + 'browser/chromeos/pipe_reader.cc', + 'browser/chromeos/pipe_reader.h', + 'browser/chromeos/external_cookie_handler.cc', + 'browser/chromeos/external_cookie_handler.h', 'browser/cocoa/about_window_controller.h', 'browser/cocoa/about_window_controller.mm', 'browser/cocoa/autocomplete_text_field.h', @@ -2060,6 +2064,14 @@ 'CHROME_V8', ], }], + ['chromeos==0', { + 'sources!': [ + 'browser/chromeos/pipe_reader.cc', + 'browser/chromeos/pipe_reader.h', + 'browser/chromeos/external_cookie_handler.cc', + 'browser/chromeos/external_cookie_handler.h', + ], + }], ['OS=="linux"', { 'dependencies': [ # Temporarily disabled while we figure some stuff out. @@ -3811,6 +3823,8 @@ 'browser/debugger/devtools_remote_listen_socket_unittest.h', 'browser/child_process_security_policy_unittest.cc', 'browser/chrome_thread_unittest.cc', + 'browser/chromeos/pipe_reader_unittest.cc', + 'browser/chromeos/external_cookie_handler_unittest.cc', # It is safe to list */cocoa/* files in the "common" file list # without an explicit exclusion since gyp is smart enough to # exclude them from non-Mac builds. @@ -4041,6 +4055,12 @@ 'tools/build/win/precompiled_wtl.cc', ], 'conditions': [ + ['chromeos==0', { + 'sources!': [ + 'browser/chromeos/pipe_reader_unittest.cc', + 'browser/chromeos/external_cookie_handler_unittest.cc', + ], + }], ['OS=="linux"', { 'dependencies': [ '../build/linux/system.gyp:gtk', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 3a32417..51fa8fb 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -578,4 +578,11 @@ const wchar_t kEnableApplicationCache[] = L"enable-application-cache"; // Override the default server used for profile sync. const wchar_t kSyncServiceURL[] = L"sync-url"; + +#if defined(OS_CHROMEOS) +// The name of the pipe over which the Chrome OS login manager will send +// single-sign-on cookies. +const wchar_t kCookiePipe[] = L"cookie-pipe"; +#endif + } // namespace switches diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 28426be..1bc7da3 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -230,6 +230,11 @@ extern const wchar_t kEnableDatabases[]; extern const wchar_t kEnableApplicationCache[]; extern const wchar_t kSyncServiceURL[]; + +#if defined(OS_CHROMEOS) +extern const wchar_t kCookiePipe[]; +#endif + } // namespace switches #endif // CHROME_COMMON_CHROME_SWITCHES_H_ |