blob: e14ce46592d87e548a8e734484840b43aa6ac85e [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "google_apis/gaia/oauth_multilogin_result.h"
#include "base/compiler_specific.h"
#include "base/json/json_reader.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece_forward.h"
OAuthMultiloginResponseStatus ParseOAuthMultiloginResponseStatus(
const std::string& status) {
if (status == "OK")
return OAuthMultiloginResponseStatus::kOk;
if (status == "RETRY")
return OAuthMultiloginResponseStatus::kRetry;
if (status == "INVALID_TOKENS")
return OAuthMultiloginResponseStatus::kInvalidTokens;
if (status == "INVALID_INPUT")
return OAuthMultiloginResponseStatus::kInvalidInput;
if (status == "ERROR")
return OAuthMultiloginResponseStatus::kError;
return OAuthMultiloginResponseStatus::kUnknownStatus;
}
OAuthMultiloginResult::OAuthMultiloginResult(
const OAuthMultiloginResult& other) {
error_ = other.error();
cookies_ = other.cookies();
failed_accounts_ = other.failed_accounts();
}
OAuthMultiloginResult& OAuthMultiloginResult::operator=(
const OAuthMultiloginResult& other) {
error_ = other.error();
cookies_ = other.cookies();
failed_accounts_ = other.failed_accounts();
return *this;
}
OAuthMultiloginResult::OAuthMultiloginResult(
const GoogleServiceAuthError& error)
: error_(error) {}
void OAuthMultiloginResult::TryParseStatusFromValue(
base::DictionaryValue* dictionary_value) {
std::string status_string;
dictionary_value->GetString("status", &status_string);
OAuthMultiloginResponseStatus status =
ParseOAuthMultiloginResponseStatus(status_string);
UMA_HISTOGRAM_ENUMERATION("Signin.OAuthMultiloginResponseStatus", status);
switch (status) {
case OAuthMultiloginResponseStatus::kUnknownStatus:
error_ = GoogleServiceAuthError(
GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE);
break;
case OAuthMultiloginResponseStatus::kOk:
error_ = GoogleServiceAuthError::AuthErrorNone();
break;
case OAuthMultiloginResponseStatus::kRetry:
// This is a transient error.
error_ =
GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
break;
case OAuthMultiloginResponseStatus::kInvalidTokens:
error_ = GoogleServiceAuthError(
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
break;
case OAuthMultiloginResponseStatus::kError:
case OAuthMultiloginResponseStatus::kInvalidInput:
error_ = GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR);
break;
}
}
// static
base::StringPiece OAuthMultiloginResult::StripXSSICharacters(
const std::string& raw_data) {
base::StringPiece body(raw_data);
return body.substr(body.find('\n'));
}
void OAuthMultiloginResult::TryParseFailedAccountsFromValue(
base::DictionaryValue* dictionary_value) {
base::ListValue* failed_accounts = nullptr;
dictionary_value->GetList("failed_accounts", &failed_accounts);
if (failed_accounts == nullptr) {
VLOG(1) << "No invalid accounts found in the response but error is set to "
"INVALID_TOKENS";
error_ = GoogleServiceAuthError(
GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE);
return;
}
for (size_t i = 0; i < failed_accounts->GetSize(); ++i) {
base::DictionaryValue* account_value = nullptr;
failed_accounts->GetDictionary(i, &account_value);
std::string gaia_id;
std::string status;
account_value->GetString("obfuscated_id", &gaia_id);
account_value->GetString("status", &status);
if (status != "OK")
failed_accounts_.push_back(gaia_id);
}
}
void OAuthMultiloginResult::TryParseCookiesFromValue(
base::DictionaryValue* dictionary_value) {
base::ListValue* cookie_list = nullptr;
dictionary_value->GetList("cookies", &cookie_list);
if (cookie_list == nullptr) {
VLOG(1) << "No cookies found in the response.";
error_ = GoogleServiceAuthError(
GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE);
return;
}
for (size_t i = 0; i < cookie_list->GetSize(); ++i) {
base::DictionaryValue* cookie_value = nullptr;
cookie_list->GetDictionary(i, &cookie_value);
std::string name;
std::string value;
std::string domain;
std::string host;
std::string path;
bool is_secure;
bool is_http_only;
std::string priority;
std::string max_age;
double expiration_delta;
cookie_value->GetString("name", &name);
cookie_value->GetString("value", &value);
cookie_value->GetString("domain", &domain);
cookie_value->GetString("host", &host);
cookie_value->GetString("path", &path);
cookie_value->GetBoolean("isSecure", &is_secure);
cookie_value->GetBoolean("isHttpOnly", &is_http_only);
cookie_value->GetString("priority", &priority);
cookie_value->GetDouble("maxAge", &expiration_delta);
base::TimeDelta before_expiration =
base::TimeDelta::FromSecondsD(expiration_delta);
if (domain.empty() && !host.empty() && host[0] != '.') {
// Host cookie case. If domain is empty but other conditions are not met,
// there must be something wrong with the received cookie.
domain = host;
}
net::CanonicalCookie new_cookie(
name, value, domain, path, base::Time::Now(),
base::Time::Now() + before_expiration, base::Time::Now(), is_secure,
is_http_only, net::CookieSameSite::NO_RESTRICTION,
net::StringToCookiePriority(priority));
if (new_cookie.IsCanonical()) {
cookies_.push_back(std::move(new_cookie));
} else {
LOG(ERROR) << "Non-canonical cookie found.";
}
}
}
OAuthMultiloginResult::OAuthMultiloginResult(const std::string& raw_data) {
base::StringPiece data = StripXSSICharacters(raw_data);
std::unique_ptr<base::DictionaryValue> dictionary_value =
base::DictionaryValue::From(base::JSONReader::ReadDeprecated(data));
if (!dictionary_value) {
error_ = GoogleServiceAuthError(
GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE);
return;
}
TryParseStatusFromValue(dictionary_value.get());
if (error_.state() == GoogleServiceAuthError::State::NONE) {
TryParseCookiesFromValue(dictionary_value.get());
} else if (error_.state() ==
GoogleServiceAuthError::State::INVALID_GAIA_CREDENTIALS) {
TryParseFailedAccountsFromValue(dictionary_value.get());
}
}
OAuthMultiloginResult::~OAuthMultiloginResult() = default;