Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / signin / signin_header_helper.cc
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.
4
5 #include "chrome/browser/signin/signin_header_helper.h"
6
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/tab_contents/tab_util.h"
13 #include "chrome/browser/ui/browser_window.h"
14 #include "chrome/common/url_constants.h"
15 #include "components/google/core/browser/google_util.h"
16 #include "components/signin/core/common/profile_management_switches.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/resource_request_info.h"
19 #include "content/public/browser/web_contents.h"
20 #include "google_apis/gaia/gaia_auth_util.h"
21 #include "net/http/http_response_headers.h"
22 #include "net/url_request/url_request.h"
23
24 #if defined(OS_ANDROID)
25 #include "chrome/browser/android/signin/account_management_screen_helper.h"
26 #else
27 #include "chrome/browser/ui/browser_commands.h"
28 #include "chrome/browser/ui/browser_finder.h"
29 #endif  // defined(OS_ANDROID)
30
31 namespace {
32
33 // Dictionary of fields in a mirror response header.
34 typedef std::map<std::string, std::string> MirrorResponseHeaderDictionary;
35
36 const char kChromeConnectedHeader[] = "X-Chrome-Connected";
37 const char kChromeManageAccountsHeader[] = "X-Chrome-Manage-Accounts";
38 const char kGaiaIdAttrName[] = "id";
39 const char kProfileModeAttrName[] = "mode";
40 const char kEnableAccountConsistencyAttrName[] = "enable_account_consistency";
41
42 const char kServiceTypeAttrName[] = "action";
43 const char kEmailAttrName[] = "email";
44 const char kIsSamlAttrName[] = "is_saml";
45 const char kContinueUrlAttrName[] = "continue_url";
46 const char kIsSameTabAttrName[] = "is_same_tab";
47
48 // Determines the service type that has been passed from GAIA in the header.
49 signin::GAIAServiceType GetGAIAServiceTypeFromHeader(
50     const std::string& header_value) {
51   if (header_value == "SIGNOUT")
52     return signin::GAIA_SERVICE_TYPE_SIGNOUT;
53   else if (header_value == "INCOGNITO")
54     return signin::GAIA_SERVICE_TYPE_INCOGNITO;
55   else if (header_value == "ADDSESSION")
56     return signin::GAIA_SERVICE_TYPE_ADDSESSION;
57   else if (header_value == "REAUTH")
58     return signin::GAIA_SERVICE_TYPE_REAUTH;
59   else if (header_value == "SIGNUP")
60     return signin::GAIA_SERVICE_TYPE_SIGNUP;
61   else if (header_value == "DEFAULT")
62     return signin::GAIA_SERVICE_TYPE_DEFAULT;
63   else
64     return signin::GAIA_SERVICE_TYPE_NONE;
65 }
66
67 // Parses the mirror response header. Its expected format is
68 // "key1=value1,key2=value2,...".
69 MirrorResponseHeaderDictionary ParseMirrorResponseHeader(
70     const std::string& header_value) {
71   std::vector<std::string> fields;
72   if (!Tokenize(header_value, std::string(","), &fields))
73     return MirrorResponseHeaderDictionary();
74
75   MirrorResponseHeaderDictionary dictionary;
76   for (std::vector<std::string>::iterator i = fields.begin();
77        i != fields.end(); ++i) {
78     std::string field(*i);
79     std::vector<std::string> tokens;
80     size_t delim = field.find_first_of('=');
81     if (delim == std::string::npos) {
82       DLOG(WARNING) << "Unexpected GAIA header field '" << field << "'.";
83       continue;
84     }
85     dictionary[field.substr(0, delim)] = net::UnescapeURLComponent(
86         field.substr(delim + 1), net::UnescapeRule::URL_SPECIAL_CHARS);
87   }
88   return dictionary;
89 }
90
91 // Returns the parameters contained in the X-Chrome-Manage-Accounts response
92 // header.
93 signin::ManageAccountsParams BuildManageAccountsParams(
94     const std::string& header_value) {
95   signin::ManageAccountsParams params;
96   MirrorResponseHeaderDictionary header_dictionary =
97       ParseMirrorResponseHeader(header_value);
98   MirrorResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
99   for (; it != header_dictionary.end(); ++it) {
100     const std::string key_name(it->first);
101     if (key_name == kServiceTypeAttrName) {
102       params.service_type =
103           GetGAIAServiceTypeFromHeader(header_dictionary[kServiceTypeAttrName]);
104     } else if (key_name == kEmailAttrName) {
105       params.email = header_dictionary[kEmailAttrName];
106     } else if (key_name == kIsSamlAttrName) {
107       params.is_saml = header_dictionary[kIsSamlAttrName] == "true";
108     } else if (key_name == kContinueUrlAttrName) {
109       params.continue_url = header_dictionary[kContinueUrlAttrName];
110     } else if (key_name == kIsSameTabAttrName) {
111       params.is_same_tab = header_dictionary[kIsSameTabAttrName] == "true";
112     } else {
113       DLOG(WARNING) << "Unexpected GAIA header attribute '" << key_name << "'.";
114     }
115   }
116   return params;
117 }
118
119 #if !defined(OS_IOS)
120 // Processes the mirror response header on the UI thread. Currently depending
121 // on the value of |header_value|, it either shows the profile avatar menu, or
122 // opens an incognito window/tab.
123 void ProcessMirrorHeaderUIThread(
124     int child_id, int route_id,
125     signin::ManageAccountsParams manage_accounts_params) {
126   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
127
128   signin::GAIAServiceType service_type = manage_accounts_params.service_type;
129   DCHECK_NE(signin::GAIA_SERVICE_TYPE_NONE, service_type);
130
131   content::WebContents* web_contents =
132       tab_util::GetWebContentsByID(child_id, route_id);
133   if (!web_contents)
134     return;
135
136 #if !defined(OS_ANDROID)
137   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
138   if (browser) {
139     BrowserWindow::AvatarBubbleMode bubble_mode;
140     switch (service_type) {
141       case signin::GAIA_SERVICE_TYPE_INCOGNITO:
142         chrome::NewIncognitoWindow(browser);
143         return;
144       case signin::GAIA_SERVICE_TYPE_ADDSESSION:
145         bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_ADD_ACCOUNT;
146         break;
147       case signin::GAIA_SERVICE_TYPE_REAUTH:
148         bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH;
149         break;
150       default:
151         bubble_mode = BrowserWindow::AVATAR_BUBBLE_MODE_ACCOUNT_MANAGEMENT;
152     }
153     browser->window()->ShowAvatarBubbleFromAvatarButton(
154         bubble_mode, manage_accounts_params);
155   }
156 #else  // defined(OS_ANDROID)
157   if (service_type == signin::GAIA_SERVICE_TYPE_INCOGNITO) {
158     GURL url(manage_accounts_params.continue_url.empty() ?
159         chrome::kChromeUINativeNewTabURL :
160         manage_accounts_params.continue_url);
161     web_contents->OpenURL(content::OpenURLParams(
162         url, content::Referrer(), OFF_THE_RECORD,
163         ui::PAGE_TRANSITION_AUTO_TOPLEVEL, false));
164   } else {
165     AccountManagementScreenHelper::OpenAccountManagementScreen(
166         Profile::FromBrowserContext(web_contents->GetBrowserContext()),
167         service_type);
168   }
169 #endif // OS_ANDROID
170 }
171 #endif // !defined(OS_IOS)
172
173 bool IsDriveOrigin(const GURL& url) {
174   if (!url.SchemeIsSecure())
175     return false;
176
177   const GURL kGoogleDriveURL("https://drive.google.com");
178   const GURL kGoogleDocsURL("https://docs.google.com");
179   return url == kGoogleDriveURL || url == kGoogleDocsURL;
180 }
181
182 } // empty namespace
183
184 namespace signin {
185
186 ManageAccountsParams::ManageAccountsParams() :
187     service_type(GAIA_SERVICE_TYPE_NONE),
188     email(""),
189     is_saml(false),
190     continue_url(""),
191     is_same_tab(false),
192     child_id(0),
193     route_id(0) {}
194
195 bool AppendMirrorRequestHeaderIfPossible(
196     net::URLRequest* request,
197     const GURL& redirect_url,
198     ProfileIOData* io_data) {
199   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
200
201   if (io_data->IsOffTheRecord() ||
202       io_data->google_services_username()->GetValue().empty()) {
203     return false;
204   }
205
206   // Only set the header for Drive always, and other Google properties if
207   // new-profile-management is enabled.
208   // Vasquette, which is integrated with most Google properties, needs the
209   // header to redirect certain user actions to Chrome native UI. Drive needs
210   // the header to tell if the current user is connected. The drive path is a
211   // temporary workaround until the more generic chrome.principals API is
212   // available.
213   const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url;
214   GURL origin(url.GetOrigin());
215   bool is_enable_account_consistency = switches::IsEnableAccountConsistency();
216   bool is_google_url =
217       !switches::IsEnableWebBasedSignin() &&
218       is_enable_account_consistency &&
219       (google_util::IsGoogleDomainUrl(
220            url,
221            google_util::ALLOW_SUBDOMAIN,
222            google_util::DISALLOW_NON_STANDARD_PORTS) ||
223        google_util::IsYoutubeDomainUrl(
224            url,
225            google_util::ALLOW_SUBDOMAIN,
226            google_util::DISALLOW_NON_STANDARD_PORTS));
227   if (!is_google_url && !IsDriveOrigin(origin))
228     return false;
229
230   std::string account_id(io_data->google_services_account_id()->GetValue());
231
232   int profile_mode_mask = PROFILE_MODE_DEFAULT;
233   if (io_data->incognito_availibility()->GetValue() ==
234           IncognitoModePrefs::DISABLED ||
235       IncognitoModePrefs::ArePlatformParentalControlsEnabledCached()) {
236     profile_mode_mask |= PROFILE_MODE_INCOGNITO_DISABLED;
237   }
238
239   // TODO(guohui): needs to make a new flag for enabling account consistency.
240   std::string header_value(base::StringPrintf("%s=%s,%s=%s,%s=%s",
241       kGaiaIdAttrName, account_id.c_str(),
242       kProfileModeAttrName, base::IntToString(profile_mode_mask).c_str(),
243       kEnableAccountConsistencyAttrName,
244       is_enable_account_consistency ? "true" : "false"));
245   request->SetExtraRequestHeaderByName(
246       kChromeConnectedHeader, header_value, false);
247   return true;
248 }
249
250 void ProcessMirrorResponseHeaderIfExists(
251     net::URLRequest* request,
252     ProfileIOData* io_data,
253     int child_id,
254     int route_id) {
255 #if defined(OS_IOS)
256   NOTREACHED();
257 #else
258   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
259   if (!gaia::IsGaiaSignonRealm(request->url().GetOrigin()))
260     return;
261
262   const content::ResourceRequestInfo* info =
263       content::ResourceRequestInfo::ForRequest(request);
264   if (!(info && info->IsMainFrame() &&
265         (info->HasUserGesture() ||
266          !ui::PageTransitionIsWebTriggerable(info->GetPageTransition()))))
267     return;
268
269   std::string header_value;
270   if (!request->response_headers()->GetNormalizedHeader(
271           kChromeManageAccountsHeader, &header_value)) {
272     return;
273   }
274
275   DCHECK(switches::IsEnableAccountConsistency() && !io_data->IsOffTheRecord());
276   ManageAccountsParams params(BuildManageAccountsParams(header_value));
277   if (params.service_type == GAIA_SERVICE_TYPE_NONE)
278     return;
279
280   params.child_id = child_id;
281   params.route_id = route_id;
282   content::BrowserThread::PostTask(
283       content::BrowserThread::UI, FROM_HERE,
284       base::Bind(ProcessMirrorHeaderUIThread, child_id, route_id, params));
285 #endif  // defined(OS_IOS)
286 }
287
288 } // namespace signin