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) {