1 // Copyright 2016 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/supervised_user/supervised_user_google_auth_navigation_throttle.h"
6
7 #include "base/bind.h"
8 #include "base/memory/ptr_util.h"
9 #include "base/notreached.h"
10 #include "build/build_config.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/signin/identity_manager_factory.h"
13 #include "chrome/browser/supervised_user/child_accounts/child_account_service.h"
14 #include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
15 #include "components/google/core/common/google_util.h"
16 #include "components/signin/public/identity_manager/consent_level.h"
17 #include "components/signin/public/identity_manager/identity_manager.h"
18 #include "content/public/browser/navigation_handle.h"
19 #include "content/public/browser/web_contents.h"
20
21 #if defined(OS_ANDROID)
22 #include "chrome/browser/supervised_user/child_accounts/child_account_service_android.h"
23 #endif
24
25 // static
26 std::unique_ptr<SupervisedUserGoogleAuthNavigationThrottle>
MaybeCreate(content::NavigationHandle * navigation_handle)27 SupervisedUserGoogleAuthNavigationThrottle::MaybeCreate(
28 content::NavigationHandle* navigation_handle) {
29 Profile* profile = Profile::FromBrowserContext(
30 navigation_handle->GetWebContents()->GetBrowserContext());
31 if (!profile->IsChild())
32 return nullptr;
33
34 return base::WrapUnique(new SupervisedUserGoogleAuthNavigationThrottle(
35 profile, navigation_handle));
36 }
37
38 SupervisedUserGoogleAuthNavigationThrottle::
SupervisedUserGoogleAuthNavigationThrottle(Profile * profile,content::NavigationHandle * navigation_handle)39 SupervisedUserGoogleAuthNavigationThrottle(
40 Profile* profile,
41 content::NavigationHandle* navigation_handle)
42 : content::NavigationThrottle(navigation_handle),
43 child_account_service_(ChildAccountServiceFactory::GetForProfile(profile))
44 #if defined(OS_ANDROID)
45 ,
46 has_shown_reauth_(false)
47 #endif
48 {
49 }
50
51 SupervisedUserGoogleAuthNavigationThrottle::
52 ~SupervisedUserGoogleAuthNavigationThrottle() = default;
53
54 content::NavigationThrottle::ThrottleCheckResult
WillStartRequest()55 SupervisedUserGoogleAuthNavigationThrottle::WillStartRequest() {
56 return WillStartOrRedirectRequest();
57 }
58
59 content::NavigationThrottle::ThrottleCheckResult
WillRedirectRequest()60 SupervisedUserGoogleAuthNavigationThrottle::WillRedirectRequest() {
61 return WillStartOrRedirectRequest();
62 }
63
GetNameForLogging()64 const char* SupervisedUserGoogleAuthNavigationThrottle::GetNameForLogging() {
65 return "SupervisedUserGoogleAuthNavigationThrottle";
66 }
67
68 content::NavigationThrottle::ThrottleCheckResult
WillStartOrRedirectRequest()69 SupervisedUserGoogleAuthNavigationThrottle::WillStartOrRedirectRequest() {
70 const GURL& url = navigation_handle()->GetURL();
71 if (!google_util::IsGoogleSearchUrl(url) &&
72 !google_util::IsGoogleHomePageUrl(url) &&
73 !google_util::IsYoutubeDomainUrl(url, google_util::ALLOW_SUBDOMAIN,
74 google_util::ALLOW_NON_STANDARD_PORTS)) {
75 return content::NavigationThrottle::PROCEED;
76 }
77
78 content::NavigationThrottle::ThrottleCheckResult result = ShouldProceed();
79
80 if (result.action() == content::NavigationThrottle::DEFER) {
81 google_auth_state_subscription_ =
82 child_account_service_->ObserveGoogleAuthState(
83 base::Bind(&SupervisedUserGoogleAuthNavigationThrottle::
84 OnGoogleAuthStateChanged,
85 base::Unretained(this)));
86 }
87
88 return result;
89 }
90
OnGoogleAuthStateChanged()91 void SupervisedUserGoogleAuthNavigationThrottle::OnGoogleAuthStateChanged() {
92 content::NavigationThrottle::ThrottleCheckResult result = ShouldProceed();
93
94 switch (result.action()) {
95 case content::NavigationThrottle::PROCEED: {
96 google_auth_state_subscription_.reset();
97 Resume();
98 break;
99 }
100 case content::NavigationThrottle::CANCEL:
101 case content::NavigationThrottle::CANCEL_AND_IGNORE: {
102 CancelDeferredNavigation(result);
103 break;
104 }
105 case content::NavigationThrottle::DEFER: {
106 // Keep blocking.
107 break;
108 }
109 case content::NavigationThrottle::BLOCK_REQUEST:
110 case content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE:
111 case content::NavigationThrottle::BLOCK_RESPONSE: {
112 NOTREACHED();
113 }
114 }
115 }
116
117 content::NavigationThrottle::ThrottleCheckResult
ShouldProceed()118 SupervisedUserGoogleAuthNavigationThrottle::ShouldProceed() {
119 ChildAccountService::AuthState authStatus =
120 child_account_service_->GetGoogleAuthState();
121 if (authStatus == ChildAccountService::AuthState::AUTHENTICATED)
122 return content::NavigationThrottle::PROCEED;
123 if (authStatus == ChildAccountService::AuthState::PENDING)
124 return content::NavigationThrottle::DEFER;
125
126 #if defined(OS_CHROMEOS)
127 // A credentials re-mint is already underway when we reach here (Mirror
128 // account reconciliation). Nothing to do here except block the navigation
129 // while re-minting is underway.
130 return content::NavigationThrottle::DEFER;
131 #elif defined(OS_ANDROID)
132 if (!has_shown_reauth_) {
133 has_shown_reauth_ = true;
134
135 content::WebContents* web_contents = navigation_handle()->GetWebContents();
136 Profile* profile =
137 Profile::FromBrowserContext(web_contents->GetBrowserContext());
138 auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
139 // This class doesn't care about browser sync consent.
140 CoreAccountInfo account_info = identity_manager->GetPrimaryAccountInfo(
141 signin::ConsentLevel::kNotRequired);
142 if (account_info.IsEmpty()) {
143 // No primary account (can happen when it was removed from the device).
144 return content::NavigationThrottle::DEFER;
145 }
146
147 ReauthenticateChildAccount(
148 web_contents, account_info.email,
149 base::Bind(&SupervisedUserGoogleAuthNavigationThrottle::
150 OnReauthenticationResult,
151 weak_ptr_factory_.GetWeakPtr()));
152 }
153 return content::NavigationThrottle::DEFER;
154 #else
155 NOTREACHED();
156
157 // This should never happen but needs to be included to avoid compilation
158 // error on debug builds.
159 return content::NavigationThrottle::CANCEL_AND_IGNORE;
160 #endif
161 }
162
OnReauthenticationResult(bool reauth_successful)163 void SupervisedUserGoogleAuthNavigationThrottle::OnReauthenticationResult(
164 bool reauth_successful) {
165 if (reauth_successful) {
166 // If reauthentication was not successful, wait until the cookies are
167 // refreshed, which will call us back separately.
168 return;
169 }
170
171 // Otherwise cancel immediately.
172 CancelDeferredNavigation(content::NavigationThrottle::CANCEL_AND_IGNORE);
173 }
174