2021-06-06 06:41:49 +00:00
|
|
|
# Copyright (c) 2015 Nicolas JOUANIN
|
|
|
|
#
|
|
|
|
# See the file license.txt for copying permission.
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
from amqtt.plugins.manager import BaseContext
|
2021-06-06 07:02:08 +00:00
|
|
|
from amqtt.plugins.topic_checking import (
|
|
|
|
BaseTopicPlugin,
|
|
|
|
TopicTabooPlugin,
|
|
|
|
TopicAccessControlListPlugin,
|
|
|
|
)
|
2021-06-06 07:00:41 +00:00
|
|
|
from amqtt.session import Session
|
2021-06-06 06:41:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
class DummyLogger(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.messages = []
|
|
|
|
|
|
|
|
def warning(self, *args, **kwargs):
|
|
|
|
self.messages.append((args, kwargs))
|
|
|
|
|
|
|
|
|
2021-06-06 07:00:41 +00:00
|
|
|
# Base plug-in object
|
|
|
|
|
|
|
|
|
2021-06-06 06:41:49 +00:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_base_no_config():
|
|
|
|
"""
|
|
|
|
Check BaseTopicPlugin returns false if no topic-check is present.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
|
|
|
context.config = {}
|
|
|
|
|
|
|
|
plugin = BaseTopicPlugin(context)
|
2021-06-06 07:00:41 +00:00
|
|
|
authorised = plugin.topic_filtering()
|
|
|
|
assert authorised is False
|
2021-06-06 06:41:49 +00:00
|
|
|
|
|
|
|
# Should have printed a couple of warnings
|
|
|
|
assert len(context.logger.messages) == 2
|
|
|
|
assert context.logger.messages[0] == (
|
2021-06-06 07:02:08 +00:00
|
|
|
("'topic-check' section not found in context configuration",),
|
|
|
|
{},
|
2021-06-06 06:41:49 +00:00
|
|
|
)
|
|
|
|
assert context.logger.messages[1] == (
|
2021-06-06 07:02:08 +00:00
|
|
|
("'auth' section not found in context configuration",),
|
|
|
|
{},
|
2021-06-06 06:41:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_base_empty_config():
|
|
|
|
"""
|
|
|
|
Check BaseTopicPlugin returns false if topic-check is empty.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
2021-06-06 07:02:08 +00:00
|
|
|
context.config = {"topic-check": {}}
|
2021-06-06 06:41:49 +00:00
|
|
|
|
|
|
|
plugin = BaseTopicPlugin(context)
|
2021-06-06 07:00:41 +00:00
|
|
|
authorised = plugin.topic_filtering()
|
|
|
|
assert authorised is False
|
2021-06-06 06:41:49 +00:00
|
|
|
|
|
|
|
# Should NOT have printed warnings
|
|
|
|
assert len(context.logger.messages) == 1
|
|
|
|
assert context.logger.messages[0] == (
|
2021-06-06 07:02:08 +00:00
|
|
|
("'auth' section not found in context configuration",),
|
|
|
|
{},
|
2021-06-06 06:41:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_base_enabled_config():
|
|
|
|
"""
|
|
|
|
Check BaseTopicPlugin returns true if enabled.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
2021-06-06 07:02:08 +00:00
|
|
|
context.config = {"topic-check": {"enabled": True}}
|
2021-06-06 06:41:49 +00:00
|
|
|
|
|
|
|
plugin = BaseTopicPlugin(context)
|
2021-06-06 07:00:41 +00:00
|
|
|
authorised = plugin.topic_filtering()
|
|
|
|
assert authorised is True
|
|
|
|
|
|
|
|
# Should NOT have printed warnings
|
|
|
|
assert len(context.logger.messages) == 0
|
|
|
|
|
|
|
|
|
|
|
|
# Taboo plug-in
|
|
|
|
|
2021-06-06 07:02:08 +00:00
|
|
|
|
2021-06-06 07:00:41 +00:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taboo_empty_config():
|
|
|
|
"""
|
|
|
|
Check TopicTabooPlugin returns false if topic-check absent.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
|
|
|
context.config = {}
|
|
|
|
|
|
|
|
plugin = TopicTabooPlugin(context)
|
|
|
|
authorised = await plugin.topic_filtering()
|
|
|
|
assert authorised is False
|
|
|
|
|
|
|
|
# Should have printed a couple of warnings
|
|
|
|
assert len(context.logger.messages) == 2
|
|
|
|
assert context.logger.messages[0] == (
|
2021-06-06 07:02:08 +00:00
|
|
|
("'topic-check' section not found in context configuration",),
|
|
|
|
{},
|
2021-06-06 07:00:41 +00:00
|
|
|
)
|
|
|
|
assert context.logger.messages[1] == (
|
2021-06-06 07:02:08 +00:00
|
|
|
("'auth' section not found in context configuration",),
|
|
|
|
{},
|
2021-06-06 07:00:41 +00:00
|
|
|
)
|
|
|
|
|
2021-06-06 07:02:08 +00:00
|
|
|
|
2021-06-06 07:00:41 +00:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taboo_not_taboo_topic():
|
|
|
|
"""
|
|
|
|
Check TopicTabooPlugin returns true if topic not taboo
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
2021-06-06 07:02:08 +00:00
|
|
|
context.config = {"topic-check": {"enabled": True}}
|
2021-06-06 07:00:41 +00:00
|
|
|
|
|
|
|
session = Session()
|
2021-06-06 07:02:08 +00:00
|
|
|
session.username = "anybody"
|
2021-06-06 07:00:41 +00:00
|
|
|
|
|
|
|
plugin = TopicTabooPlugin(context)
|
2021-06-06 07:02:08 +00:00
|
|
|
authorised = await plugin.topic_filtering(session=session, topic="not/prohibited")
|
2021-06-06 07:00:41 +00:00
|
|
|
assert authorised is True
|
|
|
|
|
|
|
|
# Should NOT have printed warnings
|
|
|
|
assert len(context.logger.messages) == 0
|
|
|
|
|
2021-06-06 07:02:08 +00:00
|
|
|
|
2021-06-06 07:00:41 +00:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taboo_anon_taboo_topic():
|
|
|
|
"""
|
|
|
|
Check TopicTabooPlugin returns false if topic is taboo and session is anonymous.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
2021-06-06 07:02:08 +00:00
|
|
|
context.config = {"topic-check": {"enabled": True}}
|
2021-06-06 07:00:41 +00:00
|
|
|
|
|
|
|
session = Session()
|
2021-06-06 07:02:08 +00:00
|
|
|
session.username = ""
|
2021-06-06 07:00:41 +00:00
|
|
|
|
|
|
|
plugin = TopicTabooPlugin(context)
|
2021-06-06 07:02:08 +00:00
|
|
|
authorised = await plugin.topic_filtering(session=session, topic="prohibited")
|
2021-06-06 07:00:41 +00:00
|
|
|
assert authorised is False
|
|
|
|
|
|
|
|
# Should NOT have printed warnings
|
|
|
|
assert len(context.logger.messages) == 0
|
|
|
|
|
2021-06-06 07:02:08 +00:00
|
|
|
|
2021-06-06 07:00:41 +00:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taboo_notadmin_taboo_topic():
|
|
|
|
"""
|
|
|
|
Check TopicTabooPlugin returns false if topic is taboo and user is not "admin".
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
2021-06-06 07:02:08 +00:00
|
|
|
context.config = {"topic-check": {"enabled": True}}
|
2021-06-06 07:00:41 +00:00
|
|
|
|
|
|
|
session = Session()
|
2021-06-06 07:02:08 +00:00
|
|
|
session.username = "notadmin"
|
2021-06-06 07:00:41 +00:00
|
|
|
|
|
|
|
plugin = TopicTabooPlugin(context)
|
2021-06-06 07:02:08 +00:00
|
|
|
authorised = await plugin.topic_filtering(session=session, topic="prohibited")
|
2021-06-06 07:00:41 +00:00
|
|
|
assert authorised is False
|
|
|
|
|
|
|
|
# Should NOT have printed warnings
|
|
|
|
assert len(context.logger.messages) == 0
|
|
|
|
|
2021-06-06 07:02:08 +00:00
|
|
|
|
2021-06-06 07:00:41 +00:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taboo_admin_taboo_topic():
|
|
|
|
"""
|
|
|
|
Check TopicTabooPlugin returns true if topic is taboo and user is "admin".
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
2021-06-06 07:02:08 +00:00
|
|
|
context.config = {"topic-check": {"enabled": True}}
|
2021-06-06 07:00:41 +00:00
|
|
|
|
|
|
|
session = Session()
|
2021-06-06 07:02:08 +00:00
|
|
|
session.username = "admin"
|
2021-06-06 07:00:41 +00:00
|
|
|
|
|
|
|
plugin = TopicTabooPlugin(context)
|
2021-06-06 07:02:08 +00:00
|
|
|
authorised = await plugin.topic_filtering(session=session, topic="prohibited")
|
2021-06-06 07:00:41 +00:00
|
|
|
assert authorised is True
|
2021-06-06 06:41:49 +00:00
|
|
|
|
|
|
|
# Should NOT have printed warnings
|
|
|
|
assert len(context.logger.messages) == 0
|
2021-06-06 07:24:13 +00:00
|
|
|
|
|
|
|
|
2021-06-06 07:42:06 +00:00
|
|
|
# TopicAccessControlListPlugin tests
|
|
|
|
|
|
|
|
|
2021-06-06 07:24:13 +00:00
|
|
|
def test_topic_ac_not_match():
|
|
|
|
"""
|
|
|
|
Test TopicAccessControlListPlugin.topic_ac returns false if topics do not match.
|
|
|
|
"""
|
|
|
|
assert (
|
|
|
|
TopicAccessControlListPlugin.topic_ac("a/topic/to/match", "a/topic/to/notmatch")
|
|
|
|
is False
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_topic_ac_not_match_longer_acl():
|
|
|
|
"""
|
|
|
|
Test TopicAccessControlListPlugin.topic_ac returns false if topics do not match and ACL topic is longer.
|
|
|
|
"""
|
2021-06-06 07:43:02 +00:00
|
|
|
assert TopicAccessControlListPlugin.topic_ac("topic", "topic/is/longer") is False
|
2021-06-06 07:24:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_topic_ac_not_match_longer_rq():
|
|
|
|
"""
|
|
|
|
Test TopicAccessControlListPlugin.topic_ac returns false if topics do not match and RQ topic is longer.
|
|
|
|
"""
|
2021-06-06 07:43:02 +00:00
|
|
|
assert TopicAccessControlListPlugin.topic_ac("topic/is/longer", "topic") is False
|
2021-06-06 07:24:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_topic_ac_match_exact():
|
|
|
|
"""
|
|
|
|
Test TopicAccessControlListPlugin.topic_ac returns true if topics match exactly.
|
|
|
|
"""
|
|
|
|
assert TopicAccessControlListPlugin.topic_ac("exact/topic", "exact/topic") is True
|
|
|
|
|
|
|
|
|
|
|
|
def test_topic_ac_match_hash():
|
|
|
|
"""
|
|
|
|
Test TopicAccessControlListPlugin.topic_ac correctly handles '+' wildcard.
|
|
|
|
"""
|
|
|
|
assert (
|
|
|
|
TopicAccessControlListPlugin.topic_ac(
|
|
|
|
"a/topic/anything/value", "a/topic/+/value"
|
|
|
|
)
|
|
|
|
is True
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_topic_ac_match_hash():
|
|
|
|
"""
|
|
|
|
Test TopicAccessControlListPlugin.topic_ac correctly handles '#' wildcard.
|
|
|
|
"""
|
|
|
|
assert (
|
|
|
|
TopicAccessControlListPlugin.topic_ac(
|
|
|
|
"topic/prefix/and/suffix", "topic/prefix/#"
|
|
|
|
)
|
|
|
|
is True
|
|
|
|
)
|
2021-06-06 07:42:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taclp_empty_config():
|
|
|
|
"""
|
|
|
|
Check TopicAccessControlListPlugin returns false if topic-check absent.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
|
|
|
context.config = {}
|
|
|
|
|
|
|
|
plugin = TopicAccessControlListPlugin(context)
|
|
|
|
authorised = await plugin.topic_filtering()
|
|
|
|
assert authorised is False
|
|
|
|
|
|
|
|
# Should have printed a couple of warnings
|
|
|
|
assert len(context.logger.messages) == 2
|
|
|
|
assert context.logger.messages[0] == (
|
|
|
|
("'topic-check' section not found in context configuration",),
|
|
|
|
{},
|
|
|
|
)
|
|
|
|
assert context.logger.messages[1] == (
|
|
|
|
("'auth' section not found in context configuration",),
|
|
|
|
{},
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taclp_true_no_pub_acl():
|
|
|
|
"""
|
|
|
|
Check TopicAccessControlListPlugin returns true if action=publish and no publish-acl given.
|
|
|
|
(This is for backward-compatibility with existing installations.)
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
2021-06-06 07:43:02 +00:00
|
|
|
context.config = {"topic-check": {"enabled": True}}
|
2021-06-06 07:42:06 +00:00
|
|
|
|
|
|
|
session = Session()
|
|
|
|
session.username = "user"
|
|
|
|
|
|
|
|
plugin = TopicAccessControlListPlugin(context)
|
2021-06-06 07:43:02 +00:00
|
|
|
authorised = await plugin.topic_filtering(
|
|
|
|
action="publish", session=session, topic="a/topic"
|
|
|
|
)
|
2021-06-06 07:42:06 +00:00
|
|
|
assert authorised is True
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taclp_false_sub_no_topic():
|
|
|
|
"""
|
|
|
|
Check TopicAccessControlListPlugin returns false user there is no topic.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
|
|
|
context.config = {
|
2021-06-06 07:43:02 +00:00
|
|
|
"topic-check": {
|
|
|
|
"enabled": True,
|
|
|
|
"acl": {"anotheruser": ["allowed/topic", "another/allowed/topic/#"]},
|
|
|
|
}
|
2021-06-06 07:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
session = Session()
|
|
|
|
session.username = "user"
|
|
|
|
|
|
|
|
plugin = TopicAccessControlListPlugin(context)
|
2021-06-06 07:43:02 +00:00
|
|
|
authorised = await plugin.topic_filtering(
|
|
|
|
action="subscribe", session=session, topic=""
|
|
|
|
)
|
2021-06-06 07:42:06 +00:00
|
|
|
assert authorised is False
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taclp_false_sub_unknown_user():
|
|
|
|
"""
|
|
|
|
Check TopicAccessControlListPlugin returns false user is not listed in ACL.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
|
|
|
context.config = {
|
2021-06-06 07:43:02 +00:00
|
|
|
"topic-check": {
|
|
|
|
"enabled": True,
|
|
|
|
"acl": {"anotheruser": ["allowed/topic", "another/allowed/topic/#"]},
|
|
|
|
}
|
2021-06-06 07:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
session = Session()
|
|
|
|
session.username = "user"
|
|
|
|
|
|
|
|
plugin = TopicAccessControlListPlugin(context)
|
2021-06-06 07:43:02 +00:00
|
|
|
authorised = await plugin.topic_filtering(
|
|
|
|
action="subscribe", session=session, topic="allowed/topic"
|
|
|
|
)
|
2021-06-06 07:42:06 +00:00
|
|
|
assert authorised is False
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taclp_false_sub_no_permission():
|
|
|
|
"""
|
|
|
|
Check TopicAccessControlListPlugin returns false if "acl" does not list allowed topic.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
|
|
|
context.config = {
|
2021-06-06 07:43:02 +00:00
|
|
|
"topic-check": {
|
|
|
|
"enabled": True,
|
|
|
|
"acl": {"user": ["allowed/topic", "another/allowed/topic/#"]},
|
|
|
|
}
|
2021-06-06 07:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
session = Session()
|
|
|
|
session.username = "user"
|
|
|
|
|
|
|
|
plugin = TopicAccessControlListPlugin(context)
|
2021-06-06 07:43:02 +00:00
|
|
|
authorised = await plugin.topic_filtering(
|
|
|
|
action="subscribe", session=session, topic="forbidden/topic"
|
|
|
|
)
|
2021-06-06 07:42:06 +00:00
|
|
|
assert authorised is False
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taclp_true_sub_permission():
|
|
|
|
"""
|
|
|
|
Check TopicAccessControlListPlugin returns true if "acl" lists allowed topic.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
|
|
|
context.config = {
|
2021-06-06 07:43:02 +00:00
|
|
|
"topic-check": {
|
|
|
|
"enabled": True,
|
|
|
|
"acl": {"user": ["allowed/topic", "another/allowed/topic/#"]},
|
|
|
|
}
|
2021-06-06 07:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
session = Session()
|
|
|
|
session.username = "user"
|
|
|
|
|
|
|
|
plugin = TopicAccessControlListPlugin(context)
|
2021-06-06 07:43:02 +00:00
|
|
|
authorised = await plugin.topic_filtering(
|
|
|
|
action="subscribe", session=session, topic="allowed/topic"
|
|
|
|
)
|
2021-06-06 07:42:06 +00:00
|
|
|
assert authorised is True
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taclp_true_pub_permission():
|
|
|
|
"""
|
|
|
|
Check TopicAccessControlListPlugin returns true if "publish-acl" lists allowed topic for publish action.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
|
|
|
context.config = {
|
2021-06-06 07:43:02 +00:00
|
|
|
"topic-check": {
|
|
|
|
"enabled": True,
|
|
|
|
"publish-acl": {"user": ["allowed/topic", "another/allowed/topic/#"]},
|
|
|
|
}
|
2021-06-06 07:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
session = Session()
|
|
|
|
session.username = "user"
|
|
|
|
|
|
|
|
plugin = TopicAccessControlListPlugin(context)
|
2021-06-06 07:43:02 +00:00
|
|
|
authorised = await plugin.topic_filtering(
|
|
|
|
action="publish", session=session, topic="allowed/topic"
|
|
|
|
)
|
2021-06-06 07:42:06 +00:00
|
|
|
assert authorised is True
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_taclp_true_anon_sub_permission():
|
|
|
|
"""
|
|
|
|
Check TopicAccessControlListPlugin handles anonymous users.
|
|
|
|
"""
|
|
|
|
context = BaseContext()
|
|
|
|
context.logger = DummyLogger()
|
|
|
|
context.config = {
|
2021-06-06 07:43:02 +00:00
|
|
|
"topic-check": {
|
|
|
|
"enabled": True,
|
|
|
|
"acl": {"anonymous": ["allowed/topic", "another/allowed/topic/#"]},
|
|
|
|
}
|
2021-06-06 07:42:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
session = Session()
|
|
|
|
session.username = None
|
|
|
|
|
|
|
|
plugin = TopicAccessControlListPlugin(context)
|
2021-06-06 07:43:02 +00:00
|
|
|
authorised = await plugin.topic_filtering(
|
|
|
|
action="subscribe", session=session, topic="allowed/topic"
|
|
|
|
)
|
2021-06-06 07:42:06 +00:00
|
|
|
assert authorised is True
|