mirror of
https://github.com/eclipse-mosquitto/mosquitto.git
synced 2026-09-22 16:15:05 +08:00
Add %c and %u support to dynsec publish ACLs.
This commit is contained in:
@@ -24,6 +24,9 @@ Broker:
|
||||
using QoS 1 or 2 will receive the quota-exceeded reason code in the
|
||||
corresponding PUBACK/PUBREC.
|
||||
- MOSQ_EVT_TICK is now passed to plugins when `per_listener_settings` is true.
|
||||
- The dynamic security plugin now supports `%c` and `%u` patterns for
|
||||
substituting client id and username respectively, in publishClientSend and
|
||||
publishClientReceive ACLs.
|
||||
|
||||
Client library:
|
||||
- Add MOSQ_OPT_DISABLE_SOCKETPAIR to allow the disabling of the socketpair
|
||||
|
||||
@@ -38,10 +38,14 @@ static int acl_check_publish_c_recv(struct mosquitto_evt_acl_check *ed, struct d
|
||||
struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL;
|
||||
struct dynsec__acl *acl, *acl_tmp = NULL;
|
||||
bool result;
|
||||
const char *clientid, *username;
|
||||
|
||||
clientid = mosquitto_client_id(ed->client);
|
||||
username = mosquitto_client_username(ed->client);
|
||||
|
||||
HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){
|
||||
HASH_ITER(hh, rolelist->role->acls.publish_c_recv, acl, acl_tmp){
|
||||
mosquitto_topic_matches_sub(acl->topic, ed->topic, &result);
|
||||
mosquitto_topic_matches_sub_with_pattern(acl->topic, ed->topic, clientid, username, &result);
|
||||
if(result){
|
||||
if(acl->allow){
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
@@ -66,10 +70,14 @@ static int acl_check_publish_c_send(struct mosquitto_evt_acl_check *ed, struct d
|
||||
struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL;
|
||||
struct dynsec__acl *acl, *acl_tmp = NULL;
|
||||
bool result;
|
||||
const char *clientid, *username;
|
||||
|
||||
clientid = mosquitto_client_id(ed->client);
|
||||
username = mosquitto_client_username(ed->client);
|
||||
|
||||
HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){
|
||||
HASH_ITER(hh, rolelist->role->acls.publish_c_send, acl, acl_tmp){
|
||||
mosquitto_topic_matches_sub(acl->topic, ed->topic, &result);
|
||||
mosquitto_topic_matches_sub_with_pattern(acl->topic, ed->topic, clientid, username, &result);
|
||||
if(result){
|
||||
if(acl->allow){
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
|
||||
@@ -32,4 +32,5 @@ _mosquitto_set_username
|
||||
_mosquitto_strdup
|
||||
_mosquitto_sub_topic_check
|
||||
_mosquitto_topic_matches_sub
|
||||
_mosquitto_topic_matches_sub_with_pattern
|
||||
_mosquitto_validate_utf8
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
mosquitto_strdup;
|
||||
mosquitto_sub_topic_check;
|
||||
mosquitto_topic_matches_sub;
|
||||
mosquitto_topic_matches_sub_with_pattern;
|
||||
mosquitto_validate_utf8;
|
||||
};
|
||||
|
||||
@@ -44,6 +44,7 @@ add_client_group_role_command = {"commands":[
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "subscribePattern", "topic": "single-wildcard/+/topic", "allow": True },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "subscribePattern", "topic": "multilevel-wildcard/#", "allow": True },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "unsubscribeLiteral", "topic": "simple/topic", "allow": False },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "subscribePattern", "topic": "pattern/#", "allow": True },
|
||||
{ "command": "addGroupClient", "groupname": "mygroup", "username": "user_one" }
|
||||
]}
|
||||
|
||||
@@ -53,6 +54,7 @@ add_client_group_role_response = {'responses': [
|
||||
{'command': 'addGroupRole'},
|
||||
{'command': 'addRoleACL'}, {'command': 'addRoleACL'},
|
||||
{'command': 'addRoleACL'}, {'command': 'addRoleACL'},
|
||||
{'command': 'addRoleACL'},
|
||||
{'command': 'addGroupClient'}
|
||||
]}
|
||||
|
||||
@@ -61,11 +63,23 @@ add_publish_acl_command = {"commands":[
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "single-wildcard/deny/deny", "priority":10, "allow": False },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "single-wildcard/+/+", "allow": True },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "multilevel-wildcard/topic/#", "allow": True },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "pattern/%u/topic/#", "allow": True },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "pattern/%u/denied", "allow": False },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "pattern/%c/topic/#", "allow": True },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "pattern/%c/denied", "allow": False },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientReceive", "topic": "single-wildcard/bob/bob", "allow": False },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientReceive", "topic": "multilevel-wildcard/topic/topic/denied", "allow": False },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientReceive", "topic": "pattern/%u/topic/#", "allow": True },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientReceive", "topic": "pattern/%u/denied", "allow": False },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientReceive", "topic": "pattern/%c/topic/#", "allow": True },
|
||||
{ "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientReceive", "topic": "pattern/%c/denied", "allow": False },
|
||||
]}
|
||||
|
||||
add_publish_acl_response = {'responses': [
|
||||
{'command': 'addRoleACL'}, {'command': 'addRoleACL'},
|
||||
{'command': 'addRoleACL'}, {'command': 'addRoleACL'},
|
||||
{'command': 'addRoleACL'}, {'command': 'addRoleACL'},
|
||||
{'command': 'addRoleACL'}, {'command': 'addRoleACL'},
|
||||
{'command': 'addRoleACL'}, {'command': 'addRoleACL'},
|
||||
{'command': 'addRoleACL'}, {'command': 'addRoleACL'},
|
||||
{'command': 'addRoleACL'}, {'command': 'addRoleACL'}
|
||||
@@ -146,8 +160,30 @@ mid = 13
|
||||
unsubscribe_multi_packet = mosq_test.gen_unsubscribe(mid, "multilevel-wildcard/topic/topic/#", proto_ver=5)
|
||||
unsuback_multi_packet_success = mosq_test.gen_unsuback(mid, 0, proto_ver=5)
|
||||
|
||||
mid = 14
|
||||
subscribe_pattern_packet = mosq_test.gen_subscribe(mid, "pattern/#", 0, proto_ver=5)
|
||||
suback_pattern_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)
|
||||
|
||||
disconnect_kick_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_ADMINISTRATIVE_ACTION, proto_ver=5)
|
||||
|
||||
mid = 15
|
||||
publish_u_pattern_packet = mosq_test.gen_publish(mid=mid, topic="pattern/user_one/topic", qos=1, proto_ver=5, payload="test")
|
||||
puback_u_packet_success = mosq_test.gen_puback(mid=mid, proto_ver=5)
|
||||
publish_u_pattern_packet_r = mosq_test.gen_publish(topic="pattern/user_one/topic", qos=0, proto_ver=5, payload="test")
|
||||
|
||||
mid = 16
|
||||
publish_u_pattern_packet_denied = mosq_test.gen_publish(mid=mid, topic="pattern/user_one/denied", qos=1, proto_ver=5, payload="test")
|
||||
puback_u_packet_denied = mosq_test.gen_puback(mid=mid, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
|
||||
|
||||
mid = 17
|
||||
publish_c_pattern_packet = mosq_test.gen_publish(mid=mid, topic="pattern/user_one/topic", qos=1, proto_ver=5, payload="test")
|
||||
puback_c_packet_success = mosq_test.gen_puback(mid=mid, proto_ver=5)
|
||||
publish_c_pattern_packet_r = mosq_test.gen_publish(topic="pattern/user_one/topic", qos=0, proto_ver=5, payload="test")
|
||||
|
||||
mid = 18
|
||||
publish_c_pattern_packet_denied = mosq_test.gen_publish(mid=mid, topic="pattern/user_one/denied", qos=1, proto_ver=5, payload="test")
|
||||
puback_c_packet_denied = mosq_test.gen_puback(mid=mid, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
|
||||
|
||||
try:
|
||||
os.mkdir(str(port))
|
||||
shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port))
|
||||
@@ -230,6 +266,9 @@ try:
|
||||
# Subscribe to "multilevel-wildcard/topic/topic/allowed" - this is now allowed
|
||||
mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_success, "suback multi 3")
|
||||
|
||||
# Subscribe to "pattern/#" - allowed
|
||||
mosq_test.do_send_receive(csock, subscribe_pattern_packet, suback_pattern_packet, "suback")
|
||||
|
||||
# Publish to "simple/topic" - this is now allowed
|
||||
csock.send(publish_simple_packet)
|
||||
mosq_test.receive_unordered(csock, publish_simple_packet_r, puback_simple_packet_success, "puback simple 3 / publish r")
|
||||
@@ -258,6 +297,21 @@ try:
|
||||
# Multi unsubscribe should be allowed
|
||||
mosq_test.do_send_receive(csock, unsubscribe_multi_packet, unsuback_multi_packet_success, "unsuback multi 1")
|
||||
|
||||
# Publish to "pattern/user_one/topic" - this is allowed
|
||||
csock.send(publish_u_pattern_packet)
|
||||
mosq_test.receive_unordered(csock, publish_u_pattern_packet_r, puback_u_packet_success, "puback pattern 1 / publish r")
|
||||
|
||||
# Publish to "pattern/user_one/denied" - this is not allowed
|
||||
mosq_test.do_send_receive(csock, publish_u_pattern_packet_denied, puback_u_packet_denied, "puback pattern 2")
|
||||
|
||||
# Publish to "pattern/cid/topic" - this is allowed
|
||||
csock.send(publish_c_pattern_packet)
|
||||
mosq_test.receive_unordered(csock, publish_c_pattern_packet_r, puback_c_packet_success, "puback pattern 3 / publish r")
|
||||
|
||||
# Publish to "pattern/cid/denied" - this is not allowed
|
||||
mosq_test.do_send_receive(csock, publish_c_pattern_packet_denied, puback_c_packet_denied, "puback pattern 4")
|
||||
|
||||
|
||||
# Delete the role, client should be kicked
|
||||
command_check(sock, delete_role_command, delete_role_response)
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, usernam
|
||||
connack_packet = mosq_test.gen_connack(rc=0)
|
||||
|
||||
mid = 2
|
||||
subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/#", 1)
|
||||
subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1)
|
||||
suback_packet = mosq_test.gen_suback(mid, 1)
|
||||
|
||||
try:
|
||||
|
||||
@@ -130,7 +130,7 @@ connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, usernam
|
||||
connack_packet = mosq_test.gen_connack(rc=0)
|
||||
|
||||
mid = 2
|
||||
subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/#", 1)
|
||||
subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1)
|
||||
suback_packet = mosq_test.gen_suback(mid, 1)
|
||||
|
||||
try:
|
||||
|
||||
@@ -111,7 +111,7 @@ connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, usernam
|
||||
connack_packet = mosq_test.gen_connack(rc=0)
|
||||
|
||||
mid = 2
|
||||
subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/#", 1)
|
||||
subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1)
|
||||
suback_packet = mosq_test.gen_suback(mid, 1)
|
||||
|
||||
try:
|
||||
|
||||
@@ -176,7 +176,7 @@ connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, usernam
|
||||
connack_packet = mosq_test.gen_connack(rc=0)
|
||||
|
||||
mid = 2
|
||||
subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/#", 1)
|
||||
subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1)
|
||||
suback_packet = mosq_test.gen_suback(mid, 1)
|
||||
|
||||
try:
|
||||
|
||||
@@ -190,6 +190,21 @@ the ACL topic filter and the topic provided in the SUBSCRIBE or UNSUBSCRIBE
|
||||
message. This means that setting a `subscribePattern` ACL with topic filter `#`
|
||||
to deny would prevent matching devices from subscribing to any topic at all.
|
||||
|
||||
#### ACL pattern substitution
|
||||
|
||||
The `publishClientSend` and `publishClientReceive` ACL types can make use of
|
||||
pattern substitution. This means that the strings `%c` and `%u` will be
|
||||
replaced with the client id and username of the client being checked,
|
||||
respectively. The pattern strings must be the only item in that level of
|
||||
hierarchy, so the ACL `topic/%count` will not be considered as a pattern.
|
||||
|
||||
For example, with an ACL of `room/%c/temperature`, a client connecting with
|
||||
client id `kitchen` would be allowed to use the topic
|
||||
`room/kitchen/temperature` only.
|
||||
|
||||
If a client does not have a username, a pattern that includes `%u` will always
|
||||
fail to match against that client.
|
||||
|
||||
#### Text name
|
||||
|
||||
This is an optional text field to give a human friendly name to this role.
|
||||
|
||||
Reference in New Issue
Block a user