Check if device already claimed

Client can claim only unclaimed device.
Cloud can claim any device.
Claim with kNone is not allowed.

BUG=25766815, 26143922

Change-Id: Id92168b7f7c290509e672a659f09b7d06af37b76
Reviewed-on: https://weave-review.googlesource.com/1960
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
diff --git a/src/config.h b/src/config.h
index 0c7ea5f..f5a8a77 100644
--- a/src/config.h
+++ b/src/config.h
@@ -21,6 +21,7 @@
 class StorageInterface;
 
 enum class RootClientTokenOwner {
+  // Keep order as it's used with order comparison operators.
   kNone,
   kClient,
   kCloud,
diff --git a/src/privet/auth_manager.cc b/src/privet/auth_manager.cc
index f38b854..f9f6777 100644
--- a/src/privet/auth_manager.cc
+++ b/src/privet/auth_manager.cc
@@ -9,6 +9,7 @@
 
 #include "src/config.h"
 #include "src/data_encoding.h"
+#include "src/privet/constants.h"
 #include "src/privet/openssl_utils.h"
 #include "src/string_utils.h"
 
@@ -80,6 +81,10 @@
   return secret;
 }
 
+bool IsClaimAllowed(RootClientTokenOwner curret, RootClientTokenOwner claimer) {
+  return claimer > curret || claimer == RootClientTokenOwner::kCloud;
+}
+
 }  // namespace
 
 AuthManager::AuthManager(Config* config,
@@ -148,6 +153,17 @@
 std::vector<uint8_t> AuthManager::ClaimRootClientAuthToken(
     RootClientTokenOwner owner,
     ErrorPtr* error) {
+  CHECK(RootClientTokenOwner::kNone != owner);
+  if (config_) {
+    auto current = config_->GetSettings().root_client_token_owner;
+    if (!IsClaimAllowed(current, owner)) {
+      Error::AddToPrintf(
+          error, FROM_HERE, errors::kDomain, errors::kAlreadyClaimed,
+          "Device already claimed by '%s'", EnumToString(current).c_str());
+      return {};
+    }
+  };
+
   pending_claims_.push_back(std::make_pair(
       std::unique_ptr<AuthManager>{new AuthManager{nullptr, {}}}, owner));
   if (pending_claims_.size() > kMaxPendingClaims)
@@ -166,8 +182,11 @@
                    [&token](const decltype(pending_claims_)::value_type& auth) {
                      return auth.first->IsValidAuthToken(token);
                    });
-  if (claim == pending_claims_.end())
+  if (claim == pending_claims_.end()) {
+    Error::AddTo(error, FROM_HERE, errors::kDomain, errors::kNotFound,
+                 "Unknown claim");
     return false;
+  }
 
   SetSecret(claim->first->GetSecret(), claim->second);
   pending_claims_.clear();
diff --git a/src/privet/auth_manager_unittest.cc b/src/privet/auth_manager_unittest.cc
index 7b4aae4..aa124e1 100644
--- a/src/privet/auth_manager_unittest.cc
+++ b/src/privet/auth_manager_unittest.cc
@@ -147,23 +147,65 @@
   }
 }
 
