Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/signin/core/browser/signin_header_helper.h" | 5 #include "components/signin/core/browser/signin_header_helper.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <map> | |
| 8 | 9 |
| 10 #include "base/logging.h" | |
| 9 #include "base/macros.h" | 11 #include "base/macros.h" |
| 10 #include "base/strings/string_number_conversions.h" | |
| 11 #include "base/strings/string_split.h" | 12 #include "base/strings/string_split.h" |
| 12 #include "base/strings/string_util.h" | |
| 13 #include "base/strings/stringprintf.h" | |
| 14 #include "build/build_config.h" | 13 #include "build/build_config.h" |
| 15 #include "components/content_settings/core/browser/cookie_settings.h" | 14 #include "components/content_settings/core/browser/cookie_settings.h" |
| 16 #include "components/google/core/browser/google_util.h" | 15 #include "components/google/core/browser/google_util.h" |
| 16 #include "components/signin/core/browser/chrome_connected_header_helper.h" | |
| 17 #include "components/signin/core/common/profile_management_switches.h" | 17 #include "components/signin/core/common/profile_management_switches.h" |
| 18 #include "google_apis/gaia/gaia_auth_util.h" | 18 #include "google_apis/gaia/gaia_auth_util.h" |
| 19 #include "google_apis/gaia/gaia_urls.h" | 19 #include "google_apis/gaia/gaia_urls.h" |
| 20 #include "net/base/escape.h" | 20 #include "net/base/escape.h" |
| 21 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | |
| 22 #include "net/http/http_response_headers.h" | 21 #include "net/http/http_response_headers.h" |
| 23 #include "net/url_request/url_request.h" | 22 #include "net/url_request/url_request.h" |
| 24 #include "url/gurl.h" | 23 #include "url/gurl.h" |
| 25 | 24 |
| 25 #if !defined(OS_IOS) && !defined(OS_ANDROID) | |
| 26 #include "components/signin/core/browser/dice_header_helper.h" | |
| 27 #endif | |
| 28 | |
| 26 namespace signin { | 29 namespace signin { |
| 27 | 30 |
| 28 namespace { | 31 namespace { |
| 29 | 32 |
| 30 // Dictionary of fields in a mirror response header. | 33 // Dictionary of fields in a mirror response header. |
| 31 typedef std::map<std::string, std::string> MirrorResponseHeaderDictionary; | 34 typedef std::map<std::string, std::string> MirrorResponseHeaderDictionary; |
| 32 | 35 |
| 33 const char kChromeManageAccountsHeader[] = "X-Chrome-Manage-Accounts"; | 36 const char kChromeManageAccountsHeader[] = "X-Chrome-Manage-Accounts"; |
| 34 const char kContinueUrlAttrName[] = "continue_url"; | 37 const char kContinueUrlAttrName[] = "continue_url"; |
| 35 const char kEmailAttrName[] = "email"; | 38 const char kEmailAttrName[] = "email"; |
| 36 const char kEnableAccountConsistencyAttrName[] = "enable_account_consistency"; | |
| 37 const char kGaiaIdAttrName[] = "id"; | |
| 38 const char kProfileModeAttrName[] = "mode"; | |
| 39 const char kIsSameTabAttrName[] = "is_same_tab"; | 39 const char kIsSameTabAttrName[] = "is_same_tab"; |
| 40 const char kIsSamlAttrName[] = "is_saml"; | 40 const char kIsSamlAttrName[] = "is_saml"; |
| 41 const char kServiceTypeAttrName[] = "action"; | 41 const char kServiceTypeAttrName[] = "action"; |
| 42 | 42 |
| 43 bool IsDriveOrigin(const GURL& url) { | |
| 44 if (!url.SchemeIsCryptographic()) | |
| 45 return false; | |
| 46 | |
| 47 const GURL kGoogleDriveURL("https://drive.google.com"); | |
| 48 const GURL kGoogleDocsURL("https://docs.google.com"); | |
| 49 return url == kGoogleDriveURL || url == kGoogleDocsURL; | |
| 50 } | |
| 51 | |
| 52 bool IsUrlEligibleToIncludeGaiaId(const GURL& url, bool is_header_request) { | |
| 53 if (is_header_request) { | |
| 54 // GAIA Id is only necessary for Drive. Don't set it otherwise. | |
| 55 return IsDriveOrigin(url.GetOrigin()); | |
| 56 } | |
| 57 | |
| 58 // Cookie requests don't have the granularity to only include the GAIA Id for | |
| 59 // Drive origin. Set it on all google.com instead. | |
| 60 if (!url.SchemeIsCryptographic()) | |
| 61 return false; | |
| 62 | |
| 63 const std::string kGoogleDomain = "google.com"; | |
| 64 std::string domain = net::registry_controlled_domains::GetDomainAndRegistry( | |
| 65 url, net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); | |
| 66 return domain == kGoogleDomain; | |
| 67 } | |
| 68 | |
| 69 // Determines the service type that has been passed from GAIA in the header. | 43 // Determines the service type that has been passed from GAIA in the header. |
| 70 GAIAServiceType GetGAIAServiceTypeFromHeader(const std::string& header_value) { | 44 GAIAServiceType GetGAIAServiceTypeFromHeader(const std::string& header_value) { |
| 71 if (header_value == "SIGNOUT") | 45 if (header_value == "SIGNOUT") |
| 72 return GAIA_SERVICE_TYPE_SIGNOUT; | 46 return GAIA_SERVICE_TYPE_SIGNOUT; |
| 73 else if (header_value == "INCOGNITO") | 47 else if (header_value == "INCOGNITO") |
| 74 return GAIA_SERVICE_TYPE_INCOGNITO; | 48 return GAIA_SERVICE_TYPE_INCOGNITO; |
| 75 else if (header_value == "ADDSESSION") | 49 else if (header_value == "ADDSESSION") |
| 76 return GAIA_SERVICE_TYPE_ADDSESSION; | 50 return GAIA_SERVICE_TYPE_ADDSESSION; |
| 77 else if (header_value == "REAUTH") | 51 else if (header_value == "REAUTH") |
| 78 return GAIA_SERVICE_TYPE_REAUTH; | 52 return GAIA_SERVICE_TYPE_REAUTH; |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 98 continue; | 72 continue; |
| 99 } | 73 } |
| 100 dictionary[field.substr(0, delim).as_string()] = net::UnescapeURLComponent( | 74 dictionary[field.substr(0, delim).as_string()] = net::UnescapeURLComponent( |
| 101 field.substr(delim + 1).as_string(), | 75 field.substr(delim + 1).as_string(), |
| 102 net::UnescapeRule::PATH_SEPARATORS | | 76 net::UnescapeRule::PATH_SEPARATORS | |
| 103 net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS); | 77 net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS); |
| 104 } | 78 } |
| 105 return dictionary; | 79 return dictionary; |
| 106 } | 80 } |
| 107 | 81 |
| 108 // Checks if the url has the required properties to have a X-Chrome-Connected | |
| 109 // header. | |
| 110 bool IsUrlEligibleForXChromeConnectedHeader(const GURL& url) { | |
| 111 // Only set the header for Drive and Gaia always, and other Google properties | |
| 112 // if account consistency is enabled. | |
| 113 // Vasquette, which is integrated with most Google properties, needs the | |
| 114 // header to redirect certain user actions to Chrome native UI. Drive and Gaia | |
| 115 // need the header to tell if the current user is connected. The drive path is | |
| 116 // a temporary workaround until the more generic chrome.principals API is | |
| 117 // available. | |
| 118 | |
| 119 // Consider the account id sensitive and limit it to secure domains. | |
| 120 if (!url.SchemeIsCryptographic()) | |
| 121 return false; | |
| 122 | |
| 123 GURL origin(url.GetOrigin()); | |
| 124 bool is_enable_account_consistency = | |
| 125 switches::IsAccountConsistencyMirrorEnabled(); | |
| 126 bool is_google_url = is_enable_account_consistency && | |
| 127 (google_util::IsGoogleDomainUrl( | |
| 128 url, google_util::ALLOW_SUBDOMAIN, | |
| 129 google_util::DISALLOW_NON_STANDARD_PORTS) || | |
| 130 google_util::IsYoutubeDomainUrl( | |
| 131 url, google_util::ALLOW_SUBDOMAIN, | |
| 132 google_util::DISALLOW_NON_STANDARD_PORTS)); | |
| 133 return is_google_url || IsDriveOrigin(origin) || | |
| 134 gaia::IsGaiaSignonRealm(origin); | |
| 135 } | |
| 136 | |
| 137 // Checks if the url has the required properties to have an account consistency | |
| 138 // header. | |
| 139 bool IsUrlEligibleForAccountConsistencyRequestHeader(const GURL& url) { | |
| 140 // TODO(droger): Support X-Chrome-ID-Consistency-Request. | |
| 141 return IsUrlEligibleForXChromeConnectedHeader(url); | |
| 142 } | |
| 143 | |
| 144 std::string BuildMirrorRequestIfPossible( | |
| 145 bool is_header_request, | |
| 146 const GURL& url, | |
| 147 const std::string& account_id, | |
| 148 const content_settings::CookieSettings* cookie_settings, | |
| 149 int profile_mode_mask) { | |
| 150 if (account_id.empty()) | |
| 151 return std::string(); | |
| 152 | |
| 153 // If signin cookies are not allowed, don't add the header. | |
| 154 if (!SettingsAllowSigninCookies(cookie_settings)) { | |
| 155 return std::string(); | |
| 156 } | |
| 157 | |
| 158 // Check if url is elligible for the header. | |
| 159 if (!IsUrlEligibleForXChromeConnectedHeader(url)) | |
| 160 return std::string(); | |
| 161 | |
| 162 std::vector<std::string> parts; | |
| 163 if (IsUrlEligibleToIncludeGaiaId(url, is_header_request)) { | |
| 164 // Only set the GAIA Id on domains that actually requires it. | |
| 165 parts.push_back( | |
| 166 base::StringPrintf("%s=%s", kGaiaIdAttrName, account_id.c_str())); | |
| 167 } | |
| 168 parts.push_back( | |
| 169 base::StringPrintf("%s=%s", kProfileModeAttrName, | |
| 170 base::IntToString(profile_mode_mask).c_str())); | |
| 171 parts.push_back(base::StringPrintf( | |
| 172 "%s=%s", kEnableAccountConsistencyAttrName, | |
| 173 switches::IsAccountConsistencyMirrorEnabled() ? "true" : "false")); | |
| 174 | |
| 175 return base::JoinString(parts, is_header_request ? "," : ":"); | |
| 176 } | |
| 177 | |
| 178 } // namespace | 82 } // namespace |
| 179 | 83 |
| 180 extern const char kChromeConnectedHeader[] = "X-Chrome-Connected"; | |
| 181 | |
| 182 ManageAccountsParams::ManageAccountsParams() | 84 ManageAccountsParams::ManageAccountsParams() |
| 183 : service_type(GAIA_SERVICE_TYPE_NONE), | 85 : service_type(GAIA_SERVICE_TYPE_NONE), |
| 184 email(""), | 86 email(""), |
| 185 is_saml(false), | 87 is_saml(false), |
| 186 continue_url(""), | 88 continue_url(""), |
| 187 is_same_tab(false) { | 89 is_same_tab(false) { |
| 188 #if !defined(OS_IOS) | 90 #if !defined(OS_IOS) |
| 189 child_id = 0; | 91 child_id = 0; |
| 190 route_id = 0; | 92 route_id = 0; |
| 191 #endif // !defined(OS_IOS) | 93 #endif // !defined(OS_IOS) |
| 192 } | 94 } |
| 193 | 95 |
| 194 ManageAccountsParams::ManageAccountsParams(const ManageAccountsParams& other) = | 96 ManageAccountsParams::ManageAccountsParams(const ManageAccountsParams& other) = |
| 195 default; | 97 default; |
| 196 | 98 |
| 197 bool SettingsAllowSigninCookies( | 99 bool SettingsAllowSigninCookies( |
| 198 const content_settings::CookieSettings* cookie_settings) { | 100 const content_settings::CookieSettings* cookie_settings) { |
| 199 GURL gaia_url = GaiaUrls::GetInstance()->gaia_url(); | 101 GURL gaia_url = GaiaUrls::GetInstance()->gaia_url(); |
| 200 GURL google_url = GaiaUrls::GetInstance()->google_url(); | 102 GURL google_url = GaiaUrls::GetInstance()->google_url(); |
| 201 return cookie_settings && | 103 return cookie_settings && |
| 202 cookie_settings->IsCookieAccessAllowed(gaia_url, gaia_url) && | 104 cookie_settings->IsCookieAccessAllowed(gaia_url, gaia_url) && |
| 203 cookie_settings->IsCookieAccessAllowed(google_url, google_url); | 105 cookie_settings->IsCookieAccessAllowed(google_url, google_url); |
| 204 } | 106 } |
| 205 | 107 |
| 206 std::string BuildMirrorRequestCookieIfPossible( | 108 bool SigninHeaderHelper::AppendOrRemoveRequestHeader( |
| 207 const GURL& url, | |
| 208 const std::string& account_id, | |
| 209 const content_settings::CookieSettings* cookie_settings, | |
| 210 int profile_mode_mask) { | |
| 211 return BuildMirrorRequestIfPossible(false /* is_header_request */, url, | |
| 212 account_id, cookie_settings, | |
| 213 profile_mode_mask); | |
| 214 } | |
| 215 | |
| 216 bool AppendOrRemoveAccountConsistentyRequestHeader( | |
| 217 net::URLRequest* request, | 109 net::URLRequest* request, |
| 110 const char* header_name, | |
| 218 const GURL& redirect_url, | 111 const GURL& redirect_url, |
| 219 const std::string& account_id, | 112 const std::string& account_id, |
| 220 const content_settings::CookieSettings* cookie_settings, | 113 const content_settings::CookieSettings* cookie_settings, |
| 221 int profile_mode_mask) { | 114 int profile_mode_mask) { |
| 222 const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url; | 115 const GURL& url = redirect_url.is_empty() ? request->url() : redirect_url; |
| 223 | 116 std::string header_value = BuildRequestHeaderIfPossible( |
| 224 // TODO(droger): Support X-Chrome-ID-Consistency-Request. | |
| 225 std::string header_name = kChromeConnectedHeader; | |
| 226 std::string header_value = BuildMirrorRequestIfPossible( | |
| 227 true /* is_header_request */, url, account_id, cookie_settings, | 117 true /* is_header_request */, url, account_id, cookie_settings, |
| 228 profile_mode_mask); | 118 profile_mode_mask); |
| 229 | 119 |
| 230 if (!header_name.empty() && header_value.empty()) { | 120 if (header_value.empty()) { |
| 231 // If the request is being redirected, and it has the account consistency | 121 // If the request is being redirected, and it has the account consistency |
| 232 // header, and current url is a Google URL, and the redirected one is not, | 122 // header, and current url is a Google URL, and the redirected one is not, |
| 233 // remove the header. | 123 // remove the header. |
| 234 if (!redirect_url.is_empty() && | 124 if (!redirect_url.is_empty() && |
| 235 request->extra_request_headers().HasHeader(header_name) && | 125 request->extra_request_headers().HasHeader(header_name) && |
| 236 IsUrlEligibleForAccountConsistencyRequestHeader(request->url()) && | 126 IsUrlEligibleForRequestHeader(request->url()) && |
| 237 !IsUrlEligibleForAccountConsistencyRequestHeader(redirect_url)) { | 127 !IsUrlEligibleForRequestHeader(redirect_url)) { |
| 238 request->RemoveRequestHeaderByName(header_name); | 128 request->RemoveRequestHeaderByName(header_name); |
| 239 } | 129 } |
| 240 return false; | 130 return false; |
| 241 } | 131 } |
| 242 request->SetExtraRequestHeaderByName(header_name, header_value, false); | 132 request->SetExtraRequestHeaderByName(header_name, header_value, false); |
| 243 return true; | 133 return true; |
| 244 } | 134 } |
| 245 | 135 |
| 136 std::string SigninHeaderHelper::BuildRequestHeaderIfPossible( | |
| 137 bool is_header_request, | |
| 138 const GURL& url, | |
| 139 const std::string& account_id, | |
| 140 const content_settings::CookieSettings* cookie_settings, | |
| 141 int profile_mode_mask) { | |
| 142 // If signin cookies are not allowed, don't add the header. | |
| 143 if (!SettingsAllowSigninCookies(cookie_settings)) | |
| 144 return std::string(); | |
| 145 | |
| 146 // Check if url is eligible for the header. | |
| 147 if (!IsUrlEligibleForRequestHeader(url)) | |
| 148 return std::string(); | |
| 149 | |
| 150 return BuildRequestHeader(is_header_request, url, account_id, | |
| 151 profile_mode_mask); | |
| 152 } | |
| 153 | |
| 154 void AppendOrRemoveAccountConsistentyRequestHeader( | |
| 155 net::URLRequest* request, | |
| 156 const GURL& redirect_url, | |
| 157 const std::string& account_id, | |
| 158 const content_settings::CookieSettings* cookie_settings, | |
| 159 int profile_mode_mask) { | |
| 160 // Dice is not enabled on mobile. | |
|
msarda
2017/06/08 23:43:05
Indent
droger
2017/06/09 09:52:24
Not done. Indentation is correct.
| |
| 161 #if !defined(OS_IOS) && !defined(OS_ANDROID) | |
| 162 DiceHeaderHelper dice_helper; | |
| 163 dice_helper.AppendOrRemoveRequestHeader(request, kDiceRequestHeader, | |
| 164 redirect_url, account_id, | |
| 165 cookie_settings, profile_mode_mask); | |
| 166 #endif | |
| 167 | |
| 168 ChromeConnectedHeaderHelper chrome_connected_helper; | |
| 169 chrome_connected_helper.AppendOrRemoveRequestHeader( | |
| 170 request, kChromeConnectedHeader, redirect_url, account_id, | |
| 171 cookie_settings, profile_mode_mask); | |
| 172 } | |
| 173 | |
| 246 ManageAccountsParams BuildManageAccountsParams( | 174 ManageAccountsParams BuildManageAccountsParams( |
| 247 const std::string& header_value) { | 175 const std::string& header_value) { |
| 248 ManageAccountsParams params; | 176 ManageAccountsParams params; |
| 249 MirrorResponseHeaderDictionary header_dictionary = | 177 MirrorResponseHeaderDictionary header_dictionary = |
| 250 ParseMirrorResponseHeader(header_value); | 178 ParseMirrorResponseHeader(header_value); |
| 251 MirrorResponseHeaderDictionary::const_iterator it = header_dictionary.begin(); | 179 MirrorResponseHeaderDictionary::const_iterator it = header_dictionary.begin(); |
| 252 for (; it != header_dictionary.end(); ++it) { | 180 for (; it != header_dictionary.end(); ++it) { |
| 253 const std::string key_name(it->first); | 181 const std::string key_name(it->first); |
| 254 if (key_name == kServiceTypeAttrName) { | 182 if (key_name == kServiceTypeAttrName) { |
| 255 params.service_type = | 183 params.service_type = |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 282 !response_headers->GetNormalizedHeader( | 210 !response_headers->GetNormalizedHeader( |
| 283 kChromeManageAccountsHeader, &header_value)) { | 211 kChromeManageAccountsHeader, &header_value)) { |
| 284 return empty_params; | 212 return empty_params; |
| 285 } | 213 } |
| 286 | 214 |
| 287 DCHECK(switches::IsAccountConsistencyMirrorEnabled() && !is_off_the_record); | 215 DCHECK(switches::IsAccountConsistencyMirrorEnabled() && !is_off_the_record); |
| 288 return BuildManageAccountsParams(header_value); | 216 return BuildManageAccountsParams(header_value); |
| 289 } | 217 } |
| 290 | 218 |
| 291 } // namespace signin | 219 } // namespace signin |
| OLD | NEW |