mirror of
https://github.com/logos-co/logos-capability-module.git
synced 2026-08-27 10:41:08 +00:00
refactor capability module to work as an internal module proper instead of cosplaying as a app layer module
refactor capability module to work as an internal module proper instead of cosplaying as a app layer module refactor capability module to work as an internal module proper instead of cosplaying as a app layer module update flake update flake
This commit is contained in:
+3
-3
@@ -12,7 +12,7 @@ endif()
|
||||
logos_module(
|
||||
NAME capability_module
|
||||
SOURCES
|
||||
src/capability_module_loader.h
|
||||
src/capability_module_impl.h
|
||||
src/capability_module_impl.cpp
|
||||
src/capability_module_interface.h
|
||||
src/capability_module_plugin.h
|
||||
src/capability_module_plugin.cpp
|
||||
)
|
||||
|
||||
Generated
+5249
-91
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,16 @@
|
||||
};
|
||||
|
||||
outputs = inputs@{ logos-module-builder, ... }:
|
||||
logos-module-builder.lib.mkLogosModule {
|
||||
src = ./.;
|
||||
configFile = ./metadata.json;
|
||||
flakeInputs = inputs;
|
||||
let
|
||||
module = logos-module-builder.lib.mkLogosModule {
|
||||
src = ./.;
|
||||
configFile = ./metadata.json;
|
||||
flakeInputs = inputs;
|
||||
tests = {
|
||||
dir = ./tests;
|
||||
};
|
||||
};
|
||||
in module // {
|
||||
checks = module.checks or {};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"description": "Coordinates permissions between modules",
|
||||
"author": "Logos Core Team",
|
||||
"type": "core",
|
||||
"interface": "provider",
|
||||
"category": "security",
|
||||
"main": "capability_module_plugin",
|
||||
"dependencies": [],
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef CAPABILITY_MODULE_IMPL_H
|
||||
#define CAPABILITY_MODULE_IMPL_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "logos_provider_object.h"
|
||||
|
||||
class CapabilityModuleImpl : public LogosProviderBase
|
||||
{
|
||||
LOGOS_PROVIDER(CapabilityModuleImpl, "capability_module", "1.0.0")
|
||||
|
||||
public:
|
||||
LOGOS_METHOD QString requestModule(const QString& fromModuleName, const QString& moduleName);
|
||||
|
||||
// Register an access restriction: only the listed caller modules may
|
||||
// obtain a token for (and therefore call) `targetModule`. Called by
|
||||
// core after it parses the access policy.
|
||||
//
|
||||
// `authToken` must be the trusted core/capability_module auth token —
|
||||
// the same gate informModuleToken uses. ModuleProxy::isAuthorized lets
|
||||
// ANY loaded module reach this method (it accepts any issued token), so
|
||||
// without this explicit check a malicious module could relax or
|
||||
// overwrite restrictions to grant itself access. Only core holds the
|
||||
// trusted token, so peers cannot forge it. Returns true on success.
|
||||
LOGOS_METHOD bool registerRestriction(const QString& authToken, const QString& targetModule, const QStringList& allowedCallers);
|
||||
|
||||
private:
|
||||
// target module -> set of caller modules allowed to reach it. A target
|
||||
// absent from this map is unrestricted (back-compat default). Populated
|
||||
// only by registerRestriction; consulted in requestModule.
|
||||
QHash<QString, QSet<QString>> m_restrictions;
|
||||
};
|
||||
|
||||
#endif // CAPABILITY_MODULE_IMPL_H
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef CAPABILITY_MODULE_INTERFACE_H
|
||||
#define CAPABILITY_MODULE_INTERFACE_H
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "interface.h"
|
||||
|
||||
// Public API of capability_module. It is internal-layer infrastructure: a
|
||||
// module developer never calls these directly — requestModule is invoked under
|
||||
// the hood by the SDK when one module first calls another, and
|
||||
// registerRestriction is invoked by core when it installs the access policy.
|
||||
class CapabilityModuleInterface : public PluginInterface
|
||||
{
|
||||
public:
|
||||
virtual ~CapabilityModuleInterface() = default;
|
||||
|
||||
// Mint an auth token allowing `fromModuleName` to call `moduleName`, inform
|
||||
// the target of the new token, and return it to the caller.
|
||||
Q_INVOKABLE virtual QString requestModule(const QString& fromModuleName,
|
||||
const QString& moduleName) = 0;
|
||||
|
||||
// Register an access restriction: only the listed caller modules may obtain
|
||||
// a token for (and therefore call) `targetModule`. `authToken` must be the
|
||||
// trusted core/capability_module token.
|
||||
Q_INVOKABLE virtual bool registerRestriction(const QString& authToken,
|
||||
const QString& targetModule,
|
||||
const QStringList& allowedCallers) = 0;
|
||||
};
|
||||
|
||||
#define CapabilityModuleInterface_iid "org.logos.CapabilityModuleInterface"
|
||||
Q_DECLARE_INTERFACE(CapabilityModuleInterface, CapabilityModuleInterface_iid)
|
||||
|
||||
#endif // CAPABILITY_MODULE_INTERFACE_H
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef CAPABILITY_MODULE_LOADER_H
|
||||
#define CAPABILITY_MODULE_LOADER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "capability_module_impl.h"
|
||||
#include "interface.h"
|
||||
#include "logos_provider_object.h"
|
||||
|
||||
class CapabilityModuleLoader : public QObject, public PluginInterface, public LogosProviderPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID LogosProviderPlugin_iid FILE "metadata.json")
|
||||
Q_INTERFACES(PluginInterface LogosProviderPlugin)
|
||||
|
||||
public:
|
||||
QString name() const override { return QStringLiteral("capability_module"); }
|
||||
QString version() const override { return QStringLiteral("1.0.0"); }
|
||||
LogosProviderObject* createProviderObject() override { return new CapabilityModuleImpl(); }
|
||||
};
|
||||
|
||||
#endif // CAPABILITY_MODULE_LOADER_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "capability_module_impl.h"
|
||||
#include "capability_module_plugin.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
@@ -6,15 +6,14 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "logos_api.h"
|
||||
#include "logos_api_client.h"
|
||||
#include "token_manager.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Constant-time token comparison (mirrors ModuleProxy's): compare over the
|
||||
// longer of the two lengths and fold any length difference into the result,
|
||||
// so neither a correct prefix nor the secret's length leaks through timing.
|
||||
// Constant-time token comparison: compare over the longer of the two lengths
|
||||
// and fold any length difference into the result, so neither a correct prefix
|
||||
// nor the secret's length leaks through timing.
|
||||
bool constantTimeEquals(const QString& a, const QString& b)
|
||||
{
|
||||
const QByteArray ba = a.toUtf8();
|
||||
@@ -31,30 +30,46 @@ bool constantTimeEquals(const QString& a, const QString& b)
|
||||
|
||||
} // namespace
|
||||
|
||||
QString CapabilityModuleImpl::requestModule(const QString& fromModuleName, const QString& moduleName)
|
||||
CapabilityModulePlugin::CapabilityModulePlugin(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
qDebug() << "CapabilityModuleImpl::requestModule called with fromModuleName:" << fromModuleName
|
||||
qDebug() << "CapabilityModulePlugin: created";
|
||||
}
|
||||
|
||||
CapabilityModulePlugin::~CapabilityModulePlugin()
|
||||
{
|
||||
qDebug() << "CapabilityModulePlugin: destroyed";
|
||||
}
|
||||
|
||||
void CapabilityModulePlugin::initLogos(LogosAPI* logosAPIInstance)
|
||||
{
|
||||
logosAPI = logosAPIInstance;
|
||||
qDebug() << "CapabilityModulePlugin: LogosAPI initialized";
|
||||
}
|
||||
|
||||
QString CapabilityModulePlugin::requestModule(const QString& fromModuleName, const QString& moduleName)
|
||||
{
|
||||
qDebug() << "CapabilityModulePlugin::requestModule called with fromModuleName:" << fromModuleName
|
||||
<< "moduleName:" << moduleName;
|
||||
|
||||
LogosAPI* api = logosAPI();
|
||||
if (!api) {
|
||||
qWarning() << "CapabilityModuleImpl::requestModule: LogosAPI not initialized";
|
||||
if (!logosAPI) {
|
||||
qWarning() << "CapabilityModulePlugin::requestModule: LogosAPI not initialized";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (fromModuleName.isEmpty() || moduleName.isEmpty()) {
|
||||
qWarning() << "CapabilityModuleImpl::requestModule: rejecting empty module name"
|
||||
qWarning() << "CapabilityModulePlugin::requestModule: rejecting empty module name"
|
||||
<< "(fromModuleName / moduleName must both be set)";
|
||||
return {};
|
||||
}
|
||||
|
||||
TokenManager* tokenManager = api->getTokenManager();
|
||||
TokenManager* tokenManager = logosAPI->getTokenManager();
|
||||
|
||||
// Known-caller gate: the requesting identity must be a module capability_module
|
||||
// already knows about. Fail closed on an unknown name rather than mint a token
|
||||
// for a self-asserted identity that was never loaded.
|
||||
if (!tokenManager->getTokenKeys().contains(fromModuleName)) {
|
||||
qWarning() << "CapabilityModuleImpl::requestModule: rejecting request from unknown"
|
||||
qWarning() << "CapabilityModulePlugin::requestModule: rejecting request from unknown"
|
||||
<< "module identity:" << fromModuleName
|
||||
<< "- no token registered for it (unverified requesting identity)";
|
||||
return {};
|
||||
@@ -64,7 +79,7 @@ QString CapabilityModuleImpl::requestModule(const QString& fromModuleName, const
|
||||
// unknown. Don't hand back a token the target would reject anyway — fail closed.
|
||||
const QString moduleToken = tokenManager->getToken(moduleName);
|
||||
if (moduleToken.isEmpty()) {
|
||||
qWarning() << "CapabilityModuleImpl::requestModule: rejecting request for unknown"
|
||||
qWarning() << "CapabilityModulePlugin::requestModule: rejecting request for unknown"
|
||||
<< "target module:" << moduleName << "- no token registered for it";
|
||||
return {};
|
||||
}
|
||||
@@ -83,7 +98,7 @@ QString CapabilityModuleImpl::requestModule(const QString& fromModuleName, const
|
||||
// and flip the default once the policy is guaranteed to be present.
|
||||
if (auto it = m_restrictions.constFind(moduleName); it != m_restrictions.constEnd()) {
|
||||
if (!it->contains(fromModuleName)) {
|
||||
qWarning() << "CapabilityModuleImpl::requestModule: access policy denies"
|
||||
qWarning() << "CapabilityModulePlugin::requestModule: access policy denies"
|
||||
<< fromModuleName << "->" << moduleName
|
||||
<< "- caller not in the allowed set";
|
||||
return {};
|
||||
@@ -92,51 +107,49 @@ QString CapabilityModuleImpl::requestModule(const QString& fromModuleName, const
|
||||
|
||||
const QString authTokenString = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||
|
||||
qDebug() << "CapabilityModuleImpl: Calling informModuleToken on target module:" << moduleName;
|
||||
qDebug() << "CapabilityModulePlugin: Calling informModuleToken on target module:" << moduleName;
|
||||
|
||||
const bool success = api->getClient(moduleName)->informModuleToken_module(
|
||||
const bool success = logosAPI->getClient(moduleName)->informModuleToken_module(
|
||||
moduleToken, moduleName, fromModuleName, authTokenString);
|
||||
if (!success) {
|
||||
qWarning() << "CapabilityModuleImpl: Failed to inform" << moduleName
|
||||
qWarning() << "CapabilityModulePlugin: Failed to inform" << moduleName
|
||||
<< "about token for" << fromModuleName;
|
||||
return {};
|
||||
}
|
||||
|
||||
qDebug() << "CapabilityModuleImpl: Successfully informed" << moduleName
|
||||
qDebug() << "CapabilityModulePlugin: Successfully informed" << moduleName
|
||||
<< "about token for" << fromModuleName;
|
||||
return authTokenString;
|
||||
}
|
||||
|
||||
bool CapabilityModuleImpl::registerRestriction(const QString& authToken,
|
||||
const QString& targetModule,
|
||||
const QStringList& allowedCallers)
|
||||
bool CapabilityModulePlugin::registerRestriction(const QString& authToken,
|
||||
const QString& targetModule,
|
||||
const QStringList& allowedCallers)
|
||||
{
|
||||
LogosAPI* api = logosAPI();
|
||||
if (!api) {
|
||||
qWarning() << "CapabilityModuleImpl::registerRestriction: LogosAPI not initialized";
|
||||
if (!logosAPI) {
|
||||
qWarning() << "CapabilityModulePlugin::registerRestriction: LogosAPI not initialized";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Trusted-channel gate: only core (or capability_module itself) may
|
||||
// register restrictions. Both hold capability_module's auth token; a
|
||||
// peer module only knows its own token and so cannot forge this. Same
|
||||
// check ModuleProxy::informModuleToken makes — necessary because the
|
||||
// peer module only knows its own token and so cannot forge this. The
|
||||
// generic isAuthorized() that fronts this method accepts ANY issued
|
||||
// token, which would otherwise let a malicious module rewrite the policy.
|
||||
TokenManager* tokenManager = api->getTokenManager();
|
||||
TokenManager* tokenManager = logosAPI->getTokenManager();
|
||||
const QString coreToken = tokenManager->getToken(QStringLiteral("core"));
|
||||
const QString capToken = tokenManager->getToken(QStringLiteral("capability_module"));
|
||||
const bool callerIsTrusted =
|
||||
(!coreToken.isEmpty() && constantTimeEquals(authToken, coreToken)) ||
|
||||
(!capToken.isEmpty() && constantTimeEquals(authToken, capToken));
|
||||
if (authToken.isEmpty() || !callerIsTrusted) {
|
||||
qWarning() << "CapabilityModuleImpl::registerRestriction: rejecting restriction for"
|
||||
qWarning() << "CapabilityModulePlugin::registerRestriction: rejecting restriction for"
|
||||
<< targetModule << "- caller is not the trusted core channel";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetModule.isEmpty()) {
|
||||
qWarning() << "CapabilityModuleImpl::registerRestriction: rejecting empty target module";
|
||||
qWarning() << "CapabilityModulePlugin::registerRestriction: rejecting empty target module";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -144,7 +157,7 @@ bool CapabilityModuleImpl::registerRestriction(const QString& authToken,
|
||||
// single source of truth and re-registers the full set each boot.
|
||||
m_restrictions.insert(targetModule, QSet<QString>(allowedCallers.begin(), allowedCallers.end()));
|
||||
|
||||
qDebug() << "CapabilityModuleImpl::registerRestriction: target" << targetModule
|
||||
qDebug() << "CapabilityModulePlugin::registerRestriction: target" << targetModule
|
||||
<< "restricted to callers" << allowedCallers;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef CAPABILITY_MODULE_PLUGIN_H
|
||||
#define CAPABILITY_MODULE_PLUGIN_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "capability_module_interface.h"
|
||||
#include "logos_api.h"
|
||||
|
||||
class CapabilityModulePlugin : public QObject, public CapabilityModuleInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID CapabilityModuleInterface_iid FILE "metadata.json")
|
||||
Q_INTERFACES(CapabilityModuleInterface PluginInterface)
|
||||
|
||||
public:
|
||||
explicit CapabilityModulePlugin(QObject* parent = nullptr);
|
||||
~CapabilityModulePlugin() override;
|
||||
|
||||
QString name() const override { return QStringLiteral("capability_module"); }
|
||||
QString version() const override { return QStringLiteral("1.0.0"); }
|
||||
|
||||
Q_INVOKABLE void initLogos(LogosAPI* logosAPIInstance);
|
||||
|
||||
Q_INVOKABLE QString requestModule(const QString& fromModuleName,
|
||||
const QString& moduleName) override;
|
||||
|
||||
Q_INVOKABLE bool registerRestriction(const QString& authToken,
|
||||
const QString& targetModule,
|
||||
const QStringList& allowedCallers) override;
|
||||
|
||||
private:
|
||||
// target module -> set of caller modules allowed to reach it. A target
|
||||
// absent from this map is unrestricted (back-compat default). Populated
|
||||
// only by registerRestriction; consulted in requestModule.
|
||||
QHash<QString, QSet<QString>> m_restrictions;
|
||||
};
|
||||
|
||||
#endif // CAPABILITY_MODULE_PLUGIN_H
|
||||
@@ -6,12 +6,8 @@ include(LogosTest)
|
||||
logos_test(
|
||||
NAME capability_module_tests
|
||||
MODULE_SOURCES
|
||||
../src/capability_module_impl.cpp
|
||||
../src/capability_module_plugin.cpp
|
||||
TEST_SOURCES
|
||||
main.cpp
|
||||
test_capability_module.cpp
|
||||
GENERATED_SOURCES
|
||||
../generated_code/logos_provider_dispatch.cpp
|
||||
GENERATED_DIR
|
||||
../generated_code
|
||||
)
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
// Unit tests for CapabilityModuleImpl.
|
||||
// Unit tests for CapabilityModulePlugin.
|
||||
//
|
||||
// capability_module is a plain legacy Qt plugin: the generic ModuleProxy wraps
|
||||
// it and dispatches Q_INVOKABLE methods by name, so the tests drive the plugin
|
||||
// methods directly (initLogos + requestModule / registerRestriction).
|
||||
//
|
||||
// requestModule() mints a UUID auth token, asks the target module to record it
|
||||
// via informModuleToken_module(), and returns the token to the caller.
|
||||
@@ -26,8 +30,9 @@
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
|
||||
#include "capability_module_impl.h"
|
||||
#include "capability_module_plugin.h"
|
||||
#include "logos_api.h"
|
||||
#include "token_manager.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -60,11 +65,11 @@ LOGOS_TEST(requestModule_returns_uuid_format_token) {
|
||||
seedModule("requester_module");
|
||||
seedModule("target_module");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
QString token = impl.requestModule("requester_module", "target_module");
|
||||
QString token = plugin.requestModule("requester_module", "target_module");
|
||||
|
||||
LOGOS_ASSERT_FALSE(token.isEmpty());
|
||||
LOGOS_ASSERT(kUuidRegex.match(token).hasMatch());
|
||||
@@ -75,13 +80,13 @@ LOGOS_TEST(requestModule_mints_unique_token_per_call) {
|
||||
seedModule("requester");
|
||||
seedModule("target");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
QSet<QString> tokens;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
tokens.insert(impl.requestModule("requester", "target"));
|
||||
tokens.insert(plugin.requestModule("requester", "target"));
|
||||
}
|
||||
|
||||
LOGOS_ASSERT_EQ(tokens.size(), 10);
|
||||
@@ -94,11 +99,11 @@ LOGOS_TEST(requestModule_works_when_target_token_is_pre_seeded) {
|
||||
seedModule("requester_module");
|
||||
TokenManager::instance().saveToken("target_module", "pre-seeded-token");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
QString token = impl.requestModule("requester_module", "target_module");
|
||||
QString token = plugin.requestModule("requester_module", "target_module");
|
||||
|
||||
LOGOS_ASSERT(kUuidRegex.match(token).hasMatch());
|
||||
}
|
||||
@@ -107,9 +112,9 @@ LOGOS_TEST(requestModule_works_when_target_token_is_pre_seeded) {
|
||||
|
||||
LOGOS_TEST(requestModule_returns_empty_when_not_initialized) {
|
||||
LogosMockSetup mock;
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
|
||||
QString token = impl.requestModule("requester", "target");
|
||||
QString token = plugin.requestModule("requester", "target");
|
||||
|
||||
LOGOS_ASSERT_TRUE(token.isEmpty());
|
||||
}
|
||||
@@ -118,11 +123,11 @@ LOGOS_TEST(requestModule_rejects_empty_fromModuleName) {
|
||||
LogosMockSetup mock;
|
||||
seedModule("target_module");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
QString token = impl.requestModule("", "target_module");
|
||||
QString token = plugin.requestModule("", "target_module");
|
||||
|
||||
LOGOS_ASSERT_TRUE(token.isEmpty());
|
||||
}
|
||||
@@ -132,12 +137,12 @@ LOGOS_TEST(requestModule_rejects_unknown_fromModuleName) {
|
||||
// Only the target is known; the requesting identity was never loaded.
|
||||
seedModule("target_module");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
// Spoofing a non-loaded identity must not mint a token.
|
||||
QString token = impl.requestModule("spoofed_module", "target_module");
|
||||
QString token = plugin.requestModule("spoofed_module", "target_module");
|
||||
|
||||
LOGOS_ASSERT_TRUE(token.isEmpty());
|
||||
}
|
||||
@@ -147,11 +152,11 @@ LOGOS_TEST(requestModule_rejects_unknown_target) {
|
||||
// Only the caller is known; the target was never loaded.
|
||||
seedModule("requester_module");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
QString token = impl.requestModule("requester_module", "missing_target");
|
||||
QString token = plugin.requestModule("requester_module", "missing_target");
|
||||
|
||||
LOGOS_ASSERT_TRUE(token.isEmpty());
|
||||
}
|
||||
@@ -161,11 +166,11 @@ LOGOS_TEST(requestModule_succeeds_for_known_caller_and_target) {
|
||||
seedModule("requester_module");
|
||||
seedModule("target_module");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
QString token = impl.requestModule("requester_module", "target_module");
|
||||
QString token = plugin.requestModule("requester_module", "target_module");
|
||||
|
||||
LOGOS_ASSERT_FALSE(token.isEmpty());
|
||||
LOGOS_ASSERT(kUuidRegex.match(token).hasMatch());
|
||||
@@ -182,11 +187,11 @@ LOGOS_TEST(requestModule_succeeds_for_known_caller_and_target) {
|
||||
LOGOS_TEST(registerRestriction_rejects_empty_target) {
|
||||
LogosMockSetup mock;
|
||||
seedTrustedChannel();
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
LOGOS_ASSERT_FALSE(impl.registerRestriction(kTrustedToken, "", QStringList{"caller"}));
|
||||
LOGOS_ASSERT_FALSE(plugin.registerRestriction(kTrustedToken, "", QStringList{"caller"}));
|
||||
}
|
||||
|
||||
LOGOS_TEST(registerRestriction_rejects_untrusted_caller_token) {
|
||||
@@ -198,18 +203,18 @@ LOGOS_TEST(registerRestriction_rejects_untrusted_caller_token) {
|
||||
seedModule("malicious_module");
|
||||
seedModule("package_manager");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
// malicious_module tries to grant itself access using its OWN token.
|
||||
const bool ok = impl.registerRestriction(
|
||||
const bool ok = plugin.registerRestriction(
|
||||
"seed-token-malicious_module", "package_manager",
|
||||
QStringList{"malicious_module"});
|
||||
LOGOS_ASSERT_FALSE(ok);
|
||||
|
||||
// And an empty token is rejected too.
|
||||
LOGOS_ASSERT_FALSE(impl.registerRestriction(
|
||||
LOGOS_ASSERT_FALSE(plugin.registerRestriction(
|
||||
"", "package_manager", QStringList{"malicious_module"}));
|
||||
}
|
||||
|
||||
@@ -219,14 +224,14 @@ LOGOS_TEST(requestModule_allows_listed_caller_for_restricted_target) {
|
||||
seedModule("package_manager_ui");
|
||||
seedModule("package_manager");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
LOGOS_ASSERT_TRUE(impl.registerRestriction(
|
||||
LOGOS_ASSERT_TRUE(plugin.registerRestriction(
|
||||
kTrustedToken, "package_manager", QStringList{"package_manager_ui"}));
|
||||
|
||||
QString token = impl.requestModule("package_manager_ui", "package_manager");
|
||||
QString token = plugin.requestModule("package_manager_ui", "package_manager");
|
||||
|
||||
LOGOS_ASSERT_FALSE(token.isEmpty());
|
||||
LOGOS_ASSERT(kUuidRegex.match(token).hasMatch());
|
||||
@@ -238,15 +243,15 @@ LOGOS_TEST(requestModule_denies_unlisted_caller_for_restricted_target) {
|
||||
seedModule("some_other_module");
|
||||
seedModule("package_manager");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
impl.registerRestriction(kTrustedToken, "package_manager", QStringList{"package_manager_ui"});
|
||||
plugin.registerRestriction(kTrustedToken, "package_manager", QStringList{"package_manager_ui"});
|
||||
|
||||
// some_other_module is a known, loaded module (passes the identity gate)
|
||||
// but is not in package_manager's allowed-caller set — must be denied.
|
||||
QString token = impl.requestModule("some_other_module", "package_manager");
|
||||
QString token = plugin.requestModule("some_other_module", "package_manager");
|
||||
|
||||
LOGOS_ASSERT_TRUE(token.isEmpty());
|
||||
}
|
||||
@@ -258,14 +263,14 @@ LOGOS_TEST(requestModule_allows_any_caller_for_unrestricted_target) {
|
||||
seedModule("restricted_target");
|
||||
seedModule("open_target");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
// Restrict only restricted_target; open_target has no restriction.
|
||||
impl.registerRestriction(kTrustedToken, "restricted_target", QStringList{"allowed_caller"});
|
||||
plugin.registerRestriction(kTrustedToken, "restricted_target", QStringList{"allowed_caller"});
|
||||
|
||||
QString token = impl.requestModule("some_module", "open_target");
|
||||
QString token = plugin.requestModule("some_module", "open_target");
|
||||
|
||||
LOGOS_ASSERT_FALSE(token.isEmpty());
|
||||
LOGOS_ASSERT(kUuidRegex.match(token).hasMatch());
|
||||
@@ -277,11 +282,11 @@ LOGOS_TEST(requestModule_allows_all_when_no_restriction_registered) {
|
||||
seedModule("requester_module");
|
||||
seedModule("target_module");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
QString token = impl.requestModule("requester_module", "target_module");
|
||||
QString token = plugin.requestModule("requester_module", "target_module");
|
||||
|
||||
LOGOS_ASSERT_FALSE(token.isEmpty());
|
||||
}
|
||||
@@ -293,15 +298,15 @@ LOGOS_TEST(registerRestriction_overwrites_previous_for_same_target) {
|
||||
seedModule("new_caller");
|
||||
seedModule("target_module");
|
||||
|
||||
CapabilityModuleImpl impl;
|
||||
CapabilityModulePlugin plugin;
|
||||
LogosAPI api("capability_module");
|
||||
impl.init(&api);
|
||||
plugin.initLogos(&api);
|
||||
|
||||
impl.registerRestriction(kTrustedToken, "target_module", QStringList{"old_caller"});
|
||||
plugin.registerRestriction(kTrustedToken, "target_module", QStringList{"old_caller"});
|
||||
// Re-register (as core does each boot) with a different allowed set.
|
||||
impl.registerRestriction(kTrustedToken, "target_module", QStringList{"new_caller"});
|
||||
plugin.registerRestriction(kTrustedToken, "target_module", QStringList{"new_caller"});
|
||||
|
||||
// old_caller is no longer allowed; new_caller is.
|
||||
LOGOS_ASSERT_TRUE(impl.requestModule("old_caller", "target_module").isEmpty());
|
||||
LOGOS_ASSERT_FALSE(impl.requestModule("new_caller", "target_module").isEmpty());
|
||||
LOGOS_ASSERT_TRUE(plugin.requestModule("old_caller", "target_module").isEmpty());
|
||||
LOGOS_ASSERT_FALSE(plugin.requestModule("new_caller", "target_module").isEmpty());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user