14-dynsec-acl.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. #!/usr/bin/env python3
  2. # Test ACL for allow/deny. This does not consider ACL priority and the ACLs do not overlap.
  3. from mosq_test_helper import *
  4. import json
  5. import shutil
  6. def write_config(filename, port):
  7. with open(filename, 'w') as f:
  8. f.write("listener %d\n" % (port))
  9. f.write("allow_anonymous false\n")
  10. f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n")
  11. f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port))
  12. def command_check(sock, command_payload, expected_response):
  13. command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload))
  14. sock.send(command_packet)
  15. response = json.loads(mosq_test.read_publish(sock))
  16. if response != expected_response:
  17. print(expected_response)
  18. print(response)
  19. raise ValueError(response)
  20. port = mosq_test.get_port()
  21. conf_file = os.path.basename(__file__).replace('.py', '.conf')
  22. write_config(conf_file, port)
  23. add_client_command_with_id = { "commands": [{
  24. "command": "createClient", "username": "user_one",
  25. "password": "password", "clientid": "cid",
  26. "correlationData": "2" }]
  27. }
  28. add_client_response_with_id = {'responses': [{'command': 'createClient', 'correlationData': '2'}]}
  29. add_client_group_role_command = {"commands":[
  30. { "command": "createGroup", "groupname": "mygroup" },
  31. { "command": "createRole", "rolename": "myrole" },
  32. { "command": "addGroupRole", "groupname": "mygroup", "rolename": "myrole" },
  33. { "command": "addRoleACL", "rolename": "myrole", "acltype": "subscribeLiteral", "topic": "simple/topic", "allow": True },
  34. { "command": "addRoleACL", "rolename": "myrole", "acltype": "subscribePattern", "topic": "single-wildcard/+/topic", "allow": True },
  35. { "command": "addRoleACL", "rolename": "myrole", "acltype": "subscribePattern", "topic": "multilevel-wildcard/#", "allow": True },
  36. { "command": "addRoleACL", "rolename": "myrole", "acltype": "unsubscribeLiteral", "topic": "simple/topic", "allow": False },
  37. { "command": "addGroupClient", "groupname": "mygroup", "username": "user_one" }
  38. ]}
  39. add_client_group_role_response = {'responses': [
  40. {'command': 'createGroup'},
  41. {'command': 'createRole'},
  42. {'command': 'addGroupRole'},
  43. {'command': 'addRoleACL'}, {'command': 'addRoleACL'},
  44. {'command': 'addRoleACL'}, {'command': 'addRoleACL'},
  45. {'command': 'addGroupClient'}
  46. ]}
  47. add_publish_acl_command = {"commands":[
  48. { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "simple/topic", "allow": True },
  49. { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "single-wildcard/deny/deny", "priority":10, "allow": False },
  50. { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "single-wildcard/+/+", "allow": True },
  51. { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "multilevel-wildcard/topic/#", "allow": True },
  52. { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientReceive", "topic": "single-wildcard/bob/bob", "allow": False },
  53. { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientReceive", "topic": "multilevel-wildcard/topic/topic/denied", "allow": False },
  54. ]}
  55. add_publish_acl_response = {'responses': [
  56. {'command': 'addRoleACL'}, {'command': 'addRoleACL'},
  57. {'command': 'addRoleACL'}, {'command': 'addRoleACL'},
  58. {'command': 'addRoleACL'}, {'command': 'addRoleACL'}
  59. ]}
  60. delete_role_command = {"commands":[
  61. { "command": "deleteRole", "rolename": "myrole"}
  62. ]}
  63. delete_role_response = {'responses': [{'command': 'deleteRole'}]}
  64. rc = 1
  65. keepalive = 10
  66. connect_packet_admin = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin")
  67. connack_packet_admin = mosq_test.gen_connack(rc=0)
  68. mid = 2
  69. subscribe_packet_admin = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1)
  70. suback_packet_admin = mosq_test.gen_suback(mid, 1)
  71. # Success
  72. connect_packet_with_id1 = mosq_test.gen_connect("cid", keepalive=keepalive, username="user_one", password="password", proto_ver=5)
  73. connack_packet_with_id1 = mosq_test.gen_connack(rc=0, proto_ver=5)
  74. mid = 4
  75. subscribe_simple_packet = mosq_test.gen_subscribe(mid, "simple/topic", 0, proto_ver=5)
  76. suback_simple_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5)
  77. suback_simple_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
  78. mid = 5
  79. subscribe_single_packet = mosq_test.gen_subscribe(mid, "single-wildcard/bob/topic", 0, proto_ver=5)
  80. suback_single_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5)
  81. suback_single_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
  82. mid = 6
  83. subscribe_multi_packet = mosq_test.gen_subscribe(mid, "multilevel-wildcard/topic/topic/#", 0, proto_ver=5)
  84. suback_multi_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5)
  85. suback_multi_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
  86. mid = 7
  87. publish_simple_packet = mosq_test.gen_publish(mid=mid, topic="simple/topic", qos=1, payload="message", proto_ver=5)
  88. puback_simple_packet_success = mosq_test.gen_puback(mid, proto_ver=5)
  89. puback_simple_packet_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
  90. publish_simple_packet_r = mosq_test.gen_publish(topic="simple/topic", qos=0, payload="message", proto_ver=5)
  91. # This message is in single-wildcard/+/+ so could be allowed, but the single-wildcard/deny/deny with higher priority should override
  92. mid = 9
  93. publish_single_packet_denied = mosq_test.gen_publish(mid=mid, topic="single-wildcard/deny/deny", qos=1, payload="message", proto_ver=5)
  94. puback_single_packet_denied_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
  95. mid = 8
  96. publish_single_packet = mosq_test.gen_publish(mid=mid, topic="single-wildcard/bob/topic", qos=1, payload="message", proto_ver=5)
  97. puback_single_packet_success = mosq_test.gen_puback(mid, proto_ver=5)
  98. puback_single_packet_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
  99. publish_single_packet_r = mosq_test.gen_publish(topic="single-wildcard/bob/topic", qos=0, payload="message", proto_ver=5)
  100. mid = 9
  101. publish_multi_packet = mosq_test.gen_publish(mid=mid, topic="multilevel-wildcard/topic/topic/allowed", qos=1, payload="message", proto_ver=5)
  102. puback_multi_packet_success = mosq_test.gen_puback(mid, proto_ver=5)
  103. puback_multi_packet_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
  104. mid = 10
  105. publish_multi_denied_packet = mosq_test.gen_publish(mid=mid, topic="multilevel-wildcard/topic/topic/denied", qos=1, payload="message", proto_ver=5)
  106. puback_multi_denied_packet = mosq_test.gen_puback(mid, proto_ver=5)
  107. publish_multi_packet_r = mosq_test.gen_publish(topic="multilevel-wildcard/topic/topic/allowed", qos=0, payload="message", proto_ver=5)
  108. mid = 11
  109. unsubscribe_simple_packet = mosq_test.gen_unsubscribe(mid, "simple/topic", proto_ver=5)
  110. unsuback_simple_packet_fail = mosq_test.gen_unsuback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
  111. mid = 12
  112. unsubscribe_single_packet = mosq_test.gen_unsubscribe(mid, "single-wildcard/bob/topic", proto_ver=5)
  113. unsuback_single_packet_success = mosq_test.gen_unsuback(mid, 0, proto_ver=5)
  114. mid = 13
  115. unsubscribe_multi_packet = mosq_test.gen_unsubscribe(mid, "multilevel-wildcard/topic/topic/#", proto_ver=5)
  116. unsuback_multi_packet_success = mosq_test.gen_unsuback(mid, 0, proto_ver=5)
  117. disconnect_kick_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_ADMINISTRATIVE_ACTION, proto_ver=5)
  118. try:
  119. os.mkdir(str(port))
  120. shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port))
  121. except FileExistsError:
  122. pass
  123. broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
  124. try:
  125. sock = mosq_test.do_client_connect(connect_packet_admin, connack_packet_admin, timeout=5, port=port)
  126. mosq_test.do_send_receive(sock, subscribe_packet_admin, suback_packet_admin, "suback")
  127. # Add client
  128. command_check(sock, add_client_command_with_id, add_client_response_with_id)
  129. # Client with username, password, and client id
  130. csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error="connack 1")
  131. # Subscribe to "simple/topic" - not allowed
  132. mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_fail, "suback simple 1")
  133. # Subscribe to "single-wildcard/bob/topic" - not allowed
  134. mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_fail, "suback single 1")
  135. # Subscribe to "multilevel-wildcard/topic/topic/topic" - not allowed
  136. mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_fail, "suback multi 1")
  137. # Publish to "simple/topic" - not allowed
  138. mosq_test.do_send_receive(csock, publish_simple_packet, puback_simple_packet_fail, "puback simple 1")
  139. # Publish to "single-wildcard/bob/topic" - not allowed
  140. mosq_test.do_send_receive(csock, publish_single_packet, puback_single_packet_fail, "puback single 1")
  141. # Publish to "multilevel-wildcard/topic/topic/topic" - not allowed
  142. mosq_test.do_send_receive(csock, publish_multi_packet, puback_multi_packet_fail, "puback multi 1")
  143. # Create a group, add a role to the group, add the client to the group
  144. # Add some subscribe/unsubscribe ACLs - this will kick the client
  145. command_check(sock, add_client_group_role_command, add_client_group_role_response)
  146. mosq_test.expect_packet(csock, "disconnect kick 1", disconnect_kick_packet)
  147. csock.close()
  148. # Reconnect
  149. csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error="connack 2")
  150. # Subscribe to "simple/topic" - this is now allowed
  151. mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_success, "suback simple 2")
  152. # Subscribe to "single-wildcard/bob/topic" - this is now allowed
  153. mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_success, "suback single 2")
  154. # Subscribe to "multilevel-wildcard/topic/topic/topic" - this is now allowed
  155. mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_success, "suback multi 2")
  156. # Publish to "simple/topic" - not allowed
  157. mosq_test.do_send_receive(csock, publish_simple_packet, puback_simple_packet_fail, "puback 2")
  158. # Publish to "single-wildcard/bob/topic" - not allowed
  159. mosq_test.do_send_receive(csock, publish_single_packet, puback_single_packet_fail, "puback single 2")
  160. # Publish to "multilevel-wildcard/topic/topic/topic" - not allowed
  161. mosq_test.do_send_receive(csock, publish_multi_packet, puback_multi_packet_fail, "puback multi 2")
  162. # Add some publish ACLs - this will kick the client
  163. command_check(sock, add_publish_acl_command, add_publish_acl_response)
  164. mosq_test.expect_packet(csock, "disconnect kick 2", disconnect_kick_packet)
  165. csock.close()
  166. # Reconnect
  167. csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error="connack 3")
  168. # Subscribe to "simple/topic" - this is now allowed
  169. mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_success, "suback simple 3")
  170. # Subscribe to "single-wildcard/bob/topic" - this is now allowed
  171. mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_success, "suback single 3")
  172. # Subscribe to "multilevel-wildcard/topic/topic/allowed" - this is now allowed
  173. mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_success, "suback multi 3")
  174. # Publish to "simple/topic" - this is now allowed
  175. csock.send(publish_simple_packet)
  176. mosq_test.receive_unordered(csock, publish_simple_packet_r, puback_simple_packet_success, "puback simple 3 / publish r")
  177. # Publish to "single-wildcard/bob/topic" - this is now allowed
  178. csock.send(publish_single_packet)
  179. mosq_test.receive_unordered(csock, publish_single_packet_r, puback_single_packet_success, "puback single 3 / publish r")
  180. # Publish to "single-wildcard/deny/deny" - this is stillnot allowed
  181. mosq_test.do_send_receive(csock, publish_single_packet_denied, puback_single_packet_denied_fail, "puback single denied 1")
  182. # Publish to "multilevel-wildcard/topic/topic/allowed" - this is now allowed
  183. csock.send(publish_multi_packet)
  184. mosq_test.receive_unordered(csock, publish_multi_packet_r, puback_multi_packet_success, "puback multi 3 / publish r")
  185. # Publish to "multilevel-wildcard/topic/topic/denied" - receiving is denied by publishClientReceive
  186. mosq_test.do_send_receive(csock, publish_multi_denied_packet, puback_multi_denied_packet, "puback multi denied")
  187. mosq_test.do_ping(csock)
  188. # Simple unsubscribe should be denied
  189. mosq_test.do_send_receive(csock, unsubscribe_simple_packet, unsuback_simple_packet_fail, "unsuback simple 1")
  190. # Single unsubscribe should be allowed
  191. mosq_test.do_send_receive(csock, unsubscribe_single_packet, unsuback_single_packet_success, "unsuback single 1")
  192. # Multi unsubscribe should be allowed
  193. mosq_test.do_send_receive(csock, unsubscribe_multi_packet, unsuback_multi_packet_success, "unsuback multi 1")
  194. # Delete the role, client should be kicked
  195. command_check(sock, delete_role_command, delete_role_response)
  196. mosq_test.expect_packet(csock, "disconnect kick 3", disconnect_kick_packet)
  197. csock.close()
  198. # Reconnect - these should all be denied again.
  199. csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error="connack 4")
  200. # Subscribe to "simple/topic" - not allowed
  201. mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_fail, "suback simple 4")
  202. # Subscribe to "single-wildcard/bob/topic" - not allowed
  203. mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_fail, "suback single 4")
  204. # Subscribe to "multilevel-wildcard/topic/topic/topic" - not allowed
  205. mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_fail, "suback multi 4")
  206. # Publish to "simple/topic" - not allowed
  207. mosq_test.do_send_receive(csock, publish_simple_packet, puback_simple_packet_fail, "puback simple 4")
  208. # Publish to "single-wildcard/bob/topic" - not allowed
  209. mosq_test.do_send_receive(csock, publish_single_packet, puback_single_packet_fail, "puback single 4")
  210. # Publish to "multilevel-wildcard/topic/topic/topic" - not allowed
  211. mosq_test.do_send_receive(csock, publish_multi_packet, puback_multi_packet_fail, "puback multi 4")
  212. csock.close()
  213. rc = 0
  214. sock.close()
  215. except mosq_test.TestError:
  216. pass
  217. finally:
  218. os.remove(conf_file)
  219. try:
  220. os.remove(f"{port}/dynamic-security.json")
  221. except FileNotFoundError:
  222. pass
  223. os.rmdir(f"{port}")
  224. broker.terminate()
  225. broker.wait()
  226. (stdo, stde) = broker.communicate()
  227. if rc:
  228. print(stde.decode('utf-8'))
  229. exit(rc)
  230. publishClientSend
  231. publishClientReceive
  232. subscribeLiteral
  233. subscribePattern