Merge pull request #160 from romancardenas/master

Access Control List and documentation for topic checking
pull/8/head
Nicolas 2019-01-07 22:56:53 +01:00 zatwierdzone przez GitHub
commit 2bb4bafb0c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
9 zmienionych plików z 136 dodań i 2 usunięć

Wyświetl plik

@ -77,6 +77,15 @@ The :class:`~hbmqtt.broker.Broker` ``__init__`` method accepts a ``config`` para
plugins: ['auth.anonymous'] #List of plugins to activate for authentication among all registered plugins
allow-anonymous: true / false
password-file: /some/passwd_file
topic-check:
enabled: true / false # Set to False if topic filtering is not needed
plugins: ['topic_acl'] #List of plugins to activate for topic filtering among all registered plugins
acl:
# username: [list of allowed topics]
username1: ['repositories/+/master', 'calendar/#', 'data/memes'] # List of topics on which client1 can publish and subscribe
username2: ...
anonymous: [] # List of topics on which an anonymous client can publish and subscribe
The ``listeners`` section allows to define network listeners which must be started by the :class:`~hbmqtt.broker.Broker`. Several listeners can be setup. ``default`` subsection defines common attributes for all listeners. Each listener can have the following settings:
@ -92,5 +101,14 @@ The ``auth`` section setup authentication behaviour:
* ``allow-anonymous`` : used by the internal :class:`hbmqtt.plugins.authentication.AnonymousAuthPlugin` plugin. This parameter enables (``on``) or disable anonymous connection, ie. connection without username.
* ``password-file`` : used by the internal :class:`hbmqtt.plugins.authentication.FileAuthPlugin` plugin. This parameter gives to path of the password file to load for authenticating users.
The ``topic-check`` section setup access control policies for publishing and subscribing to topics:
* ``enabled``: set to true if you want to impose an access control policy. Otherwise, set it to false.
* ``plugins``: defines the list of activated plugins. Note the plugins must be defined in the ``hbmqtt.broker.plugins`` `entry point <https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins>`_.
* additional parameters: depending on the plugin used for access control, additional parameters should be added.
* In case of ``topic_acl`` plugin, the Access Control List (ACL) must be defined in the parameter ``acl``.
* For each username, a list with the allowed topics must be defined.
* If the client logs in anonymously, the ``anonymous`` entry within the ACL is used in order to grant/deny subscriptions.
.. [1] See `PyYAML <http://pyyaml.org/wiki/PyYAMLDocumentation>`_ for loading YAML files as Python dict.

Wyświetl plik

@ -35,3 +35,50 @@ class TopicTabooPlugin(BaseTopicPlugin):
else:
return False
return filter_result
class TopicAccessControlListPlugin(BaseTopicPlugin):
def __init__(self, context):
super().__init__(context)
@staticmethod
def topic_ac(topic_requested, topic_allowed):
req_split = topic_requested.split('/')
allowed_split = topic_allowed.split('/')
ret = True
for i in range(max(len(req_split), len(allowed_split))):
try:
a_aux = req_split[i]
b_aux = allowed_split[i]
except IndexError:
ret = False
break
if b_aux == '#':
break
elif (b_aux == '+') or (b_aux == a_aux):
continue
else:
ret = False
break
return ret
@asyncio.coroutine
def topic_filtering(self, *args, **kwargs):
filter_result = super().topic_filtering(*args, **kwargs)
if filter_result:
session = kwargs.get('session', None)
req_topic = kwargs.get('topic', None)
if req_topic:
username = session.username
if username is None:
username = 'anonymous'
allowed_topics = self.topic_config['acl'].get(username, None)
if allowed_topics:
for allowed_topic in allowed_topics:
if self.topic_ac(req_topic, allowed_topic):
return True
return False
else:
return False
else:
return False

Wyświetl plik

@ -0,0 +1,54 @@
import logging
import asyncio
import os
from hbmqtt.broker import Broker
logger = logging.getLogger(__name__)
config = {
'listeners': {
'default': {
'type': 'tcp',
'bind': '0.0.0.0:1883',
},
'ws-mqtt': {
'bind': '127.0.0.1:8080',
'type': 'ws',
'max_connections': 10,
},
},
'sys_interval': 10,
'auth': {
'allow-anonymous': True,
'password-file': os.path.join(os.path.dirname(os.path.realpath(__file__)), "passwd"),
'plugins': [
'auth_file', 'auth_anonymous'
]
},
'topic-check': {
'enabled': True,
'plugins': [
'topic_acl'
],
'acl': {
# username: [list of allowed topics]
'test': ['repositories/+/master', 'calendar/#', 'data/memes'],
'anonymous': []
}
}
}
broker = Broker(config)
@asyncio.coroutine
def test_coro():
yield from broker.start()
if __name__ == '__main__':
formatter = "[%(asctime)s] :: %(levelname)s :: %(name)s :: %(message)s"
logging.basicConfig(level=logging.INFO, format=formatter)
asyncio.get_event_loop().run_until_complete(test_coro())
asyncio.get_event_loop().run_forever()

Wyświetl plik

@ -24,7 +24,9 @@ config = {
'plugins': [
'auth_file', 'auth_anonymous'
]
},
'topic-check': {
'enabled': False
}
}

Wyświetl plik

@ -19,6 +19,9 @@ def test_coro():
yield from C.connect('mqtt://0.0.0.0:1883')
yield from C.publish('data/classified', b'TOP SECRET', qos=0x01)
yield from C.publish('data/memes', b'REAL FUN', qos=0x01)
yield from C.publish('repositories/hbmqtt/master', b'NEW STABLE RELEASE', qos=0x01)
yield from C.publish('repositories/hbmqtt/devel', b'THIS NEEDS TO BE CHECKED', qos=0x01)
yield from C.publish('calendar/hbmqtt/releases', b'NEW RELEASE', qos=0x01)
logger.info("messages published")
yield from C.disconnect()
except ConnectException as ce:

Wyświetl plik

@ -18,10 +18,14 @@ logger = logging.getLogger(__name__)
def uptime_coro():
C = MQTTClient()
yield from C.connect('mqtt://test:test@0.0.0.0:1883')
# yield from C.connect('mqtt://0.0.0.0:1883')
# Subscribe to '$SYS/broker/uptime' with QOS=1
yield from C.subscribe([
('data/memes', QOS_1), # Topic allowed
('data/classified', QOS_1), # Topic forbidden
('repositories/hbmqtt/master', QOS_1), # Topic allowed
('repositories/hbmqtt/devel', QOS_1), # Topic forbidden
('calendar/hbmqtt/releases', QOS_1), # Topic allowed
])
logger.info("Subscribed")
try:

Wyświetl plik

@ -40,6 +40,9 @@ default_config = {
'plugins': [
'auth_file', 'auth_anonymous'
]
},
'topic-check': {
'enabled': False
}
}

Wyświetl plik

@ -7,4 +7,6 @@ auth:
allow-anonymous: true
plugins:
- auth_file
- auth_anonymous
- auth_anonymous
topic-check:
enabled: False

Wyświetl plik

@ -48,6 +48,7 @@ setup(
'auth_anonymous = hbmqtt.plugins.authentication:AnonymousAuthPlugin',
'auth_file = hbmqtt.plugins.authentication:FileAuthPlugin',
'topic_taboo = hbmqtt.plugins.topic_checking:TopicTabooPlugin',
'topic_acl = hbmqtt.plugins.topic_checking:TopicAccessControlListPlugin',
'broker_sys = hbmqtt.plugins.sys.broker:BrokerSysPlugin',
],
'hbmqtt.client.plugins': [