| // 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/privet/mock_delegates.h" |
| #include "src/test/mock_access_revocation_manager.h" |
| #include "src/test/mock_clock.h" |
| |
| using testing::_; |
| using testing::Return; |
| using testing::SaveArg; |
| using testing::StrictMock; |
| |
| namespace weave { |
| namespace privet { |
| |
| class MockAccessRevocationManager : public test::MockAccessRevocationManager { |
| public: |
| MockAccessRevocationManager() { |
| EXPECT_CALL(*this, AddEntryAddedCallback(_)) |
| .WillOnce(SaveArg<0>(&changed_callback_)); |
| EXPECT_CALL(*this, IsBlocked(_, _, _)).WillRepeatedly(Return(false)); |
| } |
| |
| base::Closure changed_callback_; |
| }; |
| |
| 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: |
| std::vector<uint8_t> DelegateToUser(const std::vector<uint8_t>& token, |
| base::TimeDelta ttl, |
| const UserInfo& user_info) const { |
| return auth_.DelegateToUser(token, ttl, user_info); |
| } |
| 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_; |
| StrictMock<MockAccessRevocationManager> black_list_; |
| AuthManager auth_{kSecret1, kFingerprint, kSecret2, &clock_, &black_list_}; |
| }; |
| |
| 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("WC2FRggaG52hAEIBFEYJRDIzNABCCkBGBRobnaEAUFAF46oQlMmXgnLstt7wU2w=", |
| Base64Encode(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, TestUserId{"234"}}, {}))); |
| EXPECT_EQ("WC2FRggaG52hAEIBCEYJRDI1NwBCCkBGBRobnaEAUEdWRNHcu/0mA6c3e0tgDrk=", |
| Base64Encode(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kManager, TestUserId{"257"}}, {}))); |
| EXPECT_EQ("WC2FRggaG52hAEIBAkYJRDQ1NgBCCkBGBRobnaEAUH2ZLgUPdTtjNRa+PoDkMW4=", |
| Base64Encode(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kOwner, TestUserId{"456"}}, {}))); |
| auto new_time = clock_.Now() + base::TimeDelta::FromDays(11); |
| EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); |
| EXPECT_EQ("WC2FRggaG6whgEIBDkYJRDM0NQBCCkBGBRobrCGAUDAFptj7bbYmbpaa6Wpb1Wo=", |
| Base64Encode(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kUser, TestUserId{"345"}}, {}))); |
| } |
| |
| TEST_F(AuthManagerTest, CreateSameToken) { |
| EXPECT_EQ(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, TestUserId{"555"}}, {}), |
| auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, TestUserId{"555"}}, {})); |
| } |
| |
| TEST_F(AuthManagerTest, CreateSameTokenWithApp) { |
| EXPECT_EQ(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, |
| {AuthType::kLocal, {1, 2, 3}, {4, 5, 6}}}, |
| {}), |
| auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, |
| {AuthType::kLocal, {1, 2, 3}, {4, 5, 6}}}, |
| {})); |
| } |
| |
| TEST_F(AuthManagerTest, CreateSameTokenWithDifferentType) { |
| EXPECT_NE(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, |
| {AuthType::kLocal, {1, 2, 3}, {4, 5, 6}}}, |
| {}), |
| auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, |
| {AuthType::kPairing, {1, 2, 3}, {4, 5, 6}}}, |
| {})); |
| } |
| |
| TEST_F(AuthManagerTest, CreateSameTokenWithDifferentApp) { |
| EXPECT_NE(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, |
| {AuthType::kLocal, {1, 2, 3}, {4, 5, 6}}}, |
| {}), |
| auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, |
| {AuthType::kLocal, {1, 2, 3}, {4, 5, 7}}}, |
| {})); |
| } |
| |
| TEST_F(AuthManagerTest, CreateTokenDifferentScope) { |
| EXPECT_NE(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, TestUserId{"456"}}, {}), |
| auth_.CreateAccessToken( |
| UserInfo{AuthScope::kOwner, TestUserId{"456"}}, {})); |
| } |
| |
| TEST_F(AuthManagerTest, CreateTokenDifferentUser) { |
| EXPECT_NE(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kOwner, TestUserId{"456"}}, {}), |
| auth_.CreateAccessToken( |
| UserInfo{AuthScope::kOwner, TestUserId{"789"}}, {})); |
| } |
| |
| TEST_F(AuthManagerTest, CreateTokenDifferentTime) { |
| auto token = auth_.CreateAccessToken( |
| UserInfo{AuthScope::kOwner, TestUserId{"567"}}, {}); |
| EXPECT_CALL(clock_, Now()) |
| .WillRepeatedly(Return(base::Time::FromTimeT(1400000000))); |
| EXPECT_NE(token, auth_.CreateAccessToken( |
| UserInfo{AuthScope::kOwner, TestUserId{"567"}}, {})); |
| } |
| |
| TEST_F(AuthManagerTest, CreateTokenDifferentInstance) { |
| EXPECT_NE(auth_.CreateAccessToken( |
| UserInfo{AuthScope::kUser, TestUserId{"123"}}, {}), |
| AuthManager({}, nullptr, {}) |
| .CreateAccessToken( |
| UserInfo{AuthScope::kUser, TestUserId{"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, TestUserId{"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(TestUserId{"5"}, user_info.id()); |
| |
| EXPECT_CALL(clock_, Now()) |
| .WillRepeatedly(Return(kStartTime + base::TimeDelta::FromSeconds(i))); |
| EXPECT_TRUE(auth.ParseAccessToken(token, &user_info, nullptr)); |
| |
| auto extended = |
| DelegateToUser(token, base::TimeDelta::FromSeconds(1000), |
| UserInfo{AuthScope::kUser, TestUserId{"234"}}); |
| EXPECT_FALSE(auth.ParseAccessToken(extended, &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, AccessTokenAfterReset) { |
| UserInfo user_info; |
| auto token1 = auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, TestUserId{"555"}}, {}); |
| EXPECT_TRUE(auth_.ParseAccessToken(token1, &user_info, nullptr)); |
| |
| black_list_.changed_callback_.Run(); |
| |
| auto token2 = auth_.CreateAccessToken( |
| UserInfo{AuthScope::kViewer, TestUserId{"555"}}, {}); |
| |
| EXPECT_NE(token1, token2); |
| EXPECT_FALSE(auth_.ParseAccessToken(token1, &user_info, nullptr)); |
| EXPECT_TRUE(auth_.ParseAccessToken(token2, &user_info, nullptr)); |
| } |
| |
| TEST_F(AuthManagerTest, GetRootClientAuthToken) { |
| EXPECT_EQ("WCCDQxkgAUYIGhudoQBCDABQX3fPR5zsPnrs9aOSvS7/eQ==", |
| Base64Encode( |
| auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient))); |
| } |
| |
| TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentOwner) { |
| EXPECT_EQ( |
| "WCCDQxkgAUYIGhudoQBCDAFQRrCQfq+g7drVwCbxpug4Ig==", |
| Base64Encode(auth_.GetRootClientAuthToken(RootClientTokenOwner::kCloud))); |
| } |
| |
| TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentTime) { |
| auto new_time = clock_.Now() + base::TimeDelta::FromDays(15); |
| EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); |
| EXPECT_EQ("WCCDQxkgAUYIGhuxZ4BCDABQ9omJ3ooMrcYUZ1DiVqNKAA==", |
| Base64Encode( |
| auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient))); |
| } |
| |
| TEST_F(AuthManagerTest, GetRootClientAuthTokenDifferentSecret) { |
| AuthManager auth{kSecret2, {}, kSecret1, &clock_}; |
| EXPECT_EQ( |
| "WCCDQxkgAUYIGhudoQBCDABQWl2CB8CiP1BiV0ksnnkQQQ==", |
| Base64Encode(auth.GetRootClientAuthToken(RootClientTokenOwner::kClient))); |
| } |
| |
| TEST_F(AuthManagerTest, IsValidAuthToken) { |
| EXPECT_TRUE(auth_.IsValidAuthToken( |
| auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient), nullptr)); |
| // Multiple attempts with random secrets. |
| for (size_t i = 0; i < 1000; ++i) { |
| AuthManager auth{{}, {}, {}, &clock_}; |
| |
| auto token = auth.GetRootClientAuthToken(RootClientTokenOwner::kClient); |
| EXPECT_FALSE(auth_.IsValidAuthToken(token, nullptr)); |
| EXPECT_TRUE(auth.IsValidAuthToken(token, nullptr)); |
| } |
| } |
| |
| TEST_F(AuthManagerTest, CreateSessionId) { |
| EXPECT_EQ("463315200:1", auth_.CreateSessionId()); |
| } |
| |
| TEST_F(AuthManagerTest, IsValidSessionId) { |
| EXPECT_TRUE(auth_.IsValidSessionId("463315200:1")); |
| EXPECT_TRUE(auth_.IsValidSessionId("463315200:2")); |
| EXPECT_TRUE(auth_.IsValidSessionId("463315150")); |
| |
| // Future |
| EXPECT_FALSE(auth_.IsValidSessionId("463315230:1")); |
| |
| // Expired |
| EXPECT_FALSE(auth_.IsValidSessionId("463315100:1")); |
| } |
| |
| TEST_F(AuthManagerTest, CreateAccessTokenFromAuth) { |
| std::vector<uint8_t> access_token; |
| AuthScope scope; |
| base::TimeDelta ttl; |
| auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kCloud); |
| auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000), |
| UserInfo{AuthScope::kUser, TestUserId{"234"}}); |
| EXPECT_EQ( |
| "WEWIQxkgAUYIGhudoQBCDAFGCBobnaEARgUaG52k6EIBDkUJQzIzNE0RSzQ2MzMxNTIwMDox" |
| "UIujtnCZlcRvJPOra5BwM6E=", |
| Base64Encode(extended)); |
| EXPECT_TRUE( |
| auth_.CreateAccessTokenFromAuth(extended, 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_EQ(AuthScope::kUser, user_info.scope()); |
| |
| EXPECT_EQ(TestUserId{"234"}, user_info.id()); |
| } |
| |
| TEST_F(AuthManagerTest, CreateAccessTokenFromAuthNotMinted) { |
| std::vector<uint8_t> access_token; |
| auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); |
| ErrorPtr error; |
| EXPECT_FALSE(auth_.CreateAccessTokenFromAuth( |
| root, base::TimeDelta::FromDays(1), nullptr, nullptr, nullptr, &error)); |
| EXPECT_TRUE(error->HasError("invalidAuthCode")); |
| } |
| |
| TEST_F(AuthManagerTest, CreateAccessTokenFromAuthValidateAfterSomeTime) { |
| auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); |
| auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000), |
| UserInfo{AuthScope::kUser, TestUserId{"234"}}); |
| |
| // new_time < session_id_expiration < token_expiration. |
| auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(15); |
| EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); |
| EXPECT_TRUE( |
| auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), |
| nullptr, nullptr, nullptr, nullptr)); |
| } |
| |
| TEST_F(AuthManagerTest, CreateAccessTokenFromAuthExpired) { |
| auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); |
| auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(10), |
| UserInfo{AuthScope::kUser, TestUserId{"234"}}); |
| ErrorPtr error; |
| |
| // token_expiration < new_time < session_id_expiration. |
| auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(15); |
| EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); |
| EXPECT_FALSE( |
| auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), |
| nullptr, nullptr, nullptr, &error)); |
| EXPECT_TRUE(error->HasError("invalidAuthCode")); |
| } |
| |
| TEST_F(AuthManagerTest, CreateAccessTokenFromAuthExpiredSessionid) { |
| auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kClient); |
| auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000), |
| UserInfo{AuthScope::kUser, TestUserId{"234"}}); |
| ErrorPtr error; |
| |
| // session_id_expiration < new_time < token_expiration. |
| auto new_time = clock_.Now() + base::TimeDelta::FromSeconds(200); |
| EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(new_time)); |
| EXPECT_FALSE( |
| auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), |
| nullptr, nullptr, nullptr, &error)); |
| EXPECT_TRUE(error->HasError("invalidAuthCode")); |
| } |
| |
| TEST_F(AuthManagerTest, CreateAccessTokenFromAuthRevoked) { |
| TestUserId user{"234"}; |
| EXPECT_CALL(black_list_, IsBlocked(user.user, _, clock_.Now())) |
| .WillOnce(Return(true)); |
| std::vector<uint8_t> access_token; |
| auto root = auth_.GetRootClientAuthToken(RootClientTokenOwner::kCloud); |
| auto extended = DelegateToUser(root, base::TimeDelta::FromSeconds(1000), |
| UserInfo{AuthScope::kUser, user}); |
| ErrorPtr error; |
| EXPECT_FALSE( |
| auth_.CreateAccessTokenFromAuth(extended, base::TimeDelta::FromDays(1), |
| nullptr, nullptr, nullptr, &error)); |
| EXPECT_TRUE(error->HasError("invalidAuthCode")); |
| } |
| |
| 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_, nullptr, {}}; |
| }; |
| |
| 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)); |
| } |
| |
| } // namespace privet |
| } // namespace weave |