Commit dd9cc622 authored by dimich@chromium.org's avatar dimich@chromium.org

Identity API: getAuthToken request queues (token cache prelude)

Serializing calls to getAuthToken will eliminate unnecessary calls to
GAIA and consolidate UI flows.

There is are two queues of requests for each (extension, scopes)
tuple, one for the non-interactive IssueToken flow and one for the
interactive prompt.

BUG=228908
(step #1 of the bug description)

Review URL: https://codereview.chromium.org/14270007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@194627 0039d316-1c4b-4281-b951-d872f2087c98
parent 667e934f
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
#include "chrome/browser/extensions/api/identity/identity_api.h" #include "chrome/browser/extensions/api/identity/identity_api.h"
#include <set>
#include <string>
#include <vector>
#include "base/lazy_instance.h" #include "base/lazy_instance.h"
#include "base/stringprintf.h" #include "base/stringprintf.h"
#include "base/values.h" #include "base/values.h"
...@@ -60,6 +64,7 @@ namespace identity = api::experimental_identity; ...@@ -60,6 +64,7 @@ namespace identity = api::experimental_identity;
IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction() IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction()
: should_prompt_for_scopes_(false), : should_prompt_for_scopes_(false),
should_prompt_for_signin_(false) {} should_prompt_for_signin_(false) {}
IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {} IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {}
bool IdentityGetAuthTokenFunction::RunImpl() { bool IdentityGetAuthTokenFunction::RunImpl() {
...@@ -85,8 +90,7 @@ bool IdentityGetAuthTokenFunction::RunImpl() { ...@@ -85,8 +90,7 @@ bool IdentityGetAuthTokenFunction::RunImpl() {
return false; return false;
} }
// Balanced in OnIssueAdviceSuccess|OnMintTokenSuccess|OnMintTokenFailure| // Balanced in CompleteFunctionWithResult|CompleteFunctionWithError
// InstallUIAbort|SigninFailed.
AddRef(); AddRef();
if (!HasLoginToken()) { if (!HasLoginToken()) {
...@@ -95,28 +99,107 @@ bool IdentityGetAuthTokenFunction::RunImpl() { ...@@ -95,28 +99,107 @@ bool IdentityGetAuthTokenFunction::RunImpl() {
Release(); Release();
return false; return false;
} }
// Display a login prompt. If the subsequent mint fails, don't display the // Display a login prompt.
// prompt again. StartSigninFlow();
should_prompt_for_signin_ = false;
ShowLoginPopup();
} else { } else {
TokenService* token_service = TokenServiceFactory::GetForProfile(profile()); TokenService* token_service = TokenServiceFactory::GetForProfile(profile());
refresh_token_ = token_service->GetOAuth2LoginRefreshToken(); refresh_token_ = token_service->GetOAuth2LoginRefreshToken();
StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE); StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
} }
return true; return true;
} }
void IdentityGetAuthTokenFunction::OnMintTokenSuccess( void IdentityGetAuthTokenFunction::CompleteFunctionWithResult(
const std::string& access_token) { const std::string& access_token) {
SetResult(Value::CreateStringValue(access_token)); SetResult(Value::CreateStringValue(access_token));
SendResponse(true); SendResponse(true);
Release(); // Balanced in RunImpl. Release(); // Balanced in RunImpl.
} }
void IdentityGetAuthTokenFunction::CompleteFunctionWithError(
const std::string& error) {
error_ = error;
SendResponse(false);
Release(); // Balanced in RunImpl.
}
void IdentityGetAuthTokenFunction::StartSigninFlow() {
// Display a login prompt. If the subsequent mint fails, don't display the
// login prompt again.
should_prompt_for_signin_ = false;
ShowLoginPopup();
}
void IdentityGetAuthTokenFunction::StartMintTokenFlow(
IdentityMintRequestQueue::MintType type) {
mint_token_flow_type_ = type;
// Flows are serialized to prevent excessive traffic to GAIA, and
// to consolidate UI pop-ups.
const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
std::set<std::string> scopes(oauth2_info.scopes.begin(),
oauth2_info.scopes.end());
IdentityAPI* id_api =
extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(profile_);
// If there is an interactive flow in progress, non-interactive
// requests should complete immediately since a consent UI is
// known to be required.
if (!should_prompt_for_scopes_ && !id_api->mint_queue()->empty(
IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE,
GetExtension()->id(), scopes)) {
CompleteFunctionWithError(identity_constants::kNoGrant);
return;
}
id_api->mint_queue()->RequestStart(type,
GetExtension()->id(),
scopes,
this);
}
void IdentityGetAuthTokenFunction::CompleteMintTokenFlow() {
IdentityMintRequestQueue::MintType type = mint_token_flow_type_;
const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension());
std::set<std::string> scopes(oauth2_info.scopes.begin(),
oauth2_info.scopes.end());
extensions::IdentityAPI::GetFactoryInstance()->GetForProfile(
profile_)->mint_queue()->RequestComplete(type,
GetExtension()->id(),
scopes,
this);
}
void IdentityGetAuthTokenFunction::StartMintToken(
IdentityMintRequestQueue::MintType type) {
switch (type) {
case IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE:
StartGaiaRequest(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE);
break;
case IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE:
install_ui_.reset(new ExtensionInstallPrompt(GetAssociatedWebContents()));
ShowOAuthApprovalDialog(issue_advice_);
break;
default:
NOTREACHED() << "Unexepected mint type in StartMintToken: " << type;
break;
};
}
void IdentityGetAuthTokenFunction::OnMintTokenSuccess(
const std::string& access_token) {
CompleteMintTokenFlow();
CompleteFunctionWithResult(access_token);
}
void IdentityGetAuthTokenFunction::OnMintTokenFailure( void IdentityGetAuthTokenFunction::OnMintTokenFailure(
const GoogleServiceAuthError& error) { const GoogleServiceAuthError& error) {
CompleteMintTokenFlow();
switch (error.state()) { switch (error.state()) {
case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
case GoogleServiceAuthError::ACCOUNT_DELETED: case GoogleServiceAuthError::ACCOUNT_DELETED:
...@@ -125,8 +208,7 @@ void IdentityGetAuthTokenFunction::OnMintTokenFailure( ...@@ -125,8 +208,7 @@ void IdentityGetAuthTokenFunction::OnMintTokenFailure(
profile())->ReportAuthError(error); profile())->ReportAuthError(error);
if (should_prompt_for_signin_) { if (should_prompt_for_signin_) {
// Display a login prompt and try again (once). // Display a login prompt and try again (once).
should_prompt_for_signin_ = false; StartSigninFlow();
ShowLoginPopup();
return; return;
} }
break; break;
...@@ -135,52 +217,48 @@ void IdentityGetAuthTokenFunction::OnMintTokenFailure( ...@@ -135,52 +217,48 @@ void IdentityGetAuthTokenFunction::OnMintTokenFailure(
break; break;
} }
error_ = std::string(identity_constants::kAuthFailure) + error.ToString(); CompleteFunctionWithError(
SendResponse(false); std::string(identity_constants::kAuthFailure) + error.ToString());
Release(); // Balanced in RunImpl.
} }
void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess( void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess(
const IssueAdviceInfo& issue_advice) { const IssueAdviceInfo& issue_advice) {
CompleteMintTokenFlow();
should_prompt_for_signin_ = false; should_prompt_for_signin_ = false;
// Existing grant was revoked and we used NO_FORCE, so we got info back // Existing grant was revoked and we used NO_FORCE, so we got info back
// instead. // instead.
if (should_prompt_for_scopes_) { if (should_prompt_for_scopes_) {
install_ui_.reset(new ExtensionInstallPrompt(GetAssociatedWebContents())); issue_advice_ = issue_advice;
ShowOAuthApprovalDialog(issue_advice); StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE);
} else { } else {
error_ = identity_constants::kNoGrant; CompleteFunctionWithError(identity_constants::kNoGrant);
SendResponse(false);
Release(); // Balanced in RunImpl.
} }
} }
void IdentityGetAuthTokenFunction::SigninSuccess(const std::string& token) { void IdentityGetAuthTokenFunction::SigninSuccess(const std::string& token) {
refresh_token_ = token; refresh_token_ = token;
StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE); StartMintTokenFlow(IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE);
} }
void IdentityGetAuthTokenFunction::SigninFailed() { void IdentityGetAuthTokenFunction::SigninFailed() {
error_ = identity_constants::kUserNotSignedIn; CompleteFunctionWithError(identity_constants::kUserNotSignedIn);
SendResponse(false);
Release();
} }
void IdentityGetAuthTokenFunction::InstallUIProceed() { void IdentityGetAuthTokenFunction::InstallUIProceed() {
DCHECK(install_ui_->record_oauth2_grant()); DCHECK(install_ui_->record_oauth2_grant());
// The user has accepted the scopes, so we may now force (recording a grant // The user has accepted the scopes, so we may now force (recording a grant
// and receiving a token). // and receiving a token).
StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE); StartGaiaRequest(OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE);
} }
void IdentityGetAuthTokenFunction::InstallUIAbort(bool user_initiated) { void IdentityGetAuthTokenFunction::InstallUIAbort(bool user_initiated) {
error_ = identity_constants::kUserRejected; CompleteMintTokenFlow();
SendResponse(false); CompleteFunctionWithError(identity_constants::kUserRejected);
Release(); // Balanced in RunImpl.
} }
void IdentityGetAuthTokenFunction::StartFlow(OAuth2MintTokenFlow::Mode mode) { void IdentityGetAuthTokenFunction::StartGaiaRequest(
signin_flow_.reset(NULL); OAuth2MintTokenFlow::Mode mode) {
mint_token_flow_.reset(CreateMintTokenFlow(mode)); mint_token_flow_.reset(CreateMintTokenFlow(mode));
mint_token_flow_->Start(); mint_token_flow_->Start();
} }
...@@ -339,6 +417,10 @@ void IdentityAPI::Initialize() { ...@@ -339,6 +417,10 @@ void IdentityAPI::Initialize() {
content::Source<TokenService>(token_service)); content::Source<TokenService>(token_service));
} }
IdentityMintRequestQueue* IdentityAPI::mint_queue() {
return &mint_queue_;
}
void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) { void IdentityAPI::ReportAuthError(const GoogleServiceAuthError& error) {
if (!signin_manager_) if (!signin_manager_)
Initialize(); Initialize();
......
...@@ -5,11 +5,13 @@ ...@@ -5,11 +5,13 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_ #ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_
#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_ #define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_
#include <map>
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "chrome/browser/extensions/api/identity/identity_mint_queue.h"
#include "chrome/browser/extensions/api/identity/identity_signin_flow.h" #include "chrome/browser/extensions/api/identity/identity_signin_flow.h"
#include "chrome/browser/extensions/api/identity/web_auth_flow.h" #include "chrome/browser/extensions/api/identity/web_auth_flow.h"
#include "chrome/browser/extensions/api/profile_keyed_api_factory.h" #include "chrome/browser/extensions/api/profile_keyed_api_factory.h"
...@@ -36,9 +38,27 @@ extern const char kInteractionRequired[]; ...@@ -36,9 +38,27 @@ extern const char kInteractionRequired[];
extern const char kInvalidRedirect[]; extern const char kInvalidRedirect[];
} // namespace identity_constants } // namespace identity_constants
// identity.getAuthToken fetches an OAuth 2 function for the
// caller. The request has three sub-flows: non-interactive,
// interactive, and sign-in.
//
// In the non-interactive flow, getAuthToken requests a token from
// GAIA. GAIA may respond with a token, an error, or "consent
// required". In the consent required cases, getAuthToken proceeds to
// the second, interactive phase.
//
// The interactive flow presents a scope approval dialog to the
// user. If the user approves the request, a grant will be recorded on
// the server, and an access token will be returned to the caller.
//
// In some cases we need to display a sign-in dialog. Normally the
// profile will be signed in already, but if it turns out we need a
// new login token, there is a sign-in flow. If that flow completes
// successfully, getAuthToken proceeds to the non-interactive flow.
class IdentityGetAuthTokenFunction : public AsyncExtensionFunction, class IdentityGetAuthTokenFunction : public AsyncExtensionFunction,
public OAuth2MintTokenFlow::Delegate,
public ExtensionInstallPrompt::Delegate, public ExtensionInstallPrompt::Delegate,
public IdentityMintRequestQueue::Request,
public OAuth2MintTokenFlow::Delegate,
public IdentitySigninFlow::Delegate { public IdentitySigninFlow::Delegate {
public: public:
DECLARE_EXTENSION_FUNCTION("experimental.identity.getAuthToken", DECLARE_EXTENSION_FUNCTION("experimental.identity.getAuthToken",
...@@ -56,6 +76,18 @@ class IdentityGetAuthTokenFunction : public AsyncExtensionFunction, ...@@ -56,6 +76,18 @@ class IdentityGetAuthTokenFunction : public AsyncExtensionFunction,
// ExtensionFunction: // ExtensionFunction:
virtual bool RunImpl() OVERRIDE; virtual bool RunImpl() OVERRIDE;
// Helpers to report async function results to the caller.
void CompleteFunctionWithResult(const std::string& access_token);
void CompleteFunctionWithError(const std::string& error);
// Initiate/complete the sub-flows.
void StartSigninFlow();
void StartMintTokenFlow(IdentityMintRequestQueue::MintType type);
void CompleteMintTokenFlow();
// IdentityMintRequestQueue::Request implementation:
virtual void StartMintToken(IdentityMintRequestQueue::MintType type) OVERRIDE;
// OAuth2MintTokenFlow::Delegate implementation: // OAuth2MintTokenFlow::Delegate implementation:
virtual void OnMintTokenSuccess(const std::string& access_token) OVERRIDE; virtual void OnMintTokenSuccess(const std::string& access_token) OVERRIDE;
virtual void OnMintTokenFailure( virtual void OnMintTokenFailure(
...@@ -71,9 +103,10 @@ class IdentityGetAuthTokenFunction : public AsyncExtensionFunction, ...@@ -71,9 +103,10 @@ class IdentityGetAuthTokenFunction : public AsyncExtensionFunction,
virtual void InstallUIProceed() OVERRIDE; virtual void InstallUIProceed() OVERRIDE;
virtual void InstallUIAbort(bool user_initiated) OVERRIDE; virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
// Starts a MintTokenFlow with the given mode. // Starts a mint token request to GAIA.
void StartFlow(OAuth2MintTokenFlow::Mode mode); void StartGaiaRequest(OAuth2MintTokenFlow::Mode mode);
// Methods for invoking UI. Overridable for testing.
virtual void ShowLoginPopup(); virtual void ShowLoginPopup();
virtual void ShowOAuthApprovalDialog(const IssueAdviceInfo& issue_advice); virtual void ShowOAuthApprovalDialog(const IssueAdviceInfo& issue_advice);
// Caller owns the returned instance. // Caller owns the returned instance.
...@@ -84,12 +117,14 @@ class IdentityGetAuthTokenFunction : public AsyncExtensionFunction, ...@@ -84,12 +117,14 @@ class IdentityGetAuthTokenFunction : public AsyncExtensionFunction,
virtual bool HasLoginToken() const; virtual bool HasLoginToken() const;
bool should_prompt_for_scopes_; bool should_prompt_for_scopes_;
IdentityMintRequestQueue::MintType mint_token_flow_type_;
scoped_ptr<OAuth2MintTokenFlow> mint_token_flow_; scoped_ptr<OAuth2MintTokenFlow> mint_token_flow_;
std::string refresh_token_; std::string refresh_token_;
bool should_prompt_for_signin_; bool should_prompt_for_signin_;
// When launched in interactive mode, and if there is no existing grant, // When launched in interactive mode, and if there is no existing grant,
// a permissions prompt will be popped up to the user. // a permissions prompt will be popped up to the user.
IssueAdviceInfo issue_advice_;
scoped_ptr<ExtensionInstallPrompt> install_ui_; scoped_ptr<ExtensionInstallPrompt> install_ui_;
scoped_ptr<IdentitySigninFlow> signin_flow_; scoped_ptr<IdentitySigninFlow> signin_flow_;
}; };
...@@ -132,6 +167,9 @@ class IdentityAPI : public ProfileKeyedAPI, ...@@ -132,6 +167,9 @@ class IdentityAPI : public ProfileKeyedAPI,
virtual ~IdentityAPI(); virtual ~IdentityAPI();
void Initialize(); void Initialize();
// Request serialization queue for getAuthToken.
IdentityMintRequestQueue* mint_queue();
void ReportAuthError(const GoogleServiceAuthError& error); void ReportAuthError(const GoogleServiceAuthError& error);
// ProfileKeyedAPI implementation. // ProfileKeyedAPI implementation.
...@@ -160,6 +198,7 @@ class IdentityAPI : public ProfileKeyedAPI, ...@@ -160,6 +198,7 @@ class IdentityAPI : public ProfileKeyedAPI,
GoogleServiceAuthError error_; GoogleServiceAuthError error_;
// Used to listen to notifications from the TokenService. // Used to listen to notifications from the TokenService.
content::NotificationRegistrar registrar_; content::NotificationRegistrar registrar_;
IdentityMintRequestQueue mint_queue_;
}; };
template <> template <>
......
// Copyright (c) 2013 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/extensions/api/identity/identity_mint_queue.h"
#include "base/logging.h"
#include "base/stl_util.h"
namespace extensions {
IdentityMintRequestQueue::IdentityMintRequestQueue() {
}
IdentityMintRequestQueue::~IdentityMintRequestQueue() {
std::map<RequestKey, RequestList>::const_iterator it;
for (it = request_queue_.begin(); it != request_queue_.end(); ++it)
DCHECK_EQ(it->second.size(), 0lu);
}
IdentityMintRequestQueue::RequestKey::RequestKey(
IdentityMintRequestQueue::MintType type,
const std::string& extension_id,
const std::set<std::string> scopes) : type(type),
extension_id(extension_id),
scopes(scopes) {
}
IdentityMintRequestQueue::RequestKey::~RequestKey() {
}
bool IdentityMintRequestQueue::RequestKey::operator<(
const RequestKey& rhs) const {
if (type < rhs.type)
return true;
else if (rhs.type < type)
return false;
if (extension_id < rhs.extension_id)
return true;
else if (rhs.extension_id < extension_id)
return false;
return scopes < rhs.scopes;
}
void IdentityMintRequestQueue::RequestStart(
IdentityMintRequestQueue::MintType type,
const std::string& extension_id,
const std::set<std::string> scopes,
IdentityMintRequestQueue::Request* request) {
RequestKey key(type, extension_id, scopes);
request_queue_[key].push_back(request);
// If this is the first request, start it now. RequestComplete will start
// all other requests.
if (request_queue_[key].size() == 1)
request_queue_[key].front()->StartMintToken(type);
}
void IdentityMintRequestQueue::RequestComplete(
IdentityMintRequestQueue::MintType type,
const std::string& extension_id,
const std::set<std::string> scopes,
IdentityMintRequestQueue::Request* request) {
RequestKey key(type, extension_id, scopes);
CHECK(request_queue_[key].front() == request);
request_queue_[key].pop_front();
if (request_queue_[key].size() > 0)
request_queue_[key].front()->StartMintToken(type);
}
bool IdentityMintRequestQueue::empty(IdentityMintRequestQueue::MintType type,
const std::string& extension_id,
const std::set<std::string> scopes) const {
RequestKey key(type, extension_id, scopes);
return !ContainsKey(request_queue_, key) ||
(request_queue_.find(key))->second.empty();
}
} // namespace extensions
// 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.
#ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_MINT_QUEUE_H_
#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_MINT_QUEUE_H_
#include <list>
#include <map>
#include <set>
#include <string>
namespace extensions {
// getAuthToken requests are serialized to avoid excessive traffic to
// GAIA and to consolidate UI pop-ups. IdentityMintRequestQueue
// maitains a set of queues, one for each RequestKey.
//
// The queue calls StartMintToken on each Request when it reaches the
// head of the line.
//
// The queue does not own Requests. Request pointers must be valid
// until they are removed from the queue with RequestComplete.
class IdentityMintRequestQueue {
public:
enum MintType {
MINT_TYPE_NONINTERACTIVE,
MINT_TYPE_INTERACTIVE
};
IdentityMintRequestQueue();
virtual ~IdentityMintRequestQueue();
class Request {
public:
virtual ~Request() {}
virtual void StartMintToken(IdentityMintRequestQueue::MintType type) = 0;
};
struct RequestKey {
RequestKey(IdentityMintRequestQueue::MintType type,
const std::string& extension_id,
const std::set<std::string> scopes);
~RequestKey();
bool operator<(const RequestKey& rhs) const;
IdentityMintRequestQueue::MintType type;
std::string extension_id;
std::set<std::string> scopes;
};
// Adds a request to the queue specified by the id and scopes.
void RequestStart(IdentityMintRequestQueue::MintType type,
const std::string& extension_id,
const std::set<std::string> scopes,
IdentityMintRequestQueue::Request* request);
// Removes a request from the queue specified by the id and scopes.
void RequestComplete(IdentityMintRequestQueue::MintType type,
const std::string& extension_id,
const std::set<std::string> scopes,
IdentityMintRequestQueue::Request* request);
bool empty(IdentityMintRequestQueue::MintType type,
const std::string& extension_id,
const std::set<std::string> scopes) const;
private:
typedef std::list<IdentityMintRequestQueue::Request*> RequestList;
std::map<RequestKey, RequestList> request_queue_;
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_MINT_QUEUE_H_
// Copyright (c) 2013 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/extensions/api/identity/identity_mint_queue.h"
#include <vector>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using extensions::IdentityMintRequestQueue;
namespace {
class MockRequest : public extensions::IdentityMintRequestQueue::Request {
public:
MOCK_METHOD1(StartMintToken, void(IdentityMintRequestQueue::MintType));
};
} // namespace
TEST(IdentityMintQueueTest, SerialRequests) {
IdentityMintRequestQueue::MintType type =
IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE;
IdentityMintRequestQueue queue;
std::string extension_id("ext_id");
MockRequest request1;
MockRequest request2;
EXPECT_CALL(request1, StartMintToken(type)).Times(1);
queue.RequestStart(type, extension_id, std::set<std::string>(), &request1);
queue.RequestComplete(type, extension_id, std::set<std::string>(), &request1);
EXPECT_CALL(request2, StartMintToken(type)).Times(1);
queue.RequestStart(type, extension_id, std::set<std::string>(), &request2);
queue.RequestComplete(type, extension_id, std::set<std::string>(), &request2);
}
TEST(IdentityMintQueueTest, InteractiveType) {
IdentityMintRequestQueue::MintType type =
IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
IdentityMintRequestQueue queue;
std::string extension_id("ext_id");
MockRequest request1;
EXPECT_CALL(request1, StartMintToken(type)).Times(1);
queue.RequestStart(type, extension_id, std::set<std::string>(), &request1);
queue.RequestComplete(type, extension_id, std::set<std::string>(), &request1);
}
TEST(IdentityMintQueueTest, ParallelRequests) {
IdentityMintRequestQueue::MintType type =
IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE;
IdentityMintRequestQueue queue;
std::string extension_id("ext_id");
MockRequest request1;
MockRequest request2;
MockRequest request3;
EXPECT_CALL(request1, StartMintToken(type)).Times(1);
queue.RequestStart(type, extension_id, std::set<std::string>(), &request1);
queue.RequestStart(type, extension_id, std::set<std::string>(), &request2);
queue.RequestStart(type, extension_id, std::set<std::string>(), &request3);
EXPECT_CALL(request2, StartMintToken(type)).Times(1);
queue.RequestComplete(type, extension_id, std::set<std::string>(), &request1);
EXPECT_CALL(request3, StartMintToken(type)).Times(1);
queue.RequestComplete(type, extension_id, std::set<std::string>(), &request2);
queue.RequestComplete(type, extension_id, std::set<std::string>(), &request3);
}
TEST(IdentityMintQueueTest, ParallelRequestsFromTwoExtensions) {
IdentityMintRequestQueue::MintType type =
IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE;
IdentityMintRequestQueue queue;
std::string extension_id1("ext_id_1");
std::string extension_id2("ext_id_2");
MockRequest request1;
MockRequest request2;
EXPECT_CALL(request1, StartMintToken(type)).Times(1);
EXPECT_CALL(request2, StartMintToken(type)).Times(1);
queue.RequestStart(type, extension_id1, std::set<std::string>(), &request1);
queue.RequestStart(type, extension_id2, std::set<std::string>(), &request2);
queue.RequestComplete(type, extension_id1,
std::set<std::string>(), &request1);
queue.RequestComplete(type, extension_id2,
std::set<std::string>(), &request2);
}
TEST(IdentityMintQueueTest, ParallelRequestsForDifferentScopes) {
IdentityMintRequestQueue::MintType type =
IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE;
IdentityMintRequestQueue queue;
std::string extension_id("ext_id");
MockRequest request1;
MockRequest request2;
std::set<std::string> scopes1;
std::set<std::string> scopes2;
scopes1.insert("a");
scopes1.insert("b");
scopes2.insert("a");
EXPECT_CALL(request1, StartMintToken(type)).Times(1);
EXPECT_CALL(request2, StartMintToken(type)).Times(1);
queue.RequestStart(type, extension_id, scopes1, &request1);
queue.RequestStart(type, extension_id, scopes2, &request2);
queue.RequestComplete(type, extension_id, scopes1, &request1);
queue.RequestComplete(type, extension_id, scopes2, &request2);
}
TEST(IdentityMintQueueTest, KeyComparisons) {
std::string extension_id1("ext_id_1");
std::string extension_id2("ext_id_2");
std::set<std::string> scopes1;
std::set<std::string> scopes2;
std::set<std::string> scopes3;
scopes1.insert("a");
scopes1.insert("b");
scopes2.insert("a");
std::vector<std::string> ids;
ids.push_back(extension_id1);
ids.push_back(extension_id2);
std::vector<std::set<std::string> > scopesets;
scopesets.push_back(scopes1);
scopesets.push_back(scopes2);
scopesets.push_back(scopes3);
std::vector<IdentityMintRequestQueue::RequestKey> keys;
typedef std::vector<
IdentityMintRequestQueue::RequestKey>::const_iterator
RequestKeyIterator;
std::vector<std::string>::const_iterator id_it;
std::vector<std::set<std::string> >::const_iterator scope_it;
for (id_it = ids.begin(); id_it != ids.end(); ++id_it) {
for (scope_it = scopesets.begin(); scope_it != scopesets.end();
++scope_it) {
keys.push_back(IdentityMintRequestQueue::RequestKey(
IdentityMintRequestQueue::MINT_TYPE_NONINTERACTIVE, *id_it,
*scope_it));
keys.push_back(IdentityMintRequestQueue::RequestKey(
IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE, *id_it, *scope_it));
}
}
// keys should not be less than themselves
for (RequestKeyIterator it = keys.begin(); it != keys.end(); ++it) {
EXPECT_FALSE(*it < *it);
}
// keys should not equal different keys
for (RequestKeyIterator it1 = keys.begin(); it1 != keys.end(); ++it1) {
RequestKeyIterator it2 = it1;
for (++it2; it2 != keys.end(); ++it2) {
EXPECT_TRUE(*it1 < *it2 || *it2 < *it1);
EXPECT_FALSE(*it1 < *it2 && *it2 < *it1);
}
}
}
TEST(IdentityMintQueueTest, Empty) {
IdentityMintRequestQueue::MintType type =
IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE;
IdentityMintRequestQueue queue;
std::string extension_id("ext_id");
MockRequest request1;
EXPECT_TRUE(queue.empty(type, extension_id, std::set<std::string>()));
EXPECT_CALL(request1, StartMintToken(type)).Times(1);
queue.RequestStart(type, extension_id, std::set<std::string>(), &request1);
EXPECT_FALSE(queue.empty(type, extension_id, std::set<std::string>()));
queue.RequestComplete(type, extension_id, std::set<std::string>(), &request1);
EXPECT_TRUE(queue.empty(type, extension_id, std::set<std::string>()));
}
...@@ -222,6 +222,8 @@ ...@@ -222,6 +222,8 @@
'browser/extensions/api/i18n/i18n_api.h', 'browser/extensions/api/i18n/i18n_api.h',
'browser/extensions/api/identity/identity_api.cc', 'browser/extensions/api/identity/identity_api.cc',
'browser/extensions/api/identity/identity_api.h', 'browser/extensions/api/identity/identity_api.h',
'browser/extensions/api/identity/identity_mint_queue.cc',
'browser/extensions/api/identity/identity_mint_queue.h',
'browser/extensions/api/identity/identity_signin_flow.cc', 'browser/extensions/api/identity/identity_signin_flow.cc',
'browser/extensions/api/identity/identity_signin_flow.h', 'browser/extensions/api/identity/identity_signin_flow.h',
'browser/extensions/api/identity/web_auth_flow.cc', 'browser/extensions/api/identity/web_auth_flow.cc',
......
...@@ -705,6 +705,7 @@ ...@@ -705,6 +705,7 @@
'browser/extensions/api/discovery/discovery_api_unittest.cc', 'browser/extensions/api/discovery/discovery_api_unittest.cc',
'browser/extensions/api/extension_action/extension_browser_actions_api_unittest.cc', 'browser/extensions/api/extension_action/extension_browser_actions_api_unittest.cc',
'browser/extensions/api/file_system/file_system_api_unittest.cc', 'browser/extensions/api/file_system/file_system_api_unittest.cc',
'browser/extensions/api/identity/identity_mint_queue_unittest.cc',
'browser/extensions/api/identity/web_auth_flow_unittest.cc', 'browser/extensions/api/identity/web_auth_flow_unittest.cc',
'browser/extensions/api/idle/idle_api_unittest.cc', 'browser/extensions/api/idle/idle_api_unittest.cc',
'browser/extensions/api/messaging/native_message_process_host_unittest.cc', 'browser/extensions/api/messaging/native_message_process_host_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment