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
|
||||
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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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': [
|
||||
'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.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:
|
|
@ -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:
|
|
@ -40,6 +40,9 @@ default_config = {
|
|||
'plugins': [
|
||||
'auth_file', 'auth_anonymous'
|
||||
]
|
||||
},
|
||||
'topic-check': {
|
||||
'enabled': False
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,4 +7,6 @@ auth:
|
|||
allow-anonymous: true
|
||||
plugins:
|
||||
- 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_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': [
|
||||
|
|
Ładowanie…
Reference in New Issue