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