kopia lustrzana https://github.com/Yakifo/amqtt
Merge pull request #160 from romancardenas/master
Access Control List and documentation for topic checkingpull/8/head
commit
2bb4bafb0c
|
@ -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
|
plugins: ['auth.anonymous'] #List of plugins to activate for authentication among all registered plugins
|
||||||
allow-anonymous: true / false
|
allow-anonymous: true / false
|
||||||
password-file: /some/passwd_file
|
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:
|
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.
|
* ``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.
|
* ``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.
|
.. [1] See `PyYAML <http://pyyaml.org/wiki/PyYAMLDocumentation>`_ for loading YAML files as Python dict.
|
||||||
|
|
|
@ -35,3 +35,50 @@ class TopicTabooPlugin(BaseTopicPlugin):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
return filter_result
|
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
|
||||||
|
|
|
@ -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()
|
|
@ -24,7 +24,9 @@ config = {
|
||||||
'plugins': [
|
'plugins': [
|
||||||
'auth_file', 'auth_anonymous'
|
'auth_file', 'auth_anonymous'
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
'topic-check': {
|
||||||
|
'enabled': False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,9 @@ def test_coro():
|
||||||
yield from C.connect('mqtt://0.0.0.0:1883')
|
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/classified', b'TOP SECRET', qos=0x01)
|
||||||
yield from C.publish('data/memes', b'REAL FUN', 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")
|
logger.info("messages published")
|
||||||
yield from C.disconnect()
|
yield from C.disconnect()
|
||||||
except ConnectException as ce:
|
except ConnectException as ce:
|
|
@ -18,10 +18,14 @@ logger = logging.getLogger(__name__)
|
||||||
def uptime_coro():
|
def uptime_coro():
|
||||||
C = MQTTClient()
|
C = MQTTClient()
|
||||||
yield from C.connect('mqtt://test:test@0.0.0.0:1883')
|
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
|
# Subscribe to '$SYS/broker/uptime' with QOS=1
|
||||||
yield from C.subscribe([
|
yield from C.subscribe([
|
||||||
('data/memes', QOS_1), # Topic allowed
|
('data/memes', QOS_1), # Topic allowed
|
||||||
('data/classified', QOS_1), # Topic forbidden
|
('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")
|
logger.info("Subscribed")
|
||||||
try:
|
try:
|
|
@ -40,6 +40,9 @@ default_config = {
|
||||||
'plugins': [
|
'plugins': [
|
||||||
'auth_file', 'auth_anonymous'
|
'auth_file', 'auth_anonymous'
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
'topic-check': {
|
||||||
|
'enabled': False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,4 +7,6 @@ auth:
|
||||||
allow-anonymous: true
|
allow-anonymous: true
|
||||||
plugins:
|
plugins:
|
||||||
- auth_file
|
- auth_file
|
||||||
- auth_anonymous
|
- auth_anonymous
|
||||||
|
topic-check:
|
||||||
|
enabled: False
|
1
setup.py
1
setup.py
|
@ -48,6 +48,7 @@ setup(
|
||||||
'auth_anonymous = hbmqtt.plugins.authentication:AnonymousAuthPlugin',
|
'auth_anonymous = hbmqtt.plugins.authentication:AnonymousAuthPlugin',
|
||||||
'auth_file = hbmqtt.plugins.authentication:FileAuthPlugin',
|
'auth_file = hbmqtt.plugins.authentication:FileAuthPlugin',
|
||||||
'topic_taboo = hbmqtt.plugins.topic_checking:TopicTabooPlugin',
|
'topic_taboo = hbmqtt.plugins.topic_checking:TopicTabooPlugin',
|
||||||
|
'topic_acl = hbmqtt.plugins.topic_checking:TopicAccessControlListPlugin',
|
||||||
'broker_sys = hbmqtt.plugins.sys.broker:BrokerSysPlugin',
|
'broker_sys = hbmqtt.plugins.sys.broker:BrokerSysPlugin',
|
||||||
],
|
],
|
||||||
'hbmqtt.client.plugins': [
|
'hbmqtt.client.plugins': [
|
||||||
|
|
Ładowanie…
Reference in New Issue