blob: dd745c840952268399835d34302c9c78cd5ce53f [file] [log] [blame]
// Copyright 2022 The Chromium OS 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 <string>
#include <utility>
#include <base/run_loop.h>
#include <base/test/bind.h>
#include <gtest/gtest.h>
#include <mojo/public/cpp/bindings/remote.h>
#include "mojo_service_manager/daemon/mojo_test_environment.h"
#include "mojo_service_manager/daemon/service_manager.h"
#include "mojo_service_manager/daemon/service_policy_test_util.h"
#include "mojo_service_manager/daemon/test.mojom.h"
namespace chromeos {
namespace mojo_service_manager {
namespace {
class ServiceManagerTestBase : public ::testing::Test {
public:
explicit ServiceManagerTestBase(Configuration config)
: service_manager_(std::move(config),
CreateServicePolicyMapForTest({
{"FooService", {"owner", {"requester"}}},
})) {}
protected:
mojo::Remote<mojom::ServiceManager> ConnectServiceManagerAs(
const std::string& security_context) {
mojo::Remote<mojom::ServiceManager> remote;
service_manager_.AddReceiver(
mojom::ProcessIdentity::New(security_context, 0, 0, 0),
remote.BindNewPipeAndPassReceiver());
return remote;
}
MojoTaskEnvironment env_{base::test::TaskEnvironment::TimeSource::MOCK_TIME};
ServiceManager service_manager_;
};
class ServiceManagerTest : public ServiceManagerTestBase {
public:
ServiceManagerTest() : ServiceManagerTestBase(Configuration{}) {}
};
class PermissiveServiceManagerTest : public ServiceManagerTestBase {
public:
PermissiveServiceManagerTest()
: ServiceManagerTestBase(Configuration{.is_permissive = true}) {}
};
mojom::ErrorOrServiceStatePtr Query(
const mojo::Remote<mojom::ServiceManager>& service_manager,
const std::string& service_name) {
mojom::ErrorOrServiceStatePtr result;
base::RunLoop run_loop;
service_manager->Query(service_name,
base::BindLambdaForTesting(
[&](mojom::ErrorOrServiceStatePtr result_inner) {
result = std::move(result_inner);
run_loop.Quit();
}));
run_loop.Run();
return result;
}
class FakeServcieProvider : public mojom::ServiceProvider, public mojom::Foo {
public:
// Overrides mojom::ServiceProvider.
void Request(mojom::ProcessIdentityPtr client_identity,
mojo::ScopedMessagePipeHandle receiver) override {
CHECK(receiver.is_valid()) << "Receiver pipe is not valid.";
last_client_identity_ = std::move(client_identity);
foo_receiver_set_.Add(
this, mojo::PendingReceiver<mojom::Foo>(std::move(receiver)));
}
// Overrides mojom::Foo.
void Ping(PingCallback callback) override { std::move(callback).Run(); }
mojo::Receiver<mojom::ServiceProvider> receiver_{this};
mojo::ReceiverSet<mojom::Foo> foo_receiver_set_;
mojom::ProcessIdentityPtr last_client_identity_;
};
void ExpectServiceProviderDisconnectWithError(FakeServcieProvider* provider,
mojom::ErrorCode expected_error) {
base::RunLoop run_loop;
provider->receiver_.set_disconnect_with_reason_handler(
base::BindLambdaForTesting(
[&](uint32_t error, const std::string& message) {
EXPECT_EQ(error, static_cast<uint32_t>(expected_error));
run_loop.Quit();
}));
run_loop.Run();
}
class FakeServcieObserver : public mojom::ServiceObserver {
public:
// Overrides mojom::ServiceObserver.
void OnServiceEvent(mojom::ServiceEventPtr event) override {
last_event_ = std::move(event);
if (callback_) {
std::move(callback_).Run();
callback_.Reset();
}
}
mojo::Receiver<mojom::ServiceObserver> receiver_{this};
base::OnceClosure callback_;
mojom::ServiceEventPtr last_event_;
};
void ExpectServiceEvent(FakeServcieObserver* observer) {
base::RunLoop run_loop;
observer->callback_ = run_loop.QuitClosure();
run_loop.Run();
}
void ExpectFooServiceConnected(mojo::Remote<mojom::Foo>* service) {
service->set_disconnect_with_reason_handler(base::BindLambdaForTesting(
[&](uint32_t error, const std::string& message) {
CHECK(false) << "Reset with error: " << error
<< ",message: " << message;
}));
service->FlushForTesting();
CHECK(service->is_connected()) << "Foo service is disconnected.";
service->set_disconnect_with_reason_handler(base::DoNothing());
base::RunLoop run_loop;
service->get()->Ping(run_loop.QuitClosure());
run_loop.Run();
}
void ExpectFooServiceDisconnectWithError(mojo::Remote<mojom::Foo>* service,
mojom::ErrorCode expected_error) {
base::RunLoop run_loop;
service->set_disconnect_with_reason_handler(base::BindLambdaForTesting(
[&](uint32_t error, const std::string& message) {
EXPECT_EQ(error, static_cast<uint32_t>(expected_error));
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(ServiceManagerTest, RegisterAndUnregister) {
FakeServcieProvider povider;
ConnectServiceManagerAs("owner")->Register(
"FooService", povider.receiver_.BindNewPipeAndPassRemote());
EXPECT_EQ(Query(ConnectServiceManagerAs("requester"), "FooService"),
mojom::ErrorOrServiceState::NewState(mojom::ServiceState::New(
/*is_registered=*/true,
/*owner=*/mojom::ProcessIdentity::New("owner", 0, 0, 0))));
// Reset the receiver to unregister from service manager.
povider.receiver_.reset();
EXPECT_EQ(Query(ConnectServiceManagerAs("requester"), "FooService"),
mojom::ErrorOrServiceState::NewState(mojom::ServiceState::New(
/*is_registered=*/false, /*owner=*/nullptr)));
}
TEST_F(ServiceManagerTest, RegisterError) {
{
FakeServcieProvider povider;
ConnectServiceManagerAs("owner")->Register(
"NotFoundService", povider.receiver_.BindNewPipeAndPassRemote());
ExpectServiceProviderDisconnectWithError(
&povider, mojom::ErrorCode::kServiceNotFound);
}
{
FakeServcieProvider povider;
ConnectServiceManagerAs("not_owner")
->Register("FooService", povider.receiver_.BindNewPipeAndPassRemote());
ExpectServiceProviderDisconnectWithError(
&povider, mojom::ErrorCode::kPermissionDenied);
}
{
auto remote = ConnectServiceManagerAs("owner");
FakeServcieProvider povider1;
FakeServcieProvider povider2;
remote->Register("FooService",
povider1.receiver_.BindNewPipeAndPassRemote());
remote->Register("FooService",
povider2.receiver_.BindNewPipeAndPassRemote());
ExpectServiceProviderDisconnectWithError(
&povider2, mojom::ErrorCode::kServiceHasBeenRegistered);
}
}
TEST_F(ServiceManagerTest, Request) {
FakeServcieProvider provider;
ConnectServiceManagerAs("owner")->Register(
"FooService", provider.receiver_.BindNewPipeAndPassRemote());
mojo::Remote<mojom::Foo> foo;
ConnectServiceManagerAs("requester")
->Request("FooService", std::nullopt,
foo.BindNewPipeAndPassReceiver().PassPipe());
ExpectFooServiceConnected(&foo);
EXPECT_EQ(provider.last_client_identity_->security_context, "requester");
}
TEST_F(ServiceManagerTest, RequestBeforeRegister) {
// Request without a timeout (set timeout to std::nullopt) so it will wait
// until the service is registered.
mojo::Remote<mojom::Foo> foo;
ConnectServiceManagerAs("requester")
->Request("FooService", std::nullopt,
foo.BindNewPipeAndPassReceiver().PassPipe());
FakeServcieProvider provider;
ConnectServiceManagerAs("owner")->Register(
"FooService", provider.receiver_.BindNewPipeAndPassRemote());
ExpectFooServiceConnected(&foo);
EXPECT_EQ(provider.last_client_identity_->security_context, "requester");
}
TEST_F(ServiceManagerTest, RequestError) {
{
// Test service not found.
mojo::Remote<mojom::Foo> foo;
ConnectServiceManagerAs("requester")
->Request("NotFoundService", std::nullopt,
foo.BindNewPipeAndPassReceiver().PassPipe());
ExpectFooServiceDisconnectWithError(&foo,
mojom::ErrorCode::kServiceNotFound);
}
{
// Test permission denied.
mojo::Remote<mojom::Foo> foo;
ConnectServiceManagerAs("not_a_requester")
->Request("FooService", std::nullopt,
foo.BindNewPipeAndPassReceiver().PassPipe());
ExpectFooServiceDisconnectWithError(&foo,
mojom::ErrorCode::kPermissionDenied);
}
}
TEST_F(ServiceManagerTest, RequestTimeout) {
auto remote = ConnectServiceManagerAs("requester");
mojo::Remote<mojom::Foo> foo1;
remote->Request("FooService", base::Seconds(0),
foo1.BindNewPipeAndPassReceiver().PassPipe());
mojo::Remote<mojom::Foo> foo2;
remote->Request("FooService", base::Seconds(5),
foo2.BindNewPipeAndPassReceiver().PassPipe());
mojo::Remote<mojom::Foo> foo3;
remote->Request("FooService", base::Seconds(10),
foo3.BindNewPipeAndPassReceiver().PassPipe());
// No timeout.
mojo::Remote<mojom::Foo> foo4;
remote->Request("FooService", std::nullopt,
foo4.BindNewPipeAndPassReceiver().PassPipe());
// Wait for the first two timeout.
ExpectFooServiceDisconnectWithError(&foo1, mojom::ErrorCode::kTimeout);
ExpectFooServiceDisconnectWithError(&foo2, mojom::ErrorCode::kTimeout);
// Now it is at 5 seconds. Register the service so the rest of them can
// connected successfully.
FakeServcieProvider provider;
ConnectServiceManagerAs("owner")->Register(
"FooService", provider.receiver_.BindNewPipeAndPassRemote());
ExpectFooServiceConnected(&foo3);
ExpectFooServiceConnected(&foo4);
}
TEST_F(ServiceManagerTest, Query) {
EXPECT_EQ(Query(ConnectServiceManagerAs("requester"), "FooService"),
mojom::ErrorOrServiceState::NewState(mojom::ServiceState::New(
/*is_registered=*/false, /*owner=*/nullptr)));
}
TEST_F(ServiceManagerTest, QueryError) {
// Test service not found.
EXPECT_EQ(Query(ConnectServiceManagerAs("requester"), "NotFoundService")
->get_error()
->code,
mojom::ErrorCode::kServiceNotFound);
// Test permission denied.
EXPECT_EQ(Query(ConnectServiceManagerAs("not_requester"), "FooService")
->get_error()
->code,
mojom::ErrorCode::kPermissionDenied);
}
TEST_F(ServiceManagerTest, ServiceObserverGetEvent) {
FakeServcieObserver observer;
ConnectServiceManagerAs("requester")
->AddServiceObserver(observer.receiver_.BindNewPipeAndPassRemote());
FakeServcieProvider povider;
ConnectServiceManagerAs("owner")->Register(
"FooService", povider.receiver_.BindNewPipeAndPassRemote());
ExpectServiceEvent(&observer);
EXPECT_EQ(observer.last_event_,
mojom::ServiceEvent::New(
mojom::ServiceEvent::Type::kRegistered, "FooService",
mojom::ProcessIdentity::New("owner", 0, 0, 0)));
// Reset the receiver to unregister from service manager.
povider.receiver_.reset();
ExpectServiceEvent(&observer);
EXPECT_EQ(observer.last_event_,
mojom::ServiceEvent::New(
mojom::ServiceEvent::Type::kUnRegistered, "FooService",
mojom::ProcessIdentity::New("owner", 0, 0, 0)));
}
TEST_F(ServiceManagerTest, ServiceObserverNotRequester) {
FakeServcieObserver observer_not_a_requester;
ConnectServiceManagerAs("not_requester")
->AddServiceObserver(
observer_not_a_requester.receiver_.BindNewPipeAndPassRemote());
// Register a service and the observer should not receiver the event.
FakeServcieProvider provider;
ConnectServiceManagerAs("owner")->Register(
"FooService", provider.receiver_.BindNewPipeAndPassRemote());
// Run until all the async mojo operations are fulfilled.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(observer_not_a_requester.last_event_.is_null());
}
TEST_F(PermissiveServiceManagerTest, RegisterPermissive) {
{
// Test normal case.
FakeServcieProvider povider;
ConnectServiceManagerAs("owner")->Register(
"FooService", povider.receiver_.BindNewPipeAndPassRemote());
EXPECT_EQ(Query(ConnectServiceManagerAs("requester"), "FooService"),
mojom::ErrorOrServiceState::NewState(mojom::ServiceState::New(
/*is_registered=*/true,
/*owner=*/mojom::ProcessIdentity::New("owner", 0, 0, 0))));
}
{
// Test service can be owned by "not_owner".
FakeServcieProvider povider;
ConnectServiceManagerAs("not_owner")
->Register("FooService", povider.receiver_.BindNewPipeAndPassRemote());
EXPECT_EQ(
Query(ConnectServiceManagerAs("requester"), "FooService"),
mojom::ErrorOrServiceState::NewState(mojom::ServiceState::New(
/*is_registered=*/true,
/*owner=*/mojom::ProcessIdentity::New("not_owner", 0, 0, 0))));
}
{
// Test "NotInPolicyService" can be owned.
FakeServcieProvider povider;
ConnectServiceManagerAs("owner")->Register(
"NotInPolicyService", povider.receiver_.BindNewPipeAndPassRemote());
EXPECT_EQ(Query(ConnectServiceManagerAs("requester"), "NotInPolicyService"),
mojom::ErrorOrServiceState::NewState(mojom::ServiceState::New(
/*is_registered=*/true,
/*owner=*/mojom::ProcessIdentity::New("owner", 0, 0, 0))));
}
}
TEST_F(PermissiveServiceManagerTest, RequestPermissive) {
{
// Test normal case.
FakeServcieProvider provider;
ConnectServiceManagerAs("owner")->Register(
"FooService", provider.receiver_.BindNewPipeAndPassRemote());
mojo::Remote<mojom::Foo> foo;
ConnectServiceManagerAs("requester")
->Request("FooService", std::nullopt,
foo.BindNewPipeAndPassReceiver().PassPipe());
ExpectFooServiceConnected(&foo);
EXPECT_EQ(provider.last_client_identity_->security_context, "requester");
}
{
// Test request by not_requester.
FakeServcieProvider provider;
ConnectServiceManagerAs("owner")->Register(
"FooService", provider.receiver_.BindNewPipeAndPassRemote());
mojo::Remote<mojom::Foo> foo;
ConnectServiceManagerAs("not_requester")
->Request("FooService", std::nullopt,
foo.BindNewPipeAndPassReceiver().PassPipe());
ExpectFooServiceConnected(&foo);
EXPECT_EQ(provider.last_client_identity_->security_context,
"not_requester");
}
{
// Test request NotInPolicyService.
FakeServcieProvider provider;
ConnectServiceManagerAs("owner")->Register(
"NotInPolicyService", provider.receiver_.BindNewPipeAndPassRemote());
mojo::Remote<mojom::Foo> foo;
ConnectServiceManagerAs("requester")
->Request("NotInPolicyService", std::nullopt,
foo.BindNewPipeAndPassReceiver().PassPipe());
ExpectFooServiceConnected(&foo);
EXPECT_EQ(provider.last_client_identity_->security_context, "requester");
}
}
TEST_F(PermissiveServiceManagerTest, RequestTimeoutPermissive) {
{
// Test normal case.
mojo::Remote<mojom::Foo> foo;
ConnectServiceManagerAs("requester")
->Request("FooService", base::Seconds(5),
foo.BindNewPipeAndPassReceiver().PassPipe());
ExpectFooServiceDisconnectWithError(&foo, mojom::ErrorCode::kTimeout);
}
{
// Test request by not_requester.
mojo::Remote<mojom::Foo> foo;
ConnectServiceManagerAs("not_requester")
->Request("FooService", base::Seconds(5),
foo.BindNewPipeAndPassReceiver().PassPipe());
ExpectFooServiceDisconnectWithError(&foo, mojom::ErrorCode::kTimeout);
}
{
// Test request NotInPolicyService.
mojo::Remote<mojom::Foo> foo;
ConnectServiceManagerAs("requester")
->Request("NotInPolicyService", base::Seconds(5),
foo.BindNewPipeAndPassReceiver().PassPipe());
ExpectFooServiceDisconnectWithError(&foo, mojom::ErrorCode::kTimeout);
}
}
TEST_F(PermissiveServiceManagerTest, QueryPermissive) {
// Test service not found.
EXPECT_EQ(Query(ConnectServiceManagerAs("requester"), "NotFoundService")
->get_error()
->code,
mojom::ErrorCode::kServiceNotFound);
// Test permission denied is not raised for not_requester.
EXPECT_FALSE(Query(ConnectServiceManagerAs("not_requester"), "FooService")
->is_error());
// Test normal requester.
EXPECT_FALSE(
Query(ConnectServiceManagerAs("requester"), "FooService")->is_error());
}
TEST_F(PermissiveServiceManagerTest, ServiceObserverPermissive) {
// Test if observer can receive events from services which it is not a
// requester.
FakeServcieObserver observer;
ConnectServiceManagerAs("not_requester")
->AddServiceObserver(observer.receiver_.BindNewPipeAndPassRemote());
FakeServcieProvider povider;
ConnectServiceManagerAs("owner")->Register(
"FooService", povider.receiver_.BindNewPipeAndPassRemote());
ExpectServiceEvent(&observer);
// Reset the receiver to unregister from service manager.
povider.receiver_.reset();
ExpectServiceEvent(&observer);
}
} // namespace
} // namespace mojo_service_manager
} // namespace chromeos