blob: 1dbcb13b8439061d06b16667ae17c43062804a39 [file] [log] [blame]
// Copyright (c) 2012 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 "base/command_line.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/api/identity/identity_api.h"
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_function_test_utils.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/profile_oauth2_token_service.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/test_switches.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/test/test_utils.h"
#include "extensions/common/id_util.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "google_apis/gaia/oauth2_mint_token_flow.h"
#include "grit/browser_resources.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using testing::_;
using testing::Return;
using testing::ReturnRef;
namespace extensions {
namespace {
namespace errors = identity_constants;
namespace utils = extension_function_test_utils;
static const char kAccessToken[] = "auth_token";
static const char kExtensionId[] = "ext_id";
// This helps us be able to wait until an AsyncExtensionFunction calls
// SendResponse.
class SendResponseDelegate
: public UIThreadExtensionFunction::DelegateForTests {
public:
SendResponseDelegate() : should_post_quit_(false) {}
virtual ~SendResponseDelegate() {}
void set_should_post_quit(bool should_quit) {
should_post_quit_ = should_quit;
}
bool HasResponse() {
return response_.get() != NULL;
}
bool GetResponse() {
EXPECT_TRUE(HasResponse());
return *response_.get();
}
virtual void OnSendResponse(UIThreadExtensionFunction* function,
bool success,
bool bad_message) OVERRIDE {
ASSERT_FALSE(bad_message);
ASSERT_FALSE(HasResponse());
response_.reset(new bool);
*response_ = success;
if (should_post_quit_) {
base::MessageLoopForUI::current()->Quit();
}
}
private:
scoped_ptr<bool> response_;
bool should_post_quit_;
};
class AsyncExtensionBrowserTest : public ExtensionBrowserTest {
protected:
// Asynchronous function runner allows tests to manipulate the browser window
// after the call happens.
void RunFunctionAsync(
UIThreadExtensionFunction* function,
const std::string& args) {
response_delegate_.reset(new SendResponseDelegate);
function->set_test_delegate(response_delegate_.get());
scoped_ptr<base::ListValue> parsed_args(utils::ParseList(args));
EXPECT_TRUE(parsed_args.get()) <<
"Could not parse extension function arguments: " << args;
function->SetArgs(parsed_args.get());
if (!function->GetExtension()) {
scoped_refptr<Extension> empty_extension(
utils::CreateEmptyExtension());
function->set_extension(empty_extension.get());
}
function->set_context(browser()->profile());
function->set_has_callback(true);
function->Run();
}
std::string WaitForError(UIThreadExtensionFunction* function) {
RunMessageLoopUntilResponse();
EXPECT_FALSE(function->GetResultList()) << "Did not expect a result";
return function->GetError();
}
base::Value* WaitForSingleResult(UIThreadExtensionFunction* function) {
RunMessageLoopUntilResponse();
EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: "
<< function->GetError();
const base::Value* single_result = NULL;
if (function->GetResultList() != NULL &&
function->GetResultList()->Get(0, &single_result)) {
return single_result->DeepCopy();
}
return NULL;
}
private:
void RunMessageLoopUntilResponse() {
// If the RunImpl of |function| didn't already call SendResponse, run the
// message loop until they do.
if (!response_delegate_->HasResponse()) {
response_delegate_->set_should_post_quit(true);
content::RunMessageLoop();
}
EXPECT_TRUE(response_delegate_->HasResponse());
}
scoped_ptr<SendResponseDelegate> response_delegate_;
};
class TestOAuth2MintTokenFlow : public OAuth2MintTokenFlow {
public:
enum ResultType {
ISSUE_ADVICE_SUCCESS,
MINT_TOKEN_SUCCESS,
MINT_TOKEN_FAILURE,
MINT_TOKEN_BAD_CREDENTIALS
};
TestOAuth2MintTokenFlow(ResultType result,
OAuth2MintTokenFlow::Delegate* delegate)
: OAuth2MintTokenFlow(NULL, delegate, OAuth2MintTokenFlow::Parameters()),
result_(result),
delegate_(delegate) {
}
virtual void Start() OVERRIDE {
switch (result_) {
case ISSUE_ADVICE_SUCCESS: {
IssueAdviceInfo info;
delegate_->OnIssueAdviceSuccess(info);
break;
}
case MINT_TOKEN_SUCCESS: {
delegate_->OnMintTokenSuccess(kAccessToken, 3600);
break;
}
case MINT_TOKEN_FAILURE: {
GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED);
delegate_->OnMintTokenFailure(error);
break;
}
case MINT_TOKEN_BAD_CREDENTIALS: {
GoogleServiceAuthError error(
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
delegate_->OnMintTokenFailure(error);
break;
}
}
}
private:
ResultType result_;
OAuth2MintTokenFlow::Delegate* delegate_;
};
// Waits for a specific GURL to generate a NOTIFICATION_LOAD_STOP event and
// saves a pointer to the window embedding the WebContents, which can be later
// closed.
class WaitForGURLAndCloseWindow : public content::WindowedNotificationObserver {
public:
explicit WaitForGURLAndCloseWindow(GURL url)
: WindowedNotificationObserver(
content::NOTIFICATION_LOAD_STOP,
content::NotificationService::AllSources()),
url_(url) {}
// NotificationObserver:
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE {
content::NavigationController* web_auth_flow_controller =
content::Source<content::NavigationController>(source).ptr();
content::WebContents* web_contents =
web_auth_flow_controller->GetWebContents();
if (web_contents->GetURL() == url_) {
// It is safe to keep the pointer here, because we know in a test, that
// the WebContents won't go away before CloseEmbedderWebContents is
// called. Don't copy this code to production.
embedder_web_contents_ = web_contents->GetEmbedderWebContents();
// Condtionally invoke parent class so that Wait will not exit
// until the target URL arrives.
content::WindowedNotificationObserver::Observe(type, source, details);
}
}
// Closes the window embedding the WebContents. The action is separated from
// the Observe method to make sure the list of observers is not deleted,
// while some event is already being processed. (That causes ASAN failures.)
void CloseEmbedderWebContents() {
if (embedder_web_contents_)
embedder_web_contents_->Close();
}
private:
GURL url_;
content::WebContents* embedder_web_contents_;
};
} // namespace
class MockGetAuthTokenFunction : public IdentityGetAuthTokenFunction {
public:
MockGetAuthTokenFunction() : login_access_token_result_(true),
login_ui_result_(true),
scope_ui_result_(true),
login_ui_shown_(false),
scope_ui_shown_(false) {
}
void set_login_access_token_result(bool result) {
login_access_token_result_ = result;
}
void set_login_ui_result(bool result) {
login_ui_result_ = result;
}
void set_scope_ui_failure(GaiaWebAuthFlow::Failure failure) {
scope_ui_result_ = false;
scope_ui_failure_ = failure;
}
void set_scope_ui_oauth_error(const std::string& oauth_error) {
scope_ui_result_ = false;
scope_ui_failure_ = GaiaWebAuthFlow::OAUTH_ERROR;
scope_ui_oauth_error_ = oauth_error;
}
bool login_ui_shown() const {
return login_ui_shown_;
}
bool scope_ui_shown() const {
return scope_ui_shown_;
}
virtual void StartLoginAccessTokenRequest() OVERRIDE {
if (login_access_token_result_) {
OnGetTokenSuccess(login_token_request_.get(), "access_token",
base::Time::Now() + base::TimeDelta::FromHours(1LL));
} else {
GoogleServiceAuthError error(
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
OnGetTokenFailure(login_token_request_.get(), error);
}
}
virtual void ShowLoginPopup() OVERRIDE {
EXPECT_FALSE(login_ui_shown_);
login_ui_shown_ = true;
if (login_ui_result_)
SigninSuccess();
else
SigninFailed();
}
virtual void ShowOAuthApprovalDialog(
const IssueAdviceInfo& issue_advice) OVERRIDE {
scope_ui_shown_ = true;
if (scope_ui_result_) {
OnGaiaFlowCompleted(kAccessToken, "3600");
} else if (scope_ui_failure_ == GaiaWebAuthFlow::SERVICE_AUTH_ERROR) {
GoogleServiceAuthError error(GoogleServiceAuthError::CONNECTION_FAILED);
OnGaiaFlowFailure(scope_ui_failure_, error, "");
} else {
GoogleServiceAuthError error(GoogleServiceAuthError::NONE);
OnGaiaFlowFailure(scope_ui_failure_, error, scope_ui_oauth_error_);
}
}
MOCK_CONST_METHOD0(HasLoginToken, bool());
MOCK_METHOD1(CreateMintTokenFlow,
OAuth2MintTokenFlow* (const std::string& login_access_token));
private:
~MockGetAuthTokenFunction() {}
bool login_access_token_result_;
bool login_ui_result_;
bool scope_ui_result_;
GaiaWebAuthFlow::Failure scope_ui_failure_;
std::string scope_ui_oauth_error_;
bool login_ui_shown_;
bool scope_ui_shown_;
};
class MockQueuedMintRequest : public IdentityMintRequestQueue::Request {
public:
MOCK_METHOD1(StartMintToken, void(IdentityMintRequestQueue::MintType));
};
class GetAuthTokenFunctionTest : public AsyncExtensionBrowserTest {
protected:
enum OAuth2Fields {
NONE = 0,
CLIENT_ID = 1,
SCOPES = 2,
AS_COMPONENT = 4
};
virtual ~GetAuthTokenFunctionTest() {}
// Helper to create an extension with specific OAuth2Info fields set.
// |fields_to_set| should be computed by using fields of Oauth2Fields enum.
const Extension* CreateExtension(int fields_to_set) {
const Extension* ext;
base::FilePath manifest_path =
test_data_dir_.AppendASCII("platform_apps/oauth2");
base::FilePath component_manifest_path =
test_data_dir_.AppendASCII("packaged_app/component_oauth2");
if ((fields_to_set & AS_COMPONENT) == 0)
ext = LoadExtension(manifest_path);
else
ext = LoadExtensionAsComponent(component_manifest_path);
OAuth2Info& oauth2_info =
const_cast<OAuth2Info&>(OAuth2Info::GetOAuth2Info(ext));
if ((fields_to_set & CLIENT_ID) != 0)
oauth2_info.client_id = "client1";
if ((fields_to_set & SCOPES) != 0) {
oauth2_info.scopes.push_back("scope1");
oauth2_info.scopes.push_back("scope2");
}
extension_id_ = ext->id();
oauth_scopes_ = std::set<std::string>(oauth2_info.scopes.begin(),
oauth2_info.scopes.end());
return ext;
}
IdentityAPI* id_api() {
return IdentityAPI::GetFactoryInstance()->GetForProfile(
browser()->profile());
}
const std::string GetPrimaryAccountId() {
ProfileOAuth2TokenService* token_service =
ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile());
return token_service->GetPrimaryAccountId();
}
void SetCachedToken(const IdentityTokenCacheValue& token_data) {
ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_);
id_api()->SetCachedToken(key, token_data);
}
const IdentityTokenCacheValue& GetCachedToken() {
ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_);
return id_api()->GetCachedToken(key);
}
void QueueRequestStart(IdentityMintRequestQueue::MintType type,
IdentityMintRequestQueue::Request* request) {
ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_);
id_api()->mint_queue()->RequestStart(type, key, request);
}
void QueueRequestComplete(IdentityMintRequestQueue::MintType type,
IdentityMintRequestQueue::Request* request) {
ExtensionTokenKey key(extension_id_, GetPrimaryAccountId(), oauth_scopes_);
id_api()->mint_queue()->RequestComplete(type, key, request);
}
private:
std::string extension_id_;
std::set<std::string> oauth_scopes_;
};
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
NoClientId) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(SCOPES));
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{}]", browser());
EXPECT_EQ(std::string(errors::kInvalidClientId), error);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
NoScopes) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID));
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{}]", browser());
EXPECT_EQ(std::string(errors::kInvalidScopes), error);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
NonInteractiveNotSignedIn) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false));
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{}]", browser());
EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
NonInteractiveMintFailure) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{}]", browser());
EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
NonInteractiveLoginAccessTokenFailure) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(true));
func->set_login_access_token_result(false);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{}]", browser());
EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
NonInteractiveMintAdviceSuccess) {
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(extension.get());
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{}]", browser());
EXPECT_EQ(std::string(errors::kNoGrant), error);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE,
GetCachedToken().status());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
NonInteractiveMintBadCredentials) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{}]", browser());
EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
NonInteractiveSuccess) {
#if defined(OS_WIN) && defined(USE_ASH)
// Disable this test in Metro+Ash for now (http://crbug.com/262796).
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
return;
#endif
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
func->set_extension(extension.get());
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
func.get(), "[{}]", browser()));
std::string access_token;
EXPECT_TRUE(value->GetAsString(&access_token));
EXPECT_EQ(std::string(kAccessToken), access_token);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
GetCachedToken().status());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveLoginCanceled) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false));
func->set_login_ui_result(false);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
EXPECT_TRUE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveMintBadCredentialsLoginCanceled) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::MINT_TOKEN_BAD_CREDENTIALS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
func->set_login_ui_result(false);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
EXPECT_TRUE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveLoginSuccessNoToken) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false));
func->set_login_ui_result(false);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
EXPECT_TRUE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveLoginSuccessMintFailure) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(false));
func->set_login_ui_result(true);
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::MINT_TOKEN_FAILURE, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
EXPECT_TRUE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveLoginSuccessLoginAccessTokenFailure) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false));
func->set_login_ui_result(true);
func->set_login_access_token_result(false);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
EXPECT_TRUE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveLoginSuccessMintSuccess) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(false));
func->set_login_ui_result(true);
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
func.get(), "[{\"interactive\": true}]", browser()));
std::string access_token;
EXPECT_TRUE(value->GetAsString(&access_token));
EXPECT_EQ(std::string(kAccessToken), access_token);
EXPECT_TRUE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveLoginSuccessApprovalAborted) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(false));
func->set_login_ui_result(true);
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
func->set_scope_ui_failure(GaiaWebAuthFlow::WINDOW_CLOSED);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_EQ(std::string(errors::kUserRejected), error);
EXPECT_TRUE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveLoginSuccessApprovalSuccess) {
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(extension.get());
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(false));
func->set_login_ui_result(true);
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_))
.WillOnce(Return(flow));
scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
func.get(), "[{\"interactive\": true}]", browser()));
std::string access_token;
EXPECT_TRUE(value->GetAsString(&access_token));
EXPECT_EQ(std::string(kAccessToken), access_token);
EXPECT_TRUE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveApprovalAborted) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
func->set_scope_ui_failure(GaiaWebAuthFlow::WINDOW_CLOSED);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_EQ(std::string(errors::kUserRejected), error);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveApprovalLoadFailed) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
func->set_scope_ui_failure(GaiaWebAuthFlow::LOAD_FAILED);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_EQ(std::string(errors::kPageLoadFailure), error);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveApprovalInvalidRedirect) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
func->set_scope_ui_failure(GaiaWebAuthFlow::INVALID_REDIRECT);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_EQ(std::string(errors::kInvalidRedirect), error);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveApprovalConnectionFailure) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(CreateExtension(CLIENT_ID | SCOPES));
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
func->set_scope_ui_failure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_TRUE(StartsWithASCII(error, errors::kAuthFailure, false));
EXPECT_FALSE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveApprovalOAuthErrors) {
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
std::map<std::string, std::string> error_map;
error_map.insert(std::make_pair("access_denied", errors::kUserRejected));
error_map.insert(std::make_pair("invalid_scope", errors::kInvalidScopes));
error_map.insert(std::make_pair(
"unmapped_error", std::string(errors::kAuthFailure) + "unmapped_error"));
for (std::map<std::string, std::string>::const_iterator
it = error_map.begin();
it != error_map.end();
++it) {
scoped_refptr<MockGetAuthTokenFunction> func(
new MockGetAuthTokenFunction());
func->set_extension(extension.get());
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
// Make sure we don't get a cached issue_advice result, which would cause
// flow to be leaked.
id_api()->EraseAllCachedTokens();
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
func->set_scope_ui_oauth_error(it->first);
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{\"interactive\": true}]", browser());
EXPECT_EQ(it->second, error);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
}
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveApprovalSuccess) {
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(extension.get());
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_))
.WillOnce(Return(flow));
scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
func.get(), "[{\"interactive\": true}]", browser()));
std::string access_token;
EXPECT_TRUE(value->GetAsString(&access_token));
EXPECT_EQ(std::string(kAccessToken), access_token);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
GetCachedToken().status());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveQueue) {
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(extension.get());
// Create a fake request to block the queue.
MockQueuedMintRequest queued_request;
IdentityMintRequestQueue::MintType type =
IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE;
EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
QueueRequestStart(type, &queued_request);
// The real request will start processing, but wait in the queue behind
// the blocker.
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
RunFunctionAsync(func.get(), "[{}]");
// Verify that we have fetched the login token at this point.
testing::Mock::VerifyAndClearExpectations(func.get());
// The flow will be created after the first queued request clears.
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
QueueRequestComplete(type, &queued_request);
scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
std::string access_token;
EXPECT_TRUE(value->GetAsString(&access_token));
EXPECT_EQ(std::string(kAccessToken), access_token);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueue) {
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(extension.get());
// Create a fake request to block the queue.
MockQueuedMintRequest queued_request;
IdentityMintRequestQueue::MintType type =
IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
QueueRequestStart(type, &queued_request);
// The real request will start processing, but wait in the queue behind
// the blocker.
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow1 = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow1));
RunFunctionAsync(func.get(), "[{\"interactive\": true}]");
// Verify that we have fetched the login token and run the first flow.
testing::Mock::VerifyAndClearExpectations(func.get());
EXPECT_FALSE(func->scope_ui_shown());
// The UI will be displayed and a token retrieved after the first
// queued request clears.
QueueRequestComplete(type, &queued_request);
scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
std::string access_token;
EXPECT_TRUE(value->GetAsString(&access_token));
EXPECT_EQ(std::string(kAccessToken), access_token);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveQueuedNoninteractiveFails) {
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(extension.get());
// Create a fake request to block the interactive queue.
MockQueuedMintRequest queued_request;
IdentityMintRequestQueue::MintType type =
IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
QueueRequestStart(type, &queued_request);
// Non-interactive requests fail without hitting GAIA, because a
// consent UI is known to be up.
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{}]", browser());
EXPECT_EQ(std::string(errors::kNoGrant), error);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
QueueRequestComplete(type, &queued_request);
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
NonInteractiveCacheHit) {
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(extension.get());
// pre-populate the cache with a token
IdentityTokenCacheValue token(kAccessToken,
base::TimeDelta::FromSeconds(3600));
SetCachedToken(token);
// Get a token. Should not require a GAIA request.
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(true));
scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
func.get(), "[{}]", browser()));
std::string access_token;
EXPECT_TRUE(value->GetAsString(&access_token));
EXPECT_EQ(std::string(kAccessToken), access_token);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
NonInteractiveIssueAdviceCacheHit) {
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(extension.get());
// pre-populate the cache with advice
IssueAdviceInfo info;
IdentityTokenCacheValue token(info);
SetCachedToken(token);
// Should return an error without a GAIA request.
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(true));
std::string error = utils::RunFunctionAndReturnError(
func.get(), "[{}]", browser());
EXPECT_EQ(std::string(errors::kNoGrant), error);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
InteractiveCacheHit) {
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
func->set_extension(extension.get());
// Create a fake request to block the queue.
MockQueuedMintRequest queued_request;
IdentityMintRequestQueue::MintType type =
IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
EXPECT_CALL(queued_request, StartMintToken(type)).Times(1);
QueueRequestStart(type, &queued_request);
// The real request will start processing, but wait in the queue behind
// the blocker.
EXPECT_CALL(*func.get(), HasLoginToken()).WillOnce(Return(true));
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_)).WillOnce(Return(flow));
RunFunctionAsync(func.get(), "[{\"interactive\": true}]");
// Populate the cache with a token while the request is blocked.
IdentityTokenCacheValue token(kAccessToken,
base::TimeDelta::FromSeconds(3600));
SetCachedToken(token);
// When we wake up the request, it returns the cached token without
// displaying a UI, or hitting GAIA.
QueueRequestComplete(type, &queued_request);
scoped_ptr<base::Value> value(WaitForSingleResult(func.get()));
std::string access_token;
EXPECT_TRUE(value->GetAsString(&access_token));
EXPECT_EQ(std::string(kAccessToken), access_token);
EXPECT_FALSE(func->login_ui_shown());
EXPECT_FALSE(func->scope_ui_shown());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
LoginInvalidatesTokenCache) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES));
func->set_extension(extension.get());
// pre-populate the cache with a token
IdentityTokenCacheValue token(kAccessToken,
base::TimeDelta::FromSeconds(3600));
SetCachedToken(token);
// Because the user is not signed in, the token will be removed,
// and we'll hit GAIA for new tokens.
EXPECT_CALL(*func.get(), HasLoginToken())
.WillOnce(Return(false));
func->set_login_ui_result(true);
TestOAuth2MintTokenFlow* flow = new TestOAuth2MintTokenFlow(
TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS, func.get());
EXPECT_CALL(*func.get(), CreateMintTokenFlow(_))
.WillOnce(Return(flow));
scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
func.get(), "[{\"interactive\": true}]", browser()));
std::string access_token;
EXPECT_TRUE(value->GetAsString(&access_token));
EXPECT_EQ(std::string(kAccessToken), access_token);
EXPECT_TRUE(func->login_ui_shown());
EXPECT_TRUE(func->scope_ui_shown());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
GetCachedToken().status());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithChromeClientId) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
scoped_refptr<const Extension> extension(
CreateExtension(SCOPES | AS_COMPONENT));
func->set_extension(extension.get());
const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension.get());
EXPECT_TRUE(oauth2_info.client_id.empty());
EXPECT_FALSE(func->GetOAuth2ClientId().empty());
EXPECT_NE("client1", func->GetOAuth2ClientId());
}
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithNormalClientId) {
scoped_refptr<MockGetAuthTokenFunction> func(new MockGetAuthTokenFunction());
scoped_refptr<const Extension> extension(
CreateExtension(CLIENT_ID | SCOPES | AS_COMPONENT));
func->set_extension(extension.get());
EXPECT_EQ("client1", func->GetOAuth2ClientId());
}
class RemoveCachedAuthTokenFunctionTest : public ExtensionBrowserTest {
protected:
bool InvalidateDefaultToken() {
scoped_refptr<IdentityRemoveCachedAuthTokenFunction> func(
new IdentityRemoveCachedAuthTokenFunction);
func->set_extension(utils::CreateEmptyExtension(kExtensionId).get());
return utils::RunFunction(
func.get(),
std::string("[{\"token\": \"") + kAccessToken + "\"}]",
browser(),
extension_function_test_utils::NONE);
}
IdentityAPI* id_api() {
return IdentityAPI::GetFactoryInstance()->GetForProfile(
browser()->profile());
}
void SetCachedToken(IdentityTokenCacheValue& token_data) {
ExtensionTokenKey key(extensions::id_util::GenerateId(kExtensionId),
"test@example.com",
std::set<std::string>());
id_api()->SetCachedToken(key, token_data);
}
const IdentityTokenCacheValue& GetCachedToken() {
return id_api()->GetCachedToken(
ExtensionTokenKey(extensions::id_util::GenerateId(kExtensionId),
"test@example.com",
std::set<std::string>()));
}
};
IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NotFound) {
EXPECT_TRUE(InvalidateDefaultToken());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND,
GetCachedToken().status());
}
IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, Advice) {
IssueAdviceInfo info;
IdentityTokenCacheValue advice(info);
SetCachedToken(advice);
EXPECT_TRUE(InvalidateDefaultToken());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE,
GetCachedToken().status());
}
IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NonMatchingToken) {
IdentityTokenCacheValue token("non_matching_token",
base::TimeDelta::FromSeconds(3600));
SetCachedToken(token);
EXPECT_TRUE(InvalidateDefaultToken());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
GetCachedToken().status());
EXPECT_EQ("non_matching_token", GetCachedToken().token());
}
IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, MatchingToken) {
IdentityTokenCacheValue token(kAccessToken,
base::TimeDelta::FromSeconds(3600));
SetCachedToken(token);
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN,
GetCachedToken().status());
EXPECT_TRUE(InvalidateDefaultToken());
EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND,
GetCachedToken().status());
}
class LaunchWebAuthFlowFunctionTest : public AsyncExtensionBrowserTest {
public:
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
// Reduce performance test variance by disabling background networking.
command_line->AppendSwitch(switches::kDisableBackgroundNetworking);
}
};
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, UserCloseWindow) {
net::SpawnedTestServer https_server(
net::SpawnedTestServer::TYPE_HTTPS,
net::SpawnedTestServer::kLocalhost,
base::FilePath(FILE_PATH_LITERAL(
"chrome/test/data/extensions/api_test/identity")));
ASSERT_TRUE(https_server.Start());
GURL auth_url(https_server.GetURL("files/interaction_required.html"));
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
new IdentityLaunchWebAuthFlowFunction());
scoped_refptr<Extension> empty_extension(
utils::CreateEmptyExtension());
function->set_extension(empty_extension.get());
WaitForGURLAndCloseWindow popup_observer(auth_url);
std::string args = "[{\"interactive\": true, \"url\": \"" +
auth_url.spec() + "\"}]";
RunFunctionAsync(function.get(), args);
popup_observer.Wait();
popup_observer.CloseEmbedderWebContents();
EXPECT_EQ(std::string(errors::kUserRejected), WaitForError(function.get()));
}
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, InteractionRequired) {
net::SpawnedTestServer https_server(
net::SpawnedTestServer::TYPE_HTTPS,
net::SpawnedTestServer::kLocalhost,
base::FilePath(FILE_PATH_LITERAL(
"chrome/test/data/extensions/api_test/identity")));
ASSERT_TRUE(https_server.Start());
GURL auth_url(https_server.GetURL("files/interaction_required.html"));
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
new IdentityLaunchWebAuthFlowFunction());
scoped_refptr<Extension> empty_extension(
utils::CreateEmptyExtension());
function->set_extension(empty_extension.get());
std::string args = "[{\"interactive\": false, \"url\": \"" +
auth_url.spec() + "\"}]";
std::string error =
utils::RunFunctionAndReturnError(function.get(), args, browser());
EXPECT_EQ(std::string(errors::kInteractionRequired), error);
}
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, LoadFailed) {
net::SpawnedTestServer https_server(
net::SpawnedTestServer::TYPE_HTTPS,
net::SpawnedTestServer::kLocalhost,
base::FilePath(FILE_PATH_LITERAL(
"chrome/test/data/extensions/api_test/identity")));
ASSERT_TRUE(https_server.Start());
GURL auth_url(https_server.GetURL("files/five_hundred.html"));
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
new IdentityLaunchWebAuthFlowFunction());
scoped_refptr<Extension> empty_extension(
utils::CreateEmptyExtension());
function->set_extension(empty_extension.get());
std::string args = "[{\"interactive\": true, \"url\": \"" +
auth_url.spec() + "\"}]";
std::string error =
utils::RunFunctionAndReturnError(function.get(), args, browser());
EXPECT_EQ(std::string(errors::kPageLoadFailure), error);
}
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) {
#if defined(OS_WIN) && defined(USE_ASH)
// Disable this test in Metro+Ash for now (http://crbug.com/262796).
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
return;
#endif
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
new IdentityLaunchWebAuthFlowFunction());
scoped_refptr<Extension> empty_extension(
utils::CreateEmptyExtension());
function->set_extension(empty_extension.get());
function->InitFinalRedirectURLPrefixForTest("abcdefghij");
scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
function.get(),
"[{\"interactive\": false,"
"\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]",
browser()));
std::string url;
EXPECT_TRUE(value->GetAsString(&url));
EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"),
url);
}
IN_PROC_BROWSER_TEST_F(
LaunchWebAuthFlowFunctionTest, InteractiveFirstNavigationSuccess) {
#if defined(OS_WIN) && defined(USE_ASH)
// Disable this test in Metro+Ash for now (http://crbug.com/262796).
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
return;
#endif
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
new IdentityLaunchWebAuthFlowFunction());
scoped_refptr<Extension> empty_extension(
utils::CreateEmptyExtension());
function->set_extension(empty_extension.get());
function->InitFinalRedirectURLPrefixForTest("abcdefghij");
scoped_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult(
function.get(),
"[{\"interactive\": true,"
"\"url\": \"https://abcdefghij.chromiumapp.org/callback#test\"}]",
browser()));
std::string url;
EXPECT_TRUE(value->GetAsString(&url));
EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"),
url);
}
IN_PROC_BROWSER_TEST_F(
LaunchWebAuthFlowFunctionTest, InteractiveSecondNavigationSuccess) {
net::SpawnedTestServer https_server(
net::SpawnedTestServer::TYPE_HTTPS,
net::SpawnedTestServer::kLocalhost,
base::FilePath(FILE_PATH_LITERAL(
"chrome/test/data/extensions/api_test/identity")));
ASSERT_TRUE(https_server.Start());
GURL auth_url(https_server.GetURL("files/redirect_to_chromiumapp.html"));
scoped_refptr<IdentityLaunchWebAuthFlowFunction> function(
new IdentityLaunchWebAuthFlowFunction());
scoped_refptr<Extension> empty_extension(
utils::CreateEmptyExtension());
function->set_extension(empty_extension.get());
function->InitFinalRedirectURLPrefixForTest("abcdefghij");
std::string args = "[{\"interactive\": true, \"url\": \"" +
auth_url.spec() + "\"}]";
scoped_ptr<base::Value> value(
utils::RunFunctionAndReturnSingleResult(function.get(), args, browser()));
std::string url;
EXPECT_TRUE(value->GetAsString(&url));
EXPECT_EQ(std::string("https://abcdefghij.chromiumapp.org/callback#test"),
url);
}
} // namespace extensions
// Tests the chrome.identity API implemented by custom JS bindings .
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeIdentityJsBindings) {
ASSERT_TRUE(RunExtensionTest("identity/js_bindings")) << message_;
}