Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / signin / signin_header_helper.cc
blob03872021ed57cb923a3951d244d816fedcc58a4e
1 // Copyright 2013 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.
5 #include "chrome/browser/signin/signin_header_helper.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "chrome/browser/prefs/incognito_mode_prefs.h"
11 #include "chrome/browser/profiles/profile_io_data.h"
12 #include "chrome/browser/signin/chrome_signin_client.h"
13 #include "chrome/browser/tab_contents/tab_util.h"
14 #include "chrome/browser/ui/browser_window.h"
15 #include "chrome/common/url_constants.h"
16 #include "components/google/core/browser/google_util.h"
17 #include "components/signin/core/common/profile_management_switches.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/resource_request_info.h"
20 #include "content/public/browser/web_contents.h"
21 #include "google_apis/gaia/gaia_auth_util.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/url_request/url_request.h"
25 #if defined(OS_ANDROID)
26 #include "chrome/browser/android/signin/account_management_screen_helper.h"
27 #elif !defined(OS_IOS)
28 #include "chrome/browser/ui/browser_commands.h"
29 #include "chrome/browser/ui/browser_finder.h"
30 #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
31 #endif // defined(OS_ANDROID)
33 namespace {
35 // Dictionary of fields in a mirror response header.
36 typedef std::map<std::string, std::string> MirrorResponseHeaderDictionary;
38 const char kChromeConnectedHeader[] = "X-Chrome-Connected";
39 const char kChromeManageAccountsHeader[] = "X-Chrome-Manage-Accounts";
40 const char kGaiaIdAttrName[] = "id";
41 const char kProfileModeAttrName[] = "mode";
42 const char kEnableAccountConsistencyAttrName[] = "enable_account_consistency";
44 const char kServiceTypeAttrName[] = "action";
45 const char kEmailAttrName[] = "email";
46 const char kIsSamlAttrName[] = "is_saml";
47 const char kContinueUrlAttrName[] = "continue_url";
48 const char kIsSameTabAttrName[] = "is_same_tab";
50 // Determines the service type that has been passed from GAIA in the header.
51 signin::GAIAServiceType GetGAIAServiceTypeFromHeader(
52 const std::string& header_value) {
53 if (header_value == "SIGNOUT")
54 return signin::GAIA_SERVICE_TYPE_SIGNOUT;
55 else if (header_value == "INCOGNITO")
56 return signin::GAIA_SERVICE_TYPE_INCOGNITO;
57 else if (header_value == "ADDSESSION")
58 return signin::GAIA_SERVICE_TYPE_ADDSESSION;
59 else if (header_value == "REAUTH")
60 return signin::GAIA_SERVICE_TYPE_REAUTH;
61 else if (header_value == "SIGNUP")
62 return signin::GAIA_SERVICE_TYPE_SIGNUP;
63 else if (header_value == "DEFAULT")
64 return signin::GAIA_SERVICE_TYPE_DEFAULT;
65 else
66 return signin::GAIA_SERVICE_TYPE_NONE;
69 // Parses the mirror response header. Its expected format is
70 // "key1=value1,key2=value2,...".
71 MirrorResponseHeaderDictionary ParseMirrorResponseHeader(
72 const std::string& header_value) {
73 std::vector<std::string> fields;
74 if (!Tokenize(header_value, std::string(","), &fields))
75 return MirrorResponseHeaderDictionary();
77 MirrorResponseHeaderDictionary dictionary;
78 for (std::vector<std::string>::iterator i = fields.begin();
79 i != fields.end(); ++i) {
80 std::string field(*i);
81 std::vector<std::string> tokens;
82 size_t delim = field.find_first_of('=');
83 if (delim == std::string::npos) {
84 DLOG(WARNING) << "Unexpected GAIA header field '" << field << "'.";
85 continue;
87 dictionary[field.substr(0, delim)] = net::UnescapeURLComponent(
88 field.substr(delim + 1), net::UnescapeRule::URL_SPECIAL_CHARS);
90 return dictionary;
93 // Returns the parameters contained in the X-Chrome-Manage-Accounts response
94 // header.
95 signin::ManageAccountsParams BuildManageAccountsParams(
96 const std::string& header_value) {
97 signin::ManageAccountsParams params;
98 MirrorResponseHeaderDictionary header_dictionary =
99 ParseMirrorResponseHeader(header_value);
100 MirrorResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
101 for (; it != header_dictionary.end(); ++it) {
102 const std::string key_name(it->first);
103 if (key_name == kServiceTypeAttrName) {
104 params.service_type =
105 GetGAIAServiceTypeFromHeader(header_dictionary[kServiceTypeAttrName]);
106 } else if (key_name == kEmailAttrName) {
107 params.email = header_dictionary[kEmailAttrName];
108 } else if (key_name == kIsSamlAttrName) {
109 params.is_saml = header_dictionary[kIsSamlAttrName] == "true";
110 } else if (key_name == kContinueUrlAttrName) {
111 params.continue_url = header_dictionary[kContinueUrlAttrName];
112 } else if (key_name == kIsSameTabAttrName) {
113 params.is_same_tab = header_dictionary[kIsSameTabAttrName] == "true";
114 } else {
115 DLOG(WARNING) << "Unexpected GAIA header attribute '" << key_name << "'.";
118 return params;
121 #if !defined(OS_IOS)
122 // Processes the mirror response header on the UI thread. Currently depending
123 // on the value of |header_value|, it either shows the profile avatar menu, or
124 // opens an incognito window/tab.
125 void ProcessMirrorHeaderUIThread(
126 int child_id, int route_id,
127 signin::ManageAccountsParams manage_accounts_params) {
128 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
130 signin::GAIAServiceType service_type = manage_accounts_params.service_type;
131 DCHECK_NE(signin::GAIA_SERVICE_TYPE_NONE, service_type);
133 content::WebContents* web_contents =
134 tab_util::GetWebContentsByID(child_id, route_id);
135 if (!web_contents)
136 return;
138 #if !defined(OS_ANDROID)
139 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
140 if (browser) {
141 BrowserWindow::AvatarBubbleMode bubble_mode;
142 switch (service_type) {
143 case signin::GAIA_SERVICE_TYPE_INCOGNITO:
144 chrome::NewIncognitoWindow(browser);
145 return;
146 case signin::GAIA_SERVICE_TYPE_ADDSESSION:
147 bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_ADD_ACCOUNT;
148 break;
149 case signin::GAIA_SERVICE_TYPE_REAUTH:
150 bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH;
151 break;
152 default:
153 bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_ACCOUNT_MANAGEMENT;
155 browser->window()->ShowAvatarBubbleFromAvatarButton(
156 bubble_mode, manage_accounts_params);
158 #else // defined(OS_ANDROID)
159 if (service_type == signin::GAIA_SERVICE_TYPE_INCOGNITO) {
160 GURL url(manage_accounts_params.continue_url.empty() ?
161 chrome::kChromeUINativeNewTabURL :
162 manage_accounts_params.continue_url);
163 web_contents->OpenURL(content::OpenURLParams(
164 url, content::Referrer(), OFF_THE_RECORD,
165 ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false));
166 } else {
167 AccountManagementScreenHelper::OpenAccountManagementScreen(
168 Profile::FromBrowserContext(web_contents->GetBrowserContext()),
169 service_type);
171 #endif // OS_ANDROID
173 #endif // !defined(OS_IOS)
175 bool IsDriveOrigin(const GURL& url) {
176 if (!url.SchemeIsSecure())
177 return false;
179 const GURL kGoogleDriveURL("https://drive.google.com");
180 const GURL kGoogleDocsURL("https://docs.google.com");
181 return url == kGoogleDriveURL || url == kGoogleDocsURL;
184 } // empty namespace
186 namespace signin {
188 ManageAccountsParams::ManageAccountsParams() :
189 service_type(GAIA_SERVICE_TYPE_NONE),
190 email(""),
191 is_saml(false),
192 continue_url(""),
193 is_same_tab(false),
194 child_id(0),
195 route_id(0) {}
197 bool AppendMirrorRequestHeaderIfPossible(
198 net::URLRequest* request,
199 const GURL& redirect_url,
200 ProfileIOData* io_data,
201 int child_id,
202 int route_id) {
203 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
205 if (io_data->IsOffTheRecord())
206 return false;
208 std::string account_id(io_data->google_services_account_id()->GetValue());
210 if (account_id.empty())
211 return false;
213 // If signin cookies are not allowed, don't add the header.
214 if (!ChromeSigninClient::SettingsAllowSigninCookies(
215 io_data->GetCookieSettings())) {
216 return false;
219 // Only set the header for Drive and Gaia always, and other Google properties
220 // if account consistency is enabled.
221 // Vasquette, which is integrated with most Google properties, needs the
222 // header to redirect certain user actions to Chrome native UI. Drive and Gaia
223 // need the header to tell if the current user is connected. The drive path is
224 // a temporary workaround until the more generic chrome.principals API is
225 // available.
226 const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url;
227 GURL origin(url.GetOrigin());
228 bool is_enable_account_consistency = switches::IsEnableAccountConsistency();
229 bool is_google_url =
230 is_enable_account_consistency &&
231 (google_util::IsGoogleDomainUrl(
232 url,
233 google_util::ALLOW_SUBDOMAIN,
234 google_util::DISALLOW_NON_STANDARD_PORTS) ||
235 google_util::IsYoutubeDomainUrl(
236 url,
237 google_util::ALLOW_SUBDOMAIN,
238 google_util::DISALLOW_NON_STANDARD_PORTS));
239 if (!is_google_url && !IsDriveOrigin(origin) &&
240 !gaia::IsGaiaSignonRealm(origin)) {
241 return false;
244 #if !defined(OS_ANDROID) && !defined(OS_IOS)
245 extensions::WebViewRendererState::WebViewInfo webview_info;
246 bool is_guest = extensions::WebViewRendererState::GetInstance()->GetInfo(
247 child_id, route_id, &webview_info);
248 // Do not set the x-chrome-connected header on requests from a native signin
249 // webview, as identified by an empty extension id which means the webview is
250 // embedded in a webui page, otherwise user may end up with a blank page as
251 // gaia uses the header to decide whether it returns 204 for certain end
252 // points.
253 if (is_guest && webview_info.owner_extension_id.empty())
254 return false;
255 #endif // !OS_ANDROID && !OS_IOS
257 int profile_mode_mask = PROFILE_MODE_DEFAULT;
258 if (io_data->incognito_availibility()->GetValue() ==
259 IncognitoModePrefs::DISABLED ||
260 IncognitoModePrefs::ArePlatformParentalControlsEnabled()) {
261 profile_mode_mask |= PROFILE_MODE_INCOGNITO_DISABLED;
264 std::string header_value(base::StringPrintf("%s=%s,%s=%s,%s=%s",
265 kGaiaIdAttrName, account_id.c_str(),
266 kProfileModeAttrName, base::IntToString(profile_mode_mask).c_str(),
267 kEnableAccountConsistencyAttrName,
268 is_enable_account_consistency ? "true" : "false"));
269 request->SetExtraRequestHeaderByName(
270 kChromeConnectedHeader, header_value, false);
271 return true;
274 void ProcessMirrorResponseHeaderIfExists(
275 net::URLRequest* request,
276 ProfileIOData* io_data,
277 int child_id,
278 int route_id) {
279 #if defined(OS_IOS)
280 NOTREACHED();
281 #else
282 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
283 if (!gaia::IsGaiaSignonRealm(request->url().GetOrigin()))
284 return;
286 const content::ResourceRequestInfo* info =
287 content::ResourceRequestInfo::ForRequest(request);
288 if (!(info && info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME))
289 return;
291 std::string header_value;
292 if (!request->response_headers()->GetNormalizedHeader(
293 kChromeManageAccountsHeader, &header_value)) {
294 return;
297 DCHECK(switches::IsEnableAccountConsistency() && !io_data->IsOffTheRecord());
298 ManageAccountsParams params(BuildManageAccountsParams(header_value));
299 if (params.service_type == GAIA_SERVICE_TYPE_NONE)
300 return;
302 params.child_id = child_id;
303 params.route_id = route_id;
304 content::BrowserThread::PostTask(
305 content::BrowserThread::UI, FROM_HERE,
306 base::Bind(ProcessMirrorHeaderUIThread, child_id, route_id, params));
307 #endif // defined(OS_IOS)
310 } // namespace signin