|  | // Copyright 2016 The Chromium 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 <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/callback.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "remoting/protocol/authenticator.h" | 
|  | #include "remoting/protocol/protocol_mock_objects.h" | 
|  | #include "remoting/protocol/validating_authenticator.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "third_party/libjingle_xmpp/xmllite/xmlelement.h" | 
|  |  | 
|  | namespace remoting { | 
|  | namespace protocol { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using testing::_; | 
|  | using testing::Return; | 
|  |  | 
|  | typedef ValidatingAuthenticator::Result ValidationResult; | 
|  |  | 
|  | constexpr char kRemoteTestJid[] = "ficticious_jid_for_testing"; | 
|  |  | 
|  | // testing::InvokeArgument<N> does not work with base::Callback, fortunately | 
|  | // gmock makes it simple to create action templates that do for the various | 
|  | // possible numbers of arguments. | 
|  | ACTION_TEMPLATE(InvokeCallbackArgument, | 
|  | HAS_1_TEMPLATE_PARAMS(int, k), | 
|  | AND_0_VALUE_PARAMS()) { | 
|  | ::std::tr1::get<k>(args).Run(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class ValidatingAuthenticatorTest : public testing::Test { | 
|  | public: | 
|  | ValidatingAuthenticatorTest(); | 
|  | ~ValidatingAuthenticatorTest() override; | 
|  |  | 
|  | void ValidateCallback( | 
|  | const std::string& remote_jid, | 
|  | const ValidatingAuthenticator::ResultCallback& callback); | 
|  |  | 
|  | protected: | 
|  | // testing::Test overrides. | 
|  | void SetUp() override; | 
|  |  | 
|  | // Calls ProcessMessage() on |validating_authenticator_| and blocks until | 
|  | // the result callback is called. | 
|  | void SendMessageAndWaitForCallback(); | 
|  |  | 
|  | // Used to set up our mock behaviors on the MockAuthenticator object passed | 
|  | // to |validating_authenticator_|.  Lifetime of the object is controlled by | 
|  | // |validating_authenticator_| so this pointer is no longer valid once | 
|  | // the owner is destroyed. | 
|  | testing::NiceMock<MockAuthenticator>* mock_authenticator_ = nullptr; | 
|  |  | 
|  | // This member is used to drive behavior in |validating_authenticator_| when | 
|  | // its validation complete callback is run. | 
|  | ValidationResult validation_result_ = ValidationResult::SUCCESS; | 
|  |  | 
|  | // Tracks whether our validation callback has been called or not. | 
|  | bool validate_complete_called_ = false; | 
|  |  | 
|  | // The object under test. | 
|  | std::unique_ptr<ValidatingAuthenticator> validating_authenticator_; | 
|  |  | 
|  | private: | 
|  | base::MessageLoop message_loop_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ValidatingAuthenticatorTest); | 
|  | }; | 
|  |  | 
|  | ValidatingAuthenticatorTest::ValidatingAuthenticatorTest() {} | 
|  |  | 
|  | ValidatingAuthenticatorTest::~ValidatingAuthenticatorTest() {} | 
|  |  | 
|  | void ValidatingAuthenticatorTest::ValidateCallback( | 
|  | const std::string& remote_jid, | 
|  | const ValidatingAuthenticator::ResultCallback& callback) { | 
|  | validate_complete_called_ = true; | 
|  | callback.Run(validation_result_); | 
|  | } | 
|  |  | 
|  | void ValidatingAuthenticatorTest::SetUp() { | 
|  | mock_authenticator_ = new testing::NiceMock<MockAuthenticator>(); | 
|  | std::unique_ptr<Authenticator> authenticator(mock_authenticator_); | 
|  |  | 
|  | validating_authenticator_.reset(new ValidatingAuthenticator( | 
|  | kRemoteTestJid, base::Bind(&ValidatingAuthenticatorTest::ValidateCallback, | 
|  | base::Unretained(this)), | 
|  | std::move(authenticator))); | 
|  | } | 
|  |  | 
|  | void ValidatingAuthenticatorTest::SendMessageAndWaitForCallback() { | 
|  | base::RunLoop run_loop; | 
|  | std::unique_ptr<buzz::XmlElement> first_message( | 
|  | Authenticator::CreateEmptyAuthenticatorMessage()); | 
|  | validating_authenticator_->ProcessMessage(first_message.get(), | 
|  | run_loop.QuitClosure()); | 
|  | run_loop.Run(); | 
|  | } | 
|  |  | 
|  | TEST_F(ValidatingAuthenticatorTest, ValidConnection_SingleMessage) { | 
|  | EXPECT_CALL(*mock_authenticator_, ProcessMessage(_, _)) | 
|  | .Times(1) | 
|  | .WillOnce(InvokeCallbackArgument<1>()); | 
|  |  | 
|  | ON_CALL(*mock_authenticator_, state()) | 
|  | .WillByDefault(Return(Authenticator::ACCEPTED)); | 
|  |  | 
|  | SendMessageAndWaitForCallback(); | 
|  | ASSERT_TRUE(validate_complete_called_); | 
|  | ASSERT_EQ(Authenticator::ACCEPTED, validating_authenticator_->state()); | 
|  | } | 
|  |  | 
|  | TEST_F(ValidatingAuthenticatorTest, ValidConnection_TwoMessages) { | 
|  | // Send the first message to the authenticator, set the mock up to act | 
|  | // like it is waiting for a second message. | 
|  | EXPECT_CALL(*mock_authenticator_, ProcessMessage(_, _)) | 
|  | .Times(2) | 
|  | .WillRepeatedly(InvokeCallbackArgument<1>()); | 
|  |  | 
|  | EXPECT_CALL(*mock_authenticator_, state()) | 
|  | .WillRepeatedly(Return(Authenticator::MESSAGE_READY)); | 
|  |  | 
|  | SendMessageAndWaitForCallback(); | 
|  | ASSERT_FALSE(validate_complete_called_); | 
|  | ASSERT_EQ(Authenticator::MESSAGE_READY, validating_authenticator_->state()); | 
|  |  | 
|  | // Now 'retrieve' the message for the client which resets the state. | 
|  | EXPECT_CALL(*mock_authenticator_, state()) | 
|  | .WillRepeatedly(Return(Authenticator::WAITING_MESSAGE)); | 
|  |  | 
|  | // This dance is needed because GMock doesn't handle unique_ptrs very well. | 
|  | // The mock method receives a raw pointer which it wraps and returns when | 
|  | // GetNextMessage() is called. | 
|  | std::unique_ptr<buzz::XmlElement> next_message( | 
|  | Authenticator::CreateEmptyAuthenticatorMessage()); | 
|  | EXPECT_CALL(*mock_authenticator_, GetNextMessagePtr()) | 
|  | .Times(1) | 
|  | .WillOnce(Return(next_message.release())); | 
|  |  | 
|  | validating_authenticator_->GetNextMessage(); | 
|  | ASSERT_EQ(Authenticator::WAITING_MESSAGE, validating_authenticator_->state()); | 
|  |  | 
|  | // Now send the second message for processing. | 
|  | EXPECT_CALL(*mock_authenticator_, state()) | 
|  | .WillRepeatedly(Return(Authenticator::ACCEPTED)); | 
|  |  | 
|  | SendMessageAndWaitForCallback(); | 
|  | ASSERT_TRUE(validate_complete_called_); | 
|  | ASSERT_EQ(Authenticator::ACCEPTED, validating_authenticator_->state()); | 
|  | } | 
|  |  | 
|  | TEST_F(ValidatingAuthenticatorTest, ValidConnection_SendBeforeAccept) { | 
|  | // This test simulates an authenticator which needs to send a message before | 
|  | // transitioning to the ACCEPTED state. | 
|  | EXPECT_CALL(*mock_authenticator_, ProcessMessage(_, _)) | 
|  | .Times(1) | 
|  | .WillRepeatedly(InvokeCallbackArgument<1>()); | 
|  |  | 
|  | EXPECT_CALL(*mock_authenticator_, state()) | 
|  | .WillOnce(Return(Authenticator::MESSAGE_READY)) | 
|  | .WillOnce(Return(Authenticator::ACCEPTED)); | 
|  |  | 
|  | // This dance is needed because GMock doesn't handle unique_ptrs very well. | 
|  | // The mock method receives a raw pointer which it wraps and returns when | 
|  | // GetNextMessage() is called. | 
|  | std::unique_ptr<buzz::XmlElement> next_message( | 
|  | Authenticator::CreateEmptyAuthenticatorMessage()); | 
|  | EXPECT_CALL(*mock_authenticator_, GetNextMessagePtr()) | 
|  | .Times(1) | 
|  | .WillOnce(Return(next_message.release())); | 
|  |  | 
|  | SendMessageAndWaitForCallback(); | 
|  | ASSERT_TRUE(validate_complete_called_); | 
|  | ASSERT_EQ(Authenticator::MESSAGE_READY, validating_authenticator_->state()); | 
|  |  | 
|  | // Now 'retrieve' the message for the client which resets the state. | 
|  | validating_authenticator_->GetNextMessage(); | 
|  | ASSERT_EQ(Authenticator::ACCEPTED, validating_authenticator_->state()); | 
|  | } | 
|  |  | 
|  | TEST_F(ValidatingAuthenticatorTest, ValidConnection_ErrorInvalidCredentials) { | 
|  | EXPECT_CALL(*mock_authenticator_, ProcessMessage(_, _)) | 
|  | .Times(1) | 
|  | .WillOnce(InvokeCallbackArgument<1>()); | 
|  |  | 
|  | ON_CALL(*mock_authenticator_, state()) | 
|  | .WillByDefault(Return(Authenticator::ACCEPTED)); | 
|  |  | 
|  | validation_result_ = ValidationResult::ERROR_INVALID_CREDENTIALS; | 
|  |  | 
|  | SendMessageAndWaitForCallback(); | 
|  | ASSERT_TRUE(validate_complete_called_); | 
|  | ASSERT_EQ(Authenticator::REJECTED, validating_authenticator_->state()); | 
|  | ASSERT_EQ(Authenticator::INVALID_CREDENTIALS, | 
|  | validating_authenticator_->rejection_reason()); | 
|  | } | 
|  |  | 
|  | TEST_F(ValidatingAuthenticatorTest, ValidConnection_ErrorRejectedByUser) { | 
|  | EXPECT_CALL(*mock_authenticator_, ProcessMessage(_, _)) | 
|  | .Times(1) | 
|  | .WillOnce(InvokeCallbackArgument<1>()); | 
|  |  | 
|  | ON_CALL(*mock_authenticator_, state()) | 
|  | .WillByDefault(Return(Authenticator::ACCEPTED)); | 
|  |  | 
|  | validation_result_ = ValidationResult::ERROR_REJECTED_BY_USER; | 
|  |  | 
|  | SendMessageAndWaitForCallback(); | 
|  | ASSERT_TRUE(validate_complete_called_); | 
|  | ASSERT_EQ(Authenticator::REJECTED, validating_authenticator_->state()); | 
|  | ASSERT_EQ(Authenticator::REJECTED_BY_USER, | 
|  | validating_authenticator_->rejection_reason()); | 
|  | } | 
|  |  | 
|  | TEST_F(ValidatingAuthenticatorTest, ValidConnection_ErrorTooManyConnections) { | 
|  | EXPECT_CALL(*mock_authenticator_, ProcessMessage(_, _)) | 
|  | .Times(1) | 
|  | .WillOnce(InvokeCallbackArgument<1>()); | 
|  |  | 
|  | ON_CALL(*mock_authenticator_, state()) | 
|  | .WillByDefault(Return(Authenticator::ACCEPTED)); | 
|  |  | 
|  | validation_result_ = ValidationResult::ERROR_TOO_MANY_CONNECTIONS; | 
|  |  | 
|  | SendMessageAndWaitForCallback(); | 
|  | ASSERT_TRUE(validate_complete_called_); | 
|  | ASSERT_EQ(Authenticator::REJECTED, validating_authenticator_->state()); | 
|  | ASSERT_EQ(Authenticator::TOO_MANY_CONNECTIONS, | 
|  | validating_authenticator_->rejection_reason()); | 
|  | } | 
|  |  | 
|  | TEST_F(ValidatingAuthenticatorTest, InvalidConnection_InvalidCredentials) { | 
|  | EXPECT_CALL(*mock_authenticator_, ProcessMessage(_, _)) | 
|  | .Times(1) | 
|  | .WillOnce(InvokeCallbackArgument<1>()); | 
|  |  | 
|  | ON_CALL(*mock_authenticator_, state()) | 
|  | .WillByDefault(Return(Authenticator::REJECTED)); | 
|  |  | 
|  | ON_CALL(*mock_authenticator_, rejection_reason()) | 
|  | .WillByDefault(Return(Authenticator::INVALID_CREDENTIALS)); | 
|  |  | 
|  | // Verify validation callback is not called for invalid connections. | 
|  | SendMessageAndWaitForCallback(); | 
|  | ASSERT_FALSE(validate_complete_called_); | 
|  | ASSERT_EQ(Authenticator::REJECTED, validating_authenticator_->state()); | 
|  | ASSERT_EQ(Authenticator::INVALID_CREDENTIALS, | 
|  | validating_authenticator_->rejection_reason()); | 
|  | } | 
|  |  | 
|  | TEST_F(ValidatingAuthenticatorTest, InvalidConnection_InvalidAccount) { | 
|  | EXPECT_CALL(*mock_authenticator_, ProcessMessage(_, _)) | 
|  | .Times(1) | 
|  | .WillOnce(InvokeCallbackArgument<1>()); | 
|  |  | 
|  | ON_CALL(*mock_authenticator_, state()) | 
|  | .WillByDefault(Return(Authenticator::REJECTED)); | 
|  |  | 
|  | ON_CALL(*mock_authenticator_, rejection_reason()) | 
|  | .WillByDefault(Return(Authenticator::INVALID_ACCOUNT)); | 
|  |  | 
|  | // Verify validation callback is not called for invalid connections. | 
|  | SendMessageAndWaitForCallback(); | 
|  | ASSERT_FALSE(validate_complete_called_); | 
|  | ASSERT_EQ(Authenticator::REJECTED, validating_authenticator_->state()); | 
|  | ASSERT_EQ(Authenticator::INVALID_ACCOUNT, | 
|  | validating_authenticator_->rejection_reason()); | 
|  | } | 
|  |  | 
|  | TEST_F(ValidatingAuthenticatorTest, InvalidConnection_ProtocolError) { | 
|  | EXPECT_CALL(*mock_authenticator_, ProcessMessage(_, _)) | 
|  | .Times(1) | 
|  | .WillOnce(InvokeCallbackArgument<1>()); | 
|  |  | 
|  | ON_CALL(*mock_authenticator_, state()) | 
|  | .WillByDefault(Return(Authenticator::REJECTED)); | 
|  |  | 
|  | ON_CALL(*mock_authenticator_, rejection_reason()) | 
|  | .WillByDefault(Return(Authenticator::PROTOCOL_ERROR)); | 
|  |  | 
|  | // Verify validation callback is not called for invalid connections. | 
|  | SendMessageAndWaitForCallback(); | 
|  | ASSERT_FALSE(validate_complete_called_); | 
|  | ASSERT_EQ(Authenticator::REJECTED, validating_authenticator_->state()); | 
|  | ASSERT_EQ(Authenticator::PROTOCOL_ERROR, | 
|  | validating_authenticator_->rejection_reason()); | 
|  | } | 
|  |  | 
|  | }  // namespace protocol | 
|  | }  // namespace remoting |