diff options
author | Vitaly Buka <vitalybuka@google.com> | 2015-12-17 20:57:01 -0800 |
---|---|---|
committer | Vitaly Buka <vitalybuka@google.com> | 2015-12-21 19:25:33 +0000 |
commit | fd2ef6869df7fdd2eb32e17bd7854df478b1f031 (patch) | |
tree | d167887273643e2f14dff79d2c49c44419c2aa43 /src | |
parent | 29bd07079198a78c59f2d8964d66e98ca0613c68 (diff) | |
download | libweave-fd2ef6869df7fdd2eb32e17bd7854df478b1f031.tar.gz |
Move most of auth logic into SecurityDelegate::CreateAccessToken
With local auth we will need to extract most of information from
macaroon auth code.
BUG=25768507
Change-Id: If7b31a1ba9a081dfae0cf8e9df6c8ed27bfe79c4
Reviewed-on: https://weave-review.googlesource.com/2049
Reviewed-by: Vitaly Buka <vitalybuka@google.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/privet/mock_delegates.h | 17 | ||||
-rw-r--r-- | src/privet/privet_handler.cc | 73 | ||||
-rw-r--r-- | src/privet/privet_handler.h | 1 | ||||
-rw-r--r-- | src/privet/privet_handler_unittest.cc | 20 | ||||
-rw-r--r-- | src/privet/security_delegate.h | 9 | ||||
-rw-r--r-- | src/privet/security_manager.cc | 44 | ||||
-rw-r--r-- | src/privet/security_manager.h | 10 | ||||
-rw-r--r-- | src/privet/security_manager_unittest.cc | 26 |
8 files changed, 132 insertions, 68 deletions
diff --git a/src/privet/mock_delegates.h b/src/privet/mock_delegates.h index bc7ec93..1586846 100644 --- a/src/privet/mock_delegates.h +++ b/src/privet/mock_delegates.h @@ -62,8 +62,14 @@ class MockDeviceDelegate : public DeviceDelegate { class MockSecurityDelegate : public SecurityDelegate { public: - MOCK_CONST_METHOD2(CreateAccessToken, - std::string(const UserInfo&, base::TimeDelta)); + MOCK_METHOD7(CreateAccessToken, + bool(AuthType, + const std::string&, + AuthScope, + std::string*, + AuthScope*, + base::TimeDelta*, + ErrorPtr*)); MOCK_CONST_METHOD3(ParseAccessToken, bool(const std::string&, UserInfo*, ErrorPtr*)); MOCK_CONST_METHOD0(GetPairingTypes, std::set<PairingType>()); @@ -84,8 +90,11 @@ class MockSecurityDelegate : public SecurityDelegate { MOCK_METHOD0(CreateSessionId, std::string()); MockSecurityDelegate() { - EXPECT_CALL(*this, CreateAccessToken(_, _)) - .WillRepeatedly(Return("GuestAccessToken")); + EXPECT_CALL(*this, CreateAccessToken(_, _, _, _, _, _, _)) + .WillRepeatedly(DoAll( + SetArgPointee<3>("GuestAccessToken"), + SetArgPointee<4>(AuthScope::kViewer), + SetArgPointee<5>(base::TimeDelta::FromSeconds(15)), Return(true))); EXPECT_CALL(*this, ClaimRootClientAuthToken(_)) .WillRepeatedly(Return("RootClientAuthToken")); diff --git a/src/privet/privet_handler.cc b/src/privet/privet_handler.cc index b55053d..ef0c54e 100644 --- a/src/privet/privet_handler.cc +++ b/src/privet/privet_handler.cc @@ -116,8 +116,6 @@ const char kWaitTimeoutKey[] = "waitTimeout"; const char kInvalidParamValueFormat[] = "Invalid parameter: '%s'='%s'"; -const int kAccessTokenExpirationSeconds = 3600; - template <class Container> std::unique_ptr<base::ListValue> ToValue(const Container& list) { std::unique_ptr<base::ListValue> value_list(new base::ListValue()); @@ -151,14 +149,6 @@ struct { {errors::kAlreadyClaimed, http::kDenied}, }; -AuthScope AuthScopeFromString(const std::string& scope, AuthScope auto_scope) { - if (scope == kAuthScopeAutoValue) - return auto_scope; - AuthScope scope_id = AuthScope::kNone; - StringToEnum(scope, &scope_id); - return scope_id; -} - std::string GetAuthTokenFromAuthHeader(const std::string& auth_header) { return SplitAtFirst(auth_header, " ", true).second; } @@ -684,58 +674,49 @@ void PrivetHandler::HandleAuth(const base::DictionaryValue& input, return ReturnError(*error, callback); } - std::string auth_code; - input.GetString(kAuthCodeKey, &auth_code); + AuthScope desired_scope = AuthScope::kOwner; + AuthScope acceptable_scope = AuthScope::kViewer; - AuthScope max_auth_scope = AuthScope::kNone; - switch (auth_type) { - case AuthType::kAnonymous: - max_auth_scope = GetAnonymousMaxScope(*cloud_, wifi_); - break; - case AuthType::kPairing: - if (!security_->IsValidPairingCode(auth_code)) { - Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, - errors::kInvalidAuthCode, kInvalidParamValueFormat, - kAuthCodeKey, auth_code.c_str()); - return ReturnError(*error, callback); - } - max_auth_scope = AuthScope::kOwner; - break; - default: + std::string requested_scope; + input.GetString(kAuthRequestedScopeKey, &requested_scope); + if (requested_scope != kAuthScopeAutoValue) { + if (!StringToEnum(requested_scope, &desired_scope)) { Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, - errors::kInvalidAuthMode, kInvalidParamValueFormat, - kAuthModeKey, auth_code_type.c_str()); + errors::kInvalidRequestedScope, + kInvalidParamValueFormat, kAuthRequestedScopeKey, + requested_scope.c_str()); return ReturnError(*error, callback); + } + acceptable_scope = std::max(desired_scope, acceptable_scope); } - std::string requested_scope; - input.GetString(kAuthRequestedScopeKey, &requested_scope); + if (auth_type == AuthType::kAnonymous) + desired_scope = GetAnonymousMaxScope(*cloud_, wifi_); - AuthScope requested_auth_scope = - AuthScopeFromString(requested_scope, max_auth_scope); - if (requested_auth_scope == AuthScope::kNone) { - Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, - errors::kInvalidRequestedScope, kInvalidParamValueFormat, - kAuthRequestedScopeKey, requested_scope.c_str()); + std::string auth_code; + input.GetString(kAuthCodeKey, &auth_code); + + std::string access_token; + base::TimeDelta access_token_ttl; + AuthScope access_token_scope = AuthScope::kNone; + if (!security_->CreateAccessToken(auth_type, auth_code, desired_scope, + &access_token, &access_token_scope, + &access_token_ttl, &error)) { return ReturnError(*error, callback); } - if (requested_auth_scope > max_auth_scope) { + if (access_token_scope < acceptable_scope) { Error::AddToPrintf(&error, FROM_HERE, errors::kDomain, errors::kAccessDenied, "Scope '%s' is not allowed", - EnumToString(requested_auth_scope).c_str()); + EnumToString(access_token_scope).c_str()); return ReturnError(*error, callback); } base::DictionaryValue output; - output.SetString( - kAuthAccessTokenKey, - security_->CreateAccessToken( - UserInfo{requested_auth_scope, ++last_user_id_}, - base::TimeDelta::FromSeconds(kAccessTokenExpirationSeconds))); + output.SetString(kAuthAccessTokenKey, access_token); output.SetString(kAuthTokenTypeKey, kAuthorizationHeaderPrefix); - output.SetInteger(kAuthExpiresInKey, kAccessTokenExpirationSeconds); - output.SetString(kAuthScopeKey, EnumToString(requested_auth_scope)); + output.SetInteger(kAuthExpiresInKey, access_token_ttl.InSeconds()); + output.SetString(kAuthScopeKey, EnumToString(access_token_scope)); callback.Run(http::kOk, output); } diff --git a/src/privet/privet_handler.h b/src/privet/privet_handler.h index 646cab2..4eba329 100644 --- a/src/privet/privet_handler.h +++ b/src/privet/privet_handler.h @@ -162,7 +162,6 @@ class PrivetHandler : public CloudDelegate::Observer { std::vector<UpdateRequestParameters> update_requests_; int last_update_request_id_{0}; - uint64_t last_user_id_{0}; uint64_t state_fingerprint_{1}; uint64_t traits_fingerprint_{1}; uint64_t components_fingerprint_{1}; diff --git a/src/privet/privet_handler_unittest.cc b/src/privet/privet_handler_unittest.cc index 53f1a05..7c9cf33 100644 --- a/src/privet/privet_handler_unittest.cc +++ b/src/privet/privet_handler_unittest.cc @@ -377,8 +377,11 @@ TEST_F(PrivetHandlerTest, AuthErrorAccessDenied) { } TEST_F(PrivetHandlerTest, AuthErrorInvalidAuthCode) { - EXPECT_CALL(security_, IsValidPairingCode("testToken")) - .WillRepeatedly(Return(false)); + auto set_error = [](ErrorPtr* error) { + Error::AddTo(error, FROM_HERE, errors::kDomain, "invalidAuthCode", ""); + }; + EXPECT_CALL(security_, CreateAccessToken(_, "testToken", _, _, _, _, _)) + .WillRepeatedly(DoAll(WithArgs<6>(Invoke(set_error)), Return(false))); const char kInput[] = R"({ 'mode': 'pairing', 'requestedScope': 'user', @@ -391,8 +394,8 @@ TEST_F(PrivetHandlerTest, AuthErrorInvalidAuthCode) { TEST_F(PrivetHandlerTest, AuthAnonymous) { const char kExpected[] = R"({ 'accessToken': 'GuestAccessToken', - 'expiresIn': 3600, - 'scope': 'user', + 'expiresIn': 15, + 'scope': 'viewer', 'tokenType': 'Privet' })"; EXPECT_JSON_EQ(kExpected, @@ -403,8 +406,11 @@ TEST_F(PrivetHandlerTest, AuthAnonymous) { TEST_F(PrivetHandlerTest, AuthPairing) { EXPECT_CALL(security_, IsValidPairingCode("testToken")) .WillRepeatedly(Return(true)); - EXPECT_CALL(security_, CreateAccessToken(_, _)) - .WillRepeatedly(Return("OwnerAccessToken")); + EXPECT_CALL(security_, CreateAccessToken(_, _, _, _, _, _, _)) + .WillRepeatedly(DoAll(SetArgPointee<3>("OwnerAccessToken"), + SetArgPointee<4>(AuthScope::kOwner), + SetArgPointee<5>(base::TimeDelta::FromSeconds(15)), + Return(true))); const char kInput[] = R"({ 'mode': 'pairing', 'requestedScope': 'owner', @@ -412,7 +418,7 @@ TEST_F(PrivetHandlerTest, AuthPairing) { })"; const char kExpected[] = R"({ 'accessToken': 'OwnerAccessToken', - 'expiresIn': 3600, + 'expiresIn': 15, 'scope': 'owner', 'tokenType': 'Privet' })"; diff --git a/src/privet/security_delegate.h b/src/privet/security_delegate.h index fdf9a84..c07b782 100644 --- a/src/privet/security_delegate.h +++ b/src/privet/security_delegate.h @@ -22,8 +22,13 @@ class SecurityDelegate { virtual ~SecurityDelegate() {} // Creates access token for the given scope, user id and |time|. - virtual std::string CreateAccessToken(const UserInfo& user_info, - base::TimeDelta ttl) const = 0; + virtual bool CreateAccessToken(AuthType auth_type, + const std::string& auth_code, + AuthScope desired_scope, + std::string* access_token, + AuthScope* granted_scope, + base::TimeDelta* ttl, + ErrorPtr* error) = 0; // Validates |token| and returns scope, user id parsed from that. virtual bool ParseAccessToken(const std::string& token, diff --git a/src/privet/security_manager.cc b/src/privet/security_manager.cc index d2b7b35..b30bb04 100644 --- a/src/privet/security_manager.cc +++ b/src/privet/security_manager.cc @@ -36,6 +36,8 @@ const int kPairingExpirationTimeMinutes = 5; const int kMaxAllowedPairingAttemts = 3; const int kPairingBlockingTimeMinutes = 1; +const int kAccessTokenExpirationSeconds = 3600; + class Spakep224Exchanger : public SecurityManager::KeyExchanger { public: explicit Spakep224Exchanger(const std::string& password) @@ -109,9 +111,45 @@ SecurityManager::~SecurityManager() { ClosePendingSession(pending_sessions_.begin()->first); } -std::string SecurityManager::CreateAccessToken(const UserInfo& user_info, - base::TimeDelta ttl) const { - return Base64Encode(auth_manager_->CreateAccessToken(user_info, ttl)); +bool SecurityManager::CreateAccessToken(AuthType auth_type, + const std::string& auth_code, + AuthScope desired_scope, + std::string* access_token, + AuthScope* access_token_scope, + base::TimeDelta* access_token_ttl, + ErrorPtr* error) { + switch (auth_type) { + case AuthType::kAnonymous: + break; + case AuthType::kPairing: + if (!IsValidPairingCode(auth_code)) { + Error::AddTo(error, FROM_HERE, errors::kDomain, + errors::kInvalidAuthCode, "Invalid authCode"); + return false; + } + break; + default: + Error::AddToPrintf(error, FROM_HERE, errors::kDomain, + errors::kInvalidAuthMode, "Unsupported auth mode"); + return false; + } + + UserInfo user_info{desired_scope, ++last_user_id_}; + base::TimeDelta ttl = + base::TimeDelta::FromSeconds(kAccessTokenExpirationSeconds); + + if (access_token) { + *access_token = + Base64Encode(auth_manager_->CreateAccessToken(user_info, ttl)); + } + + if (access_token_scope) + *access_token_scope = user_info.scope(); + + if (access_token_ttl) + *access_token_ttl = ttl; + + return true; } bool SecurityManager::ParseAccessToken(const std::string& token, diff --git a/src/privet/security_manager.h b/src/privet/security_manager.h index f65f7bd..beb7955 100644 --- a/src/privet/security_manager.h +++ b/src/privet/security_manager.h @@ -60,8 +60,13 @@ class SecurityManager : public SecurityDelegate { ~SecurityManager() override; // SecurityDelegate methods - std::string CreateAccessToken(const UserInfo& user_info, - base::TimeDelta ttl) const override; + bool CreateAccessToken(AuthType auth_type, + const std::string& auth_code, + AuthScope desired_scope, + std::string* access_token, + AuthScope* granted_scope, + base::TimeDelta* ttl, + ErrorPtr* error) override; bool ParseAccessToken(const std::string& token, UserInfo* user_info, ErrorPtr* error) const override; @@ -109,6 +114,7 @@ class SecurityManager : public SecurityDelegate { mutable base::Time block_pairing_until_; PairingStartListener on_start_; PairingEndListener on_end_; + uint64_t last_user_id_{0}; base::WeakPtrFactory<SecurityManager> weak_ptr_factory_{this}; diff --git a/src/privet/security_manager_unittest.cc b/src/privet/security_manager_unittest.cc index f48ec7f..6fc6b4c 100644 --- a/src/privet/security_manager_unittest.cc +++ b/src/privet/security_manager_unittest.cc @@ -97,6 +97,19 @@ class SecurityManagerTest : public testing::Test { std::string auth_code_base64{Base64Encode(auth_code)}; EXPECT_TRUE(security_.IsValidPairingCode(auth_code_base64)); + + std::string token; + AuthScope scope; + base::TimeDelta ttl; + EXPECT_TRUE(security_.CreateAccessToken(AuthType::kPairing, + auth_code_base64, AuthScope::kOwner, + &token, &scope, &ttl, nullptr)); + EXPECT_EQ(AuthScope::kOwner, scope); + EXPECT_EQ(base::TimeDelta::FromHours(1), ttl); + + UserInfo info; + EXPECT_TRUE(security_.ParseAccessToken(token, &info, nullptr)); + EXPECT_EQ(AuthScope::kOwner, info.scope()); } const base::Time time_ = base::Time::FromTimeT(1410000000); @@ -119,9 +132,16 @@ class SecurityManagerTest : public testing::Test { }; TEST_F(SecurityManagerTest, CreateAccessToken) { - EXPECT_EQ("TV18I+N7cDPah7Nq6o7pl5H7DjDu5nCDf/cbdE4FZFEyOjc6MTQxMDAwMDA2MA==", - security_.CreateAccessToken(UserInfo{AuthScope::kUser, 7}, - base::TimeDelta::FromMinutes(1))); + std::string token; + AuthScope scope; + base::TimeDelta ttl; + EXPECT_TRUE(security_.CreateAccessToken(AuthType::kAnonymous, "", + AuthScope::kUser, &token, &scope, + &ttl, nullptr)); + EXPECT_EQ("Cfz+qcndz8/Mo3ytgYlD7zn8qImkkdPsJVUNBmSOiXwyOjE6MTQxMDAwMzYwMA==", + token); + EXPECT_EQ(AuthScope::kUser, scope); + EXPECT_EQ(base::TimeDelta::FromHours(1), ttl); } TEST_F(SecurityManagerTest, ParseAccessToken) { |