def test_single_level_match(self): sub_ereg = MQTTUtils.convert_to_ereg("foo/+/bar") self.assertIsNotNone(re.match(sub_ereg, 'foo/buzz/bar')) self.assertIsNotNone(re.match(sub_ereg, 'foo//bar')) self.assertIsNone(re.match(sub_ereg, 'foo/bar')) self.assertIsNone(re.match(sub_ereg, 'foo/bar/')) self.assertIsNone(re.match(sub_ereg, '/foo/bar')) self.assertIsNone(re.match(sub_ereg, 'foo/one/two/bar')) self.assertIsNone(re.match(sub_ereg, 'foo/one/bar/')) self.assertIsNone(re.match(sub_ereg, '/foo/one/bar')) self.assertIsNone(re.match(sub_ereg, 'foo/+/bar')) ereg = MQTTUtils.convert_to_ereg('foo/bar/+') self.assertIsNotNone(re.match(ereg, 'foo/bar/buzz')) self.assertIsNotNone(re.match(ereg, 'foo/bar/')) self.assertIsNone(re.match(ereg, 'foo/bar/+')) self.assertIsNone(re.match(ereg, 'foo/bar/#')) self.assertIsNone(re.match(ereg, 'foo/bar/+/')) self.assertIsNone(re.match(ereg, 'foo/bar/+/#')) ereg = MQTTUtils.convert_to_ereg('+/foo/bar') self.assertIsNotNone(re.match(ereg, 'buzz/foo/bar')) self.assertIsNotNone(re.match(ereg, '/foo/bar')) self.assertIsNone(re.match(ereg, 'foo/bar')) self.assertIsNone(re.match(ereg, '//foo/bar'))
def _clear_authorization_entry(cls, ts, allow_wildcards=False): """ Validates an authorization entry :param ts: authorization entry: an asterisk string `'*'` meaning fully authorized, or a list of strings (topics, wildcards allowed) :type ts: str | list[str] :return: """ if ts == cls.ALL: return ts elif isinstance(ts, list): rs = [] for t in ts: assert isinstance(t, str) ereg = MQTTUtils.convert_to_ereg( t, allow_wildcards=allow_wildcards) assert isinstance(ereg, str) rs.append((t, re.compile(ereg))) return rs else: raise ValueError('authorization has unexpected format')
def test_single_level_mask(self): single_level = MQTTUtils.convert_to_ereg('foo/+/bar', allow_wildcards=True) self.assertIsMatch(single_level, 'foo/+/bar') self.assertIsMatch(single_level, 'foo/buzz/bar') self.assertIsMatch(single_level, 'foo//bar') self.assertIsNotMatch(single_level, 'foo/bar')
def _clear_authorization_entry(cls, ts, allow_wildcards=False): """ Validates an authorization entry :param ts: authorization entry: an asterisk string `'*'` meaning fully authorized, or a list of strings (topics, wildcards allowed) :type ts: str | list[str] :return: """ if ts == cls.ALL: return ts elif isinstance(ts, list): rs = [] for t in ts: assert isinstance(t, str) ereg = MQTTUtils.convert_to_ereg(t, allow_wildcards=allow_wildcards) assert isinstance(ereg, str) rs.append((t, re.compile(ereg))) return rs else: raise ValueError('authorization has unexpected format')
def _get_regex(self, mask): compiled = self._re_cache.get(mask, None) if not compiled: ereg = MQTTUtils.convert_to_ereg(mask) compiled = re.compile(ereg) self._re_cache[mask] = compiled return compiled
def test_substring_doesnt_match(self): super = "/foo/bar" sub = "/foo/ba" sub_ereg = MQTTUtils.convert_to_ereg(sub) self.assertTrue(re.match(sub_ereg, sub) is not None) self.assertTrue(re.match(sub_ereg, super) is None) ereg = MQTTUtils.convert_to_ereg('foo/bar') self.assertIsNotNone(re.match(ereg, 'foo/bar')) self.assertIsNone(re.match(ereg, 'foo/bar/')) self.assertIsNone(re.match(ereg, '/foo/bar')) self.assertIsNone(re.match(ereg, 'foo//bar')) self.assertIsNone(re.match(ereg, 'foo/bar/buzz')) self.assertIsNone(re.match(ereg, 'buzz/foo/bar')) self.assertIsNone(re.match(ereg, 'foo/buzz/bar'))
def test_multilevel_match(self): sub = "foo/bar/#" sub_ereg = MQTTUtils.convert_to_ereg(sub) self.assertIsNone(re.match(sub_ereg, 'foo/b')) self.assertIsNotNone(re.match(sub_ereg, 'foo/bar')) self.assertIsNotNone(re.match(sub_ereg, 'foo/bar/')) self.assertIsNotNone(re.match(sub_ereg, 'foo/bar/one')) self.assertIsNotNone(re.match(sub_ereg, 'foo/bar/one/')) self.assertIsNotNone(re.match(sub_ereg, 'foo/bar/one/two')) self.assertIsNotNone(re.match(sub_ereg, 'foo/bar/one/two/')) self.assertIsNone(re.match(sub_ereg, 'foo/bar/#')) self.assertIsNone(re.match(sub_ereg, 'foo/bar/+')) self.assertIsNone(re.match(sub_ereg, 'foo/bar/+/')) self.assertIsNone(re.match(sub_ereg, 'foo/bar/+/#'))
def test_multi_level_mask(self): """ tests for access_control.Authorization.is_subscription_allowed """ multi_level = MQTTUtils.convert_to_ereg('foo/bar/#', allow_wildcards=True) self.assertIsMatch(multi_level, 'foo/bar') self.assertIsMatch(multi_level, 'foo/bar/') self.assertIsMatch(multi_level, 'foo/bar//') self.assertIsMatch(multi_level, 'foo/bar/#') self.assertIsMatch(multi_level, 'foo/bar/+') self.assertIsMatch(multi_level, 'foo/bar/+/') self.assertIsMatch(multi_level, 'foo/bar/+/#') self.assertIsMatch(multi_level, 'foo/bar/buzz/+/#') self.assertIsMatch(multi_level, 'foo/bar/fuzz/+/buzz/#')
def test_mixed_masks(self): mixed = MQTTUtils.convert_to_ereg('foo/+/bar/#', allow_wildcards=True) self.assertIsMatch(mixed, 'foo/+/bar') self.assertIsMatch(mixed, 'foo/+/bar/#') self.assertIsMatch(mixed, 'foo/+/bar/buzz') self.assertIsMatch(mixed, 'foo/+/bar/buzz/') self.assertIsMatch(mixed, 'foo/+/bar/buzz/#') self.assertIsMatch(mixed, 'foo/+/bar/+/#') self.assertIsMatch(mixed, 'foo/+/bar/+/+/#') self.assertIsMatch(mixed, 'foo/buzz/bar') self.assertIsMatch(mixed, 'foo/buzz/bar/') self.assertIsMatch(mixed, 'foo/buzz/bar/+') self.assertIsMatch(mixed, 'foo/buzz/bar/#') self.assertIsMatch(mixed, 'foo/buzz/bar/+/') self.assertIsMatch(mixed, 'foo/buzz/bar/+/#') self.assertIsNotMatch(mixed, 'foo/#') self.assertIsNotMatch(mixed, 'foo/+/#') self.assertIsNotMatch(mixed, 'foo/+/+/#')
def subscribe(self, subscription_mask, qos): """ Subscribes the client to a topic or wildcarded mask at the informed QoS level. Calling this method also signalizes the server to enqueue the matching retained messages. When called for a (`subscripition_mask`, `qos`) pair for which the client has already a subscription it will silently ignore the command and return a suback. :param string subscription_mask: A MQTT valid topic or wildcarded mask; :param int qos: A valid QoS level (0, 1 or 2). :rtype: int :return: The granted QoS level (0, 1 or 2) or 0x80 for failed subscriptions. """ if qos not in [0, 1, 2]: self.logger.warn('client tried to subscribe with invalid qos %s' % qos) return 0x80 new_subscription = subscription_mask not in self.subscriptions or \ self.subscriptions.qos(subscription_mask) != qos if not self.authorization.is_subscription_allowed(subscription_mask): self.logger.warn("[uid: %s] is not allowed to subscribe on %s" % (self.uid, subscription_mask)) del self.subscriptions[subscription_mask] qos = 0x80 elif new_subscription: ereg = MQTTUtils.convert_to_ereg(subscription_mask) if ereg is not None: self.subscriptions.add(subscription_mask, qos, re.compile(ereg)) self.server.enqueue_retained_message(self, subscription_mask) else: qos = 0x80 if "#" in subscription_mask: print("SUBSCRIBING TO: {}".format(subscription_mask)) return qos
def test_mixed_match(self): ereg = MQTTUtils.convert_to_ereg('foo/+/bar/#') self.assertIsNotNone(re.match(ereg, 'foo/xyz/bar')) self.assertIsNotNone(re.match(ereg, 'foo/xyz/bar/')) self.assertIsNotNone(re.match(ereg, 'foo/xyz/bar/abc')) self.assertIsNotNone(re.match(ereg, 'foo//bar')) self.assertIsNotNone(re.match(ereg, 'foo//bar/')) self.assertIsNotNone(re.match(ereg, 'foo//bar/abc')) self.assertIsNone(re.match(ereg, 'foo/bar')) self.assertIsNone(re.match(ereg, 'foo/#')) self.assertIsNone(re.match(ereg, 'foo/+/bar')) self.assertIsNone(re.match(ereg, 'foo/+/bar/#')) self.assertIsNone(re.match(ereg, 'foo/+/bar/abc')) self.assertIsNone(re.match(ereg, 'foo/+/bar/abc/')) self.assertIsNone(re.match(ereg, 'foo/+/bar/abc/#')) self.assertIsNone(re.match(ereg, 'foo/+/bar/+/#')) self.assertIsNone(re.match(ereg, 'foo/xyz/bar/#')) self.assertIsNone(re.match(ereg, 'foo/xyz/bar/+')) self.assertIsNone(re.match(ereg, 'foo/xyz/bar/+/#'))
def add(self, mask, qos, pattern=None): self._re_cache[mask] = pattern or re.compile(MQTTUtils.convert_to_ereg(mask)) self._subscriptions[mask] = qos
def add(self, mask, qos, pattern=None): self._re_cache[mask] = pattern or re.compile( MQTTUtils.convert_to_ereg(mask)) self._subscriptions[mask] = qos