14-dynsec-anon-group.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/env python3
  2. # Test the anonymous group support by adding a group, setting the anon group, adding a role to the group and checking a subscription.
  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 true\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. get_anon_group_none_command = { "commands": [{
  24. "command": "getAnonymousGroup",
  25. "correlationData": "2" }]
  26. }
  27. get_anon_group_none_response = {'responses': [{'command': 'getAnonymousGroup',
  28. 'data': {'group': {'groupname': ''}},
  29. 'correlationData': '2'}]}
  30. create_group_set_anon_command = { "commands": [
  31. { "command": "createGroup", "groupname": "anon-clients", "correlationData": "3" },
  32. { "command": "setAnonymousGroup", "groupname": "anon-clients", "correlationData": "4" }
  33. ]
  34. }
  35. create_group_set_anon_response = {'responses': [
  36. {'command': 'createGroup', 'correlationData': '3'},
  37. {'command': 'setAnonymousGroup', 'correlationData': '4'},
  38. ]}
  39. get_anon_group_command = { "commands": [{
  40. "command": "getAnonymousGroup",
  41. "correlationData": "3" }]
  42. }
  43. get_anon_group_response = {'responses': [{'command': 'getAnonymousGroup',
  44. 'data': {'group': {'groupname': 'anon-clients'}},
  45. 'correlationData': '3'}]}
  46. create_role_apply_command = { "commands": [
  47. { "command": "createRole", "rolename": "anon", "correlationData": "4" },
  48. { "command": "addRoleACL", "rolename": "anon",
  49. "acltype": "subscribeLiteral", "topic": "anon/topic", "allow": True,
  50. "correlationData": "5" },
  51. { "command": "addGroupRole", "groupname": "anon-clients",
  52. "rolename": "anon", "correlationData": "6"}
  53. ]
  54. }
  55. create_role_apply_response = {'responses': [
  56. {'command': 'createRole', 'correlationData': '4'},
  57. {'command': 'addRoleACL', 'correlationData': '5'},
  58. {'command': 'addGroupRole', 'correlationData': '6'}
  59. ]}
  60. rc = 1
  61. keepalive = 10
  62. # Admin
  63. connect_packet_admin = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin")
  64. connack_packet_admin = mosq_test.gen_connack(rc=0)
  65. mid = 1
  66. subscribe_packet_admin = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1)
  67. suback_packet_admin = mosq_test.gen_suback(mid, 1)
  68. # Client
  69. connect_packet = mosq_test.gen_connect("cid", keepalive=keepalive, proto_ver=5)
  70. connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)
  71. mid = 1
  72. subscribe_packet = mosq_test.gen_subscribe(mid, "anon/topic", qos=1, proto_ver=5)
  73. suback_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5)
  74. suback_packet_success = mosq_test.gen_suback(mid, 1, proto_ver=5)
  75. disconnect_packet_kick = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_ADMINISTRATIVE_ACTION, proto_ver=5)
  76. try:
  77. os.mkdir(str(port))
  78. shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port))
  79. except FileExistsError:
  80. pass
  81. broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
  82. try:
  83. sock = mosq_test.do_client_connect(connect_packet_admin, connack_packet_admin, timeout=5, port=port)
  84. mosq_test.do_send_receive(sock, subscribe_packet_admin, suback_packet_admin, "suback admin")
  85. # Add client
  86. command_check(sock, get_anon_group_none_command, get_anon_group_none_response)
  87. # Client is anon, there is no anon group, so subscribe should fail
  88. csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)
  89. mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_fail, "suback 1")
  90. # Add group, and set to anon
  91. command_check(sock, create_group_set_anon_command, create_group_set_anon_response)
  92. command_check(sock, get_anon_group_command, get_anon_group_response)
  93. # Anon group is changed, so we are kicked
  94. mosq_test.expect_packet(csock, "disconnect 1", disconnect_packet_kick)
  95. csock.close()
  96. # Reconnect, subscribe should still fail
  97. csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)
  98. mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_fail, "suback 2")
  99. # Add role with subscribe ACL, and apply to anon group
  100. command_check(sock, create_role_apply_command, create_role_apply_response)
  101. # Anon group is changed, so we are kicked
  102. mosq_test.expect_packet(csock, "disconnect 2", disconnect_packet_kick)
  103. csock.close()
  104. # Reconnect, subscribe should now succeed
  105. csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)
  106. mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_success, "suback 3")
  107. rc = 0
  108. sock.close()
  109. except mosq_test.TestError:
  110. pass
  111. finally:
  112. os.remove(conf_file)
  113. try:
  114. os.remove(f"{port}/dynamic-security.json")
  115. except FileNotFoundError:
  116. pass
  117. os.rmdir(f"{port}")
  118. broker.terminate()
  119. broker.wait()
  120. (stdo, stde) = broker.communicate()
  121. if rc:
  122. print(stde.decode('utf-8'))
  123. exit(rc)