-TEST_F(AuthManagerTest, ClaimRootClientAuthToken) {
+class AuthManagerClaimTest : public testing::Test {
+ public:
+  void SetUp() override { EXPECT_GE(auth_.GetSecret().size(), 32u); }
+
+  bool TestClaim(RootClientTokenOwner owner, RootClientTokenOwner claimer) {
+    Config::Transaction change{&config_};
+    change.set_root_client_token_owner(owner);
+    change.Commit();
+    return !auth_.ClaimRootClientAuthToken(claimer, nullptr).empty();
+  }
+
+ protected:
+  Config config_{nullptr};
+  AuthManager auth_{&config_, {}};
+};
+
+TEST_F(AuthManagerClaimTest, WithPreviosOwner) {
+  EXPECT_DEATH(
+      TestClaim(RootClientTokenOwner::kNone, RootClientTokenOwner::kNone), "");
+  EXPECT_DEATH(
+      TestClaim(RootClientTokenOwner::kClient, RootClientTokenOwner::kNone),
+      "");
+  EXPECT_DEATH(
+      TestClaim(RootClientTokenOwner::kCloud, RootClientTokenOwner::kNone), "");
+  EXPECT_TRUE(
+      TestClaim(RootClientTokenOwner::kNone, RootClientTokenOwner::kClient));
+  EXPECT_FALSE(
+      TestClaim(RootClientTokenOwner::kClient, RootClientTokenOwner::kClient));
+  EXPECT_FALSE(
+      TestClaim(RootClientTokenOwner::kCloud, RootClientTokenOwner::kClient));
+  EXPECT_TRUE(
+      TestClaim(RootClientTokenOwner::kNone, RootClientTokenOwner::kCloud));
+  EXPECT_TRUE(
+      TestClaim(RootClientTokenOwner::kClient, RootClientTokenOwner::kCloud));
+  EXPECT_TRUE(
+      TestClaim(RootClientTokenOwner::kCloud, RootClientTokenOwner::kCloud));
+}
+
+TEST_F(AuthManagerClaimTest, NormalClaim) {
   auto token =
       auth_.ClaimRootClientAuthToken(RootClientTokenOwner::kCloud, nullptr);
   EXPECT_FALSE(auth_.IsValidAuthToken(token));
+  EXPECT_EQ(RootClientTokenOwner::kNone,
+            config_.GetSettings().root_client_token_owner);
 
   EXPECT_TRUE(auth_.ConfirmAuthToken(token, nullptr));
   EXPECT_TRUE(auth_.IsValidAuthToken(token));
+  EXPECT_EQ(RootClientTokenOwner::kCloud,
+            config_.GetSettings().root_client_token_owner);
 }
 
-TEST_F(AuthManagerTest, ClaimRootClientAuthTokenDoubleConfirm) {
+TEST_F(AuthManagerClaimTest, DoubleConfirm) {
   auto token =
       auth_.ClaimRootClientAuthToken(RootClientTokenOwner::kCloud, nullptr);
   EXPECT_TRUE(auth_.ConfirmAuthToken(token, nullptr));
   EXPECT_TRUE(auth_.ConfirmAuthToken(token, nullptr));
 }
 
-TEST_F(AuthManagerTest, DoubleClaimRootClientAuthToken) {
+TEST_F(AuthManagerClaimTest, DoubleClaim) {
   auto token1 =
       auth_.ClaimRootClientAuthToken(RootClientTokenOwner::kCloud, nullptr);
   auto token2 =
@@ -172,7 +214,7 @@
   EXPECT_FALSE(auth_.ConfirmAuthToken(token2, nullptr));
 }
 
-TEST_F(AuthManagerTest, ClaimRootClientAuthTokenOverflow) {
+TEST_F(AuthManagerClaimTest, TokenOverflow) {
   auto token =
       auth_.ClaimRootClientAuthToken(RootClientTokenOwner::kCloud, nullptr);
   for (size_t i = 0; i < 100; ++i)
diff --git a/src/privet/constants.cc b/src/privet/constants.cc
index 3cc3e4a..e1962bd 100644
--- a/src/privet/constants.cc
+++ b/src/privet/constants.cc
@@ -30,6 +30,7 @@
 const char kInvalidPassphrase[] = "invalidPassphrase";
 const char kNotFound[] = "notFound";
 const char kNotImplemented[] = "notImplemented";
+const char kAlreadyClaimed[] = "alreadyClaimed";
 
 }  // namespace errors
 }  // namespace privet
diff --git a/src/privet/constants.h b/src/privet/constants.h
index 0668879..2b001aa 100644
--- a/src/privet/constants.h
+++ b/src/privet/constants.h
@@ -32,6 +32,7 @@
 extern const char kInvalidPassphrase[];
 extern const char kNotFound[];
 extern const char kNotImplemented[];
+extern const char kAlreadyClaimed[];
 }  // namespace errors
 }  // namespace privet
 }  // namespace weave
diff --git a/src/privet/privet_handler.cc b/src/privet/privet_handler.cc
index 86a2a85..b435435 100644
--- a/src/privet/privet_handler.cc
+++ b/src/privet/privet_handler.cc
@@ -153,6 +153,7 @@
     {errors::kInvalidState, http::kInternalServerError},
     {errors::kNotFound, http::kNotFound},
     {errors::kNotImplemented, http::kNotSupported},
+    {errors::kAlreadyClaimed, http::kDenied},
 };
 
 AuthScope AuthScopeFromString(const std::string& scope, AuthScope auto_scope) {