1 // Copyright 2014 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 "chrome/browser/chromeos/login/signin/signin_error_notifier_ash.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <string>
11
12 #include "base/memory/ptr_util.h"
13 #include "base/stl_util.h"
14 #include "build/build_config.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chromeos/login/signin/signin_error_notifier_factory_ash.h"
17 #include "chrome/browser/chromeos/login/users/mock_user_manager.h"
18 #include "chrome/browser/notifications/notification_display_service_tester.h"
19 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
20 #include "chrome/browser/supervised_user/supervised_user_constants.h"
21 #include "chrome/browser/supervised_user/supervised_user_service.h"
22 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
23 #include "chrome/common/pref_names.h"
24 #include "chrome/test/base/browser_with_test_window_test.h"
25 #include "chrome/test/base/testing_profile.h"
26 #include "components/prefs/pref_service.h"
27 #include "components/signin/public/identity_manager/identity_test_environment.h"
28 #include "components/signin/public/identity_manager/identity_test_utils.h"
29 #include "components/user_manager/scoped_user_manager.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "ui/message_center/public/cpp/notification.h"
32
33 namespace {
34
35 const char kTestEmail[] = "email@example.com";
36 const char kTestSecondaryEmail[] = "email2@example.com";
37
38 // Notification ID corresponding to kProfileSigninNotificationId +
39 // kTestAccountId.
40 const char kPrimaryAccountErrorNotificationId[] =
41 "chrome://settings/signin/testing_profile";
42 const char kSecondaryAccountErrorNotificationId[] =
43 "chrome://settings/signin/testing_profile/secondary-account";
44
45 class SigninErrorNotifierTest : public BrowserWithTestWindowTest {
46 public:
SetUp()47 void SetUp() override {
48 BrowserWithTestWindowTest::SetUp();
49
50 mock_user_manager_ = new chromeos::MockUserManager();
51 user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
52 base::WrapUnique(mock_user_manager_));
53
54 SigninErrorNotifierFactory::GetForProfile(GetProfile());
55 display_service_ =
56 std::make_unique<NotificationDisplayServiceTester>(profile());
57
58 identity_test_env_profile_adaptor_ =
59 std::make_unique<IdentityTestEnvironmentProfileAdaptor>(GetProfile());
60 }
61
TearDown()62 void TearDown() override {
63 // Need to be destroyed before the profile associated to this test, which
64 // will be destroyed as part of the TearDown() process.
65 identity_test_env_profile_adaptor_.reset();
66
67 BrowserWithTestWindowTest::TearDown();
68 }
69
GetTestingFactories()70 TestingProfile::TestingFactories GetTestingFactories() override {
71 return IdentityTestEnvironmentProfileAdaptor::
72 GetIdentityTestEnvironmentFactories();
73 }
74
SetAuthError(const CoreAccountId & account_id,const GoogleServiceAuthError & error)75 void SetAuthError(const CoreAccountId& account_id,
76 const GoogleServiceAuthError& error) {
77 signin::UpdatePersistentErrorOfRefreshTokenForAccount(
78 identity_test_env()->identity_manager(), account_id, error);
79 }
80
identity_test_env()81 signin::IdentityTestEnvironment* identity_test_env() {
82 return identity_test_env_profile_adaptor_->identity_test_env();
83 }
84
85 protected:
86 std::unique_ptr<NotificationDisplayServiceTester> display_service_;
87 chromeos::MockUserManager* mock_user_manager_; // Not owned.
88 std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
89 std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
90 identity_test_env_profile_adaptor_;
91 };
92
TEST_F(SigninErrorNotifierTest,NoNotification)93 TEST_F(SigninErrorNotifierTest, NoNotification) {
94 EXPECT_FALSE(
95 display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
96 EXPECT_FALSE(
97 display_service_->GetNotification(kSecondaryAccountErrorNotificationId));
98 }
99
100 // Verify that if Supervision has just been added for the current user
101 // the notification isn't shown. This is because the Add Supervision
102 // flow itself will prompt the user to sign out, so the notification
103 // is unnecessary.
TEST_F(SigninErrorNotifierTest,NoNotificationAfterAddSupervisionEnabled)104 TEST_F(SigninErrorNotifierTest, NoNotificationAfterAddSupervisionEnabled) {
105 CoreAccountId account_id =
106 identity_test_env()->MakeAccountAvailable(kTestEmail).account_id;
107 identity_test_env()->SetPrimaryAccount(kTestEmail);
108
109 // Mark signout required.
110 SupervisedUserService* service =
111 SupervisedUserServiceFactory::GetForProfile(profile());
112 service->set_signout_required_after_supervision_enabled();
113
114 SetAuthError(
115 identity_test_env()->identity_manager()->GetPrimaryAccountId(),
116 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
117
118 EXPECT_FALSE(
119 display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
120 }
121
TEST_F(SigninErrorNotifierTest,ErrorResetForPrimaryAccount)122 TEST_F(SigninErrorNotifierTest, ErrorResetForPrimaryAccount) {
123 EXPECT_FALSE(
124 display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
125
126 CoreAccountId account_id =
127 identity_test_env()->MakePrimaryAccountAvailable(kTestEmail).account_id;
128 SetAuthError(
129 account_id,
130 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
131 EXPECT_TRUE(
132 display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
133
134 SetAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
135 EXPECT_FALSE(
136 display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
137 }
138
TEST_F(SigninErrorNotifierTest,ErrorShownForUnconsentedPrimaryAccount)139 TEST_F(SigninErrorNotifierTest, ErrorShownForUnconsentedPrimaryAccount) {
140 EXPECT_FALSE(
141 display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
142
143 CoreAccountId account_id =
144 identity_test_env()
145 ->MakeUnconsentedPrimaryAccountAvailable(kTestEmail)
146 .account_id;
147 SetAuthError(
148 account_id,
149 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
150 EXPECT_TRUE(
151 display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
152
153 SetAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
154 EXPECT_FALSE(
155 display_service_->GetNotification(kPrimaryAccountErrorNotificationId));
156 }
157
TEST_F(SigninErrorNotifierTest,ErrorResetForSecondaryAccount)158 TEST_F(SigninErrorNotifierTest, ErrorResetForSecondaryAccount) {
159 EXPECT_FALSE(
160 display_service_->GetNotification(kSecondaryAccountErrorNotificationId));
161
162 CoreAccountId account_id =
163 identity_test_env()->MakeAccountAvailable(kTestEmail).account_id;
164 SetAuthError(
165 account_id,
166 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
167 // Uses the run loop from `BrowserTaskEnvironment`.
168 base::RunLoop().RunUntilIdle();
169 EXPECT_TRUE(
170 display_service_->GetNotification(kSecondaryAccountErrorNotificationId));
171
172 SetAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
173 EXPECT_FALSE(
174 display_service_->GetNotification(kSecondaryAccountErrorNotificationId));
175 }
176
TEST_F(SigninErrorNotifierTest,ErrorTransitionForPrimaryAccount)177 TEST_F(SigninErrorNotifierTest, ErrorTransitionForPrimaryAccount) {
178 CoreAccountId account_id =
179 identity_test_env()->MakePrimaryAccountAvailable(kTestEmail).account_id;
180 SetAuthError(
181 account_id,
182 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
183
184 base::Optional<message_center::Notification> notification =
185 display_service_->GetNotification(kPrimaryAccountErrorNotificationId);
186 ASSERT_TRUE(notification);
187 base::string16 message = notification->message();
188 EXPECT_FALSE(message.empty());
189
190 // Now set another auth error.
191 SetAuthError(account_id,
192 GoogleServiceAuthError(
193 GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE));
194
195 notification =
196 display_service_->GetNotification(kPrimaryAccountErrorNotificationId);
197 ASSERT_TRUE(notification);
198 base::string16 new_message = notification->message();
199 EXPECT_FALSE(new_message.empty());
200
201 ASSERT_NE(new_message, message);
202 }
203
204 // Verify that SigninErrorNotifier ignores certain errors.
TEST_F(SigninErrorNotifierTest,AuthStatusEnumerateAllErrors)205 TEST_F(SigninErrorNotifierTest, AuthStatusEnumerateAllErrors) {
206 typedef struct {
207 GoogleServiceAuthError::State error_state;
208 bool is_error;
209 } ErrorTableEntry;
210
211 ErrorTableEntry table[] = {
212 {GoogleServiceAuthError::NONE, false},
213 {GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS, true},
214 {GoogleServiceAuthError::USER_NOT_SIGNED_UP, true},
215 {GoogleServiceAuthError::CONNECTION_FAILED, false},
216 {GoogleServiceAuthError::SERVICE_UNAVAILABLE, false},
217 {GoogleServiceAuthError::REQUEST_CANCELED, false},
218 {GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE, true},
219 {GoogleServiceAuthError::SERVICE_ERROR, true},
220 };
221 static_assert(
222 base::size(table) == GoogleServiceAuthError::NUM_STATES -
223 GoogleServiceAuthError::kDeprecatedStateCount,
224 "table size should match number of auth error types");
225 CoreAccountId account_id =
226 identity_test_env()->MakePrimaryAccountAvailable(kTestEmail).account_id;
227
228 for (size_t i = 0; i < base::size(table); ++i) {
229 SetAuthError(account_id, GoogleServiceAuthError(table[i].error_state));
230 base::Optional<message_center::Notification> notification =
231 display_service_->GetNotification(kPrimaryAccountErrorNotificationId);
232 ASSERT_EQ(table[i].is_error, !!notification) << "Failed case #" << i;
233 if (table[i].is_error) {
234 EXPECT_FALSE(notification->title().empty());
235 EXPECT_FALSE(notification->message().empty());
236 EXPECT_EQ((size_t)1, notification->buttons().size());
237 }
238 SetAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
239 }
240 }
241
TEST_F(SigninErrorNotifierTest,ChildSecondaryAccountMigrationTest)242 TEST_F(SigninErrorNotifierTest, ChildSecondaryAccountMigrationTest) {
243 CoreAccountId primary_account =
244 identity_test_env()->MakePrimaryAccountAvailable(kTestEmail).account_id;
245 CoreAccountId secondary_account =
246 identity_test_env()->MakeAccountAvailable(kTestSecondaryEmail).account_id;
247
248 // Mark the profile as a child user.
249 GetProfile()->SetSupervisedUserId(supervised_users::kChildAccountSUID);
250 base::RunLoop().RunUntilIdle();
251
252 // Invalidate the secondary account.
253 SetAuthError(
254 secondary_account,
255 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
256
257 // Expect that there is a notification, accounts didn't migrate yet.
258 base::Optional<message_center::Notification> notification =
259 display_service_->GetNotification(kSecondaryAccountErrorNotificationId);
260 ASSERT_TRUE(notification);
261 base::string16 message = notification->message();
262 EXPECT_FALSE(message.empty());
263
264 // Clear error.
265 SetAuthError(secondary_account, GoogleServiceAuthError::AuthErrorNone());
266 EXPECT_FALSE(
267 display_service_->GetNotification(kSecondaryAccountErrorNotificationId));
268
269 // Mark secondary account as migrated, message should be different.
270 profile()->GetPrefs()->SetBoolean(prefs::kEduCoexistenceArcMigrationCompleted,
271 true);
272
273 // Invalidate the secondary account.
274 SetAuthError(
275 secondary_account,
276 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
277 notification =
278 display_service_->GetNotification(kSecondaryAccountErrorNotificationId);
279 ASSERT_TRUE(notification);
280 base::string16 new_message = notification->message();
281 EXPECT_NE(new_message, message);
282 }
283
284 } // namespace
285