Expose wl_display_set_global_filter as a virtual method

Summary:
This allows a server to filter which globals are visible and bindable by
clients.

Design rationale:
Could be it's own class with Display as an arg, but we need the lifespan
to exactly match Display, and the cardinality to match Display and it
needs to be set after we're started but before clients connect.

Better to enfore rules with code than with documentation.

I'm filtering by interface name as there isn't any other good
identifier of what a wl_global refers to, even if you could assume
you can cast the userdata to a Server::Global.

Test Plan: Attached unit test

Reviewers: #plasma, graesslin, bcooksley

Reviewed By: #plasma, graesslin

Subscribers: bcooksley, graesslin, plasma-devel, #frameworks

Tags: #frameworks, #plasma

Differential Revision: https://phabricator.kde.org/D8050
This commit is contained in:
David Edmundson 2017-11-17 08:34:01 +00:00
parent 6cf6a4c72c
commit ec75361d1f
5 changed files with 317 additions and 0 deletions

View file

@ -8,6 +8,7 @@ set(SERVER_LIB_SRCS
datasource_interface.cpp
display.cpp
dpms_interface.cpp
filtered_display.cpp
global.cpp
idle_interface.cpp
idleinhibit_interface.cpp
@ -202,6 +203,7 @@ set(SERVER_LIB_HEADERS
datasource_interface.h
display.h
dpms_interface.h
filtered_display.h
fakeinput_interface.h
global.h
idle_interface.h

View file

@ -379,3 +379,15 @@ add_executable(testPointerConstraints test_pointer_constraints.cpp)
target_link_libraries( testPointerConstraints Qt5::Test Qt5::Gui KF5::WaylandServer KF5::WaylandClient Wayland::Client)
add_test(NAME kwayland-testPointerConstraints COMMAND testPointerConstraints)
ecm_mark_as_test(testPointerConstraints)
########################################################
# Test Filter
########################################################
set( testFilter_SRCS
test_wayland_filter.cpp
)
add_executable(testFilter ${testFilter_SRCS})
target_link_libraries( testFilter Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Server)
add_test(NAME kwayland-testFilter COMMAND testFilter)
ecm_mark_as_test(testFilter)

View file

@ -0,0 +1,168 @@
/********************************************************************
Copyright 2017 David Edmundson <davidedmundson@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
// Qt
#include <QtTest/QtTest>
// KWin
#include "../../src/client/compositor.h"
#include "../../src/client/connection_thread.h"
#include "../../src/client/event_queue.h"
#include "../../src/client/region.h"
#include "../../src/client/registry.h"
#include "../../src/client/surface.h"
#include "../../src/client/blur.h"
#include "../../src/server/display.h"
#include "../../src/server/compositor_interface.h"
#include "../../src/server/region_interface.h"
#include "../../src/server/blur_interface.h"
#include "../../src/server/filtered_display.h"
#include <wayland-server.h>
using namespace KWayland::Client;
class TestDisplay;
class TestFilter : public QObject
{
Q_OBJECT
public:
explicit TestFilter(QObject *parent = nullptr);
private Q_SLOTS:
void init();
void cleanup();
void testFilter_data();
void testFilter();
private:
TestDisplay *m_display;
KWayland::Server::CompositorInterface *m_compositorInterface;
KWayland::Server::BlurManagerInterface *m_blurManagerInterface;
};
static const QString s_socketName = QStringLiteral("kwayland-test-wayland-blur-0");
//The following non-realistic class allows only clients in the m_allowedClients list to access the blur interface
//all other interfaces are allowed
class TestDisplay : public KWayland::Server::FilteredDisplay
{
public:
TestDisplay(QObject *parent);
bool allowInterface(KWayland::Server::ClientConnection * client, const QByteArray & interfaceName) override;
QList<wl_client*> m_allowedClients;
};
TestDisplay::TestDisplay(QObject *parent):
KWayland::Server::FilteredDisplay(parent)
{}
bool TestDisplay::allowInterface(KWayland::Server::ClientConnection* client, const QByteArray& interfaceName)
{
if (interfaceName == "org_kde_kwin_blur_manager") {
return m_allowedClients.contains(*client);
}
return true;
}
TestFilter::TestFilter(QObject *parent)
: QObject(parent)
, m_display(nullptr)
, m_compositorInterface(nullptr)
{}
void TestFilter::init()
{
using namespace KWayland::Server;
delete m_display;
m_display = new TestDisplay(this);
m_display->setSocketName(s_socketName);
m_display->start();
QVERIFY(m_display->isRunning());
m_compositorInterface = m_display->createCompositor(m_display);
m_compositorInterface->create();
QVERIFY(m_compositorInterface->isValid());
m_blurManagerInterface = m_display->createBlurManager(m_display);
m_blurManagerInterface->create();
QVERIFY(m_blurManagerInterface->isValid());
}
void TestFilter::cleanup()
{
}
void TestFilter::testFilter_data()
{
QTest::addColumn<bool>("accessAllowed");
QTest::newRow("granted") << true;
QTest::newRow("denied") << false;
}
void TestFilter::testFilter()
{
QFETCH(bool, accessAllowed);
// setup connection
QScopedPointer<KWayland::Client::ConnectionThread> connection(new KWayland::Client::ConnectionThread());
QSignalSpy connectedSpy(connection.data(), &ConnectionThread::connected);
QVERIFY(connectedSpy.isValid());
connection->setSocketName(s_socketName);
QScopedPointer<QThread> thread(new QThread(this));
connection->moveToThread(thread.data());
thread->start();
connection->initConnection();
QVERIFY(connectedSpy.wait());
//use low level API as Server::Display::connections only lists connections which have
//been previous fetched via getConnection()
if (accessAllowed) {
wl_client *clientConnection;
wl_client_for_each(clientConnection, wl_display_get_client_list(*m_display)) {
m_display->m_allowedClients << clientConnection;
}
}
KWayland::Client::EventQueue queue;
queue.setup(connection.data());
Registry registry;
QSignalSpy registryDoneSpy(&registry, &Registry::interfacesAnnounced);
QSignalSpy compositorSpy(&registry, &Registry::compositorAnnounced);
QSignalSpy blurSpy(&registry, &Registry::blurAnnounced);
registry.setEventQueue(&queue);
registry.create(connection->display());
QVERIFY(registry.isValid());
registry.setup();
QVERIFY(registryDoneSpy.wait());
QVERIFY(compositorSpy.count() == 1);
QVERIFY(blurSpy.count() == accessAllowed ? 1 : 0);
thread->quit();
thread->wait();
}
QTEST_GUILESS_MAIN(TestFilter)
#include "test_wayland_filter.moc"

View file

@ -0,0 +1,70 @@
/********************************************************************
Copyright 2017 David Edmundson <davidedmundson@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "filtered_display.h"
#include "display.h"
#include <wayland-server.h>
#include <QByteArray>
namespace KWayland
{
namespace Server
{
class FilteredDisplay::Private
{
public:
Private(FilteredDisplay *_q);
FilteredDisplay *q;
static bool globalFilterCallback(const wl_client *client, const wl_global *global, void *data)
{
auto t = static_cast<FilteredDisplay::Private*>(data);
auto clientConnection = t->q->getConnection(const_cast<wl_client*>(client));
auto interface = wl_global_get_interface(global);
auto name = QByteArray::fromRawData(interface->name, strlen(interface->name));
return t->q->allowInterface(clientConnection, name);
};
};
FilteredDisplay::Private::Private(FilteredDisplay *_q):
q(_q)
{}
FilteredDisplay::FilteredDisplay(QObject *parent):
Display(parent),
d(new Private(this))
{
connect(this, &Display::runningChanged, [this](bool running) {
if (!running) {
return;
}
wl_display_set_global_filter(*this, Private::globalFilterCallback, d.data());
});
}
FilteredDisplay::~FilteredDisplay()
{
}
}
}

View file

@ -0,0 +1,65 @@
/********************************************************************
Copyright 2017 David Edmundson <davidedmundson@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWAYLAND_SERVER_FILTERED_DISPLAY_H
#define KWAYLAND_SERVER_FILTERED_DISPLAY_H
#include "global.h"
#include "display.h"
#include <KWayland/Server/kwaylandserver_export.h>
namespace KWayland
{
namespace Server
{
/**
* Server Implementation that allows one to restrict which globals are available to which clients
*
* Users of this class must implement the virtual @method allowInterface method.
*
* @since 5.FIXME
*/
class KWAYLANDSERVER_EXPORT FilteredDisplay : public Display
{
Q_OBJECT
public:
FilteredDisplay(QObject *parent);
~FilteredDisplay();
/**
* Return whether the @arg client can see the interface with the given @arg interfaceName
*
* When false will not see these globals for a given interface in the registry,
* and any manual attempts to bind will fail
*
* @return true if the client should be able to access the global with the following interfaceName
*/
virtual bool allowInterface(ClientConnection *client, const QByteArray &interfaceName) = 0;
private:
class Private;
QScopedPointer<Private> d;
};
}
}
#endif