blob: 1914091d56e20533336975d85fb16215fc78dffb [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/xmpp_register_support_host_request.h"
#include <stdint.h>
#include <optional>
#include "base/functional/bind.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringize_macros.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "remoting/base/constants.h"
#include "remoting/base/rsa_key_pair.h"
#include "remoting/base/test_rsa_key_pair.h"
#include "remoting/host/host_details.h"
#include "remoting/protocol/errors.h"
#include "remoting/signaling/iq_sender.h"
#include "remoting/signaling/mock_signal_strategy.h"
#include "remoting/signaling/signaling_address.h"
#include "remoting/signaling/xmpp_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
using jingle_xmpp::QName;
using jingle_xmpp::XmlElement;
using testing::_;
using testing::DeleteArg;
using testing::DoAll;
using testing::Invoke;
using testing::NotNull;
using testing::Return;
using testing::SaveArg;
namespace remoting {
using protocol::ErrorCode;
namespace {
const char kTestBotJid[] = "remotingunittest@bot.talk.google.com";
const char kTestJid[] = "User@gmail.com/chromotingABC123";
const char kTestJidNormalized[] = "user@gmail.com/chromotingABC123";
const char kTestAuthorizedHelper[] = "helpful_dude@chromoting.com";
const char kSupportId[] = "AB4RF3";
const char kSupportIdLifetime[] = "300";
const char kStanzaId[] = "123";
ACTION_P(AddListener, list) {
list->AddObserver(arg0);
}
ACTION_P(RemoveListener, list) {
list->RemoveObserver(arg0);
}
} // namespace
class XmppRegisterSupportHostRequestTest : public testing::Test {
public:
protected:
XmppRegisterSupportHostRequestTest()
: signal_strategy_(SignalingAddress(kTestJid)) {}
void SetUp() override {
key_pair_ = RsaKeyPair::FromString(kTestRsaKeyPair);
ASSERT_TRUE(key_pair_.get());
EXPECT_CALL(signal_strategy_, AddListener(NotNull()))
.WillRepeatedly(AddListener(&signal_strategy_listeners_));
EXPECT_CALL(signal_strategy_, RemoveListener(NotNull()))
.WillRepeatedly(RemoveListener(&signal_strategy_listeners_));
}
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
MockSignalStrategy signal_strategy_;
base::ObserverList<SignalStrategy::Listener, true> signal_strategy_listeners_;
scoped_refptr<RsaKeyPair> key_pair_;
std::string authorized_helper_;
base::MockCallback<RegisterSupportHostRequest::RegisterCallback> callback_;
};
TEST_F(XmppRegisterSupportHostRequestTest, Timeout) {
auto request = std::make_unique<XmppRegisterSupportHostRequest>(kTestBotJid);
request->StartRequest(&signal_strategy_, key_pair_, authorized_helper_,
std::nullopt, callback_.Get());
EXPECT_CALL(signal_strategy_, GetNextId()).WillOnce(Return(kStanzaId));
EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
.WillOnce(DoAll(DeleteArg<0>(), Return(true)));
request->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
// Generate response and verify that callback is called.
EXPECT_CALL(callback_,
Run("", base::Seconds(0), ErrorCode::SIGNALING_TIMEOUT));
task_environment_.FastForwardBy(base::Seconds(15));
}
TEST_F(XmppRegisterSupportHostRequestTest, Send) {
// |iq_request| is freed by XmppRegisterSupportHostRequest.
int64_t start_time =
static_cast<int64_t>(base::Time::Now().InSecondsFSinceUnixEpoch());
auto request = std::make_unique<XmppRegisterSupportHostRequest>(kTestBotJid);
request->StartRequest(&signal_strategy_, key_pair_, authorized_helper_,
std::nullopt, callback_.Get());
XmlElement* sent_iq = nullptr;
EXPECT_CALL(signal_strategy_, GetNextId()).WillOnce(Return(kStanzaId));
EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
.WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
request->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
task_environment_.RunUntilIdle();
// Verify format of the query.
std::unique_ptr<XmlElement> stanza(sent_iq);
ASSERT_TRUE(stanza != nullptr);
EXPECT_EQ(stanza->Attr(jingle_xmpp::QName(std::string(), "to")),
std::string(kTestBotJid));
EXPECT_EQ(stanza->Attr(jingle_xmpp::QName(std::string(), "type")), "set");
EXPECT_EQ(QName(kChromotingXmlNamespace, "register-support-host"),
stanza->FirstElement()->Name());
QName signature_tag(kChromotingXmlNamespace, "signature");
XmlElement* signature = stanza->FirstElement()->FirstNamed(signature_tag);
ASSERT_TRUE(signature != nullptr);
EXPECT_TRUE(stanza->NextNamed(signature_tag) == nullptr);
std::string time_str =
signature->Attr(QName(kChromotingXmlNamespace, "time"));
int64_t time;
EXPECT_TRUE(base::StringToInt64(time_str, &time));
int64_t now =
static_cast<int64_t>(base::Time::Now().InSecondsFSinceUnixEpoch());
EXPECT_LE(start_time, time);
EXPECT_GE(now, time);
XmlElement* host_version = stanza->FirstElement()->FirstNamed(
QName(kChromotingXmlNamespace, "host-version"));
EXPECT_EQ(STRINGIZE(VERSION), host_version->BodyText());
XmlElement* host_os_name = stanza->FirstElement()->FirstNamed(
QName(kChromotingXmlNamespace, "host-os-name"));
EXPECT_EQ(GetHostOperatingSystemName(), host_os_name->BodyText());
XmlElement* host_os_version = stanza->FirstElement()->FirstNamed(
QName(kChromotingXmlNamespace, "host-os-version"));
EXPECT_EQ(GetHostOperatingSystemVersion(), host_os_version->BodyText());
XmlElement* authorized_helper = stanza->FirstElement()->FirstNamed(
QName(kChromotingXmlNamespace, "authorized-helper"));
EXPECT_EQ(authorized_helper, nullptr);
scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::FromString(kTestRsaKeyPair);
ASSERT_TRUE(key_pair.get());
std::string expected_signature =
key_pair->SignMessage(std::string(kTestJidNormalized) + ' ' + time_str);
EXPECT_EQ(expected_signature, signature->BodyText());
// Generate response and verify that callback is called.
EXPECT_CALL(callback_, Run(kSupportId, base::Seconds(300), ErrorCode::OK));
std::unique_ptr<XmlElement> response(new XmlElement(kQNameIq));
response->AddAttr(QName(std::string(), "from"), kTestBotJid);
response->AddAttr(QName(std::string(), "type"), "result");
response->AddAttr(QName(std::string(), "id"), kStanzaId);
XmlElement* result = new XmlElement(
QName(kChromotingXmlNamespace, "register-support-host-result"));
response->AddElement(result);
XmlElement* support_id =
new XmlElement(QName(kChromotingXmlNamespace, "support-id"));
support_id->AddText(kSupportId);
result->AddElement(support_id);
XmlElement* support_id_lifetime =
new XmlElement(QName(kChromotingXmlNamespace, "support-id-lifetime"));
support_id_lifetime->AddText(kSupportIdLifetime);
result->AddElement(support_id_lifetime);
int consumed = 0;
for (auto& listener : signal_strategy_listeners_) {
if (listener.OnSignalStrategyIncomingStanza(response.get())) {
consumed++;
}
}
EXPECT_EQ(1, consumed);
task_environment_.RunUntilIdle();
}
TEST_F(XmppRegisterSupportHostRequestTest, AuthorizedHelper) {
authorized_helper_ = kTestAuthorizedHelper;
auto request = std::make_unique<XmppRegisterSupportHostRequest>(kTestBotJid);
request->StartRequest(&signal_strategy_, key_pair_, authorized_helper_,
std::nullopt, callback_.Get());
XmlElement* sent_iq = nullptr;
EXPECT_CALL(signal_strategy_, GetNextId()).WillOnce(Return(kStanzaId));
EXPECT_CALL(signal_strategy_, SendStanzaPtr(NotNull()))
.WillOnce(DoAll(SaveArg<0>(&sent_iq), Return(true)));
request->OnSignalStrategyStateChange(SignalStrategy::CONNECTED);
task_environment_.RunUntilIdle();
std::unique_ptr<XmlElement> stanza(sent_iq);
ASSERT_NE(stanza, nullptr);
// Other fields are verified in the Send test case.
XmlElement* authorized_helper = stanza->FirstElement()->FirstNamed(
QName(kChromotingXmlNamespace, "authorized-helper"));
EXPECT_EQ(authorized_helper_, authorized_helper->BodyText());
}
} // namespace remoting