Commit 2f4dc963 authored by John Z Wu's avatar John Z Wu Committed by Commit Bot

Define new CWVSyncControllerDataSource method for error handling

Token fetches by the client may fail for any number of reasons. The
errors should be categorized and returned to //ios/web_view for internal
handling.

Bug: 780937
Change-Id: I84e15d8f43ad8ca1fca8f9be41d82fad0cae6421
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2153337
Commit-Queue: John Wu <jzw@chromium.org>
Reviewed-by: default avatarHiroshi Ichikawa <ichikawa@chromium.org>
Cr-Commit-Position: refs/heads/master@{#759896}
parent b1650119
...@@ -68,6 +68,7 @@ ios_web_view_public_headers = [ ...@@ -68,6 +68,7 @@ ios_web_view_public_headers = [
"public/cwv_sync_controller.h", "public/cwv_sync_controller.h",
"public/cwv_sync_controller_data_source.h", "public/cwv_sync_controller_data_source.h",
"public/cwv_sync_controller_delegate.h", "public/cwv_sync_controller_delegate.h",
"public/cwv_sync_errors.h",
"public/cwv_translation_controller.h", "public/cwv_translation_controller.h",
"public/cwv_translation_controller_delegate.h", "public/cwv_translation_controller_delegate.h",
"public/cwv_translation_language.h", "public/cwv_translation_language.h",
...@@ -412,6 +413,7 @@ test("ios_web_view_unittests") { ...@@ -412,6 +413,7 @@ test("ios_web_view_unittests") {
"internal/passwords/cwv_password_unittest.mm", "internal/passwords/cwv_password_unittest.mm",
"internal/passwords/web_view_password_manager_client_unittest.mm", "internal/passwords/web_view_password_manager_client_unittest.mm",
"internal/signin/cwv_identity_unittest.mm", "internal/signin/cwv_identity_unittest.mm",
"internal/signin/web_view_device_accounts_provider_impl_unittest.mm",
"internal/signin/web_view_gaia_auth_fetcher_unittest.mm", "internal/signin/web_view_gaia_auth_fetcher_unittest.mm",
"internal/sync/cwv_sync_controller_unittest.mm", "internal/sync/cwv_sync_controller_unittest.mm",
"internal/sync/web_view_sync_client_unittest.mm", "internal/sync/web_view_sync_client_unittest.mm",
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#import "ios/web_view/internal/sync/cwv_sync_controller_internal.h" #import "ios/web_view/internal/sync/cwv_sync_controller_internal.h"
#import "ios/web_view/public/cwv_identity.h" #import "ios/web_view/public/cwv_identity.h"
#import "ios/web_view/public/cwv_sync_controller_data_source.h" #import "ios/web_view/public/cwv_sync_controller_data_source.h"
#import "ios/web_view/public/cwv_sync_errors.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
...@@ -25,6 +26,8 @@ void WebViewDeviceAccountsProviderImpl::GetAccessToken( ...@@ -25,6 +26,8 @@ void WebViewDeviceAccountsProviderImpl::GetAccessToken(
const std::string& client_id, const std::string& client_id,
const std::set<std::string>& scopes, const std::set<std::string>& scopes,
AccessTokenCallback callback) { AccessTokenCallback callback) {
DCHECK(CWVSyncController.dataSource);
NSMutableArray<NSString*>* scopes_array = [NSMutableArray array]; NSMutableArray<NSString*>* scopes_array = [NSMutableArray array];
for (const auto& scope : scopes) { for (const auto& scope : scopes) {
[scopes_array addObject:base::SysUTF8ToNSString(scope)]; [scopes_array addObject:base::SysUTF8ToNSString(scope)];
...@@ -51,6 +54,8 @@ void WebViewDeviceAccountsProviderImpl::GetAccessToken( ...@@ -51,6 +54,8 @@ void WebViewDeviceAccountsProviderImpl::GetAccessToken(
std::vector<DeviceAccountsProvider::AccountInfo> std::vector<DeviceAccountsProvider::AccountInfo>
WebViewDeviceAccountsProviderImpl::GetAllAccounts() const { WebViewDeviceAccountsProviderImpl::GetAllAccounts() const {
DCHECK(CWVSyncController.dataSource);
NSArray<CWVIdentity*>* identities = NSArray<CWVIdentity*>* identities =
[CWVSyncController.dataSource allKnownIdentities]; [CWVSyncController.dataSource allKnownIdentities];
std::vector<AccountInfo> account_infos; std::vector<AccountInfo> account_infos;
...@@ -67,6 +72,30 @@ AuthenticationErrorCategory ...@@ -67,6 +72,30 @@ AuthenticationErrorCategory
WebViewDeviceAccountsProviderImpl::GetAuthenticationErrorCategory( WebViewDeviceAccountsProviderImpl::GetAuthenticationErrorCategory(
const std::string& gaia_id, const std::string& gaia_id,
NSError* error) const { NSError* error) const {
// TODO(crbug.com/780937): Implement fully. DCHECK(CWVSyncController.dataSource);
return kAuthenticationErrorCategoryUnknownErrors;
CWVIdentity* identity =
[[CWVIdentity alloc] initWithEmail:nil
fullName:nil
gaiaID:base::SysUTF8ToNSString(gaia_id)];
CWVSyncError sync_error =
[CWVSyncController.dataSource syncErrorForNSError:error
identity:identity];
switch (sync_error) {
case CWVSyncErrorNone:
NOTREACHED();
return kAuthenticationErrorCategoryUnknownErrors;
case CWVSyncErrorInvalidGAIACredentials:
return kAuthenticationErrorCategoryAuthorizationErrors;
case CWVSyncErrorUserNotSignedUp:
return kAuthenticationErrorCategoryUnknownIdentityErrors;
case CWVSyncErrorConnectionFailed:
return kAuthenticationErrorCategoryNetworkServerErrors;
case CWVSyncErrorServiceUnavailable:
return kAuthenticationErrorCategoryAuthorizationForbiddenErrors;
case CWVSyncErrorRequestCanceled:
return kAuthenticationErrorCategoryUserCancellationErrors;
case CWVSyncErrorUnexpectedServiceResponse:
return kAuthenticationErrorCategoryUnknownErrors;
}
} }
// Copyright 2020 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.
#import "ios/web_view/internal/signin/web_view_device_accounts_provider_impl.h"
#import <Foundation/Foundation.h>
#include <map>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/test/bind_test_util.h"
#include "components/signin/public/identity_manager/ios/device_accounts_provider.h"
#import "ios/web_view/public/cwv_identity.h"
#import "ios/web_view/public/cwv_sync_controller.h"
#import "ios/web_view/public/cwv_sync_controller_data_source.h"
#import "ios/web_view/public/cwv_sync_errors.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ios_web_view {
using WebViewDeviceAccountsProviderImplTest = PlatformTest;
// Tests that access tokens are fetched and returned.
TEST_F(WebViewDeviceAccountsProviderImplTest, GetAccessToken) {
id data_source =
OCMStrictProtocolMock(@protocol(CWVSyncControllerDataSource));
CWVSyncController.dataSource = data_source;
OCMExpect(([data_source
fetchAccessTokenForIdentity:[OCMArg checkWithBlock:^BOOL(
CWVIdentity* identity) {
return [identity.gaiaID isEqualToString:@"gaia-id"];
}]
scopes:@[ @"scope-1", @"scope-2" ]
completionHandler:[OCMArg checkWithBlock:^(void (^completion)(
NSString*, NSDate*, NSError*)) {
completion(@"access-token", NSDate.distantFuture, nil);
return YES;
}]]));
bool callback_called = false;
WebViewDeviceAccountsProviderImpl accounts_provider;
accounts_provider.GetAccessToken(
"gaia-id", "client-id", {"scope-1", "scope-2"},
base::BindLambdaForTesting(
[&](NSString* access_token, NSDate* expiration_date, NSError* error) {
callback_called = true;
EXPECT_NSEQ(@"access-token", access_token);
EXPECT_NSEQ(NSDate.distantFuture, expiration_date);
EXPECT_FALSE(error);
}));
EXPECT_TRUE(callback_called);
[data_source verify];
}
// Tests that all device accounts are properly returned.
TEST_F(WebViewDeviceAccountsProviderImplTest, GetAllAccounts) {
id data_source =
OCMStrictProtocolMock(@protocol(CWVSyncControllerDataSource));
CWVSyncController.dataSource = data_source;
CWVIdentity* identity = [[CWVIdentity alloc] initWithEmail:@"foo@chromium.org"
fullName:nil
gaiaID:@"gaia-id"];
OCMExpect([data_source allKnownIdentities]).andReturn(@[ identity ]);
WebViewDeviceAccountsProviderImpl accounts_provider;
std::vector<DeviceAccountsProvider::AccountInfo> accounts =
accounts_provider.GetAllAccounts();
ASSERT_EQ(1UL, accounts.size());
DeviceAccountsProvider::AccountInfo account_info = accounts[0];
EXPECT_EQ("foo@chromium.org", account_info.email);
EXPECT_EQ("gaia-id", account_info.gaia);
[data_source verify];
}
// Tests that authentication error categories are properly mapped.
TEST_F(WebViewDeviceAccountsProviderImplTest, GetAuthenticationErrorCategory) {
id data_source =
OCMStrictProtocolMock(@protocol(CWVSyncControllerDataSource));
CWVSyncController.dataSource = data_source;
std::map<CWVSyncError, AuthenticationErrorCategory> error_mapping = {
{CWVSyncErrorInvalidGAIACredentials,
kAuthenticationErrorCategoryAuthorizationErrors},
{CWVSyncErrorUserNotSignedUp,
kAuthenticationErrorCategoryUnknownIdentityErrors},
{CWVSyncErrorConnectionFailed,
kAuthenticationErrorCategoryNetworkServerErrors},
{CWVSyncErrorServiceUnavailable,
kAuthenticationErrorCategoryAuthorizationForbiddenErrors},
{CWVSyncErrorRequestCanceled,
kAuthenticationErrorCategoryUserCancellationErrors},
{CWVSyncErrorUnexpectedServiceResponse,
kAuthenticationErrorCategoryUnknownErrors}};
NSError* error = [NSError errorWithDomain:@"TestErrorDomain"
code:-100
userInfo:nil];
WebViewDeviceAccountsProviderImpl accounts_provider;
for (const auto& pair : error_mapping) {
OCMExpect([data_source
syncErrorForNSError:error
identity:[OCMArg checkWithBlock:^BOOL(
CWVIdentity* identity) {
return
[identity.gaiaID isEqualToString:@"gaia-id"];
}]])
.andReturn(pair.first);
EXPECT_EQ(pair.second, accounts_provider.GetAuthenticationErrorCategory(
"gaia-id", error));
}
[data_source verify];
}
}
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#import "ios/web_view/public/cwv_identity.h" #import "ios/web_view/public/cwv_identity.h"
#import "ios/web_view/public/cwv_sync_controller_data_source.h" #import "ios/web_view/public/cwv_sync_controller_data_source.h"
#import "ios/web_view/public/cwv_sync_controller_delegate.h" #import "ios/web_view/public/cwv_sync_controller_delegate.h"
#import "ios/web_view/public/cwv_sync_errors.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
......
...@@ -15,40 +15,6 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -15,40 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
@protocol CWVSyncControllerDataSource; @protocol CWVSyncControllerDataSource;
@protocol CWVSyncControllerDelegate; @protocol CWVSyncControllerDelegate;
// The error domain for sync errors.
FOUNDATION_EXPORT CWV_EXPORT NSErrorDomain const CWVSyncErrorDomain;
// NSString description for the type of error.
FOUNDATION_EXPORT CWV_EXPORT
NSErrorUserInfoKey const CWVSyncErrorDescriptionKey;
// NSString message describing the error in more detail.
FOUNDATION_EXPORT CWV_EXPORT NSErrorUserInfoKey const CWVSyncErrorMessageKey;
// NSValue wrapped BOOL indicating if the error is transient.
FOUNDATION_EXPORT CWV_EXPORT
NSErrorUserInfoKey const CWVSyncErrorIsTransientKey;
// Possible error codes during syncing.
typedef NS_ENUM(NSInteger, CWVSyncError) {
// No error.
CWVSyncErrorNone = 0,
// The credentials supplied to GAIA were either invalid, or the locally
// cached credentials have expired.
CWVSyncErrorInvalidGAIACredentials = -100,
// The GAIA user is not authorized to use the service.
CWVSyncErrorUserNotSignedUp = -200,
// Could not connect to server to verify credentials. This could be in
// response to either failure to connect to GAIA or failure to connect to
// the service needing GAIA tokens during authentication.
CWVSyncErrorConnectionFailed = -300,
// The service is not available; try again later.
CWVSyncErrorServiceUnavailable = -400,
// The requestor of the authentication step cancelled the request
// prior to completion.
CWVSyncErrorRequestCanceled = -500,
// Indicates the service responded to a request, but we cannot
// interpret the response.
CWVSyncErrorUnexpectedServiceResponse = -600,
};
// Used to manage syncing for autofill and password data. Usage: // Used to manage syncing for autofill and password data. Usage:
// 1. Set the |dataSource| and |delegate|. // 1. Set the |dataSource| and |delegate|.
// 2. Call |startSyncWithIdentity:| to start syncing with identity. // 2. Call |startSyncWithIdentity:| to start syncing with identity.
......
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import "cwv_export.h"
#import "cwv_sync_errors.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@class CWVIdentity; @class CWVIdentity;
...@@ -31,6 +34,13 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -31,6 +34,13 @@ NS_ASSUME_NONNULL_BEGIN
// become stale and need to be removed. // become stale and need to be removed.
- (NSArray<CWVIdentity*>*)allKnownIdentities; - (NSArray<CWVIdentity*>*)allKnownIdentities;
// Used to map a NSError for |identity| to its closest CWVSyncError equivalent.
// If |fetchAccessTokenForIdentity:scopes:completionHandler|'s completion is
// called with an error, this delegate method will be called to allow the client
// inform the library of the type of error it is.
- (CWVSyncError)syncErrorForNSError:(NSError*)error
identity:(CWVIdentity*)identity;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
......
...@@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
// property to see if |unlockWithPassphrase:| is necessary. // property to see if |unlockWithPassphrase:| is necessary.
- (void)syncControllerDidStartSync:(CWVSyncController*)syncController; - (void)syncControllerDidStartSync:(CWVSyncController*)syncController;
// Called when sync fails. |error|'s code is a CWVSyncError. // Called when sync fails. |error| details are described in cwv_sync_errors.h.
// May need to call |stopSyncAndClearIdentity| and try starting again later. // May need to call |stopSyncAndClearIdentity| and try starting again later.
- (void)syncController:(CWVSyncController*)syncController - (void)syncController:(CWVSyncController*)syncController
didFailWithError:(NSError*)error; didFailWithError:(NSError*)error;
......
// Copyright 2020 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 IOS_WEB_VIEW_PUBLIC_CWV_SYNC_ERRORS_H_
#define IOS_WEB_VIEW_PUBLIC_CWV_SYNC_ERRORS_H_
#import <Foundation/Foundation.h>
#import "cwv_export.h"
NS_ASSUME_NONNULL_BEGIN
// The error domain for sync errors.
FOUNDATION_EXPORT CWV_EXPORT NSErrorDomain const CWVSyncErrorDomain;
// NSString description for the type of error.
FOUNDATION_EXPORT CWV_EXPORT
NSErrorUserInfoKey const CWVSyncErrorDescriptionKey;
// NSString message describing the error in more detail.
FOUNDATION_EXPORT CWV_EXPORT NSErrorUserInfoKey const CWVSyncErrorMessageKey;
// NSValue wrapped BOOL indicating if the error is transient.
FOUNDATION_EXPORT CWV_EXPORT
NSErrorUserInfoKey const CWVSyncErrorIsTransientKey;
// Possible error codes during syncing.
typedef NS_ENUM(NSInteger, CWVSyncError) {
// No error.
CWVSyncErrorNone = 0,
// The credentials supplied to GAIA were either invalid, or the locally
// cached credentials have expired.
CWVSyncErrorInvalidGAIACredentials = -100,
// The GAIA user is not authorized to use the service.
CWVSyncErrorUserNotSignedUp = -200,
// Could not connect to server to verify credentials. This could be in
// response to either failure to connect to GAIA or failure to connect to
// the service needing GAIA tokens during authentication.
CWVSyncErrorConnectionFailed = -300,
// The service is not available; try again later.
CWVSyncErrorServiceUnavailable = -400,
// The requestor of the authentication step cancelled the request
// prior to completion.
CWVSyncErrorRequestCanceled = -500,
// Indicates the service responded to a request, but we cannot
// interpret the response.
CWVSyncErrorUnexpectedServiceResponse = -600,
};
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_PUBLIC_CWV_SYNC_ERRORS_H_
...@@ -37,4 +37,9 @@ ...@@ -37,4 +37,9 @@
return [self identities]; return [self identities];
} }
- (CWVSyncError)syncErrorForNSError:(NSError*)error
identity:(CWVIdentity*)identity {
return CWVSyncErrorUnexpectedServiceResponse;
}
@end @end
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