buffet: Add RegistrationStatus::InvalidCredentials
If the device has been deleted from GCD then an oauth request will
return an error of invalid_grant. Make sure we handle this and set
the registration status to InvalidCredentials.
BUG=brillo:16
TEST=FEATURES=test emerge-${BOARD} buffet (I also tested this manually
by deregistering the device using
https://gcd.sandbox.google.com/clouddevices#) and
test_that <ipaddress> buffet_Registration buffet_BasicDBusAPI
Change-Id: I60aa68186d9c6f3a7ce812e4dddfda626ea2b42f
Reviewed-on: https://chromium-review.googlesource.com/254170
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Nathan Bullock <nathanbullock@google.com>
Tested-by: Nathan Bullock <nathanbullock@google.com>
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index e482d81..0b98cb7 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -66,27 +66,6 @@
return {chromeos::http::request_header::kAuthorization, authorization};
}
-std::unique_ptr<base::DictionaryValue> ParseOAuthResponse(
- const chromeos::http::Response* response, chromeos::ErrorPtr* error) {
- int code = 0;
- auto resp = chromeos::http::ParseJsonResponse(response, &code, error);
- if (resp && code >= chromeos::http::status_code::BadRequest) {
- if (error) {
- std::string error_code, error_message;
- if (resp->GetString("error", &error_code) &&
- resp->GetString("error_description", &error_message)) {
- chromeos::Error::AddTo(error, FROM_HERE, buffet::kErrorDomainOAuth2,
- error_code, error_message);
- } else {
- chromeos::Error::AddTo(error, FROM_HERE, buffet::kErrorDomainOAuth2,
- "unexpected_response", "Unexpected OAuth error");
- }
- }
- return std::unique_ptr<base::DictionaryValue>();
- }
- return resp;
-}
-
inline void SetUnexpectedError(chromeos::ErrorPtr* error) {
chromeos::Error::AddTo(error, FROM_HERE, buffet::kErrorDomainGCD,
"unexpected_response", "Unexpected GCD error");
@@ -94,9 +73,6 @@
void ParseGCDError(const base::DictionaryValue* json,
chromeos::ErrorPtr* error) {
- if (!error)
- return;
-
const base::Value* list_value = nullptr;
const base::ListValue* error_list = nullptr;
if (!json->Get("error.errors", &list_value) ||
@@ -310,6 +286,31 @@
return have_credentials;
}
+std::unique_ptr<base::DictionaryValue>
+DeviceRegistrationInfo::ParseOAuthResponse(
+ const chromeos::http::Response* response, chromeos::ErrorPtr* error) {
+ int code = 0;
+ auto resp = chromeos::http::ParseJsonResponse(response, &code, error);
+ if (resp && code >= chromeos::http::status_code::BadRequest) {
+ std::string error_code, error_message;
+ if (!resp->GetString("error", &error_code)) {
+ error_code = "unexpected_response";
+ }
+ if (error_code == "invalid_grant") {
+ LOG(INFO) << "The device's registration has been revoked.";
+ SetRegistrationStatus(RegistrationStatus::kInvalidCredentials);
+ }
+ // I have never actually seen an error_description returned.
+ if (!resp->GetString("error_description", &error_message)) {
+ error_message = "Unexpected OAuth error";
+ }
+ chromeos::Error::AddTo(error, FROM_HERE, buffet::kErrorDomainOAuth2,
+ error_code, error_message);
+ return std::unique_ptr<base::DictionaryValue>();
+ }
+ return resp;
+}
+
bool DeviceRegistrationInfo::ValidateAndRefreshAccessToken(
chromeos::ErrorPtr* error) {
LOG(INFO) << "Checking access token expiration.";
diff --git a/buffet/device_registration_info.h b/buffet/device_registration_info.h
index 95c8b0d..7b2e3da 100644
--- a/buffet/device_registration_info.h
+++ b/buffet/device_registration_info.h
@@ -150,6 +150,11 @@
// Makes sure the access token is available and up-to-date.
bool ValidateAndRefreshAccessToken(chromeos::ErrorPtr* error);
+ // Parse the OAuth response, and sets registration status to
+ // kInvalidCredentials if our registration is no longer valid.
+ std::unique_ptr<base::DictionaryValue> ParseOAuthResponse(
+ const chromeos::http::Response* response, chromeos::ErrorPtr* error);
+
// This attempts to open the XMPP channel. The XMPP channel needs to be
// restarted anytime the access_token is refreshed.
void StartXmpp();
diff --git a/buffet/device_registration_info_unittest.cc b/buffet/device_registration_info_unittest.cc
index dee5614..2912fb6 100644
--- a/buffet/device_registration_info_unittest.cc
+++ b/buffet/device_registration_info_unittest.cc
@@ -124,6 +124,28 @@
response->ReplyJson(chromeos::http::status_code::Ok, &json);
}
+void OAuth2HandlerFail(const ServerRequest& request,
+ ServerResponse* response) {
+ base::DictionaryValue json;
+ EXPECT_EQ("refresh_token", request.GetFormField("grant_type"));
+ EXPECT_EQ(test_data::kRefreshToken, request.GetFormField("refresh_token"));
+ EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id"));
+ EXPECT_EQ(test_data::kClientSecret, request.GetFormField("client_secret"));
+ json.SetString("error", "unable_to_authenticate");
+ response->ReplyJson(chromeos::http::status_code::BadRequest, &json);
+}
+
+void OAuth2HandlerDeregister(const ServerRequest& request,
+ ServerResponse* response) {
+ base::DictionaryValue json;
+ EXPECT_EQ("refresh_token", request.GetFormField("grant_type"));
+ EXPECT_EQ(test_data::kRefreshToken, request.GetFormField("refresh_token"));
+ EXPECT_EQ(test_data::kClientId, request.GetFormField("client_id"));
+ EXPECT_EQ(test_data::kClientSecret, request.GetFormField("client_secret"));
+ json.SetString("error", "invalid_grant");
+ response->ReplyJson(chromeos::http::status_code::BadRequest, &json);
+}
+
void DeviceInfoHandler(const ServerRequest& request, ServerResponse* response) {
std::string auth = "Bearer ";
auth += test_data::kAccessToken;
@@ -293,6 +315,46 @@
EXPECT_EQ(1, transport_->GetRequestCount());
}
+TEST_F(DeviceRegistrationInfoTest, CheckAuthenticationFailure) {
+ SetDefaultDeviceRegistration(&data_);
+ storage_->Save(&data_);
+ EXPECT_TRUE(dev_reg_->Load());
+ EXPECT_EQ(RegistrationStatus::kOffline,
+ dev_reg_->GetRegistrationStatus());
+
+ transport_->AddHandler(dev_reg_->GetOAuthURL("token"),
+ chromeos::http::request_type::kPost,
+ base::Bind(OAuth2HandlerFail));
+ transport_->ResetRequestCount();
+ chromeos::ErrorPtr error;
+ EXPECT_FALSE(dev_reg_->CheckRegistration(&error));
+ EXPECT_EQ(1, transport_->GetRequestCount());
+ EXPECT_TRUE(error->HasError(buffet::kErrorDomainOAuth2,
+ "unable_to_authenticate"));
+ EXPECT_EQ(RegistrationStatus::kOffline,
+ dev_reg_->GetRegistrationStatus());
+}
+
+TEST_F(DeviceRegistrationInfoTest, CheckDeregistration) {
+ SetDefaultDeviceRegistration(&data_);
+ storage_->Save(&data_);
+ EXPECT_TRUE(dev_reg_->Load());
+ EXPECT_EQ(RegistrationStatus::kOffline,
+ dev_reg_->GetRegistrationStatus());
+
+ transport_->AddHandler(dev_reg_->GetOAuthURL("token"),
+ chromeos::http::request_type::kPost,
+ base::Bind(OAuth2HandlerDeregister));
+ transport_->ResetRequestCount();
+ chromeos::ErrorPtr error;
+ EXPECT_FALSE(dev_reg_->CheckRegistration(&error));
+ EXPECT_EQ(1, transport_->GetRequestCount());
+ EXPECT_TRUE(error->HasError(buffet::kErrorDomainOAuth2,
+ "invalid_grant"));
+ EXPECT_EQ(RegistrationStatus::kInvalidCredentials,
+ dev_reg_->GetRegistrationStatus());
+}
+
TEST_F(DeviceRegistrationInfoTest, GetDeviceInfo) {
SetDefaultDeviceRegistration(&data_);
storage_->Save(&data_);
diff --git a/buffet/registration_status.cc b/buffet/registration_status.cc
index 4fb1721..3b44ae3 100644
--- a/buffet/registration_status.cc
+++ b/buffet/registration_status.cc
@@ -18,6 +18,8 @@
return "registering";
case RegistrationStatus::kRegistered:
return "registered";
+ case RegistrationStatus::kInvalidCredentials:
+ return "invalid_credentials";
}
return "unknown";
}
diff --git a/buffet/registration_status.h b/buffet/registration_status.h
index fa2e2cb..eee0c81 100644
--- a/buffet/registration_status.h
+++ b/buffet/registration_status.h
@@ -16,6 +16,7 @@
kUnregistered, // We have no credentials.
kRegistering, // We've just been given credentials.
kRegistered, // We're registered and online.
+ kInvalidCredentials, // Our registration has been revoked.
};
std::string StatusToString(RegistrationStatus status);