| // Copyright 2015 The Weave 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 "src/privet/auth_manager.h" | 
 |  | 
 | #include <gmock/gmock.h> | 
 | #include <gtest/gtest.h> | 
 | #include <weave/settings.h> | 
 |  | 
 | #include "src/config.h" | 
 | #include "src/data_encoding.h" | 
 | #include "src/test/mock_clock.h" | 
 |  | 
 | using testing::Return; | 
 |  | 
 | namespace weave { | 
 | namespace privet { | 
 |  | 
 | class AuthManagerTest : public testing::Test { | 
 |  public: | 
 |   void SetUp() override { | 
 |     EXPECT_GE(auth_.GetAuthSecret().size(), 32u); | 
 |     EXPECT_GE(auth_.GetAccessSecret().size(), 32u); | 
 |     EXPECT_GE(auth_.GetCertificateFingerprint().size(), 32u); | 
 |  | 
 |     EXPECT_CALL(clock_, Now()) | 
 |         .WillRepeatedly(Return(base::Time::FromTimeT(1410000000))); | 
 |   } | 
 |  | 
 |  protected: | 
 |   const std::vector<uint8_t> kSecret1{ | 
 |       78, 40, 39, 68, 29, 19, 70, 86, 38, 61, 13, 55, 33, 32, 51, 52, | 
 |       34, 43, 97, 48, 8,  56, 11, 99, 50, 59, 24, 26, 31, 71, 76, 28}; | 
 |   const std::vector<uint8_t> kSecret2{ | 
 |       69, 53, 17, 37, 80, 73, 2,  5, 79, 64, 41, 57, 12, 54, 65, 63, | 
 |       72, 74, 93, 81, 20, 95, 89, 3, 94, 92, 27, 21, 49, 90, 36, 6}; | 
 |   const std::vector<uint8_t> kFingerprint{ | 
 |       22, 47, 23, 77, 42, 98, 96, 25,  83, 16, 9, 14, 91, 44, 15, 75, | 
 |       60, 62, 10, 18, 82, 35, 88, 100, 30, 45, 7, 46, 67, 84, 58, 85}; | 
 |  | 
 |   test::MockClock clock_; | 
 |   AuthManager auth_{kSecret1, kFingerprint, kSecret2, &clock_}; | 
 | }; | 
 |  | 
 | TEST_F(AuthManagerTest, RandomSecret) { | 
 |   AuthManager auth{{}, {}, {}, &clock_}; | 
 |   EXPECT_EQ(auth.GetAuthSecret().size(), 32u); | 
 |   EXPECT_EQ(auth.GetAccessSecret().size(), 32u); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, DifferentSecret) { | 
 |   AuthManager auth{kSecret2, {}, kSecret1}; | 
 |   EXPECT_EQ(auth.GetAuthSecret().size(), 32u); | 
 |   EXPECT_EQ(auth.GetAccessSecret().size(), 32u); | 
 |   EXPECT_NE(auth_.GetAccessSecret(), auth.GetAccessSecret()); | 
 |   EXPECT_NE(auth_.GetAuthSecret(), auth.GetAuthSecret()); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, Constructor) { | 
 |   EXPECT_EQ(kSecret1, auth_.GetAuthSecret()); | 
 |   EXPECT_EQ(kSecret2, auth_.GetAccessSecret()); | 
 |   EXPECT_EQ(kFingerprint, auth_.GetCertificateFingerprint()); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, CreateAccessToken) { | 
 |   EXPECT_EQ("xy58xYHWQUp5VT2tDmYLNIgdRiJ5ZQbgyCQuOGyQa5AwOjE0MTAwMDAwMDA6", | 
 |             Base64Encode(auth_.CreateAccessToken( | 
 |                 UserInfo{AuthScope::kNone, "123"}, {}))); | 
 |   EXPECT_EQ("U0U8NQ4J0btA4kwPkG8deFkDLQOPPANbw53gGmcTUMUxOjE0MTAwMDAwMDA6MjM0", | 
 |             Base64Encode(auth_.CreateAccessToken( | 
 |                 UserInfo{AuthScope::kViewer, "234"}, {}))); | 
 |   EXPECT_EQ("BooVCAQZ+ectnq2RYrzL3qymLfGm5YLGp9NMCuXAM3EzOjE0MTAwMDAwMDA6MjU3", | 
 |             Base64Encode(auth_.CreateAccessToken( | 
 |                 UserInfo{AuthScope::kManager, "257"}, {}))); | 
 |   EXPECT_EQ("YjgJgNR2uDzfTxHokbuDfguwOB72/F9mbzDO2PehIS80OjE0MTAwMDAwMDA6NDU2", | 
 |             Base64Encode(auth_.CreateAccessToken( | 
 |                 UserInfo{AuthScope::kOwner, "456"}, {}))); | 
 |   auto new_time = clock_.Now() + base::TimeDelta::FromDays(11); | 
 |   EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); | 
 |   EXPECT_EQ("d3t7075JF0Vb/c/Ihunk+xn2gxzUkHotdaIS9vc+A6kyOjE0MTA5NTA0MDA6MzQ1", | 
 |             Base64Encode(auth_.CreateAccessToken( | 
 |                 UserInfo{AuthScope::kUser, "345"}, {}))); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, CreateSameToken) { | 
 |   EXPECT_EQ(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, "555"}, {}), | 
 |             auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, "555"}, {})); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, CreateTokenDifferentScope) { | 
 |   EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, "456"}, {}), | 
 |             auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, "456"}, {})); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, CreateTokenDifferentUser) { | 
 |   EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, "456"}, {}), | 
 |             auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, "789"}, {})); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, CreateTokenDifferentTime) { | 
 |   auto token = auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, "567"}, {}); | 
 |   EXPECT_CALL(clock_, Now()) | 
 |       .WillRepeatedly(Return(base::Time::FromTimeT(1400000000))); | 
 |   EXPECT_NE(token, | 
 |             auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, "567"}, {})); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, CreateTokenDifferentInstance) { | 
 |   EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kUser, "123"}, {}), | 
 |             AuthManager({}, {}).CreateAccessToken( | 
 |                 UserInfo{AuthScope::kUser, "123"}, {})); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, ParseAccessToken) { | 
 |   // Multiple attempts with random secrets. | 
 |   const auto kStartTime = base::Time::FromTimeT(1412121212); | 
 |   for (size_t i = 0; i < 1000; ++i) { | 
 |     EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(kStartTime)); | 
 |  | 
 |     AuthManager auth{{}, {}, {}, &clock_}; | 
 |  | 
 |     auto token = auth.CreateAccessToken(UserInfo{AuthScope::kUser, "5"}, | 
 |                                         base::TimeDelta::FromSeconds(i)); | 
 |     UserInfo user_info; | 
 |     EXPECT_FALSE(auth_.ParseAccessToken(token, &user_info, nullptr)); | 
 |     EXPECT_TRUE(auth.ParseAccessToken(token, &user_info, nullptr)); | 
 |     EXPECT_EQ(AuthScope::kUser, user_info.scope()); | 
 |     EXPECT_EQ("5", user_info.user_id()); | 
 |  | 
 |     EXPECT_CALL(clock_, Now()) | 
 |         .WillRepeatedly(Return(kStartTime + base::TimeDelta::FromSeconds(i))); | 
 |     EXPECT_TRUE(auth.ParseAccessToken(token, &user_info, nullptr)); | 
 |  | 
 |     EXPECT_CALL(clock_, Now()) | 
 |         .WillRepeatedly( | 
 |             Return(kStartTime + base::TimeDelta::FromSeconds(i + 1))); | 
 |     EXPECT_FALSE(auth.ParseAccessToken(token, &user_info, nullptr)); | 
 |   } | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, GetRootClientAuthToken) { | 
 |   EXPECT_EQ("UK1ACOc3cWGjGBoTIX2bd3qCQgECRgMaVArkgA==", | 
 |             Base64Encode(auth_.GetRootClientAuthToken())); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentTime) { | 
 |   auto new_time = clock_.Now() + base::TimeDelta::FromDays(15); | 
 |   EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); | 
 |   EXPECT_EQ("UBpNF8g/GbNUmAyHg1qqJr+CQgECRgMaVB6rAA==", | 
 |             Base64Encode(auth_.GetRootClientAuthToken())); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentSecret) { | 
 |   AuthManager auth{kSecret2, {}, kSecret1, &clock_}; | 
 |   EXPECT_EQ("UFTBUcgd9d0HnPRnLeroN2mCQgECRgMaVArkgA==", | 
 |             Base64Encode(auth.GetRootClientAuthToken())); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerTest, IsValidAuthToken) { | 
 |   EXPECT_TRUE(auth_.IsValidAuthToken(auth_.GetRootClientAuthToken(), nullptr)); | 
 |   // Multiple attempts with random secrets. | 
 |   for (size_t i = 0; i < 1000; ++i) { | 
 |     AuthManager auth{{}, {}, {}, &clock_}; | 
 |  | 
 |     auto token = auth.GetRootClientAuthToken(); | 
 |     EXPECT_FALSE(auth_.IsValidAuthToken(token, nullptr)); | 
 |     EXPECT_TRUE(auth.IsValidAuthToken(token, nullptr)); | 
 |   } | 
 | } | 
 |  | 
 | class AuthManagerClaimTest : public testing::Test { | 
 |  public: | 
 |   void SetUp() override { EXPECT_EQ(auth_.GetAuthSecret().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, nullptr)); | 
 |   EXPECT_EQ(RootClientTokenOwner::kNone, | 
 |             config_.GetSettings().root_client_token_owner); | 
 |  | 
 |   EXPECT_TRUE(auth_.ConfirmClientAuthToken(token, nullptr)); | 
 |   EXPECT_TRUE(auth_.IsValidAuthToken(token, nullptr)); | 
 |   EXPECT_EQ(RootClientTokenOwner::kCloud, | 
 |             config_.GetSettings().root_client_token_owner); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerClaimTest, DoubleConfirm) { | 
 |   auto token = | 
 |       auth_.ClaimRootClientAuthToken(RootClientTokenOwner::kCloud, nullptr); | 
 |   EXPECT_TRUE(auth_.ConfirmClientAuthToken(token, nullptr)); | 
 |   EXPECT_TRUE(auth_.ConfirmClientAuthToken(token, nullptr)); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerClaimTest, DoubleClaim) { | 
 |   auto token1 = | 
 |       auth_.ClaimRootClientAuthToken(RootClientTokenOwner::kCloud, nullptr); | 
 |   auto token2 = | 
 |       auth_.ClaimRootClientAuthToken(RootClientTokenOwner::kCloud, nullptr); | 
 |   EXPECT_TRUE(auth_.ConfirmClientAuthToken(token1, nullptr)); | 
 |   EXPECT_FALSE(auth_.ConfirmClientAuthToken(token2, nullptr)); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerClaimTest, TokenOverflow) { | 
 |   auto token = | 
 |       auth_.ClaimRootClientAuthToken(RootClientTokenOwner::kCloud, nullptr); | 
 |   for (size_t i = 0; i < 100; ++i) | 
 |     auth_.ClaimRootClientAuthToken(RootClientTokenOwner::kCloud, nullptr); | 
 |   EXPECT_FALSE(auth_.ConfirmClientAuthToken(token, nullptr)); | 
 | } | 
 |  | 
 | TEST_F(AuthManagerClaimTest, CreateAccessTokenFromAuth) { | 
 |   std::vector<uint8_t> access_token; | 
 |   AuthScope scope; | 
 |   base::TimeDelta ttl; | 
 |   EXPECT_TRUE(auth_.CreateAccessTokenFromAuth( | 
 |       auth_.GetRootClientAuthToken(), base::TimeDelta::FromDays(1), | 
 |       &access_token, &scope, &ttl, nullptr)); | 
 |   UserInfo user_info; | 
 |   EXPECT_TRUE(auth_.ParseAccessToken(access_token, &user_info, nullptr)); | 
 |   EXPECT_EQ(scope, user_info.scope()); | 
 |   EXPECT_FALSE(user_info.user_id().empty()); | 
 | } | 
 |  | 
 | }  // namespace privet | 
 | }  // namespace weave |