OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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/message_loop.h" |
| 6 #include "chrome/browser/extensions/api/identity/web_auth_flow.h" |
| 7 #include "chrome/browser/ui/extensions/web_auth_flow_window.h" |
| 8 #include "chrome/test/base/testing_profile.h" |
| 9 #include "content/browser/web_contents/navigation_controller_impl.h" |
| 10 #include "content/browser/web_contents/web_contents_impl.h" |
| 11 #include "content/public/browser/invalidate_type.h" |
| 12 #include "content/public/browser/navigation_controller.h" |
| 13 #include "content/public/common/page_transition_types.h" |
| 14 #include "content/public/common/referrer.h" |
| 15 #include "testing/gmock/include/gmock/gmock.h" |
| 16 #include "testing/gtest/include/gtest/gtest.h" |
| 17 |
| 18 using content::BrowserContext; |
| 19 using content::NavigationController; |
| 20 using content::WebContents; |
| 21 using content::WebContentsDelegate; |
| 22 using extensions::WebAuthFlow; |
| 23 using testing::Return; |
| 24 using testing::ReturnRef; |
| 25 |
| 26 namespace { |
| 27 |
| 28 class MockDelegate : public WebAuthFlow::Delegate { |
| 29 public: |
| 30 MOCK_METHOD1(OnAuthFlowSuccess, void(const std::string& redirect_url)); |
| 31 MOCK_METHOD0(OnAuthFlowFailure, void()); |
| 32 }; |
| 33 |
| 34 class MockNavigationController : public NavigationControllerImpl { |
| 35 public: |
| 36 explicit MockNavigationController(BrowserContext* browser_context) |
| 37 : NavigationControllerImpl(NULL, browser_context, NULL) { |
| 38 } |
| 39 |
| 40 void LoadURL(const GURL& url, |
| 41 const content::Referrer& referrer, |
| 42 content::PageTransition type, |
| 43 const std::string& extra_headers) OVERRIDE { |
| 44 // Do nothing in tests. |
| 45 } |
| 46 }; |
| 47 |
| 48 class MockWebContents : public WebContentsImpl { |
| 49 public: |
| 50 explicit MockWebContents(BrowserContext* browser_context) |
| 51 : WebContentsImpl(browser_context, NULL, MSG_ROUTING_NONE, |
| 52 NULL, NULL, NULL), |
| 53 delegate_(NULL), |
| 54 controller_(browser_context) { } |
| 55 virtual ~MockWebContents() { } |
| 56 |
| 57 void SetDelegate(WebContentsDelegate* delegate) OVERRIDE { |
| 58 delegate_ = delegate; |
| 59 } |
| 60 |
| 61 NavigationController& GetController() OVERRIDE { |
| 62 return controller_; |
| 63 } |
| 64 |
| 65 const NavigationController& GetController() const OVERRIDE { |
| 66 return controller_; |
| 67 } |
| 68 |
| 69 MockNavigationController& mock_controller() { |
| 70 return controller_; |
| 71 } |
| 72 |
| 73 MOCK_CONST_METHOD0(GetURL, const GURL& ()); |
| 74 MOCK_CONST_METHOD0(IsLoading, bool ()); |
| 75 |
| 76 private: |
| 77 WebContentsDelegate* delegate_; |
| 78 MockNavigationController controller_; |
| 79 }; |
| 80 |
| 81 class MockWebAuthFlowWindow : public WebAuthFlowWindow { |
| 82 public: |
| 83 MockWebAuthFlowWindow() : WebAuthFlowWindow(NULL, NULL, NULL) { |
| 84 } |
| 85 |
| 86 virtual void Show() OVERRIDE { |
| 87 // Do nothing in tests. |
| 88 } |
| 89 }; |
| 90 |
| 91 class MockWebAuthFlow : public WebAuthFlow { |
| 92 public: |
| 93 MockWebAuthFlow( |
| 94 MockDelegate* delegate, |
| 95 BrowserContext* browser_context, |
| 96 const std::string& extension_id, |
| 97 const GURL& provider_url) |
| 98 : WebAuthFlow(delegate, |
| 99 browser_context, |
| 100 extension_id, |
| 101 provider_url), |
| 102 browser_context_(browser_context), |
| 103 web_contents(NULL), |
| 104 window_(NULL) { } |
| 105 |
| 106 virtual WebContents* CreateWebContents() OVERRIDE { |
| 107 web_contents = new MockWebContents(browser_context_); |
| 108 return web_contents; |
| 109 } |
| 110 |
| 111 virtual WebAuthFlowWindow* CreateAuthWindow() OVERRIDE { |
| 112 window_ = new MockWebAuthFlowWindow(); |
| 113 return window_; |
| 114 } |
| 115 |
| 116 MockWebContents& contents() { |
| 117 return *web_contents; |
| 118 } |
| 119 |
| 120 MockWebAuthFlowWindow& window() { |
| 121 return *window_; |
| 122 } |
| 123 |
| 124 bool HasWindow() const { |
| 125 return window_ != NULL; |
| 126 } |
| 127 |
| 128 virtual ~MockWebAuthFlow() { } |
| 129 |
| 130 private: |
| 131 BrowserContext* browser_context_; |
| 132 MockWebContents* web_contents; |
| 133 MockWebAuthFlowWindow* window_; |
| 134 }; |
| 135 |
| 136 } // namespace |
| 137 |
| 138 class WebAuthFlowTest : public testing::Test { |
| 139 protected: |
| 140 void CreateAuthFlow(const std::string& extension_id, const GURL& url) { |
| 141 flow_.reset(new MockWebAuthFlow(&delegate_, &profile_, extension_id, url)); |
| 142 } |
| 143 |
| 144 MockWebAuthFlow& flow() { |
| 145 return *flow_.get(); |
| 146 } |
| 147 |
| 148 // Wrapper methods to call private methods of the WebAuthFlow. |
| 149 void CallLoadingStateChanged(bool loading) { |
| 150 EXPECT_CALL(flow_->contents(), IsLoading()).WillOnce(Return(loading)); |
| 151 flow_->LoadingStateChanged(&flow_->contents()); |
| 152 } |
| 153 void CallNavigationStateChanged(unsigned changed_flags) { |
| 154 flow_->NavigationStateChanged(&flow_->contents(), changed_flags); |
| 155 } |
| 156 bool CallIsValidRedirectUrl(const GURL& url) { |
| 157 return flow_->IsValidRedirectUrl(url); |
| 158 } |
| 159 void CallOnClose() { |
| 160 flow_->OnClose(); |
| 161 } |
| 162 |
| 163 ~WebAuthFlowTest() { |
| 164 // Delete flow_ before we run pending tasks in the message loop. |
| 165 flow_.reset(); |
| 166 // Finish running the message loop to run clean up tasks. |
| 167 message_loop_.RunAllPending(); |
| 168 } |
| 169 |
| 170 MessageLoop message_loop_; |
| 171 scoped_ptr<MockWebAuthFlow> flow_; |
| 172 MockDelegate delegate_; |
| 173 TestingProfile profile_; |
| 174 }; |
| 175 |
| 176 TEST_F(WebAuthFlowTest, SilentRedirectToChromiumAppUrl) { |
| 177 std::string ext_id = "abcdefghij"; |
| 178 GURL url("https://accounts.google.com/o/oauth2/auth"); |
| 179 GURL result("https://abcdefghij.chromiumapp.org/google_cb"); |
| 180 |
| 181 CreateAuthFlow(ext_id, url); |
| 182 EXPECT_CALL(delegate_, OnAuthFlowSuccess(result.spec())).Times(1); |
| 183 flow_->Start(); |
| 184 EXPECT_CALL(flow_->contents(), GetURL()).WillRepeatedly(ReturnRef(result)); |
| 185 |
| 186 CallNavigationStateChanged(content::INVALIDATE_TYPE_URL); |
| 187 } |
| 188 |
| 189 TEST_F(WebAuthFlowTest, SilentRedirectToChromeExtensionSchemeUrl) { |
| 190 std::string ext_id = "abcdefghij"; |
| 191 GURL url("https://accounts.google.com/o/oauth2/auth"); |
| 192 GURL result("chrome-extension://abcdefghij/google_cb"); |
| 193 |
| 194 CreateAuthFlow(ext_id, url); |
| 195 |
| 196 EXPECT_CALL(delegate_, OnAuthFlowSuccess(result.spec())).Times(1); |
| 197 flow_->Start(); |
| 198 EXPECT_CALL(flow_->contents(), GetURL()).WillRepeatedly(ReturnRef(result)); |
| 199 |
| 200 CallNavigationStateChanged(content::INVALIDATE_TYPE_URL); |
| 201 } |
| 202 |
| 203 TEST_F(WebAuthFlowTest, UIResultsInSuccess) { |
| 204 std::string ext_id = "abcdefghij"; |
| 205 GURL url("https://accounts.google.com/o/oauth2/auth"); |
| 206 GURL result("chrome-extension://abcdefghij/google_cb"); |
| 207 |
| 208 CreateAuthFlow(ext_id, url); |
| 209 |
| 210 EXPECT_CALL(delegate_, OnAuthFlowSuccess(result.spec())).Times(1); |
| 211 flow_->Start(); |
| 212 CallLoadingStateChanged(false); |
| 213 EXPECT_TRUE(flow_->HasWindow()); |
| 214 EXPECT_CALL(flow_->contents(), GetURL()).WillOnce(ReturnRef(result)); |
| 215 CallNavigationStateChanged(content::INVALIDATE_TYPE_URL); |
| 216 } |
| 217 |
| 218 TEST_F(WebAuthFlowTest, UIClosedByUser) { |
| 219 std::string ext_id = "abcdefghij"; |
| 220 GURL url("https://accounts.google.com/o/oauth2/auth"); |
| 221 GURL result("chrome-extension://abcdefghij/google_cb"); |
| 222 |
| 223 CreateAuthFlow(ext_id, url); |
| 224 |
| 225 EXPECT_CALL(delegate_, OnAuthFlowFailure()).Times(1); |
| 226 flow_->Start(); |
| 227 CallLoadingStateChanged(false); |
| 228 EXPECT_TRUE(flow_->HasWindow()); |
| 229 CallOnClose(); |
| 230 } |
| 231 |
| 232 TEST_F(WebAuthFlowTest, IsValidRedirectUrl) { |
| 233 std::string ext_id = "abcdefghij"; |
| 234 GURL url("https://accounts.google.com/o/oauth2/auth"); |
| 235 CreateAuthFlow(ext_id, url); |
| 236 |
| 237 // Positive cases. |
| 238 EXPECT_TRUE(CallIsValidRedirectUrl( |
| 239 GURL("https://abcdefghij.chromiumapp.org/"))); |
| 240 EXPECT_TRUE(CallIsValidRedirectUrl( |
| 241 GURL("https://abcdefghij.chromiumapp.org/callback"))); |
| 242 EXPECT_TRUE(CallIsValidRedirectUrl( |
| 243 GURL("chrome-extension://abcdefghij/"))); |
| 244 EXPECT_TRUE(CallIsValidRedirectUrl( |
| 245 GURL("chrome-extension://abcdefghij/callback"))); |
| 246 |
| 247 // Negative cases. |
| 248 EXPECT_FALSE(CallIsValidRedirectUrl( |
| 249 GURL("https://www.foo.com/"))); |
| 250 // http scheme is not allowed. |
| 251 EXPECT_FALSE(CallIsValidRedirectUrl( |
| 252 GURL("http://abcdefghij.chromiumapp.org/callback"))); |
| 253 EXPECT_FALSE(CallIsValidRedirectUrl( |
| 254 GURL("https://abcd.chromiumapp.org/callback"))); |
| 255 EXPECT_FALSE(CallIsValidRedirectUrl( |
| 256 GURL("chrome-extension://abcd/callback"))); |
| 257 EXPECT_FALSE(CallIsValidRedirectUrl( |
| 258 GURL("chrome-extension://abcdefghijkl/"))); |
| 259 } |
OLD | NEW |